From 32644ca5c62531a3136bb4e4bdbdf816aa3fb0cc Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Tue, 6 Apr 2010 22:51:43 +0000 Subject: reorganized 'pdogg' for template Makefile and easy packaging svn path=/trunk/externals/pdogg/; revision=13380 --- HISTORY | 128 ++++ LICENSE.txt | 481 ++++++++++++++ Makefile | 273 ++++++++ README.txt | 110 ++++ oggamp~-help.pd | 54 ++ oggamp~.c | 1427 +++++++++++++++++++++++++++++++++++++++++ oggamp~/HISTORY | 44 -- oggamp~/oggamp~-help.pd | 54 -- oggamp~/oggamp~.c | 1427 ----------------------------------------- oggamp~/oggamp~.libs | 1 - oggamp~/readme | 87 --- oggcast~-help.pd | 65 ++ oggcast~.c | 1466 +++++++++++++++++++++++++++++++++++++++++++ oggcast~/HISTORY | 83 --- oggcast~/oggcast~-help.pd | 65 -- oggcast~/oggcast~.c | 1466 ------------------------------------------- oggcast~/oggcast~.libs | 1 - oggcast~/readme | 116 ---- oggread~-help.pd | 44 ++ oggread~.c | 416 ++++++++++++ oggread~/oggread~-help.pd | 44 -- oggread~/oggread~.c | 416 ------------ oggread~/oggread~.libs | 1 - oggread~/readme | 75 --- oggwrite~-help.pd | 59 ++ oggwrite~.c | 751 ++++++++++++++++++++++ oggwrite~/oggwrite~-help.pd | 59 -- oggwrite~/oggwrite~.c | 751 ---------------------- oggwrite~/oggwrite~.libs | 1 - oggwrite~/readme | 110 ---- pdogg-meta.pd | 8 + readme | 79 --- 32 files changed, 5282 insertions(+), 4880 deletions(-) create mode 100644 HISTORY create mode 100644 LICENSE.txt create mode 100644 Makefile create mode 100644 README.txt create mode 100644 oggamp~-help.pd create mode 100644 oggamp~.c delete mode 100644 oggamp~/HISTORY delete mode 100644 oggamp~/oggamp~-help.pd delete mode 100644 oggamp~/oggamp~.c delete mode 100644 oggamp~/oggamp~.libs delete mode 100644 oggamp~/readme create mode 100644 oggcast~-help.pd create mode 100644 oggcast~.c delete mode 100644 oggcast~/HISTORY delete mode 100644 oggcast~/oggcast~-help.pd delete mode 100644 oggcast~/oggcast~.c delete mode 100644 oggcast~/oggcast~.libs delete mode 100644 oggcast~/readme create mode 100644 oggread~-help.pd create mode 100644 oggread~.c delete mode 100644 oggread~/oggread~-help.pd delete mode 100644 oggread~/oggread~.c delete mode 100644 oggread~/oggread~.libs delete mode 100644 oggread~/readme create mode 100644 oggwrite~-help.pd create mode 100644 oggwrite~.c delete mode 100644 oggwrite~/oggwrite~-help.pd delete mode 100644 oggwrite~/oggwrite~.c delete mode 100644 oggwrite~/oggwrite~.libs delete mode 100644 oggwrite~/readme create mode 100644 pdogg-meta.pd delete mode 100644 readme diff --git a/HISTORY b/HISTORY new file mode 100644 index 0000000..0b8f999 --- /dev/null +++ b/HISTORY @@ -0,0 +1,128 @@ +Version history of oggamp~ external for pure-data + +v 0.3 (2nd january 2004): +- removed the crappy resampling to get rid of audio clicks +- multichannel support + +v 0.2f (20st july 2002): +- recompiled with the final 1.0 release of Ogg Vorbis + +v 0.2e (21st june 2002 - stable release): +- added downsamling +- cleaned up code a bit +- added some more info-printout +- fixed some bugs to make it work correctly on Linux + thanks to Oliver Thuns at radiostudio.org +- now disconnects correctly at end-of-stream (when no + chained stream follows) +- KNOWN BUG: graphic buffer status display might cause ugly + printout of Tcl/Tk commands to console window on Linux + under some circumstances (e.g. in case server dies) + +v 0.2d (12th june 2002): +- added upsamling +- finally fixed the End-Of-Stream bug: it's now + possible to listen to a playlist with correct + update of stream information + +v 0.2c (10th june 2002): +- fixed some bugs, introduced some new ones... + +v 0.2a (11th mar. 2002): +- introduced child thread for connect: now pd + does no longer 'stop' audio; as a side effect it + is now possible to connect to an oggcast~ stream + from the same instance of pd +- threads now use pthreads libraray on Win to have + things compatible with UNIX +- fixed a small bug that made 'old' audio appear on + the beginning of 'new' one after reconnecting + +v 0.1c (19th feb. 2002): +- first (sort of) stable release + +************************************************************************ + +Version history of oggcast~ external for pure-data + +v 0.2k (29th Sept. 2003): +- fixed a bug in multichannel support +- fixed a memory leak in Icecast2 login + +v 0.2i (13th September 2003): +- updated login scheme to work with latest Icecast2 server + from CVS (tested on Windows and Debian Linux) +- changed handling of comment tags: it's no longer necessarry + to replace spaces with underscores! Just use spaces for spaces. +- fixed (no: avoided!) the bug that deleted underscores from + the Pd patch +- made Icecast2 login the default scheme + +v 0.2h (27th March 2003): +- added HTTP base auth login for (alpha) Icecast2 server: + message 'server 1' switches to Icecast2, 'server 0' back + top JRoar and PeerCast compatibility (which is the default) + +v 0.2g (3rd August 2002): +- finally fixed the bug that made oggcast~ crash after a + while. seems to be realted with output from child thread + using t_clocks but couldn't proove that + +v 0.2f (20st july 2002): +- recompiled with the final 1.0 release of Ogg Vorbis +- changed the URL to the new akustische-kunst.org domain + +v 0.2e (5th july 2002): +- added simple downsampling to stream at lower sample rates + +v 0.2d (21st june 2002 - stable release!): +- cleaned up code a bit +- now clean disconnect in case server dies or closes + socket + +v 0.2c (13th june 2002): +- fixed some small bugs +- buffer size now in kbytes per channel +- some more info-printout + +v 0.2b (12th june 2002): +- completetly rewritten multithreaded version, first + sort-of-stable release +- KNOWN BUG: eats too much CPU power + +v 0.1g (23rd feb. 2002, not for public use!): +- added multithreading functionality: now sending data + to server in a second thread +- now included the static ogg vorbis libraries - no dll's + needed under win any longer +- fixed a bug that sometimes made pd crash + +v 0.1f (11th feb. 2002): +- converted ringbuffer to simple buffer of fixed size + +v 0.1e (10th feb. 2002): +- now really fixed the bug that made pd die when server + disconnected + +v 0.1d (9th feb. 2002): +- fixed a bug in the "vorbis" setting that made on-the-run + changes impossible +- introduced a function to check writeability of the socket +- fixed the bug that crashed pd due to an access violation + in vorbis.dll when send() returned an error (more of a + fast workaround) +- corrected bitrate setting, now it really is _k_bps + + +v 0.1c (9th feb. 2002): +- added support for setting / changing the comment tags in + the ogg/vorbis header, spaces have to be replaced by '=' +- fixed a bug in oggcast_stream() that made Pd crash - now + it's vorbis.dll that makes Pd crash... ;-( + +v 0.1b (not released): +- added support for changing encoder settings when stream + is running (no need to disconnect), seems to be unstable + on linux (with JRoar) + +v 0.1: first public release diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..eb685a5 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,481 @@ + GNU LIBRARY GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the library GPL. It is + numbered 2 because it goes with version 2 of the ordinary GPL.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Library General Public License, applies to some +specially designated Free Software Foundation software, and to any +other libraries whose authors decide to use it. You can use it for +your libraries, 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 library, or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link a program with the library, you must provide +complete object files to the recipients so that they can relink them +with the library, after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + Our method of protecting your rights has two steps: (1) copyright +the library, and (2) offer you this license which gives you legal +permission to copy, distribute and/or modify the library. + + Also, for each distributor's protection, we want to make certain +that everyone understands that there is no warranty for this free +library. If the library is modified by someone else and passed on, we +want its recipients to know that what they have is not the original +version, 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 companies distributing free +software will individually obtain patent licenses, thus in effect +transforming the program into proprietary software. To prevent this, +we have made it clear that any patent must be licensed for everyone's +free use or not licensed at all. + + Most GNU software, including some libraries, is covered by the ordinary +GNU General Public License, which was designed for utility programs. This +license, the GNU Library General Public License, applies to certain +designated libraries. This license is quite different from the ordinary +one; be sure to read it in full, and don't assume that anything in it is +the same as in the ordinary license. + + The reason we have a separate public license for some libraries is that +they blur the distinction we usually make between modifying or adding to a +program and simply using it. Linking a program with a library, without +changing the library, is in some sense simply using the library, and is +analogous to running a utility program or application program. However, in +a textual and legal sense, the linked executable is a combined work, a +derivative of the original library, and the ordinary General Public License +treats it as such. + + Because of this blurred distinction, using the ordinary General +Public License for libraries did not effectively promote software +sharing, because most developers did not use the libraries. We +concluded that weaker conditions might promote sharing better. + + However, unrestricted linking of non-free programs would deprive the +users of those programs of all benefit from the free status of the +libraries themselves. This Library General Public License is intended to +permit developers of non-free programs to use free libraries, while +preserving your freedom as a user of such programs to change the free +libraries that are incorporated in them. (We have not seen how to achieve +this as regards changes in header files, but we have achieved it as regards +changes in the actual functions of the Library.) The hope is that this +will lead to faster development of free libraries. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, while the latter only +works together with the library. + + Note that it is possible for a library to be covered by the ordinary +General Public License rather than by this special one. + + GNU LIBRARY GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library which +contains a notice placed by the copyright holder or other authorized +party saying it may be distributed under the terms of this Library +General Public License (also called "this License"). Each licensee is +addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also compile or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + c) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + d) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the 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. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Library General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..a49c584 --- /dev/null +++ b/Makefile @@ -0,0 +1,273 @@ +## Pd library template version 1.0 +# For instructions on how to use this template, see: +# http://puredata.info/docs/developer/MakefileTemplate +LIBRARY_NAME = pdogg + +# add your .c source files to the SOURCES variable, help files will be +# included automatically +SOURCES = oggamp~.c oggcast~.c oggread~.c oggwrite~.c + +# For objects that only build on certain platforms, add those to the SOURCES +# line for the right platforms. +SOURCES_android = +SOURCES_cygwin = +SOURCES_macosx = +SOURCES_iphoneos = +SOURCES_linux = +SOURCES_windows = + +# list all pd objects (i.e. myobject.pd) files here, and their helpfiles will +# be included automatically +PDOBJECTS = + +# example patches and related files, in the 'examples' subfolder +EXAMPLES = + +# manuals and related files, in the 'manual' subfolder +MANUAL = + +# if you want to include any other files in the source and binary tarballs, +# list them here. This can be anything from header files, example patches, +# documentation, etc. README.txt and LICENSE.txt are required and therefore +# automatically included +EXTRA_DIST = pdogg.c makefile.msvc HISTORY + + + +#------------------------------------------------------------------------------# +# +# you shouldn't need to edit anything below here, if we did it right :) +# +#------------------------------------------------------------------------------# + +# get library version from meta file +LIBRARY_VERSION = $(shell sed -n 's|^\#X text [0-9][0-9]* [0-9][0-9]* VERSION \(.*\);|\1|p' $(LIBRARY_NAME)-meta.pd) + +# where Pd lives +PD_PATH = ../../pd +# where to install the library +prefix = /usr/local +libdir = $(prefix)/lib +pkglibdir = $(libdir)/pd-externals +objectsdir = $(pkglibdir) + + +INSTALL = install +INSTALL_FILE = $(INSTALL) -p -m 644 +INSTALL_DIR = $(INSTALL) -p -m 755 -d + +CFLAGS = -DPD -I$(PD_PATH)/src -Wall -W -g +LDFLAGS = +LIBS = -logg -lvorbis -lvorbisenc -lvorbisfile +ALLSOURCES := $(SOURCES) $(SOURCES_android) $(SOURCES_cygwin) $(SOURCES_macosx) \ + $(SOURCES_iphoneos) $(SOURCES_linux) $(SOURCES_windows) + +UNAME := $(shell uname -s) +ifeq ($(UNAME),Darwin) + CPU := $(shell uname -p) + ifeq ($(CPU),arm) # iPhone/iPod Touch + SOURCES += $(SOURCES_macosx) + EXTENSION = pd_darwin + OS = iphoneos + IPHONE_BASE=/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin + CC=$(IPHONE_BASE)/gcc + CPP=$(IPHONE_BASE)/cpp + CXX=$(IPHONE_BASE)/g++ + ISYSROOT = -isysroot /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS3.0.sdk + IPHONE_CFLAGS = -miphoneos-version-min=3.0 $(ISYSROOT) -arch armv6 + OPT_CFLAGS = -fast -funroll-loops -fomit-frame-pointer + CFLAGS := $(IPHONE_CFLAGS) $(OPT_CFLAGS) $(CFLAGS) \ + -I/Applications/Pd-extended.app/Contents/Resources/include + LDFLAGS += -arch armv6 -bundle -undefined dynamic_lookup $(ISYSROOT) + LIBS += -lc + STRIP = strip -x + DISTDIR=$(LIBRARY_NAME)-$(LIBRARY_VERSION) + DISTBINDIR=$(DISTDIR)-$(OS) + else # Mac OS X + SOURCES += $(SOURCES_macosx) + EXTENSION = pd_darwin + OS = macosx + OPT_CFLAGS = -ftree-vectorize -ftree-vectorizer-verbose=2 -fast + FAT_FLAGS = -arch i386 -arch ppc -mmacosx-version-min=10.4 + CFLAGS += $(FAT_FLAGS) -fPIC -I/sw/include \ + -I/Applications/Pd-extended.app/Contents/Resources/include + LDFLAGS += $(FAT_FLAGS) -bundle -undefined dynamic_lookup -L/sw/lib + # if the 'pd' binary exists, check the linking against it to aid with stripping + LDFLAGS += $(shell test -e $(PD_PATH)/bin/pd && echo -bundle_loader $(PD_PATH)/bin/pd) + LIBS += -lc + STRIP = strip -x + DISTDIR=$(LIBRARY_NAME)-$(LIBRARY_VERSION) + DISTBINDIR=$(DISTDIR)-$(OS) + endif +endif +ifeq ($(UNAME),Linux) + SOURCES += $(SOURCES_linux) + EXTENSION = pd_linux + OS = linux + OPT_CFLAGS = -O6 -funroll-loops -fomit-frame-pointer + CFLAGS += -fPIC + LDFLAGS += -Wl,--export-dynamic -shared -fPIC + LIBS += -lc + STRIP = strip --strip-unneeded -R .note -R .comment + DISTDIR=$(LIBRARY_NAME)-$(LIBRARY_VERSION) + DISTBINDIR=$(DISTDIR)-$(OS)-$(shell uname -m) +endif +ifeq (CYGWIN,$(findstring CYGWIN,$(UNAME))) + SOURCES += $(SOURCES_cygwin) + EXTENSION = dll + OS = cygwin + OPT_CFLAGS = -O6 -funroll-loops -fomit-frame-pointer + CFLAGS += + LDFLAGS += -Wl,--export-dynamic -shared -L$(PD_PATH)/src + LIBS += -lc -lpd + STRIP = strip --strip-unneeded -R .note -R .comment + DISTDIR=$(LIBRARY_NAME)-$(LIBRARY_VERSION) + DISTBINDIR=$(DISTDIR)-$(OS) +endif +ifeq (MINGW,$(findstring MINGW,$(UNAME))) + SOURCES += $(SOURCES_windows) + EXTENSION = dll + OS = windows + OPT_CFLAGS = -O3 -funroll-loops -fomit-frame-pointer -march=i686 -mtune=pentium4 + CFLAGS += -mms-bitfields + LDFLAGS += -s -shared -Wl,--enable-auto-import + LIBS += -L$(PD_PATH)/src -L$(PD_PATH)/bin -L$(PD_PATH)/obj -lpd -lwsock32 -lkernel32 -luser32 -lgdi32 + STRIP = strip --strip-unneeded -R .note -R .comment + DISTDIR=$(LIBRARY_NAME)-$(LIBRARY_VERSION) + DISTBINDIR=$(DISTDIR)-$(OS) +endif + +CFLAGS += $(OPT_CFLAGS) + + +.PHONY = install libdir_install single_install install-doc install-exec install-examples install-manual clean dist etags + +all: $(SOURCES:.c=.$(EXTENSION)) + +%.o: %.c + $(CC) $(CFLAGS) -o "$*.o" -c "$*.c" + +%.$(EXTENSION): %.o + $(CC) $(LDFLAGS) -o "$*.$(EXTENSION)" "$*.o" $(LIBS) + chmod a-x "$*.$(EXTENSION)" + +# this links everything into a single binary file +$(LIBRARY_NAME): $(SOURCES:.c=.o) $(LIBRARY_NAME).o + $(CC) $(LDFLAGS) -o $(LIBRARY_NAME).$(EXTENSION) $(SOURCES:.c=.o) $(LIBRARY_NAME).o $(LIBS) + chmod a-x $(LIBRARY_NAME).$(EXTENSION) + + +install: libdir_install + +# The meta and help files are explicitly installed to make sure they are +# actually there. Those files are not optional, then need to be there. +libdir_install: $(SOURCES:.c=.$(EXTENSION)) install-doc install-examples install-manual + $(INSTALL_DIR) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME) + $(INSTALL_FILE) $(LIBRARY_NAME)-meta.pd \ + $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME) + test -z "$(strip $(SOURCES))" || (\ + $(INSTALL_FILE) $(SOURCES:.c=.$(EXTENSION)) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME) && \ + $(STRIP) $(addprefix $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/,$(SOURCES:.c=.$(EXTENSION)))) + test -z "$(strip $(PDOBJECTS))" || \ + $(INSTALL_FILE) $(PDOBJECTS) \ + $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME) + +# install library linked as single binary +single_install: $(LIBRARY_NAME) install-doc install-exec + $(INSTALL_DIR) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME) + $(INSTALL_FILE) $(LIBRARY_NAME).$(EXTENSION) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME) + $(STRIP) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/$(LIBRARY_NAME).$(EXTENSION) + +install-doc: + $(INSTALL_DIR) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME) + test -z "$(strip $(SOURCES))" || \ + $(INSTALL_FILE) $(SOURCES:.c=-help.pd) \ + $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME) + test -z "$(strip $(PDOBJECTS))" || \ + $(INSTALL_FILE) $(PDOBJECTS:.pd=-help.pd) \ + $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME) + $(INSTALL_FILE) README.txt $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/README.txt + $(INSTALL_FILE) LICENSE.txt $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/LICENSE.txt + +install-examples: + test -z "$(strip $(EXAMPLES))" || \ + $(INSTALL_DIR) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/examples && \ + for file in $(EXAMPLES); do \ + $(INSTALL_FILE) examples/$$file $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/examples; \ + done + +install-manual: + test -z "$(strip $(MANUAL))" || \ + $(INSTALL_DIR) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/manual && \ + for file in $(MANUAL); do \ + $(INSTALL_FILE) manual/$$file $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/manual; \ + done + + +clean: + -rm -f -- $(SOURCES:.c=.o) + -rm -f -- $(SOURCES:.c=.$(EXTENSION)) + -rm -f -- $(LIBRARY_NAME).$(EXTENSION) + +distclean: clean + -rm -f -- $(DISTBINDIR).tar.gz + -rm -rf -- $(DISTBINDIR) + -rm -f -- $(DISTDIR).tar.gz + -rm -rf -- $(DISTDIR) + + +$(DISTBINDIR): + $(INSTALL_DIR) $(DISTBINDIR) + +libdir: all $(DISTBINDIR) + $(INSTALL_FILE) $(LIBRARY_NAME)-meta.pd $(DISTBINDIR) + $(INSTALL_FILE) $(SOURCES) $(DISTBINDIR) + $(INSTALL_FILE) $(SOURCES:.c=-help.pd) $(DISTBINDIR) + test -z "$(strip $(EXTRA_DIST))" || \ + $(INSTALL_FILE) $(EXTRA_DIST) $(DISTBINDIR) +# tar --exclude-vcs -czpf $(DISTBINDIR).tar.gz $(DISTBINDIR) + +$(DISTDIR): + $(INSTALL_DIR) $(DISTDIR) + +dist: $(DISTDIR) + $(INSTALL_FILE) Makefile $(DISTDIR) + $(INSTALL_FILE) README.txt $(DISTDIR) + $(INSTALL_FILE) LICENSE.txt $(DISTDIR) + $(INSTALL_FILE) $(LIBRARY_NAME)-meta.pd $(DISTDIR) + test -z "$(strip $(ALLSOURCES))" || \ + $(INSTALL_FILE) $(ALLSOURCES) $(DISTDIR) + test -z "$(strip $(ALLSOURCES))" || \ + $(INSTALL_FILE) $(ALLSOURCES:.c=-help.pd) $(DISTDIR) + test -z "$(strip $(PDOBJECTS))" || \ + $(INSTALL_FILE) $(PDOBJECTS) $(DISTDIR) + test -z "$(strip $(PDOBJECTS))" || \ + $(INSTALL_FILE) $(PDOBJECTS:.pd=-help.pd) $(DISTDIR) + test -z "$(strip $(EXTRA_DIST))" || \ + $(INSTALL_FILE) $(EXTRA_DIST) $(DISTDIR) + test -z "$(strip $(EXAMPLES))" || \ + $(INSTALL_DIR) $(DISTDIR)/examples && \ + for file in $(EXAMPLES); do \ + $(INSTALL_FILE) examples/$$file $(DISTDIR)/examples; \ + done + test -z "$(strip $(MANUAL))" || \ + $(INSTALL_DIR) $(DISTDIR)/manual && \ + for file in $(MANUAL); do \ + $(INSTALL_FILE) manual/$$file $(DISTDIR)/manual; \ + done + tar --exclude-vcs -czpf $(DISTDIR).tar.gz $(DISTDIR) + + +etags: + etags *.h $(SOURCES) ../../pd/src/*.[ch] /usr/include/*.h /usr/include/*/*.h + +showsetup: + @echo "PD_PATH: $(PD_PATH)" + @echo "objectsdir: $(objectsdir)" + @echo "LIBRARY_NAME: $(LIBRARY_NAME)" + @echo "LIBRARY_VERSION: $(LIBRARY_VERSION)" + @echo "SOURCES: $(SOURCES)" + @echo "PDOBJECTS: $(PDOBJECTS)" + @echo "ALLSOURCES: $(ALLSOURCES)" + @echo "UNAME: $(UNAME)" + @echo "CPU: $(CPU)" diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..b2c476d --- /dev/null +++ b/README.txt @@ -0,0 +1,110 @@ +Version 0.25 +copyright (c) 2002-2004 by Olaf Matthes + +pdogg~ is a collection of ogg/vorbis externals for pd (by Miller +Puckette). + +It includes: +- oggamp~ : streaming client +- oggcast~ : streamer (for Icecast2) +- oggread~ : reads files from disk +- oggwrite~ : writes files to disk + +To use pdogg start pd with '-lib path\to\pdogg' flag. +On Win32 systems Pd 0.35 test 17 or later is necessary to get it working! + +To compile pdogg~ you need the ogg/vorbis library from +http://www.vorbis.com/ and under win additionally Pthreads-win32 from +http://sources.redhat.com/pthreads-win32/. +You have to modify the makefile to make it point to the place where the +libraries can be found on your system. + + +This software is published under LGPL terms. + +This is software with ABSOLUTELY NO WARRANTY. +Use it at your OWN RISK. It's possible to damage e.g. hardware or your hearing +due to a bug or for other reasons. + +***************************************************************************** + +pdogg~ uses the ogg/vorbis library to encode audio data. +The latest version of ogg/vorbis can be found at http://www.vorbis.com/ + +The original version was found at: +http://www.akustische-kunst.de/puredata/ + +Please report any bugs to olaf.matthes@gmx.de! + +***************************************************************************** + +oggamp~ Usage: + +To run oggamp~ innormal mode, just use [oggamp~] or, to get the buffer status +displayed, use [oggamp~ 1]. + +Message "connect " connects to an IceCast2 server. +Note that no response about succesfull connection is send by the server. All +messages in the pd console window about connection status depend on the ability +to receive data from the server. +Use "connecturl " to use url-like server adresses (like http://host:post/ +stream.ogg). + +Known bugs and other things: +- pd halts for a moment when oggamp~ connects to the server. This results in a + short audio drop out of sound currently played back. +- resampling not jet supported +- playback does not stop on a buffer underrun +- oggamp~ disconnects at end of stream, i.e. it is not possible to play back + files streamed one after another without manual reconnect + +***************************************************************************** + +oggcast~ Usage: + +Use message "vbr " to set the vorbis +encoding parameters. Resampling is currently not supported, so 'samplerate' +should be the one pd is running at. 'channels' specyfies the number of channels +to stream. This can be set to 2 (default) or 1 which means mono stream taking +the leftmost audio input only. 'quality' can be a value between 0.0 and 1.0 +giving the quality of the stream. 0.4 (default) results in a stream that's +about 128kbps. + +Since Vorbis uses VBR encoding, bitrates vary depending on the type of audio +signal to be encoded. A pure sine [osc~] results in the smalest stream, com- +plex audio signals will increase this value significantly. To test the maximum +bitrate that might occur for a quality setting use noise~ as signal input. + +Use message "vorbis " to set encoding quality on the basis of bitrates. +When setting all three bitrate parameters to the same value one gets a +constant bitrate stream. Values are in kbps! + +Message "connect " connects to the IceCast2 server. +Note that no response about succesfull connection is send by the server. All +messages in the pd console window about connection status depend on the ability +to send data to the server. +The mountpoint should end with '.ogg' to indiocate to the player/client that +it is an ogg/vorbis encoded stream. + +Use "passwd " to set your password (default is 'letmein') and +"disconnect" to disconnect from the server. "print" prints out the current +vorbis encoder settings. + +To set the comment tags in the ogg/vorbis header (which can be displayed by +the receiving client) use message " ". Supported tags are: +TITLE, ARTIST, GENRE, PERFORMER, LOCATION, COPYRIGHT, CONTACT, DESCRIPTION and +DATE (which is automatically set to the date/time the broadcast started). To +get spaces use '=' or '_' instead. Note that under Win2k '=' sometimes get lost +from the patch after saving!!! + + +Listening to it: + +To listen to ogg/vorbis encoded livestreams many player need an extra plug-in. +Have a look at http://www.vorbis.com/ to find the appropiate plug-in for your +player. +To play back the stream just open lacation http://:/. + +Note that changing encoding parameters or header comments while oggcast~ is +streaming to the server might result in audible dropouts. diff --git a/oggamp~-help.pd b/oggamp~-help.pd new file mode 100644 index 0000000..9f30cb8 --- /dev/null +++ b/oggamp~-help.pd @@ -0,0 +1,54 @@ +#N canvas 176 105 678 455 10; +#X obj 38 420 dac~; +#X floatatom 100 419 5 0 0; +#X text 145 419 connection state; +#X msg 80 83 connect localhost puredata.ogg 8000; +#X msg 109 131 disconnect; +#X msg 35 60 connect ogg.bbc.co.uk radio1_low.ogg 8001; +#X msg 93 106 connect 141.53.196.149 puredata.ogg 8000; +#X msg 147 224 recover 0; +#X msg 163 251 recover 1; +#X msg 125 153 print; +#X obj 15 389 *~ 0; +#X obj 57 391 *~ 0; +#X floatatom 20 332 5 0 0; +#X obj 20 354 / 100; +#X text 57 7 oggamp~ version 0.2 - Ogg Vorbis sreaming client; +#X msg 139 191 recover -1; +#X text 218 191 resume (default): mute audio and refill buffer; +#X text 230 249 reconnect (disconnect and connect again); +#X text 138 303 CREATION ARGUMENTS:; +#X text 260 303 oggamp~ ; +#X text 167 324 - turn graphical buffer status display on +(1) or off (0 \, default); +#X text 167 351 - number of outlets (default = 2) \, mono +to stereo and stereo to mono conversion supported; +#X text 333 50 written by Olaf Matthes ; +#X text 333 63 get source at http://www.akustische-kunst.de/; +#X msg 530 406 \; pd dsp 1; +#X msg 596 406 \; pd dsp 0; +#X obj 530 380 loadbang; +#X msg 16 34 connect radio.jcraft.com test.ogg 8000; +#X obj 15 300 oggamp~ 1 2 256; +#X text 154 171 BEHAVIOUR ON BUFFER UNDERRUNS:; +#X text 166 378 - size of circular buffer in kbytes (default += 256k); +#X text 219 222 disconnect on buffer underrun; +#X connect 3 0 28 0; +#X connect 4 0 28 0; +#X connect 5 0 28 0; +#X connect 6 0 28 0; +#X connect 7 0 28 0; +#X connect 8 0 28 0; +#X connect 9 0 28 0; +#X connect 10 0 0 0; +#X connect 11 0 0 1; +#X connect 12 0 13 0; +#X connect 13 0 10 1; +#X connect 13 0 11 1; +#X connect 15 0 28 0; +#X connect 26 0 24 0; +#X connect 27 0 28 0; +#X connect 28 0 10 0; +#X connect 28 1 11 0; +#X connect 28 2 1 0; diff --git a/oggamp~.c b/oggamp~.c new file mode 100644 index 0000000..a9ff78b --- /dev/null +++ b/oggamp~.c @@ -0,0 +1,1427 @@ +/* ------------------------- oggamp~ ------------------------------------------ */ +/* */ +/* Tilde object to receive an Ogg Vorbis stream from an IceCast2 server. */ +/* Written by Olaf Matthes */ +/* Get source at http://www.akustische-kunst.de/puredata/ */ +/* */ +/* Graphical buffer status display written by Yves Degoyon. */ +/* */ +/* Thanks for hours (maybe days?) of beta testing to Oliver Thuns. */ +/* */ +/* This library is free software; you can redistribute it and/or */ +/* modify it under the terms of the GNU Lesser General Public */ +/* License as published by the Free Software Foundation; either */ +/* version 2 of the License, or (at your option) any later version. */ +/* */ +/* This library is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */ +/* Lesser General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU Lesser General Public */ +/* License along with this library; if not, write to the */ +/* Free Software Foundation, Inc., 59 Temple Place - Suite 330, */ +/* Boston, MA 02111-1307, USA. */ +/* */ +/* Based on PureData by Miller Puckette and others. */ +/* Uses the Ogg Vorbis decoding library which can be found at */ +/* http://www.vorbis.com/ */ +/* */ +/* ---------------------------------------------------------------------------- */ + + /* Pd includes */ +#include "m_pd.h" +#include "g_canvas.h" + /* Vorbis includes */ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef _WIN32 +#include /* for 'write' in pute-function only */ +#include +#include +#else +#include +#include +#include +#include +#include +#include +#include +#define SOCKET_ERROR -1 +#endif + +#ifdef _MSC_VER +#pragma warning( disable : 4244 ) +#pragma warning( disable : 4305 ) +#endif + +#ifdef WIN32 +#define sys_closesocket closesocket +#else +#define sys_closesocket close +#endif + + +/************************* oggamp~ object ******************************/ + +/* + Each instance of oggamp~ owns a "child" thread for doing the data + transfer. The parent thread signals the child each time: + (1) a connection wants opening or closing; + (2) we've eaten another 1/16 of the shared buffer (so that the + child thread should check if it's time to receive some more.) + The child signals the parent whenever a receive has completed. Signalling + is done by setting "conditions" and putting data in mutex-controlled common + areas. +*/ + + +#define REQUEST_NOTHING 0 +#define REQUEST_CONNECT 1 +#define REQUEST_CLOSE 2 +#define REQUEST_QUIT 3 +#define REQUEST_BUSY 4 +#define REQUEST_DATA 5 +#define REQUEST_RECONNECT 6 + +#define STATE_IDLE 0 +#define STATE_STARTUP 1 /* connecting and filling the buffer */ +#define STATE_STREAM 2 /* streaming aund audio output */ + +#define READSIZE 65536 /* _encoded_ data we request from buffer */ +#define READ 1024 /* amount of data we pass on to decoder */ +#define DEFBUFPERCHAN 262144 /* audio output buffer default: 256k */ +#define MINBUFSIZE (4 * READSIZE) +#define MAXBUFSIZE 16777216 /* arbitrary; just don't want to hang malloc */ +#define STRBUF_SIZE 1024 /* char received from server on startup */ +#define OBJWIDTH 68 /* width of buffer statis display */ +#define OBJHEIGHT 10 /* height of buffer statis display */ +#define MAXSTREAMCHANS 250 /* maximum number of channels */ + +static char *oggamp_version = "oggamp~: ogg/vorbis streaming client version 0.3, written by Olaf Matthes"; + +static t_class *oggamp_class; + +typedef struct _oggamp +{ + t_object x_obj; + t_canvas *x_canvas; /* remember canvas */ + t_outlet *x_connection; + t_clock *x_clock; + + t_float *x_buf; /* audio data buffer */ + t_int x_bufsize; /* buffer size in bytes */ + t_int x_noutlets; /* number of audio outlets */ + t_sample **x_outvec; /* audio vectors */ + t_int x_vecsize; /* vector size for transfers */ + t_int x_state; /* opened, running, or idle */ + + /* parameters to communicate with subthread */ + t_int x_requestcode; /* pending request from parent to I/O thread */ + t_int x_connecterror; /* slot for "errno" return */ + t_int x_streamchannels; /* number of channels in Ogg Vorbis bitstream */ + t_int x_streamrate; /* sample rate of stream */ + + /* buffer stuff */ + t_int x_fifosize; /* buffer size appropriately rounded down */ + t_int x_fifohead; /* index of next byte to get from file */ + t_int x_fifotail; /* index of next byte the ugen will read */ + t_int x_fifobytes; /* number of bytes available in buffer */ + t_int x_eof; /* true if ogg stream has ended */ + t_int x_sigcountdown; /* counter for signalling child for more data */ + t_int x_sigperiod; /* number of ticks per signal */ + t_int x_siginterval; /* number of times per buffer (depends on data rate) */ + + /* ogg/vorbis related stuff */ + ogg_stream_state x_os; /* take physical pages, weld into a logical stream of packets */ + ogg_sync_state x_oy; /* sync and verify incoming physical bitstream */ + ogg_page x_og; /* one Ogg bitstream page. Vorbis packets are inside */ + ogg_packet x_op; /* one raw packet of data for decode */ + vorbis_info x_vi; /* struct that stores all the static vorbis bitstream settings */ + vorbis_comment x_vc; /* struct that stores all the user comments */ + vorbis_dsp_state x_vd; /* central working state for the packet->PCM decoder */ + vorbis_block x_vb; /* local working space for packet->PCM decode */ + t_int x_eos; /* end of stream */ + char *x_buffer; /* buffer used to pass on to ogg/vorbis */ + + t_int x_vorbis; /* info about encoder status */ + t_int x_sync; /* indicates whether the decoder has been synced */ + t_float x_pages; /* number of pages that have been output to server */ + t_outlet *x_outpages; /* output to send them to */ + + + t_int x_connectstate; /* indicates the state of socket connection */ + t_int x_fd; /* the socket number */ + t_int x_graphic; /* indicates if we show a graphic bar */ + t_float x_resample; /* indicates if we need to resample signal (1 = no resampling) */ + t_int x_recover; /* indicate how to behave on buffer underruns */ + t_int x_disconnect; /* indicates that user want's to disconnect */ + t_int x_samplerate; /* Pd's sample rate */ + + /* server stuff */ + char *x_hostname; /* name or IP of host to connect to */ + char *x_mountpoint; /* mountpoint of ogg-bitstream */ + t_int x_port; /* port number on which the connection is made */ + + /* tread stuff */ + pthread_mutex_t x_mutex; + pthread_cond_t x_requestcondition; + pthread_cond_t x_answercondition; + pthread_t x_childthread; +} t_oggamp; + + /* check if we can read from socket */ +static int oggamp_check_for_data(t_int sock) +{ + + fd_set set; + struct timeval tv; + t_int ret; + + tv.tv_sec = 0; + tv.tv_usec = 20000; + FD_ZERO(&set); + FD_SET(sock, &set); + ret = select(sock + 1, &set, NULL, NULL, &tv); + if (ret > 0) + return 1; + return 0; +} + + /* receive 'size' bytes from server */ +static int oggamp_child_receive(int fd, char *buffer, int size) +{ + int ret = -1; + int i; + + ret = recv(fd, buffer, size, 0); + if(ret < 0) + { + post("oggamp~: receive error" ); + } + return ret; +} + + /* ogg/vorbis decoder sync init */ +static void oggamp_vorbis_sync_init(t_oggamp *x) +{ + ogg_sync_init(&(x->x_oy)); /* Now we can read pages */ + x->x_sync = 1; +} + + /* ogg/vorbis decoder setup */ +static int oggamp_vorbis_init(t_oggamp *x, int fd) +{ + int i; + int result; /* error return code */ + int bytes; /* number of bytes receive returned */ + char *buffer; /* buffer for undecoded ogg vorbis data */ + + if(!x->x_sync)oggamp_vorbis_sync_init(x); /* init the sync */ + + x->x_eos = 0; /* indicate beginning of new stream */ + + /* submit a 4k block to libvorbis' ogg layer */ + buffer = ogg_sync_buffer(&(x->x_oy),4096); + post("oggamp~: prebuffering..."); + bytes = oggamp_child_receive(fd, buffer, 4096); + ogg_sync_wrote(&(x->x_oy),bytes); + result = ogg_sync_pageout(&(x->x_oy),&(x->x_og)); + + /* we might need more data */ + if(result == -1) + { + post("reading more..."); + buffer = ogg_sync_buffer(&(x->x_oy),4096); + bytes = oggamp_child_receive(fd, buffer, 4096); + ogg_sync_wrote(&(x->x_oy),bytes); + result = ogg_sync_pageout(&(x->x_oy),&(x->x_og)); + } + /* Get the first page. */ + if(result != 1) + { + /* error case. Must not be Vorbis data */ + error("oggamp~: input does not appear to be an ogg bitstream (error %d)", result); + return -1; + } + + ogg_stream_init(&(x->x_os),ogg_page_serialno(&(x->x_og))); + + vorbis_info_init(&(x->x_vi)); + vorbis_comment_init(&(x->x_vc)); + if(ogg_stream_pagein(&(x->x_os),&(x->x_og))<0){ + /* error; stream version mismatch perhaps */ + error("oggamp~: error reading first page of ogg bitstream data"); + return -1; + } + + if(ogg_stream_packetout(&(x->x_os),&(x->x_op))!=1){ + /* no page? must not be vorbis */ + error("oggamp~: error reading initial header packet"); + return -1; + } + + if(vorbis_synthesis_headerin(&(x->x_vi),&(x->x_vc),&(x->x_op))<0){ + /* error case; not a vorbis header */ + error("oggamp~: this ogg bitstream does not contain Vorbis audio data"); + return -1; + } + + i=0; + while(i<2){ + while(i<2){ + result = ogg_sync_pageout(&(x->x_oy),&(x->x_og)); + if(result == 0)break; /* Need more data */ + /* Don't complain about missing or corrupt data yet. We'll + catch it at the packet output phase */ + if(result == 1) + { + ogg_stream_pagein(&(x->x_os),&(x->x_og)); /* we can ignore any errors here + as they'll also become apparent + at packetout */ + while(i<2){ + result = ogg_stream_packetout(&(x->x_os),&(x->x_op)); + if(result==0)break; + if(result<0){ + /* Uh oh; data at some point was corrupted or missing! + We can't tolerate that in a header. Die. */ + error("oggamp~: corrupt secondary header, exiting"); + return -1; + } + vorbis_synthesis_headerin(&(x->x_vi),&(x->x_vc),&(x->x_op)); + i++; + } + } + } + /* no harm in not checking before adding more */ + buffer = ogg_sync_buffer(&(x->x_oy),READ); + /* read in next 4k of data */ + bytes = oggamp_child_receive(fd, buffer, READ); + if(bytes==0 && i<2){ + error("oggamp~: end of stream before finding all Vorbis headers"); + return -1; + } + ogg_sync_wrote(&(x->x_oy),bytes); + } + + /* Throw the comments plus a few lines about the bitstream we're decoding */ + post("oggamp~: reading Ogg Vorbis header..."); + { + char **ptr = x->x_vc.user_comments; + while(*ptr){ + post(" %s",*ptr); + ++ptr; + } + post("oggamp~: bitstream is %d channels @ %ld Hz with %ldkbps", x->x_vi.channels, x->x_vi.rate, x->x_vi.bitrate_nominal / 1000); + x->x_streamchannels = x->x_vi.channels; + x->x_streamrate = x->x_vi.rate; + if(x->x_samplerate != x->x_streamrate) /* upsampling */ + { /* we would need to use upsampling */ + post("oggamp~: resampling from %ld Hz to %ld Hz not supported !", x->x_vi.rate, x->x_samplerate); + return (-1); + } + post("oggamp~: encoded by: %s", x->x_vc.vendor); + } + + /* OK, got and parsed all three headers. Initialize the Vorbis packet->PCM decoder. */ + vorbis_synthesis_init(&(x->x_vd),&(x->x_vi)); /* central decode state */ + vorbis_block_init(&(x->x_vd),&(x->x_vb)); /* local state */ + x->x_vorbis = 1; + return (1); +} + + /* clear the ogg/vorbis decoder */ +static void oggamp_vorbis_sync_clear(t_oggamp *x) +{ + /* OK, clean up the framer */ + ogg_sync_clear(&(x->x_oy)); + x->x_sync = 0; + post("oggamp~: decoder cleared"); +} + /* deinit the ogg/vorbis decoder */ +static void oggamp_vorbis_deinit(t_oggamp *x) +{ + if(x->x_vorbis) + { + x->x_vorbis = 0; + + /* clean up this logical bitstream; before exit we see if we're + followed by another [chained] */ + ogg_stream_clear(&(x->x_os)); + + /* ogg_page and ogg_packet structs always point to storage in + libvorbis. They're never freed or manipulated directly */ + + vorbis_block_clear(&(x->x_vb)); + vorbis_dsp_clear(&(x->x_vd)); + vorbis_comment_clear(&(x->x_vc)); + vorbis_info_clear(&(x->x_vi)); /* must be called last */ + + post("oggamp~: decoder deinitialised"); + /* only clear completely in case we're going to disconnect */ + /* !! must not be called when receiving chained streams !! */ + if(x->x_disconnect)oggamp_vorbis_sync_clear(x); + } +} + + /* decode ogg/vorbis and receive new data */ +static int oggamp_decode_input(t_oggamp *x, float *buf, int fifohead, int fifosize, int fd) +{ + int i, result; + + float **pcm; /* pointer to decoded float samples */ + char *buffer; /* buffer for ogg vorbis */ + int samples; /* number of samples returned by decoder at each block! */ + int n = 0; /* total number of samples returned by decoder at this call */ + int bytes; /* number of bytes submitted to decoder */ + int position = fifohead; + int streamchannels = x->x_streamchannels; + int channels = x->x_noutlets; + + + /* the rest is just a straight decode loop until end of stream */ + while(!x->x_eos) + { + result = ogg_sync_pageout(&(x->x_oy),&(x->x_og)); + if(result == 0)break; /* need more data */ + if(result < 0) + { /* missing or corrupt data at this page position */ + error("oggamp~: corrupt or missing data in bitstream, continuing..."); + } + else{ + ogg_stream_pagein(&(x->x_os),&(x->x_og)); /* can safely ignore errors at this point */ + while(1) + { + result = ogg_stream_packetout(&(x->x_os),&(x->x_op)); + + if(result==0)break; /* need more data */ + if(result<0) + { /* missing or corrupt data at this page position */ + /* no reason to complain; already complained above */ + }else + { + /* we have a packet. Decode it */ + if(vorbis_synthesis(&(x->x_vb),&(x->x_op))==0) /* test for success! */ + vorbis_synthesis_blockin(&(x->x_vd),&(x->x_vb)); + /* + + **pcm is a multichannel float vector. In stereo, for + example, pcm[0] is left, and pcm[1] is right. samples is + the size of each channel. Convert the float values + (-1.<=range<=1.) to whatever PCM format and write it out */ + + while((samples = vorbis_synthesis_pcmout(&(x->x_vd),&pcm))>0) + { + int j; + + /* copy into our output buffer */ + if(streamchannels >= channels) + { + for(j = 0; j < samples; j++) + { + for(i = 0; i < channels; i++) + { + buf[position + i] = pcm[i][j]; + } + position = (position + channels) % fifosize; + } + vorbis_synthesis_read(&(x->x_vd),samples); /* tell libvorbis how + many samples we + actually consumed */ + n += samples; /* sum up the samples we got from decoder */ + } + else + { + for(j = 0; j < samples; j++) + { + /* copy the channels we have */ + for(i = 0; i < streamchannels; i++) + { + buf[position + i] = pcm[i][j]; + } + /* fill rest of buffer with silence */ + for(i = streamchannels; i < channels; i++) + { + buf[position + i] = 0.; + } + position = (position + channels) % fifosize; + } + vorbis_synthesis_read(&(x->x_vd),samples); /* tell libvorbis how + many samples we + actually consumed */ + n += samples; /* sum up the samples we got from decoder */ + } + } + } + } + if(ogg_page_eos(&(x->x_og)))x->x_eos=1; + } + } + + /* read data from socket */ + if(!x->x_eos) /* read from input until end of stream */ + { + buffer = ogg_sync_buffer(&(x->x_oy), READ); + /* read next 4k of data out of buffer */ + bytes = oggamp_child_receive(fd, buffer, READ); + if(bytes < 0) + { + x->x_eos = 1; /* indicate end of stream */ + return (bytes); + } + ogg_sync_wrote(&(x->x_oy),bytes); + } + else /* we read through all the file... */ + { /* will have to reinit decoder for new file */ + post("oggamp~: end of stream detected"); + return (0); + } + return (n*x->x_vi.channels); +} + + /* connect to shoutcast server */ +static int oggamp_child_connect(char *hostname, char *mountpoint, t_int portno) +{ + struct sockaddr_in server; + struct hostent *hp; + + /* variables used for communication with server */ + char *sptr = NULL; + char request[STRBUF_SIZE]; /* string to be send to server */ + char *url; /* used for relocation */ + fd_set fdset; + struct timeval tv; + t_int sockfd; /* socket to server */ + t_int relocate, numrelocs = 0; + t_int i, ret, rest, nanswers=0; + char *cpoint = NULL; + t_int eof = 0; + + sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (sockfd < 0) + { + error("oggamp~: internal error while attempting to open socket"); + return (-1); + } + + /* connect socket using hostname provided in command line */ + server.sin_family = AF_INET; + hp = gethostbyname(hostname); + if (hp == 0) + { + post("oggamp~: bad host?"); + sys_closesocket(sockfd); + return (-1); + } + memcpy((char *)&server.sin_addr, (char *)hp->h_addr, hp->h_length); + + /* assign client port number */ + server.sin_port = htons((unsigned short)portno); + + /* try to connect. */ + post("oggamp~: connecting to http://%s:%d/%s", hostname, portno, mountpoint); + if (connect(sockfd, (struct sockaddr *) &server, sizeof (server)) < 0) + { + error("oggamp~: connection failed!\n"); + sys_closesocket(sockfd); + return (-1); + } + + /* sheck if we can read/write from/to the socket */ + FD_ZERO( &fdset); + FD_SET( sockfd, &fdset); + tv.tv_sec = 0; /* seconds */ + tv.tv_usec = 500; /* microseconds */ + + ret = select(sockfd + 1, &fdset, NULL, NULL, &tv); + if(ret != 0) + { + error("oggamp~: can not read from socket"); + sys_closesocket(sockfd); + return (-1); + } + + /* build up stuff we need to send to server */ + sprintf(request, "GET /%s HTTP/1.0 \r\nHost: %s\r\nUser-Agent: oggamp~ 0.2\r\nAccept: audio/x-ogg\r\n\r\n", + mountpoint, hostname); + + if ( send(sockfd, request, strlen(request), 0) < 0 ) /* say hello to server */ + { + post("oggamp~: could not contact server..."); + return (-1); + } + + /* read first line of response */ + i = 0; + while(i < STRBUF_SIZE - 1) + { + if(oggamp_check_for_data(sockfd)) + { + if (recv(sockfd, request + i, 1, 0) <= 0) + { + error("oggamp~: could not read from socket, quitting"); + sys_closesocket(sockfd); + return (-1); + } + if (request[i] == '\n') + break; + if (request[i] != '\r') + i++; + } + } + request[i] = '\0'; + + /* time to parse content of the response... */ + if(strstr(request, "HTTP/1.0 200 OK")) /* server is ready */ + { + post("oggamp~: IceCast2 server detected"); + while(!eof) /* read everything we can get */ + { + i = 0; + while(i < STRBUF_SIZE - 1) + { + if(oggamp_check_for_data(sockfd)) + { + if (recv(sockfd, request + i, 1, 0) <= 0) + { + error("oggamp~: could not read from socket, quitting"); + sys_closesocket(sockfd); + return (-1); + } + if(request[i] == '\n') /* leave at end of line */ + break; + if(request[i] == 0x0A) /* leave at end of line */ + break; + if(request[i] != '\r') /* go on until 'return' */ + i++; + } + } + request[i] = '\0'; /* make it a null terminated string */ + + if(sptr = strstr(request, "application/x-ogg")) + { /* check for content type */ + post("oggamp~: Ogg Vorbis stream found"); + } + if(sptr = strstr(request, "ice-name:")) + { /* display ice-name */ + post("oggamp~: \"%s\"", sptr + 10); + } + if(i == 0)eof = 1; /* we got last '\r\n' from server */ + } + } + else /* wrong server or wrong answer */ + { + post("oggamp~: unknown response from server"); + sys_closesocket(sockfd); + return (-1); + } + post("oggamp~: connected to http://%s:%d/%s", hp->h_name, portno, mountpoint); + + return (sockfd); +} + + +static void oggamp_child_dographics(t_oggamp *x) +{ + /* do graphics stuff :: create rectangle */ + if ( x->x_graphic && glist_isvisible( x->x_canvas ) ) + { + sys_vgui(".x%x.c create rectangle %d %d %d %d -fill lightblue -tags %xPBAR\n", + x->x_canvas, x->x_obj.te_xpix, x->x_obj.te_ypix-OBJHEIGHT-1, + x->x_obj.te_xpix + OBJWIDTH, x->x_obj.te_ypix - 1, x ); + } +} + +static void oggamp_child_updategraphics(t_oggamp *x) +{ + /* update buffer status display */ + if(x->x_graphic && glist_isvisible(x->x_canvas)) + { + /* update graphical read status */ + char color[32]; + + sys_vgui(".x%x.c delete rectangle %xSTATUS\n", x->x_canvas, x); + if(x->x_fifobytes < (x->x_fifosize / 8)) + { + strcpy(color, "red"); + } + else + { + strcpy(color, "lightgreen"); + } + sys_vgui(".x%x.c create rectangle %d %d %d %d -fill %s -tags %xSTATUS\n", + x->x_canvas, x->x_obj.te_xpix, x->x_obj.te_ypix-OBJHEIGHT-1, + x->x_obj.te_xpix+((x->x_fifobytes*OBJWIDTH)/x->x_fifosize), + x->x_obj.te_ypix - 1, color, x); + } +} +static void oggamp_child_delgraphics(t_oggamp *x) +{ + if(x->x_graphic) /* delete graphics */ + { + sys_vgui(".x%x.c delete rectangle %xPBAR\n", x->x_canvas, x ); + sys_vgui(".x%x.c delete rectangle %xSTATUS\n", x->x_canvas, x ); + } +} + +static void oggamp_child_disconnect(t_int fd) +{ + sys_closesocket(fd); + post("oggamp~: connection closed"); +} +/************** the child thread which performs data I/O ***********/ + +#if 0 /* set this to get debugging output */ +static void pute(char *s) /* debug routine */ +{ + write(2, s, strlen(s)); +} +#else +#define pute(x) +#endif + +#if 1 +#define oggamp_cond_wait pthread_cond_wait +#define oggamp_cond_signal pthread_cond_signal +#else +#include /* debugging version... */ +#include +static void oggamp_fakewait(pthread_mutex_t *b) +{ + struct timeval timout; + timout.tv_sec = 0; + timout.tv_usec = 1000000; + pthread_mutex_unlock(b); + select(0, 0, 0, 0, &timout); + pthread_mutex_lock(b); +} + +void oggamp_banana( void) +{ + struct timeval timout; + timout.tv_sec = 0; + timout.tv_usec = 200000; + pute("banana1\n"); + select(0, 0, 0, 0, &timout); + pute("banana2\n"); +} + + +#define oggamp_cond_wait(a,b) oggamp_fakewait(b) +#define oggamp_cond_signal(a) +#endif + +static void *oggamp_child_main(void *zz) +{ + t_oggamp *x = zz; + pute("1\n"); + pthread_mutex_lock(&x->x_mutex); + while (1) + { + int fd, fifohead; + char *buffer; /* Ogg Vorbis data */ + float *buf; /* encoded PCM floats */ + pute("0\n"); + if (x->x_requestcode == REQUEST_NOTHING) + { + pute("wait 2\n"); + oggamp_cond_signal(&x->x_answercondition); + oggamp_cond_wait(&x->x_requestcondition, &x->x_mutex); + pute("3\n"); + } + // connect to Icecast2 server + else if (x->x_requestcode == REQUEST_CONNECT) + { + char boo[80]; + int sysrtn, wantbytes; + + /* copy connect stuff out of the data structure so we can + relinquish the mutex while we're in oggcast_child_connect(). */ + char *hostname = x->x_hostname; + char *mountpoint = x->x_mountpoint; + t_int portno = x->x_port; + x->x_disconnect = 0; + /* alter the request code so that an ensuing "open" will get + noticed. */ + pute("4\n"); + x->x_requestcode = REQUEST_BUSY; + x->x_connecterror = 0; + + /* if there's already a connection open, close it */ + if (x->x_fd >= 0) + { + fd = x->x_fd; + pthread_mutex_unlock(&x->x_mutex); + oggamp_child_disconnect(fd); + pthread_mutex_lock(&x->x_mutex); + x->x_connectstate = 0; + clock_delay(x->x_clock, 0); + x->x_fd = -1; + if (x->x_requestcode != REQUEST_BUSY) + goto lost; + } + /* open the socket with the mutex unlocked */ + pthread_mutex_unlock(&x->x_mutex); + fd = oggamp_child_connect(hostname, mountpoint, portno); + pthread_mutex_lock(&x->x_mutex); + pute("5\n"); + /* copy back into the instance structure. */ + x->x_connectstate = 1; + clock_delay(x->x_clock, 0); + x->x_fd = fd; + if (fd < 0) + { + x->x_connecterror = fd; + x->x_eof = 1; + x->x_connectstate = 0; + clock_delay(x->x_clock, 0); + pute("connect failed\n"); + goto lost; + } + else + { + /* initialise the decoder */ + if(oggamp_vorbis_init(x, fd) != -1) + { + post("oggamp~: decoder initialised"); + oggamp_child_dographics(x); + } + else + { + post("oggamp~: could not init decoder"); + oggamp_child_disconnect(fd); + post("oggamp~: connection closed due to bitstream error"); + x->x_disconnect = 1; + x->x_fd = -1; + x->x_eof = 1; + x->x_connectstate = 0; + clock_delay(x->x_clock, 0); + pute("initialisation failed\n"); + goto lost; + } + } + /* check if another request has been made; if so, field it */ + if (x->x_requestcode != REQUEST_BUSY) + goto lost; + pute("6\n"); + x->x_fifohead = fifohead = 0; + /* set fifosize from bufsize. fifosize must be a + multiple of the number of bytes eaten for each DSP + tick. We pessimistically assume MAXVECSIZE samples + per tick since that could change. There could be a + problem here if the vector size increases while a + stream is being played... */ + x->x_fifosize = x->x_bufsize - (x->x_bufsize % + (x->x_streamchannels * 2)); + /* arrange for the "request" condition to be signalled x->x_siginterval + times per buffer */ + sprintf(boo, "fifosize %d\n", + x->x_fifosize); + pute(boo); + x->x_sigcountdown = x->x_sigperiod = (x->x_fifosize / (x->x_siginterval * x->x_noutlets * x->x_vecsize)); + + /* in a loop, wait for the fifo to get hungry and feed it */ + while (x->x_requestcode == REQUEST_BUSY) + { + int fifosize = x->x_fifosize; + buf = x->x_buf; + pute("77\n"); + if (x->x_eof) + break; + /* try to get new data from decoder whenever + there is some space at end of buffer */ + if(x->x_fifobytes < fifosize - READSIZE) + { + sprintf(boo, "head %d, tail %d\n", x->x_fifohead, x->x_fifotail); + pute(boo); + + /* we pass x on to the routine since we need the ogg vorbis stuff + to be presend. all other values should not be changed because + mutex is unlocked ! */ + pute("decode... "); + pthread_mutex_unlock(&x->x_mutex); + sysrtn = oggamp_decode_input(x, buf, fifohead, fifosize, fd); + oggamp_child_updategraphics(x); + pthread_mutex_lock(&x->x_mutex); + if (x->x_requestcode != REQUEST_BUSY) + break; + if (sysrtn == 0) + { + if (x->x_eos && !x->x_disconnect) /* got end of stream */ + { + pute("end of stream\n"); + oggamp_vorbis_deinit(x); + if(oggamp_vorbis_init(x, fd) == -1) /* reinit stream */ + { + x->x_state = STATE_IDLE; + x->x_disconnect = 1; + goto quit; + } + } + else if (x->x_eos && x->x_disconnect) /* we're disconnecting */ + { + pute("end of stream: disconnecting\n"); + break; /* go to disconnect */ + } + } + else if (sysrtn < 0) /* got any other error from decoder */ + { + pute("connecterror\n"); + x->x_connecterror = sysrtn; + break; + } + x->x_fifohead = (fifohead + sysrtn) % fifosize; + x->x_fifobytes += sysrtn; + sprintf(boo, "after: head %d, tail %d\n", + x->x_fifohead, x->x_fifotail); + pute(boo); + /* check wether the buffer is filled enough to start streaming */ + } + else /* there is enough data in the buffer :: do nothing */ + { + x->x_state = STATE_STREAM; + pute("wait 7...\n"); + oggamp_cond_signal(&x->x_answercondition); + oggamp_cond_wait(&x->x_requestcondition, &x->x_mutex); + pute("7 done\n"); + continue; + } + pute("8\n"); + fd = x->x_fd; + buf = x->x_buf; + fifohead = x->x_fifohead; + + /* signal parent in case it's waiting for data */ + oggamp_cond_signal(&x->x_answercondition); + } + +lost: + + if (x->x_requestcode == REQUEST_BUSY) + x->x_requestcode = REQUEST_NOTHING; + /* fell out of read loop: close connection if necessary, + set EOF and signal once more */ + if (x->x_fd >= 0) + { + fd = x->x_fd; + pthread_mutex_unlock(&x->x_mutex); + oggamp_vorbis_deinit(x); + oggamp_child_disconnect(fd); + pthread_mutex_lock(&x->x_mutex); + x->x_fd = -1; + oggamp_child_delgraphics(x); + } + oggamp_cond_signal(&x->x_answercondition); + + } + /* reconnect to server */ + else if (x->x_requestcode == REQUEST_RECONNECT) + { + if (x->x_fd >= 0) + { + fd = x->x_fd; + pthread_mutex_unlock(&x->x_mutex); + oggamp_vorbis_deinit(x); + oggamp_child_disconnect(fd); + pthread_mutex_lock(&x->x_mutex); + oggamp_child_delgraphics(x); + x->x_fd = -1; + } + /* connect again */ + x->x_requestcode = REQUEST_CONNECT; + + x->x_connectstate = 0; + clock_delay(x->x_clock, 0); + oggamp_cond_signal(&x->x_answercondition); + } + /* close connection to server (disconnect) */ + else if (x->x_requestcode == REQUEST_CLOSE) + { +quit: + if (x->x_fd >= 0) + { + fd = x->x_fd; + pthread_mutex_unlock(&x->x_mutex); + oggamp_vorbis_deinit(x); + oggamp_child_disconnect(fd); + pthread_mutex_lock(&x->x_mutex); + oggamp_child_delgraphics(x); + x->x_fd = -1; + } + if (x->x_requestcode == REQUEST_CLOSE) + x->x_requestcode = REQUEST_NOTHING; + else if (x->x_requestcode == REQUEST_BUSY) + x->x_requestcode = REQUEST_NOTHING; + else if (x->x_requestcode == REQUEST_CONNECT) + x->x_requestcode = REQUEST_NOTHING; + x->x_connectstate = 0; + clock_delay(x->x_clock, 0); + oggamp_cond_signal(&x->x_answercondition); + } + // quit everything + else if (x->x_requestcode == REQUEST_QUIT) + { + if (x->x_fd >= 0) + { + fd = x->x_fd; + pthread_mutex_unlock(&x->x_mutex); + oggamp_vorbis_deinit(x); + oggamp_child_disconnect(fd); + pthread_mutex_lock(&x->x_mutex); + x->x_fd = -1; + } + x->x_connectstate = 0; + clock_delay(x->x_clock, 0); + x->x_requestcode = REQUEST_NOTHING; + oggamp_cond_signal(&x->x_answercondition); + break; + } + else + { + pute("13\n"); + } + } + pute("thread exit\n"); + pthread_mutex_unlock(&x->x_mutex); + return (0); +} + +/******** the object proper runs in the calling (parent) thread ****/ + +static void oggamp_tick(t_oggamp *x) +{ + outlet_float(x->x_connection, x->x_connectstate); +} + +static void *oggamp_new(t_floatarg fdographics, t_floatarg fnchannels, t_floatarg fbufsize) +{ + t_oggamp *x; + int nchannels = fnchannels, bufsize = fbufsize * 1024, i; + float *buf; + + if (nchannels < 1) + nchannels = 2; /* two channels as default */ + else if (nchannels > MAXSTREAMCHANS) + nchannels = MAXSTREAMCHANS; + /* check / set buffer size */ + if (!bufsize) bufsize = DEFBUFPERCHAN * nchannels; + else if (bufsize < MINBUFSIZE) + bufsize = MINBUFSIZE; + else if (bufsize > MAXBUFSIZE) + bufsize = MAXBUFSIZE; + buf = getbytes(bufsize*sizeof(t_float)); + if (!buf) return (0); + + x = (t_oggamp *)pd_new(oggamp_class); + + for (i = 0; i < nchannels; i++) + outlet_new(&x->x_obj, gensym("signal")); + x->x_noutlets = nchannels; + x->x_connection = outlet_new(&x->x_obj, gensym("float")); + x->x_clock = clock_new(x, (t_method)oggamp_tick); + x->x_outvec = (t_sample **)getbytes(nchannels * sizeof(t_sample *)); + + pthread_mutex_init(&x->x_mutex, 0); + pthread_cond_init(&x->x_requestcondition, 0); + pthread_cond_init(&x->x_answercondition, 0); + + x->x_vecsize = 2; + x->x_disconnect = 0; + x->x_state = STATE_IDLE; + x->x_canvas = canvas_getcurrent(); + x->x_streamchannels = 2; + x->x_fd = -1; + x->x_buf = buf; + x->x_bufsize = bufsize; + x->x_siginterval = 16; /* signal 16 times per buffer */ + x->x_fifosize = x->x_fifohead = x->x_fifotail = x->x_fifobytes = x->x_requestcode = 0; + + x->x_connectstate = 0; /* indicating state of connection */ + + x->x_samplerate = x->x_streamrate = sys_getsr(); + x->x_resample = 0; + x->x_vorbis = 0; + x->x_sync = 0; + x->x_recover = -1; /* just ignore buffer underruns */ + + /* graphical buffer status display */ + x->x_graphic = (int)fdographics; + x->x_canvas = canvas_getcurrent(); + + post(oggamp_version); + post("oggamp~: set buffer to %dk bytes", bufsize/1024); + + /* start child thread */ + pthread_create(&x->x_childthread, 0, oggamp_child_main, x); + return (x); +} + +static t_int *oggamp_perform(t_int *w) +{ + t_oggamp *x = (t_oggamp *)(w[1]); + t_int vecsize = x->x_vecsize, noutlets = x->x_noutlets, i, j, r; + t_float *fp; + t_float *buffer = x->x_buf; + + if (x->x_state == STATE_STREAM) + { + t_int wantbytes, getbytes, havebytes, nchannels, streamchannels = x->x_streamchannels; + + pthread_mutex_lock(&x->x_mutex); + + /* get 'getbytes' bytes from input buffer, convert them to + 'wantbytes' which is the number of bytes after resampling */ + wantbytes = noutlets * vecsize; /* number of bytes we get after resampling */ + havebytes = x->x_fifobytes; + + /* check for error */ + if(havebytes < wantbytes) + { + if(x->x_connecterror) + { /* report error and close connection */ + pd_error(x, "dsp: error %d", x->x_connecterror); + x->x_state = STATE_IDLE; + x->x_requestcode = REQUEST_CLOSE; + x->x_disconnect = 1; + oggamp_cond_signal(&x->x_requestcondition); + pthread_mutex_unlock(&x->x_mutex); + } + if(!x->x_disconnect) /* it's not due to disconnect */ + { + if(x->x_recover == 0) /* disconnect */ + { + x->x_state = STATE_IDLE; + x->x_requestcode = REQUEST_CLOSE; + x->x_disconnect = 1; + oggamp_cond_signal(&x->x_requestcondition); + pthread_mutex_unlock(&x->x_mutex); + } + else if(x->x_recover == 1) /* reconnect */ + { + x->x_state = STATE_IDLE; + x->x_requestcode = REQUEST_RECONNECT; + x->x_disconnect = 1; + oggamp_cond_signal(&x->x_requestcondition); + pthread_mutex_unlock(&x->x_mutex); + } + else /* resume */ + { + x->x_state = STATE_IDLE; + x->x_disconnect = 0; + oggamp_cond_signal(&x->x_requestcondition); + pthread_mutex_unlock(&x->x_mutex); + } + } + goto idle; + } + + /* output audio */ + buffer += x->x_fifotail; /* go to actual audio position */ + + for(j = 0; j < vecsize; j++) + { + for(i = 0; i < noutlets; i++) + { + x->x_outvec[i][j] = *buffer++; + } + } + + + x->x_fifotail += wantbytes; + x->x_fifobytes -= wantbytes; + if (x->x_fifotail >= x->x_fifosize) + x->x_fifotail = 0; + /* signal the child thread */ + if ((--x->x_sigcountdown) <= 0) + { + oggamp_cond_signal(&x->x_requestcondition); + x->x_sigcountdown = x->x_sigperiod; + } + pthread_mutex_unlock(&x->x_mutex); + } + else + { + idle: + for (i = 0; i < noutlets; i++) + for (j = vecsize, fp = x->x_outvec[i]; j--; ) + *fp++ = 0; + } + + return (w+2); +} + + +static void oggamp_disconnect(t_oggamp *x) +{ + /* LATER rethink whether you need the mutex just to set a variable? */ + pthread_mutex_lock(&x->x_mutex); + x->x_disconnect = 1; + x->x_state = STATE_IDLE; + x->x_requestcode = REQUEST_CLOSE; + oggamp_cond_signal(&x->x_requestcondition); + pthread_mutex_unlock(&x->x_mutex); +} + + + /* connect method. Called as: + connect + */ + +static void oggamp_connect(t_oggamp *x, t_symbol *s, int argc, t_atom *argv) +{ + t_symbol *hostsym = atom_getsymbolarg(0, argc, argv); + t_symbol *mountsym = atom_getsymbolarg(1, argc, argv); + t_float portno = atom_getfloatarg(2, argc, argv); + if (!*hostsym->s_name) /* check for hostname */ + return; + if (!portno) /* check wether the portnumber is specified */ + portno = 8000; /* ...assume port 8000 as standard */ + pthread_mutex_lock(&x->x_mutex); + if(x->x_fd == -1) + { + x->x_hostname = hostsym->s_name; + x->x_mountpoint = mountsym->s_name; + x->x_port = portno; + x->x_requestcode = REQUEST_CONNECT; + /* empty buffer */ + x->x_fifotail = 0; + x->x_fifohead = 0; + x->x_fifobytes = 0; + x->x_streamchannels = 2; + x->x_eof = 0; + x->x_connecterror = 0; + x->x_state = STATE_STARTUP; + x->x_disconnect = 0; + oggamp_cond_signal(&x->x_requestcondition); + } + else post("oggamp~: already connected"); + pthread_mutex_unlock(&x->x_mutex); +} + + /* connect using url like "http://localhost:8000/mountpoint" */ +static void oggamp_connect_url(t_oggamp *x, t_symbol *url) +{ + char *hname, *port; + char *h, *p; + char *hostptr; + char *r_hostptr; + char *pathptr; + char *portptr; + char *p0; + char *defaultportstr = "8000"; + t_int stringlength; + t_int portno; + + /* strip http:// or ftp:// */ + p = url->s_name; + if (strncmp(p, "http://", 7) == 0) + p += 7; + + if (strncmp(p, "ftp://", 6) == 0) + p += 6; + + hostptr = p; + while (*p && *p != '/') /* look for end of hostname:port */ + p++; + p++; /* also skip '/' */ + pathptr = p; + + r_hostptr = --p; + while (*p && hostptr < p && *p != ':' && *p != ']') /* split at ':' */ + p--; + + if (!*p || p < hostptr || *p != ':') { + portptr = NULL; + } + else{ + portptr = p + 1; + r_hostptr = p - 1; + } + if (*hostptr == '[' && *r_hostptr == ']') { + hostptr++; + r_hostptr--; + } + + stringlength = r_hostptr - hostptr + 1; + h = getbytes(stringlength + 1); + if (h == NULL) { + hname = NULL; + port = NULL; + pathptr = NULL; + } + strncpy(h, hostptr, stringlength); + *(h+stringlength) = '\0'; + hname = h; /* the hostname */ + + if (portptr) { + stringlength = (pathptr - portptr); + if(!stringlength) portptr = NULL; + } + if (portptr == NULL) { + portptr = defaultportstr; + stringlength = strlen(defaultportstr); + } + p0 = getbytes(stringlength + 1); + if (p0 == NULL) { + freebytes(h, stringlength + 1); + hname = NULL; + port = NULL; + pathptr = NULL; + } + strncpy(p0, portptr, stringlength); + *(p0 + stringlength) = '\0'; + + for (p = p0; *p && isdigit((unsigned char) *p); p++) ; + + *p = '\0'; + port = (unsigned char *) p0; + /* convert port from string to int */ + portno = (int)strtol(port, NULL, 10); + freebytes(p0, stringlength + 1); + /* set values and signal child to connect */ + pthread_mutex_lock(&x->x_mutex); + if(x->x_fd == -1) + { + x->x_hostname = hname; + x->x_mountpoint = pathptr; + x->x_port = portno; + x->x_requestcode = REQUEST_CONNECT; + x->x_fifotail = 0; + x->x_fifohead = 0; + x->x_fifobytes = 0; + x->x_streamchannels = 2; + x->x_eof = 0; + x->x_connecterror = 0; + x->x_state = STATE_STARTUP; + oggamp_cond_signal(&x->x_requestcondition); + } + else post("oggamp~: already connected"); + pthread_mutex_unlock(&x->x_mutex); +} + +static void oggamp_float(t_oggamp *x, t_floatarg f) +{ + if (f != 0) + { + pthread_mutex_lock(&x->x_mutex); + if(x->x_fd == -1) + { + x->x_requestcode = REQUEST_CONNECT; + + x->x_fifotail = 0; + x->x_fifohead = 0; + x->x_fifobytes = 0; + x->x_streamchannels = 2; + x->x_eof = 0; + x->x_connecterror = 0; + x->x_state = STATE_STARTUP; + oggamp_cond_signal(&x->x_requestcondition); + } + else post("oggamp~: already connected"); + pthread_mutex_unlock(&x->x_mutex); + } + else oggamp_disconnect(x); +} + +static void oggamp_dsp(t_oggamp *x, t_signal **sp) +{ + int i, noutlets = x->x_noutlets; + pthread_mutex_lock(&x->x_mutex); + x->x_vecsize = sp[0]->s_n; + + x->x_sigperiod = (x->x_fifosize / (x->x_siginterval * x->x_streamchannels * x->x_vecsize)); + for (i = 0; i < noutlets; i++) + x->x_outvec[i] = sp[i]->s_vec; + pthread_mutex_unlock(&x->x_mutex); + dsp_add(oggamp_perform, 1, x); +} + +static void oggamp_print(t_oggamp *x) +{ + pthread_mutex_lock(&x->x_mutex); + if(x->x_fd >= 0) + { + post("oggamp~: connected to http://%s:%d/%s", x->x_hostname, x->x_port, x->x_mountpoint); + post("oggamp~: bitstream is %d channels @ %ld Hz with %ldkbps nominal bitrate", + x->x_streamchannels, x->x_streamrate, x->x_vi.bitrate_nominal / 1000); + } else post("oggamp~: not connected"); + if(x->x_recover == 0) + post("oggamp~: recover mode set to \"disconnect\" (0)"); + else if(x->x_recover == 1) + post("oggamp~: recover mode set to \"reconnect\" (1)"); + else if(x->x_recover == -1) + post("oggamp~: recover mode set to \"resume\" (-1)"); + pthread_mutex_unlock(&x->x_mutex); +} + + /* set behavior for buffer underruns */ +static void oggamp_recover(t_oggamp *x, t_floatarg f) +{ + pthread_mutex_lock(&x->x_mutex); + if(f <= -1) + { /* mute audio and try to fill buffer again: the default */ + post("oggamp~: set recover mode to \"resume\" (-1)"); + f = -1; + } + else if(f >= 1) + { /* reconnect to server */ + post("oggamp~: set recover mode to \"reconnect\" (1)"); + f = 1; + } + else + { /* disconnect from server */ + post("oggamp~: set recover mode to \"disconnect\" (0)"); + f = 0; + } + x->x_recover = f; + pthread_mutex_unlock(&x->x_mutex); +} + +static void oggamp_free(t_oggamp *x) +{ + /* request QUIT and wait for acknowledge */ + void *threadrtn; + pthread_mutex_lock(&x->x_mutex); + x->x_requestcode = REQUEST_QUIT; + x->x_disconnect = 1; + post("stopping oggamp thread..."); + oggamp_cond_signal(&x->x_requestcondition); + while (x->x_requestcode != REQUEST_NOTHING) + { + post("signalling..."); + oggamp_cond_signal(&x->x_requestcondition); + oggamp_cond_wait(&x->x_answercondition, &x->x_mutex); + } + pthread_mutex_unlock(&x->x_mutex); + if (pthread_join(x->x_childthread, &threadrtn)) + error("oggamp_free: join failed"); + post("... done."); + + pthread_cond_destroy(&x->x_requestcondition); + pthread_cond_destroy(&x->x_answercondition); + pthread_mutex_destroy(&x->x_mutex); + freebytes(x->x_buf, x->x_bufsize*sizeof(t_float)); + freebytes(x->x_outvec, x->x_noutlets * sizeof(t_sample *)); + clock_free(x->x_clock); +} + +void oggamp_tilde_setup(void) +{ + oggamp_class = class_new(gensym("oggamp~"), (t_newmethod)oggamp_new, + (t_method)oggamp_free, sizeof(t_oggamp), 0, A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT, 0); + class_addfloat(oggamp_class, (t_method)oggamp_float); + class_addmethod(oggamp_class, (t_method)oggamp_disconnect, gensym("disconnect"), 0); + class_addmethod(oggamp_class, (t_method)oggamp_dsp, gensym("dsp"), 0); + class_addmethod(oggamp_class, (t_method)oggamp_connect, gensym("connect"), A_GIMME, 0); + class_addmethod(oggamp_class, (t_method)oggamp_connect_url, gensym("connecturl"), A_SYMBOL, 0); + class_addmethod(oggamp_class, (t_method)oggamp_recover, gensym("recover"), A_FLOAT, 0); + class_addmethod(oggamp_class, (t_method)oggamp_print, gensym("print"), 0); +} diff --git a/oggamp~/HISTORY b/oggamp~/HISTORY deleted file mode 100644 index f800e80..0000000 --- a/oggamp~/HISTORY +++ /dev/null @@ -1,44 +0,0 @@ -Version history of oggamp~ external for pure-data - -v 0.3 (2nd january 2004): -- removed the crappy resampling to get rid of audio clicks -- multichannel support - -v 0.2f (20st july 2002): -- recompiled with the final 1.0 release of Ogg Vorbis - -v 0.2e (21st june 2002 - stable release): -- added downsamling -- cleaned up code a bit -- added some more info-printout -- fixed some bugs to make it work correctly on Linux - thanks to Oliver Thuns at radiostudio.org -- now disconnects correctly at end-of-stream (when no - chained stream follows) -- KNOWN BUG: graphic buffer status display might cause ugly - printout of Tcl/Tk commands to console window on Linux - under some circumstances (e.g. in case server dies) - -v 0.2d (12th june 2002): -- added upsamling -- finally fixed the End-Of-Stream bug: it's now - possible to listen to a playlist with correct - update of stream information - -v 0.2c (10th june 2002): -- fixed some bugs, introduced some new ones... - -v 0.2a (11th mar. 2002): -- introduced child thread for connect: now pd - does no longer 'stop' audio; as a side effect it - is now possible to connect to an oggcast~ stream - from the same instance of pd -- threads now use pthreads libraray on Win to have - things compatible with UNIX -- fixed a small bug that made 'old' audio appear on - the beginning of 'new' one after reconnecting - -v 0.1c (19th feb. 2002): -- first (sort of) stable release - - diff --git a/oggamp~/oggamp~-help.pd b/oggamp~/oggamp~-help.pd deleted file mode 100644 index 9f30cb8..0000000 --- a/oggamp~/oggamp~-help.pd +++ /dev/null @@ -1,54 +0,0 @@ -#N canvas 176 105 678 455 10; -#X obj 38 420 dac~; -#X floatatom 100 419 5 0 0; -#X text 145 419 connection state; -#X msg 80 83 connect localhost puredata.ogg 8000; -#X msg 109 131 disconnect; -#X msg 35 60 connect ogg.bbc.co.uk radio1_low.ogg 8001; -#X msg 93 106 connect 141.53.196.149 puredata.ogg 8000; -#X msg 147 224 recover 0; -#X msg 163 251 recover 1; -#X msg 125 153 print; -#X obj 15 389 *~ 0; -#X obj 57 391 *~ 0; -#X floatatom 20 332 5 0 0; -#X obj 20 354 / 100; -#X text 57 7 oggamp~ version 0.2 - Ogg Vorbis sreaming client; -#X msg 139 191 recover -1; -#X text 218 191 resume (default): mute audio and refill buffer; -#X text 230 249 reconnect (disconnect and connect again); -#X text 138 303 CREATION ARGUMENTS:; -#X text 260 303 oggamp~ ; -#X text 167 324 - turn graphical buffer status display on -(1) or off (0 \, default); -#X text 167 351 - number of outlets (default = 2) \, mono -to stereo and stereo to mono conversion supported; -#X text 333 50 written by Olaf Matthes ; -#X text 333 63 get source at http://www.akustische-kunst.de/; -#X msg 530 406 \; pd dsp 1; -#X msg 596 406 \; pd dsp 0; -#X obj 530 380 loadbang; -#X msg 16 34 connect radio.jcraft.com test.ogg 8000; -#X obj 15 300 oggamp~ 1 2 256; -#X text 154 171 BEHAVIOUR ON BUFFER UNDERRUNS:; -#X text 166 378 - size of circular buffer in kbytes (default -= 256k); -#X text 219 222 disconnect on buffer underrun; -#X connect 3 0 28 0; -#X connect 4 0 28 0; -#X connect 5 0 28 0; -#X connect 6 0 28 0; -#X connect 7 0 28 0; -#X connect 8 0 28 0; -#X connect 9 0 28 0; -#X connect 10 0 0 0; -#X connect 11 0 0 1; -#X connect 12 0 13 0; -#X connect 13 0 10 1; -#X connect 13 0 11 1; -#X connect 15 0 28 0; -#X connect 26 0 24 0; -#X connect 27 0 28 0; -#X connect 28 0 10 0; -#X connect 28 1 11 0; -#X connect 28 2 1 0; diff --git a/oggamp~/oggamp~.c b/oggamp~/oggamp~.c deleted file mode 100644 index a9ff78b..0000000 --- a/oggamp~/oggamp~.c +++ /dev/null @@ -1,1427 +0,0 @@ -/* ------------------------- oggamp~ ------------------------------------------ */ -/* */ -/* Tilde object to receive an Ogg Vorbis stream from an IceCast2 server. */ -/* Written by Olaf Matthes */ -/* Get source at http://www.akustische-kunst.de/puredata/ */ -/* */ -/* Graphical buffer status display written by Yves Degoyon. */ -/* */ -/* Thanks for hours (maybe days?) of beta testing to Oliver Thuns. */ -/* */ -/* This library is free software; you can redistribute it and/or */ -/* modify it under the terms of the GNU Lesser General Public */ -/* License as published by the Free Software Foundation; either */ -/* version 2 of the License, or (at your option) any later version. */ -/* */ -/* This library is distributed in the hope that it will be useful, */ -/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ -/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */ -/* Lesser General Public License for more details. */ -/* */ -/* You should have received a copy of the GNU Lesser General Public */ -/* License along with this library; if not, write to the */ -/* Free Software Foundation, Inc., 59 Temple Place - Suite 330, */ -/* Boston, MA 02111-1307, USA. */ -/* */ -/* Based on PureData by Miller Puckette and others. */ -/* Uses the Ogg Vorbis decoding library which can be found at */ -/* http://www.vorbis.com/ */ -/* */ -/* ---------------------------------------------------------------------------- */ - - /* Pd includes */ -#include "m_pd.h" -#include "g_canvas.h" - /* Vorbis includes */ -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef _WIN32 -#include /* for 'write' in pute-function only */ -#include -#include -#else -#include -#include -#include -#include -#include -#include -#include -#define SOCKET_ERROR -1 -#endif - -#ifdef _MSC_VER -#pragma warning( disable : 4244 ) -#pragma warning( disable : 4305 ) -#endif - -#ifdef WIN32 -#define sys_closesocket closesocket -#else -#define sys_closesocket close -#endif - - -/************************* oggamp~ object ******************************/ - -/* - Each instance of oggamp~ owns a "child" thread for doing the data - transfer. The parent thread signals the child each time: - (1) a connection wants opening or closing; - (2) we've eaten another 1/16 of the shared buffer (so that the - child thread should check if it's time to receive some more.) - The child signals the parent whenever a receive has completed. Signalling - is done by setting "conditions" and putting data in mutex-controlled common - areas. -*/ - - -#define REQUEST_NOTHING 0 -#define REQUEST_CONNECT 1 -#define REQUEST_CLOSE 2 -#define REQUEST_QUIT 3 -#define REQUEST_BUSY 4 -#define REQUEST_DATA 5 -#define REQUEST_RECONNECT 6 - -#define STATE_IDLE 0 -#define STATE_STARTUP 1 /* connecting and filling the buffer */ -#define STATE_STREAM 2 /* streaming aund audio output */ - -#define READSIZE 65536 /* _encoded_ data we request from buffer */ -#define READ 1024 /* amount of data we pass on to decoder */ -#define DEFBUFPERCHAN 262144 /* audio output buffer default: 256k */ -#define MINBUFSIZE (4 * READSIZE) -#define MAXBUFSIZE 16777216 /* arbitrary; just don't want to hang malloc */ -#define STRBUF_SIZE 1024 /* char received from server on startup */ -#define OBJWIDTH 68 /* width of buffer statis display */ -#define OBJHEIGHT 10 /* height of buffer statis display */ -#define MAXSTREAMCHANS 250 /* maximum number of channels */ - -static char *oggamp_version = "oggamp~: ogg/vorbis streaming client version 0.3, written by Olaf Matthes"; - -static t_class *oggamp_class; - -typedef struct _oggamp -{ - t_object x_obj; - t_canvas *x_canvas; /* remember canvas */ - t_outlet *x_connection; - t_clock *x_clock; - - t_float *x_buf; /* audio data buffer */ - t_int x_bufsize; /* buffer size in bytes */ - t_int x_noutlets; /* number of audio outlets */ - t_sample **x_outvec; /* audio vectors */ - t_int x_vecsize; /* vector size for transfers */ - t_int x_state; /* opened, running, or idle */ - - /* parameters to communicate with subthread */ - t_int x_requestcode; /* pending request from parent to I/O thread */ - t_int x_connecterror; /* slot for "errno" return */ - t_int x_streamchannels; /* number of channels in Ogg Vorbis bitstream */ - t_int x_streamrate; /* sample rate of stream */ - - /* buffer stuff */ - t_int x_fifosize; /* buffer size appropriately rounded down */ - t_int x_fifohead; /* index of next byte to get from file */ - t_int x_fifotail; /* index of next byte the ugen will read */ - t_int x_fifobytes; /* number of bytes available in buffer */ - t_int x_eof; /* true if ogg stream has ended */ - t_int x_sigcountdown; /* counter for signalling child for more data */ - t_int x_sigperiod; /* number of ticks per signal */ - t_int x_siginterval; /* number of times per buffer (depends on data rate) */ - - /* ogg/vorbis related stuff */ - ogg_stream_state x_os; /* take physical pages, weld into a logical stream of packets */ - ogg_sync_state x_oy; /* sync and verify incoming physical bitstream */ - ogg_page x_og; /* one Ogg bitstream page. Vorbis packets are inside */ - ogg_packet x_op; /* one raw packet of data for decode */ - vorbis_info x_vi; /* struct that stores all the static vorbis bitstream settings */ - vorbis_comment x_vc; /* struct that stores all the user comments */ - vorbis_dsp_state x_vd; /* central working state for the packet->PCM decoder */ - vorbis_block x_vb; /* local working space for packet->PCM decode */ - t_int x_eos; /* end of stream */ - char *x_buffer; /* buffer used to pass on to ogg/vorbis */ - - t_int x_vorbis; /* info about encoder status */ - t_int x_sync; /* indicates whether the decoder has been synced */ - t_float x_pages; /* number of pages that have been output to server */ - t_outlet *x_outpages; /* output to send them to */ - - - t_int x_connectstate; /* indicates the state of socket connection */ - t_int x_fd; /* the socket number */ - t_int x_graphic; /* indicates if we show a graphic bar */ - t_float x_resample; /* indicates if we need to resample signal (1 = no resampling) */ - t_int x_recover; /* indicate how to behave on buffer underruns */ - t_int x_disconnect; /* indicates that user want's to disconnect */ - t_int x_samplerate; /* Pd's sample rate */ - - /* server stuff */ - char *x_hostname; /* name or IP of host to connect to */ - char *x_mountpoint; /* mountpoint of ogg-bitstream */ - t_int x_port; /* port number on which the connection is made */ - - /* tread stuff */ - pthread_mutex_t x_mutex; - pthread_cond_t x_requestcondition; - pthread_cond_t x_answercondition; - pthread_t x_childthread; -} t_oggamp; - - /* check if we can read from socket */ -static int oggamp_check_for_data(t_int sock) -{ - - fd_set set; - struct timeval tv; - t_int ret; - - tv.tv_sec = 0; - tv.tv_usec = 20000; - FD_ZERO(&set); - FD_SET(sock, &set); - ret = select(sock + 1, &set, NULL, NULL, &tv); - if (ret > 0) - return 1; - return 0; -} - - /* receive 'size' bytes from server */ -static int oggamp_child_receive(int fd, char *buffer, int size) -{ - int ret = -1; - int i; - - ret = recv(fd, buffer, size, 0); - if(ret < 0) - { - post("oggamp~: receive error" ); - } - return ret; -} - - /* ogg/vorbis decoder sync init */ -static void oggamp_vorbis_sync_init(t_oggamp *x) -{ - ogg_sync_init(&(x->x_oy)); /* Now we can read pages */ - x->x_sync = 1; -} - - /* ogg/vorbis decoder setup */ -static int oggamp_vorbis_init(t_oggamp *x, int fd) -{ - int i; - int result; /* error return code */ - int bytes; /* number of bytes receive returned */ - char *buffer; /* buffer for undecoded ogg vorbis data */ - - if(!x->x_sync)oggamp_vorbis_sync_init(x); /* init the sync */ - - x->x_eos = 0; /* indicate beginning of new stream */ - - /* submit a 4k block to libvorbis' ogg layer */ - buffer = ogg_sync_buffer(&(x->x_oy),4096); - post("oggamp~: prebuffering..."); - bytes = oggamp_child_receive(fd, buffer, 4096); - ogg_sync_wrote(&(x->x_oy),bytes); - result = ogg_sync_pageout(&(x->x_oy),&(x->x_og)); - - /* we might need more data */ - if(result == -1) - { - post("reading more..."); - buffer = ogg_sync_buffer(&(x->x_oy),4096); - bytes = oggamp_child_receive(fd, buffer, 4096); - ogg_sync_wrote(&(x->x_oy),bytes); - result = ogg_sync_pageout(&(x->x_oy),&(x->x_og)); - } - /* Get the first page. */ - if(result != 1) - { - /* error case. Must not be Vorbis data */ - error("oggamp~: input does not appear to be an ogg bitstream (error %d)", result); - return -1; - } - - ogg_stream_init(&(x->x_os),ogg_page_serialno(&(x->x_og))); - - vorbis_info_init(&(x->x_vi)); - vorbis_comment_init(&(x->x_vc)); - if(ogg_stream_pagein(&(x->x_os),&(x->x_og))<0){ - /* error; stream version mismatch perhaps */ - error("oggamp~: error reading first page of ogg bitstream data"); - return -1; - } - - if(ogg_stream_packetout(&(x->x_os),&(x->x_op))!=1){ - /* no page? must not be vorbis */ - error("oggamp~: error reading initial header packet"); - return -1; - } - - if(vorbis_synthesis_headerin(&(x->x_vi),&(x->x_vc),&(x->x_op))<0){ - /* error case; not a vorbis header */ - error("oggamp~: this ogg bitstream does not contain Vorbis audio data"); - return -1; - } - - i=0; - while(i<2){ - while(i<2){ - result = ogg_sync_pageout(&(x->x_oy),&(x->x_og)); - if(result == 0)break; /* Need more data */ - /* Don't complain about missing or corrupt data yet. We'll - catch it at the packet output phase */ - if(result == 1) - { - ogg_stream_pagein(&(x->x_os),&(x->x_og)); /* we can ignore any errors here - as they'll also become apparent - at packetout */ - while(i<2){ - result = ogg_stream_packetout(&(x->x_os),&(x->x_op)); - if(result==0)break; - if(result<0){ - /* Uh oh; data at some point was corrupted or missing! - We can't tolerate that in a header. Die. */ - error("oggamp~: corrupt secondary header, exiting"); - return -1; - } - vorbis_synthesis_headerin(&(x->x_vi),&(x->x_vc),&(x->x_op)); - i++; - } - } - } - /* no harm in not checking before adding more */ - buffer = ogg_sync_buffer(&(x->x_oy),READ); - /* read in next 4k of data */ - bytes = oggamp_child_receive(fd, buffer, READ); - if(bytes==0 && i<2){ - error("oggamp~: end of stream before finding all Vorbis headers"); - return -1; - } - ogg_sync_wrote(&(x->x_oy),bytes); - } - - /* Throw the comments plus a few lines about the bitstream we're decoding */ - post("oggamp~: reading Ogg Vorbis header..."); - { - char **ptr = x->x_vc.user_comments; - while(*ptr){ - post(" %s",*ptr); - ++ptr; - } - post("oggamp~: bitstream is %d channels @ %ld Hz with %ldkbps", x->x_vi.channels, x->x_vi.rate, x->x_vi.bitrate_nominal / 1000); - x->x_streamchannels = x->x_vi.channels; - x->x_streamrate = x->x_vi.rate; - if(x->x_samplerate != x->x_streamrate) /* upsampling */ - { /* we would need to use upsampling */ - post("oggamp~: resampling from %ld Hz to %ld Hz not supported !", x->x_vi.rate, x->x_samplerate); - return (-1); - } - post("oggamp~: encoded by: %s", x->x_vc.vendor); - } - - /* OK, got and parsed all three headers. Initialize the Vorbis packet->PCM decoder. */ - vorbis_synthesis_init(&(x->x_vd),&(x->x_vi)); /* central decode state */ - vorbis_block_init(&(x->x_vd),&(x->x_vb)); /* local state */ - x->x_vorbis = 1; - return (1); -} - - /* clear the ogg/vorbis decoder */ -static void oggamp_vorbis_sync_clear(t_oggamp *x) -{ - /* OK, clean up the framer */ - ogg_sync_clear(&(x->x_oy)); - x->x_sync = 0; - post("oggamp~: decoder cleared"); -} - /* deinit the ogg/vorbis decoder */ -static void oggamp_vorbis_deinit(t_oggamp *x) -{ - if(x->x_vorbis) - { - x->x_vorbis = 0; - - /* clean up this logical bitstream; before exit we see if we're - followed by another [chained] */ - ogg_stream_clear(&(x->x_os)); - - /* ogg_page and ogg_packet structs always point to storage in - libvorbis. They're never freed or manipulated directly */ - - vorbis_block_clear(&(x->x_vb)); - vorbis_dsp_clear(&(x->x_vd)); - vorbis_comment_clear(&(x->x_vc)); - vorbis_info_clear(&(x->x_vi)); /* must be called last */ - - post("oggamp~: decoder deinitialised"); - /* only clear completely in case we're going to disconnect */ - /* !! must not be called when receiving chained streams !! */ - if(x->x_disconnect)oggamp_vorbis_sync_clear(x); - } -} - - /* decode ogg/vorbis and receive new data */ -static int oggamp_decode_input(t_oggamp *x, float *buf, int fifohead, int fifosize, int fd) -{ - int i, result; - - float **pcm; /* pointer to decoded float samples */ - char *buffer; /* buffer for ogg vorbis */ - int samples; /* number of samples returned by decoder at each block! */ - int n = 0; /* total number of samples returned by decoder at this call */ - int bytes; /* number of bytes submitted to decoder */ - int position = fifohead; - int streamchannels = x->x_streamchannels; - int channels = x->x_noutlets; - - - /* the rest is just a straight decode loop until end of stream */ - while(!x->x_eos) - { - result = ogg_sync_pageout(&(x->x_oy),&(x->x_og)); - if(result == 0)break; /* need more data */ - if(result < 0) - { /* missing or corrupt data at this page position */ - error("oggamp~: corrupt or missing data in bitstream, continuing..."); - } - else{ - ogg_stream_pagein(&(x->x_os),&(x->x_og)); /* can safely ignore errors at this point */ - while(1) - { - result = ogg_stream_packetout(&(x->x_os),&(x->x_op)); - - if(result==0)break; /* need more data */ - if(result<0) - { /* missing or corrupt data at this page position */ - /* no reason to complain; already complained above */ - }else - { - /* we have a packet. Decode it */ - if(vorbis_synthesis(&(x->x_vb),&(x->x_op))==0) /* test for success! */ - vorbis_synthesis_blockin(&(x->x_vd),&(x->x_vb)); - /* - - **pcm is a multichannel float vector. In stereo, for - example, pcm[0] is left, and pcm[1] is right. samples is - the size of each channel. Convert the float values - (-1.<=range<=1.) to whatever PCM format and write it out */ - - while((samples = vorbis_synthesis_pcmout(&(x->x_vd),&pcm))>0) - { - int j; - - /* copy into our output buffer */ - if(streamchannels >= channels) - { - for(j = 0; j < samples; j++) - { - for(i = 0; i < channels; i++) - { - buf[position + i] = pcm[i][j]; - } - position = (position + channels) % fifosize; - } - vorbis_synthesis_read(&(x->x_vd),samples); /* tell libvorbis how - many samples we - actually consumed */ - n += samples; /* sum up the samples we got from decoder */ - } - else - { - for(j = 0; j < samples; j++) - { - /* copy the channels we have */ - for(i = 0; i < streamchannels; i++) - { - buf[position + i] = pcm[i][j]; - } - /* fill rest of buffer with silence */ - for(i = streamchannels; i < channels; i++) - { - buf[position + i] = 0.; - } - position = (position + channels) % fifosize; - } - vorbis_synthesis_read(&(x->x_vd),samples); /* tell libvorbis how - many samples we - actually consumed */ - n += samples; /* sum up the samples we got from decoder */ - } - } - } - } - if(ogg_page_eos(&(x->x_og)))x->x_eos=1; - } - } - - /* read data from socket */ - if(!x->x_eos) /* read from input until end of stream */ - { - buffer = ogg_sync_buffer(&(x->x_oy), READ); - /* read next 4k of data out of buffer */ - bytes = oggamp_child_receive(fd, buffer, READ); - if(bytes < 0) - { - x->x_eos = 1; /* indicate end of stream */ - return (bytes); - } - ogg_sync_wrote(&(x->x_oy),bytes); - } - else /* we read through all the file... */ - { /* will have to reinit decoder for new file */ - post("oggamp~: end of stream detected"); - return (0); - } - return (n*x->x_vi.channels); -} - - /* connect to shoutcast server */ -static int oggamp_child_connect(char *hostname, char *mountpoint, t_int portno) -{ - struct sockaddr_in server; - struct hostent *hp; - - /* variables used for communication with server */ - char *sptr = NULL; - char request[STRBUF_SIZE]; /* string to be send to server */ - char *url; /* used for relocation */ - fd_set fdset; - struct timeval tv; - t_int sockfd; /* socket to server */ - t_int relocate, numrelocs = 0; - t_int i, ret, rest, nanswers=0; - char *cpoint = NULL; - t_int eof = 0; - - sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - if (sockfd < 0) - { - error("oggamp~: internal error while attempting to open socket"); - return (-1); - } - - /* connect socket using hostname provided in command line */ - server.sin_family = AF_INET; - hp = gethostbyname(hostname); - if (hp == 0) - { - post("oggamp~: bad host?"); - sys_closesocket(sockfd); - return (-1); - } - memcpy((char *)&server.sin_addr, (char *)hp->h_addr, hp->h_length); - - /* assign client port number */ - server.sin_port = htons((unsigned short)portno); - - /* try to connect. */ - post("oggamp~: connecting to http://%s:%d/%s", hostname, portno, mountpoint); - if (connect(sockfd, (struct sockaddr *) &server, sizeof (server)) < 0) - { - error("oggamp~: connection failed!\n"); - sys_closesocket(sockfd); - return (-1); - } - - /* sheck if we can read/write from/to the socket */ - FD_ZERO( &fdset); - FD_SET( sockfd, &fdset); - tv.tv_sec = 0; /* seconds */ - tv.tv_usec = 500; /* microseconds */ - - ret = select(sockfd + 1, &fdset, NULL, NULL, &tv); - if(ret != 0) - { - error("oggamp~: can not read from socket"); - sys_closesocket(sockfd); - return (-1); - } - - /* build up stuff we need to send to server */ - sprintf(request, "GET /%s HTTP/1.0 \r\nHost: %s\r\nUser-Agent: oggamp~ 0.2\r\nAccept: audio/x-ogg\r\n\r\n", - mountpoint, hostname); - - if ( send(sockfd, request, strlen(request), 0) < 0 ) /* say hello to server */ - { - post("oggamp~: could not contact server..."); - return (-1); - } - - /* read first line of response */ - i = 0; - while(i < STRBUF_SIZE - 1) - { - if(oggamp_check_for_data(sockfd)) - { - if (recv(sockfd, request + i, 1, 0) <= 0) - { - error("oggamp~: could not read from socket, quitting"); - sys_closesocket(sockfd); - return (-1); - } - if (request[i] == '\n') - break; - if (request[i] != '\r') - i++; - } - } - request[i] = '\0'; - - /* time to parse content of the response... */ - if(strstr(request, "HTTP/1.0 200 OK")) /* server is ready */ - { - post("oggamp~: IceCast2 server detected"); - while(!eof) /* read everything we can get */ - { - i = 0; - while(i < STRBUF_SIZE - 1) - { - if(oggamp_check_for_data(sockfd)) - { - if (recv(sockfd, request + i, 1, 0) <= 0) - { - error("oggamp~: could not read from socket, quitting"); - sys_closesocket(sockfd); - return (-1); - } - if(request[i] == '\n') /* leave at end of line */ - break; - if(request[i] == 0x0A) /* leave at end of line */ - break; - if(request[i] != '\r') /* go on until 'return' */ - i++; - } - } - request[i] = '\0'; /* make it a null terminated string */ - - if(sptr = strstr(request, "application/x-ogg")) - { /* check for content type */ - post("oggamp~: Ogg Vorbis stream found"); - } - if(sptr = strstr(request, "ice-name:")) - { /* display ice-name */ - post("oggamp~: \"%s\"", sptr + 10); - } - if(i == 0)eof = 1; /* we got last '\r\n' from server */ - } - } - else /* wrong server or wrong answer */ - { - post("oggamp~: unknown response from server"); - sys_closesocket(sockfd); - return (-1); - } - post("oggamp~: connected to http://%s:%d/%s", hp->h_name, portno, mountpoint); - - return (sockfd); -} - - -static void oggamp_child_dographics(t_oggamp *x) -{ - /* do graphics stuff :: create rectangle */ - if ( x->x_graphic && glist_isvisible( x->x_canvas ) ) - { - sys_vgui(".x%x.c create rectangle %d %d %d %d -fill lightblue -tags %xPBAR\n", - x->x_canvas, x->x_obj.te_xpix, x->x_obj.te_ypix-OBJHEIGHT-1, - x->x_obj.te_xpix + OBJWIDTH, x->x_obj.te_ypix - 1, x ); - } -} - -static void oggamp_child_updategraphics(t_oggamp *x) -{ - /* update buffer status display */ - if(x->x_graphic && glist_isvisible(x->x_canvas)) - { - /* update graphical read status */ - char color[32]; - - sys_vgui(".x%x.c delete rectangle %xSTATUS\n", x->x_canvas, x); - if(x->x_fifobytes < (x->x_fifosize / 8)) - { - strcpy(color, "red"); - } - else - { - strcpy(color, "lightgreen"); - } - sys_vgui(".x%x.c create rectangle %d %d %d %d -fill %s -tags %xSTATUS\n", - x->x_canvas, x->x_obj.te_xpix, x->x_obj.te_ypix-OBJHEIGHT-1, - x->x_obj.te_xpix+((x->x_fifobytes*OBJWIDTH)/x->x_fifosize), - x->x_obj.te_ypix - 1, color, x); - } -} -static void oggamp_child_delgraphics(t_oggamp *x) -{ - if(x->x_graphic) /* delete graphics */ - { - sys_vgui(".x%x.c delete rectangle %xPBAR\n", x->x_canvas, x ); - sys_vgui(".x%x.c delete rectangle %xSTATUS\n", x->x_canvas, x ); - } -} - -static void oggamp_child_disconnect(t_int fd) -{ - sys_closesocket(fd); - post("oggamp~: connection closed"); -} -/************** the child thread which performs data I/O ***********/ - -#if 0 /* set this to get debugging output */ -static void pute(char *s) /* debug routine */ -{ - write(2, s, strlen(s)); -} -#else -#define pute(x) -#endif - -#if 1 -#define oggamp_cond_wait pthread_cond_wait -#define oggamp_cond_signal pthread_cond_signal -#else -#include /* debugging version... */ -#include -static void oggamp_fakewait(pthread_mutex_t *b) -{ - struct timeval timout; - timout.tv_sec = 0; - timout.tv_usec = 1000000; - pthread_mutex_unlock(b); - select(0, 0, 0, 0, &timout); - pthread_mutex_lock(b); -} - -void oggamp_banana( void) -{ - struct timeval timout; - timout.tv_sec = 0; - timout.tv_usec = 200000; - pute("banana1\n"); - select(0, 0, 0, 0, &timout); - pute("banana2\n"); -} - - -#define oggamp_cond_wait(a,b) oggamp_fakewait(b) -#define oggamp_cond_signal(a) -#endif - -static void *oggamp_child_main(void *zz) -{ - t_oggamp *x = zz; - pute("1\n"); - pthread_mutex_lock(&x->x_mutex); - while (1) - { - int fd, fifohead; - char *buffer; /* Ogg Vorbis data */ - float *buf; /* encoded PCM floats */ - pute("0\n"); - if (x->x_requestcode == REQUEST_NOTHING) - { - pute("wait 2\n"); - oggamp_cond_signal(&x->x_answercondition); - oggamp_cond_wait(&x->x_requestcondition, &x->x_mutex); - pute("3\n"); - } - // connect to Icecast2 server - else if (x->x_requestcode == REQUEST_CONNECT) - { - char boo[80]; - int sysrtn, wantbytes; - - /* copy connect stuff out of the data structure so we can - relinquish the mutex while we're in oggcast_child_connect(). */ - char *hostname = x->x_hostname; - char *mountpoint = x->x_mountpoint; - t_int portno = x->x_port; - x->x_disconnect = 0; - /* alter the request code so that an ensuing "open" will get - noticed. */ - pute("4\n"); - x->x_requestcode = REQUEST_BUSY; - x->x_connecterror = 0; - - /* if there's already a connection open, close it */ - if (x->x_fd >= 0) - { - fd = x->x_fd; - pthread_mutex_unlock(&x->x_mutex); - oggamp_child_disconnect(fd); - pthread_mutex_lock(&x->x_mutex); - x->x_connectstate = 0; - clock_delay(x->x_clock, 0); - x->x_fd = -1; - if (x->x_requestcode != REQUEST_BUSY) - goto lost; - } - /* open the socket with the mutex unlocked */ - pthread_mutex_unlock(&x->x_mutex); - fd = oggamp_child_connect(hostname, mountpoint, portno); - pthread_mutex_lock(&x->x_mutex); - pute("5\n"); - /* copy back into the instance structure. */ - x->x_connectstate = 1; - clock_delay(x->x_clock, 0); - x->x_fd = fd; - if (fd < 0) - { - x->x_connecterror = fd; - x->x_eof = 1; - x->x_connectstate = 0; - clock_delay(x->x_clock, 0); - pute("connect failed\n"); - goto lost; - } - else - { - /* initialise the decoder */ - if(oggamp_vorbis_init(x, fd) != -1) - { - post("oggamp~: decoder initialised"); - oggamp_child_dographics(x); - } - else - { - post("oggamp~: could not init decoder"); - oggamp_child_disconnect(fd); - post("oggamp~: connection closed due to bitstream error"); - x->x_disconnect = 1; - x->x_fd = -1; - x->x_eof = 1; - x->x_connectstate = 0; - clock_delay(x->x_clock, 0); - pute("initialisation failed\n"); - goto lost; - } - } - /* check if another request has been made; if so, field it */ - if (x->x_requestcode != REQUEST_BUSY) - goto lost; - pute("6\n"); - x->x_fifohead = fifohead = 0; - /* set fifosize from bufsize. fifosize must be a - multiple of the number of bytes eaten for each DSP - tick. We pessimistically assume MAXVECSIZE samples - per tick since that could change. There could be a - problem here if the vector size increases while a - stream is being played... */ - x->x_fifosize = x->x_bufsize - (x->x_bufsize % - (x->x_streamchannels * 2)); - /* arrange for the "request" condition to be signalled x->x_siginterval - times per buffer */ - sprintf(boo, "fifosize %d\n", - x->x_fifosize); - pute(boo); - x->x_sigcountdown = x->x_sigperiod = (x->x_fifosize / (x->x_siginterval * x->x_noutlets * x->x_vecsize)); - - /* in a loop, wait for the fifo to get hungry and feed it */ - while (x->x_requestcode == REQUEST_BUSY) - { - int fifosize = x->x_fifosize; - buf = x->x_buf; - pute("77\n"); - if (x->x_eof) - break; - /* try to get new data from decoder whenever - there is some space at end of buffer */ - if(x->x_fifobytes < fifosize - READSIZE) - { - sprintf(boo, "head %d, tail %d\n", x->x_fifohead, x->x_fifotail); - pute(boo); - - /* we pass x on to the routine since we need the ogg vorbis stuff - to be presend. all other values should not be changed because - mutex is unlocked ! */ - pute("decode... "); - pthread_mutex_unlock(&x->x_mutex); - sysrtn = oggamp_decode_input(x, buf, fifohead, fifosize, fd); - oggamp_child_updategraphics(x); - pthread_mutex_lock(&x->x_mutex); - if (x->x_requestcode != REQUEST_BUSY) - break; - if (sysrtn == 0) - { - if (x->x_eos && !x->x_disconnect) /* got end of stream */ - { - pute("end of stream\n"); - oggamp_vorbis_deinit(x); - if(oggamp_vorbis_init(x, fd) == -1) /* reinit stream */ - { - x->x_state = STATE_IDLE; - x->x_disconnect = 1; - goto quit; - } - } - else if (x->x_eos && x->x_disconnect) /* we're disconnecting */ - { - pute("end of stream: disconnecting\n"); - break; /* go to disconnect */ - } - } - else if (sysrtn < 0) /* got any other error from decoder */ - { - pute("connecterror\n"); - x->x_connecterror = sysrtn; - break; - } - x->x_fifohead = (fifohead + sysrtn) % fifosize; - x->x_fifobytes += sysrtn; - sprintf(boo, "after: head %d, tail %d\n", - x->x_fifohead, x->x_fifotail); - pute(boo); - /* check wether the buffer is filled enough to start streaming */ - } - else /* there is enough data in the buffer :: do nothing */ - { - x->x_state = STATE_STREAM; - pute("wait 7...\n"); - oggamp_cond_signal(&x->x_answercondition); - oggamp_cond_wait(&x->x_requestcondition, &x->x_mutex); - pute("7 done\n"); - continue; - } - pute("8\n"); - fd = x->x_fd; - buf = x->x_buf; - fifohead = x->x_fifohead; - - /* signal parent in case it's waiting for data */ - oggamp_cond_signal(&x->x_answercondition); - } - -lost: - - if (x->x_requestcode == REQUEST_BUSY) - x->x_requestcode = REQUEST_NOTHING; - /* fell out of read loop: close connection if necessary, - set EOF and signal once more */ - if (x->x_fd >= 0) - { - fd = x->x_fd; - pthread_mutex_unlock(&x->x_mutex); - oggamp_vorbis_deinit(x); - oggamp_child_disconnect(fd); - pthread_mutex_lock(&x->x_mutex); - x->x_fd = -1; - oggamp_child_delgraphics(x); - } - oggamp_cond_signal(&x->x_answercondition); - - } - /* reconnect to server */ - else if (x->x_requestcode == REQUEST_RECONNECT) - { - if (x->x_fd >= 0) - { - fd = x->x_fd; - pthread_mutex_unlock(&x->x_mutex); - oggamp_vorbis_deinit(x); - oggamp_child_disconnect(fd); - pthread_mutex_lock(&x->x_mutex); - oggamp_child_delgraphics(x); - x->x_fd = -1; - } - /* connect again */ - x->x_requestcode = REQUEST_CONNECT; - - x->x_connectstate = 0; - clock_delay(x->x_clock, 0); - oggamp_cond_signal(&x->x_answercondition); - } - /* close connection to server (disconnect) */ - else if (x->x_requestcode == REQUEST_CLOSE) - { -quit: - if (x->x_fd >= 0) - { - fd = x->x_fd; - pthread_mutex_unlock(&x->x_mutex); - oggamp_vorbis_deinit(x); - oggamp_child_disconnect(fd); - pthread_mutex_lock(&x->x_mutex); - oggamp_child_delgraphics(x); - x->x_fd = -1; - } - if (x->x_requestcode == REQUEST_CLOSE) - x->x_requestcode = REQUEST_NOTHING; - else if (x->x_requestcode == REQUEST_BUSY) - x->x_requestcode = REQUEST_NOTHING; - else if (x->x_requestcode == REQUEST_CONNECT) - x->x_requestcode = REQUEST_NOTHING; - x->x_connectstate = 0; - clock_delay(x->x_clock, 0); - oggamp_cond_signal(&x->x_answercondition); - } - // quit everything - else if (x->x_requestcode == REQUEST_QUIT) - { - if (x->x_fd >= 0) - { - fd = x->x_fd; - pthread_mutex_unlock(&x->x_mutex); - oggamp_vorbis_deinit(x); - oggamp_child_disconnect(fd); - pthread_mutex_lock(&x->x_mutex); - x->x_fd = -1; - } - x->x_connectstate = 0; - clock_delay(x->x_clock, 0); - x->x_requestcode = REQUEST_NOTHING; - oggamp_cond_signal(&x->x_answercondition); - break; - } - else - { - pute("13\n"); - } - } - pute("thread exit\n"); - pthread_mutex_unlock(&x->x_mutex); - return (0); -} - -/******** the object proper runs in the calling (parent) thread ****/ - -static void oggamp_tick(t_oggamp *x) -{ - outlet_float(x->x_connection, x->x_connectstate); -} - -static void *oggamp_new(t_floatarg fdographics, t_floatarg fnchannels, t_floatarg fbufsize) -{ - t_oggamp *x; - int nchannels = fnchannels, bufsize = fbufsize * 1024, i; - float *buf; - - if (nchannels < 1) - nchannels = 2; /* two channels as default */ - else if (nchannels > MAXSTREAMCHANS) - nchannels = MAXSTREAMCHANS; - /* check / set buffer size */ - if (!bufsize) bufsize = DEFBUFPERCHAN * nchannels; - else if (bufsize < MINBUFSIZE) - bufsize = MINBUFSIZE; - else if (bufsize > MAXBUFSIZE) - bufsize = MAXBUFSIZE; - buf = getbytes(bufsize*sizeof(t_float)); - if (!buf) return (0); - - x = (t_oggamp *)pd_new(oggamp_class); - - for (i = 0; i < nchannels; i++) - outlet_new(&x->x_obj, gensym("signal")); - x->x_noutlets = nchannels; - x->x_connection = outlet_new(&x->x_obj, gensym("float")); - x->x_clock = clock_new(x, (t_method)oggamp_tick); - x->x_outvec = (t_sample **)getbytes(nchannels * sizeof(t_sample *)); - - pthread_mutex_init(&x->x_mutex, 0); - pthread_cond_init(&x->x_requestcondition, 0); - pthread_cond_init(&x->x_answercondition, 0); - - x->x_vecsize = 2; - x->x_disconnect = 0; - x->x_state = STATE_IDLE; - x->x_canvas = canvas_getcurrent(); - x->x_streamchannels = 2; - x->x_fd = -1; - x->x_buf = buf; - x->x_bufsize = bufsize; - x->x_siginterval = 16; /* signal 16 times per buffer */ - x->x_fifosize = x->x_fifohead = x->x_fifotail = x->x_fifobytes = x->x_requestcode = 0; - - x->x_connectstate = 0; /* indicating state of connection */ - - x->x_samplerate = x->x_streamrate = sys_getsr(); - x->x_resample = 0; - x->x_vorbis = 0; - x->x_sync = 0; - x->x_recover = -1; /* just ignore buffer underruns */ - - /* graphical buffer status display */ - x->x_graphic = (int)fdographics; - x->x_canvas = canvas_getcurrent(); - - post(oggamp_version); - post("oggamp~: set buffer to %dk bytes", bufsize/1024); - - /* start child thread */ - pthread_create(&x->x_childthread, 0, oggamp_child_main, x); - return (x); -} - -static t_int *oggamp_perform(t_int *w) -{ - t_oggamp *x = (t_oggamp *)(w[1]); - t_int vecsize = x->x_vecsize, noutlets = x->x_noutlets, i, j, r; - t_float *fp; - t_float *buffer = x->x_buf; - - if (x->x_state == STATE_STREAM) - { - t_int wantbytes, getbytes, havebytes, nchannels, streamchannels = x->x_streamchannels; - - pthread_mutex_lock(&x->x_mutex); - - /* get 'getbytes' bytes from input buffer, convert them to - 'wantbytes' which is the number of bytes after resampling */ - wantbytes = noutlets * vecsize; /* number of bytes we get after resampling */ - havebytes = x->x_fifobytes; - - /* check for error */ - if(havebytes < wantbytes) - { - if(x->x_connecterror) - { /* report error and close connection */ - pd_error(x, "dsp: error %d", x->x_connecterror); - x->x_state = STATE_IDLE; - x->x_requestcode = REQUEST_CLOSE; - x->x_disconnect = 1; - oggamp_cond_signal(&x->x_requestcondition); - pthread_mutex_unlock(&x->x_mutex); - } - if(!x->x_disconnect) /* it's not due to disconnect */ - { - if(x->x_recover == 0) /* disconnect */ - { - x->x_state = STATE_IDLE; - x->x_requestcode = REQUEST_CLOSE; - x->x_disconnect = 1; - oggamp_cond_signal(&x->x_requestcondition); - pthread_mutex_unlock(&x->x_mutex); - } - else if(x->x_recover == 1) /* reconnect */ - { - x->x_state = STATE_IDLE; - x->x_requestcode = REQUEST_RECONNECT; - x->x_disconnect = 1; - oggamp_cond_signal(&x->x_requestcondition); - pthread_mutex_unlock(&x->x_mutex); - } - else /* resume */ - { - x->x_state = STATE_IDLE; - x->x_disconnect = 0; - oggamp_cond_signal(&x->x_requestcondition); - pthread_mutex_unlock(&x->x_mutex); - } - } - goto idle; - } - - /* output audio */ - buffer += x->x_fifotail; /* go to actual audio position */ - - for(j = 0; j < vecsize; j++) - { - for(i = 0; i < noutlets; i++) - { - x->x_outvec[i][j] = *buffer++; - } - } - - - x->x_fifotail += wantbytes; - x->x_fifobytes -= wantbytes; - if (x->x_fifotail >= x->x_fifosize) - x->x_fifotail = 0; - /* signal the child thread */ - if ((--x->x_sigcountdown) <= 0) - { - oggamp_cond_signal(&x->x_requestcondition); - x->x_sigcountdown = x->x_sigperiod; - } - pthread_mutex_unlock(&x->x_mutex); - } - else - { - idle: - for (i = 0; i < noutlets; i++) - for (j = vecsize, fp = x->x_outvec[i]; j--; ) - *fp++ = 0; - } - - return (w+2); -} - - -static void oggamp_disconnect(t_oggamp *x) -{ - /* LATER rethink whether you need the mutex just to set a variable? */ - pthread_mutex_lock(&x->x_mutex); - x->x_disconnect = 1; - x->x_state = STATE_IDLE; - x->x_requestcode = REQUEST_CLOSE; - oggamp_cond_signal(&x->x_requestcondition); - pthread_mutex_unlock(&x->x_mutex); -} - - - /* connect method. Called as: - connect - */ - -static void oggamp_connect(t_oggamp *x, t_symbol *s, int argc, t_atom *argv) -{ - t_symbol *hostsym = atom_getsymbolarg(0, argc, argv); - t_symbol *mountsym = atom_getsymbolarg(1, argc, argv); - t_float portno = atom_getfloatarg(2, argc, argv); - if (!*hostsym->s_name) /* check for hostname */ - return; - if (!portno) /* check wether the portnumber is specified */ - portno = 8000; /* ...assume port 8000 as standard */ - pthread_mutex_lock(&x->x_mutex); - if(x->x_fd == -1) - { - x->x_hostname = hostsym->s_name; - x->x_mountpoint = mountsym->s_name; - x->x_port = portno; - x->x_requestcode = REQUEST_CONNECT; - /* empty buffer */ - x->x_fifotail = 0; - x->x_fifohead = 0; - x->x_fifobytes = 0; - x->x_streamchannels = 2; - x->x_eof = 0; - x->x_connecterror = 0; - x->x_state = STATE_STARTUP; - x->x_disconnect = 0; - oggamp_cond_signal(&x->x_requestcondition); - } - else post("oggamp~: already connected"); - pthread_mutex_unlock(&x->x_mutex); -} - - /* connect using url like "http://localhost:8000/mountpoint" */ -static void oggamp_connect_url(t_oggamp *x, t_symbol *url) -{ - char *hname, *port; - char *h, *p; - char *hostptr; - char *r_hostptr; - char *pathptr; - char *portptr; - char *p0; - char *defaultportstr = "8000"; - t_int stringlength; - t_int portno; - - /* strip http:// or ftp:// */ - p = url->s_name; - if (strncmp(p, "http://", 7) == 0) - p += 7; - - if (strncmp(p, "ftp://", 6) == 0) - p += 6; - - hostptr = p; - while (*p && *p != '/') /* look for end of hostname:port */ - p++; - p++; /* also skip '/' */ - pathptr = p; - - r_hostptr = --p; - while (*p && hostptr < p && *p != ':' && *p != ']') /* split at ':' */ - p--; - - if (!*p || p < hostptr || *p != ':') { - portptr = NULL; - } - else{ - portptr = p + 1; - r_hostptr = p - 1; - } - if (*hostptr == '[' && *r_hostptr == ']') { - hostptr++; - r_hostptr--; - } - - stringlength = r_hostptr - hostptr + 1; - h = getbytes(stringlength + 1); - if (h == NULL) { - hname = NULL; - port = NULL; - pathptr = NULL; - } - strncpy(h, hostptr, stringlength); - *(h+stringlength) = '\0'; - hname = h; /* the hostname */ - - if (portptr) { - stringlength = (pathptr - portptr); - if(!stringlength) portptr = NULL; - } - if (portptr == NULL) { - portptr = defaultportstr; - stringlength = strlen(defaultportstr); - } - p0 = getbytes(stringlength + 1); - if (p0 == NULL) { - freebytes(h, stringlength + 1); - hname = NULL; - port = NULL; - pathptr = NULL; - } - strncpy(p0, portptr, stringlength); - *(p0 + stringlength) = '\0'; - - for (p = p0; *p && isdigit((unsigned char) *p); p++) ; - - *p = '\0'; - port = (unsigned char *) p0; - /* convert port from string to int */ - portno = (int)strtol(port, NULL, 10); - freebytes(p0, stringlength + 1); - /* set values and signal child to connect */ - pthread_mutex_lock(&x->x_mutex); - if(x->x_fd == -1) - { - x->x_hostname = hname; - x->x_mountpoint = pathptr; - x->x_port = portno; - x->x_requestcode = REQUEST_CONNECT; - x->x_fifotail = 0; - x->x_fifohead = 0; - x->x_fifobytes = 0; - x->x_streamchannels = 2; - x->x_eof = 0; - x->x_connecterror = 0; - x->x_state = STATE_STARTUP; - oggamp_cond_signal(&x->x_requestcondition); - } - else post("oggamp~: already connected"); - pthread_mutex_unlock(&x->x_mutex); -} - -static void oggamp_float(t_oggamp *x, t_floatarg f) -{ - if (f != 0) - { - pthread_mutex_lock(&x->x_mutex); - if(x->x_fd == -1) - { - x->x_requestcode = REQUEST_CONNECT; - - x->x_fifotail = 0; - x->x_fifohead = 0; - x->x_fifobytes = 0; - x->x_streamchannels = 2; - x->x_eof = 0; - x->x_connecterror = 0; - x->x_state = STATE_STARTUP; - oggamp_cond_signal(&x->x_requestcondition); - } - else post("oggamp~: already connected"); - pthread_mutex_unlock(&x->x_mutex); - } - else oggamp_disconnect(x); -} - -static void oggamp_dsp(t_oggamp *x, t_signal **sp) -{ - int i, noutlets = x->x_noutlets; - pthread_mutex_lock(&x->x_mutex); - x->x_vecsize = sp[0]->s_n; - - x->x_sigperiod = (x->x_fifosize / (x->x_siginterval * x->x_streamchannels * x->x_vecsize)); - for (i = 0; i < noutlets; i++) - x->x_outvec[i] = sp[i]->s_vec; - pthread_mutex_unlock(&x->x_mutex); - dsp_add(oggamp_perform, 1, x); -} - -static void oggamp_print(t_oggamp *x) -{ - pthread_mutex_lock(&x->x_mutex); - if(x->x_fd >= 0) - { - post("oggamp~: connected to http://%s:%d/%s", x->x_hostname, x->x_port, x->x_mountpoint); - post("oggamp~: bitstream is %d channels @ %ld Hz with %ldkbps nominal bitrate", - x->x_streamchannels, x->x_streamrate, x->x_vi.bitrate_nominal / 1000); - } else post("oggamp~: not connected"); - if(x->x_recover == 0) - post("oggamp~: recover mode set to \"disconnect\" (0)"); - else if(x->x_recover == 1) - post("oggamp~: recover mode set to \"reconnect\" (1)"); - else if(x->x_recover == -1) - post("oggamp~: recover mode set to \"resume\" (-1)"); - pthread_mutex_unlock(&x->x_mutex); -} - - /* set behavior for buffer underruns */ -static void oggamp_recover(t_oggamp *x, t_floatarg f) -{ - pthread_mutex_lock(&x->x_mutex); - if(f <= -1) - { /* mute audio and try to fill buffer again: the default */ - post("oggamp~: set recover mode to \"resume\" (-1)"); - f = -1; - } - else if(f >= 1) - { /* reconnect to server */ - post("oggamp~: set recover mode to \"reconnect\" (1)"); - f = 1; - } - else - { /* disconnect from server */ - post("oggamp~: set recover mode to \"disconnect\" (0)"); - f = 0; - } - x->x_recover = f; - pthread_mutex_unlock(&x->x_mutex); -} - -static void oggamp_free(t_oggamp *x) -{ - /* request QUIT and wait for acknowledge */ - void *threadrtn; - pthread_mutex_lock(&x->x_mutex); - x->x_requestcode = REQUEST_QUIT; - x->x_disconnect = 1; - post("stopping oggamp thread..."); - oggamp_cond_signal(&x->x_requestcondition); - while (x->x_requestcode != REQUEST_NOTHING) - { - post("signalling..."); - oggamp_cond_signal(&x->x_requestcondition); - oggamp_cond_wait(&x->x_answercondition, &x->x_mutex); - } - pthread_mutex_unlock(&x->x_mutex); - if (pthread_join(x->x_childthread, &threadrtn)) - error("oggamp_free: join failed"); - post("... done."); - - pthread_cond_destroy(&x->x_requestcondition); - pthread_cond_destroy(&x->x_answercondition); - pthread_mutex_destroy(&x->x_mutex); - freebytes(x->x_buf, x->x_bufsize*sizeof(t_float)); - freebytes(x->x_outvec, x->x_noutlets * sizeof(t_sample *)); - clock_free(x->x_clock); -} - -void oggamp_tilde_setup(void) -{ - oggamp_class = class_new(gensym("oggamp~"), (t_newmethod)oggamp_new, - (t_method)oggamp_free, sizeof(t_oggamp), 0, A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT, 0); - class_addfloat(oggamp_class, (t_method)oggamp_float); - class_addmethod(oggamp_class, (t_method)oggamp_disconnect, gensym("disconnect"), 0); - class_addmethod(oggamp_class, (t_method)oggamp_dsp, gensym("dsp"), 0); - class_addmethod(oggamp_class, (t_method)oggamp_connect, gensym("connect"), A_GIMME, 0); - class_addmethod(oggamp_class, (t_method)oggamp_connect_url, gensym("connecturl"), A_SYMBOL, 0); - class_addmethod(oggamp_class, (t_method)oggamp_recover, gensym("recover"), A_FLOAT, 0); - class_addmethod(oggamp_class, (t_method)oggamp_print, gensym("print"), 0); -} diff --git a/oggamp~/oggamp~.libs b/oggamp~/oggamp~.libs deleted file mode 100644 index f944930..0000000 --- a/oggamp~/oggamp~.libs +++ /dev/null @@ -1 +0,0 @@ --logg -lvorbis diff --git a/oggamp~/readme b/oggamp~/readme deleted file mode 100644 index 4e7d8b7..0000000 --- a/oggamp~/readme +++ /dev/null @@ -1,87 +0,0 @@ -Version 0.2 -copyright (c) 2002 by Olaf Matthes - -oggamp~ is an ogg/vorbis streaming client external for pd (by Miller -Puckette) that connects to an IceCast2 server. -Graphical buffer status display written by Yves Degoyon (ydegoyon@free.fr). - - -To run oggamp~ place the file oggamp~.dll for win or oggamp~.pd_linux -in the directory of our patch or start pd with '-lib oggamp~' flag. - -To compile oggamp~ on Linux get the ogg/vorbice library from -http://www.vorbis.com/. -You have to modify the makefile to make it point to the place where the -ogg/vorbis library is. - - -This software is published under LGPL terms. - -This is software with ABSOLUTELY NO WARRANTY. -Use it at your OWN RISK. It's possible to damage e.g. hardware or your hearing -due to a bug or for other reasons. - -***************************************************************************** - -oggamp~ uses the ogg/vorbice library to encode audio data. -The latest version of ogg/vorbis can be found at http://www.vorbice.com/ - -Below is the original copyright information taken from the ogg/vorbis library: - - -Copyright (c) 2001, Xiphophorus - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -- Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - -- Redistributions in binary form must reproduce the above copyright -notice, this list of conditions and the following disclaimer in the -documentation and/or other materials provided with the distribution. - -- Neither the name of the Xiphophorus nor the names of its contributors -may be used to endorse or promote products derived from this software -without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR -CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -***************************************************************************** - -Usage: - -To run oggamp~ innormal mode, just use [oggamp~] or, to get the buffer status -displayed, use [oggamp~ 1]. - -Message "connect " connects to an IceCast2 server. -Note that no response about succesfull connection is send by the server. All -messages in the pd console window about connection status depend on the ability -to receive data from the server. -Use "connecturl " to use url-like server adresses (like http://host:post/ -stream.ogg). - -Known bugs and other things: -- pd halts for a moment when oggamp~ connects to the server. This results in a - short audio drop out of sound currently played back. -- resampling not jet supported -- playback does not stop on a buffer underrun -- oggamp~ disconnects at end of stream, i.e. it is not possible to play back - files streamed one after another without manual reconnect - - -Latest version can be found at: -http://www.akustische-kunst.de/puredata/ - -Please report any bugs to olaf.matthes@gmx.de! \ No newline at end of file diff --git a/oggcast~-help.pd b/oggcast~-help.pd new file mode 100644 index 0000000..17c0999 --- /dev/null +++ b/oggcast~-help.pd @@ -0,0 +1,65 @@ +#N canvas 298 121 629 593 10; +#X floatatom 18 36 0 40 16000 0 - - -; +#X msg 468 508 \; pd dsp 1; +#X floatatom 18 524 0 0 0 0 - - -; +#X msg 185 250 disconnect; +#X msg 213 352 print; +#X msg 534 508 \; pd dsp 0; +#X obj 468 482 loadbang; +#X floatatom 97 502 10 0 0 0 - - -; +#X text 131 154 connect ; +#X text 272 45 get latest version at; +#X text 168 502 ogg pages; +#X text 48 527 connection state; +#X text 378 337 channels: 1 or 2 (default); +#X text 210 304 vbr ; +#X msg 100 57 passwd letmein; +#X obj 18 63 osc~ 440; +#X text 330 326 quality settings: 0 - 1 (low - hi); +#X text 227 412 possible tags: TITLE \, ARTIST \, PERFORMER \, DESCRIPTION +\,; +#X text 316 425 GENRE \, LOCATION \, COPYRIGHT \, CONTACT \, DATE; +#X msg 163 194 connect 141.53.196.149 puredata.ogg 8000; +#X msg 133 172 connect localhost puredata.ogg 8000; +#X msg 170 220 connect 141.53.3.18 musicforfilms.ogg 8100; +#X obj 18 475 oggcast~ 2 512; +#X text 273 34 written by Olaf Matthes ; +#X text 252 353 print current settings to console window; +#X text 225 373 comment: ; +#X msg 225 390 ARTIST your_name; +#X text 228 444 '_' (underline) or '=' (equal sign) will get replaced +with spaces!; +#X text 240 493 default is 2 channels and 512k; +#X text 241 505 bytes buffer size; +#X text 119 475 creation arguments: oggcast~ +; +#X msg 195 285 vorbis 44100 2 144 128 96; +#X obj 33 110 noise~; +#X text 19 6 oggcast~ 0.2f - send Ogg Vorbis stream to IceCast2 or +JRoar; +#X text 274 59 http://www.akustische-kunst.org/puredata/; +#X text 195 268 vorbis +; +#X msg 210 320 vbr 44100 2 0.6; +#X msg 117 95 server 0; +#X msg 129 120 server 1; +#X text 175 94 choose JRoar or old Icecast2 as server type (default) +; +#X text 188 121 choose new Icecast2 (final) as server type; +#X connect 0 0 15 0; +#X connect 3 0 22 0; +#X connect 4 0 22 0; +#X connect 6 0 1 0; +#X connect 14 0 22 0; +#X connect 15 0 22 0; +#X connect 19 0 22 0; +#X connect 20 0 22 0; +#X connect 21 0 22 0; +#X connect 22 0 2 0; +#X connect 22 1 7 0; +#X connect 26 0 22 0; +#X connect 31 0 22 0; +#X connect 32 0 22 1; +#X connect 36 0 22 0; +#X connect 37 0 22 0; +#X connect 38 0 22 0; diff --git a/oggcast~.c b/oggcast~.c new file mode 100644 index 0000000..ebd21a1 --- /dev/null +++ b/oggcast~.c @@ -0,0 +1,1466 @@ +/* ------------------------- oggcast~ ----------------------------------------- */ +/* */ +/* Tilde object to send an Ogg Vorbis stream from to IceCast2 server. */ +/* Written by Olaf Matthes (olaf.matthes@gmx.de) */ +/* Get source at http://www.akustische-kunst.org/puredata/pdogg/ */ +/* */ +/* This library is free software; you can redistribute it and/or */ +/* modify it under the terms of the GNU Lesser General Public */ +/* License as published by the Free Software Foundation; either */ +/* version 2 of the License, or (at your option) any later version. */ +/* */ +/* This library is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */ +/* Lesser General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU Lesser General Public */ +/* License along with this library; if not, write to the */ +/* Free Software Foundation, Inc., 59 Temple Place - Suite 330, */ +/* Boston, MA 02111-1307, USA. */ +/* */ +/* Based on PureData by Miller Puckette and others. */ +/* Uses the Ogg Vorbis decoding library which can be found at */ +/* http://www.vorbis.com/ */ +/* */ +/* ---------------------------------------------------------------------------- */ + + /* Pd includes */ +#include "m_pd.h" +#include "g_canvas.h" + /* Vorbis includes */ +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#ifdef WIN32 +# include /* for 'write' in pute-function only */ +# include +# include +#else +# include +# include +# include +# include +# include +# include +# include +# define SOCKET_ERROR -1 +#endif + +#ifdef _MSC_VER +# pragma warning( disable : 4244 ) +# pragma warning( disable : 4305 ) +#endif + +#ifdef WIN32 +# define sys_closesocket closesocket +# define pdogg_strdup(s) _strdup(s) +#else +# define sys_closesocket close +# define pdogg_strdup(s) strdup(s) +#endif + +/************************* oggcast~ object ******************************/ + +/* Each instance of oggcast~ owns a "child" thread for doing the data +transfer. The parent thread signals the child each time: + (1) a connection wants opening or closing; + (2) we've eaten another 1/16 of the shared buffer (so that the + child thread should check if it's time to receive some more.) +The child signals the parent whenever a receive has completed. Signalling +is done by setting "conditions" and putting data in mutex-controlled common +areas. +*/ + + +#define REQUEST_NOTHING 0 +#define REQUEST_CONNECT 1 +#define REQUEST_CLOSE 2 +#define REQUEST_QUIT 3 +#define REQUEST_BUSY 4 +#define REQUEST_DATA 5 +#define REQUEST_REINIT 6 + +#define STATE_IDLE 0 +#define STATE_STARTUP 1 /* connecting and filling the buffer */ +#define STATE_STREAM 2 /* streaming aund audio output */ + +#define READ 4096 /* amount of data we pass on to encoder */ +#define DEFBUFPERCHAN 262144 /* audio output buffer by default: 256k */ +#define MINBUFSIZE 65536 +#define MAXBUFSIZE 16777216 /* arbitrary; just don't want to hang malloc */ +#define STRBUF_SIZE 1024 /* char received from server on startup */ +#define MAXSTREAMCHANS 256 /* maximum number of channels: restricted by Pd? */ +#define UPDATE_INTERVAL 250 /* time in milliseconds between updates of output values */ + +#ifdef __linux__ // 'real' linux only, not for OS X ! +#define SEND_OPT MSG_DONTWAIT|MSG_NOSIGNAL +#else +#define SEND_OPT 0 +#endif + + +static char *oggcast_version = "oggcast~: ogg/vorbis streaming client version 0.2k, written by Olaf Matthes"; + +static t_class *oggcast_class; + +typedef struct _oggcast +{ + t_object x_obj; + t_float *x_f; + t_clock *x_clock_connect; + t_clock *x_clock_pages; + t_outlet *x_connection; /* outlet for connection state */ + t_outlet *x_outpages; /* outlet for no. of ogg pages */ + + t_float *x_buf; /* audio data buffer */ + t_int x_bufsize; /* buffer size in bytes */ + t_int x_ninlets; /* number of audio outlets */ + t_sample **x_outvec; /* audio vectors */ + t_int x_vecsize; /* vector size for transfers */ + t_int x_state; /* opened, running, or idle */ + + /* parameters to communicate with subthread */ + t_int x_requestcode; /* pending request from parent to I/O thread */ + t_int x_connecterror; /* slot for "errno" return */ + + /* buffer stuff */ + t_int x_fifosize; /* buffer size appropriately rounded down */ + t_int x_fifohead; /* index of next byte to get from file */ + t_int x_fifotail; /* index of next byte the ugen will read */ + t_int x_sigcountdown; /* counter for signalling child for more data */ + t_int x_sigperiod; /* number of ticks per signal */ + t_int x_siginterval; /* number of times per buffer (depends on data rate) */ + + /* ogg/vorbis related stuff */ + ogg_stream_state x_os; /* take physical pages, weld into a logical stream of packets */ + ogg_page x_og; /* one Ogg bitstream page. Vorbis packets are inside */ + ogg_packet x_op; /* one raw packet of data for decode */ + vorbis_info x_vi; /* struct that stores all the static vorbis bitstream settings */ + vorbis_comment x_vc; /* struct that stores all the user comments */ + vorbis_dsp_state x_vd; /* central working state for the packet->PCM decoder */ + vorbis_block x_vb; /* local working space for packet->PCM decode */ + + t_int x_eos; /* end of stream */ + t_float x_pages; /* number of pages that have been output to server */ + t_float x_lastpages; + + /* ringbuffer stuff */ + t_float *x_buffer; /* data to be buffered (ringbuffer)*/ + t_int x_bytesbuffered; /* number of unprocessed bytes in buffer */ + + /* ogg/vorbis format stuff */ + t_int x_samplerate; /* samplerate of stream (default = getsr() ) */ + t_int x_skip; /* samples from input to skip (for resampling) */ + t_float x_quality; /* desired quality level from 0.0 to 1.0 (lo to hi) */ + t_int x_br_max; /* max. bitrate of ogg/vorbis stream */ + t_int x_br_nom; /* nom. bitrate of ogg/vorbis stream */ + t_int x_br_min; /* min. bitrate of ogg/vorbis stream */ + t_int x_channels; /* number of channels (1 or 2) */ + t_int x_vbr; + + /* IceCast server stuff */ + char* x_passwd; /* password for server */ + char* x_bcname; /* name of broadcast */ + char* x_bcurl; /* url of broadcast */ + char* x_bcgenre; /* genre of broadcast */ + char* x_bcdescription; /* description */ + char* x_bcartist; /* artist */ + char* x_bclocation; + char* x_bccopyright; + char* x_bcperformer; + char* x_bccontact; + char* x_bcdate; /* system date when broadcast started */ + char* x_hostname; /* name or IP of host to connect to */ + char* x_mountpoint; /* mountpoint for IceCast server */ + t_float x_port; /* port number on which the connection is made */ + t_int x_bcpublic; /* do(n't) publish broadcast on www.oggcast.com */ + t_int x_servertype; /* type of server: 0 = JRoar or old Icecast2; 1 = new Icecast2 */ + + + + + t_int x_connectstate; /* indicates to state of socket connection */ + t_int x_outvalue; /* value that has last been output via outlet */ + t_int x_fd; /* the socket number */ + t_resample x_resample; /* resampling unit */ + t_int x_recover; /* indicate how to behave on buffer underruns */ + + /* thread stuff */ + pthread_mutex_t x_mutex; + pthread_cond_t x_requestcondition; + pthread_cond_t x_answercondition; + pthread_t x_childthread; +} t_oggcast; + + +static char base64table[65] = { + 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P', + 'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f', + 'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v', + 'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/', +}; + + /* This isn't efficient, but it doesn't need to be */ +char *oggcast_util_base64_encode(char *data) +{ + int len = strlen(data); + char *out = (char *)getbytes(len*4/3 + 4); + char *result = out; + int chunk; + + while(len > 0) { + chunk = (len >3)?3:len; + *out++ = base64table[(*data & 0xFC)>>2]; + *out++ = base64table[((*data & 0x03)<<4) | ((*(data+1) & 0xF0) >> 4)]; + + switch(chunk) { + case 3: + *out++ = base64table[((*(data+1) & 0x0F)<<2) | ((*(data+2) & 0xC0)>>6)]; + *out++ = base64table[(*(data+2)) & 0x3F]; + break; + case 2: + *out++ = base64table[((*(data+1) & 0x0F)<<2)]; + *out++ = '='; + break; + case 1: + *out++ = '='; + *out++ = '='; + break; + } + data += chunk; + len -= chunk; + } + *out = 0; + + return result; +} + + /* check server for writeability */ +static int oggcast_checkserver(t_int sock) +{ + fd_set fdset; + struct timeval ztout; + fd_set writeset; + fd_set exceptset; + + FD_ZERO(&writeset); + FD_ZERO(&exceptset); + FD_SET(sock, &writeset ); + FD_SET(sock, &exceptset ); + + if(select(sock+1, NULL, &writeset, &exceptset, &ztout) > 0) + { + if(!FD_ISSET(sock, &writeset)) + { + post("oggcast~: can not write data to the server, quitting"); + return -1; + } + if(FD_ISSET(sock, &exceptset)) + { + post("oggcast~: socket returned an error, quitting"); + return -1; + } + } + return 0; +} + + /* stream ogg/vorbis to IceCast2 server */ +static int oggcast_stream(t_oggcast *x, t_int fd) +{ + int err = -1; /* error return code */ + int pages = 0; + + /* write out pages (if any) */ + while(!x->x_eos) + { + int result=ogg_stream_pageout(&(x->x_os),&(x->x_og)); + if(result==0)break; + err = send(fd, x->x_og.header, x->x_og.header_len, SEND_OPT); + if(err < 0) + { + error("oggcast~: could not send ogg header to server (%d)", err); + x->x_eos = 1; /* indicate (artificial) end of stream */ + return err; + } + err = send(fd, x->x_og.body, x->x_og.body_len, SEND_OPT); + if(err < 0) + { + error("oggcast~: could not send ogg body to server (%d)", err); + x->x_eos = 1; /* indicate (artificial) end of stream */ + return err; + } + pages++; /* count number of pages */ + /* there might be more than one pages we have to send */ + if(ogg_page_eos(&(x->x_og)))x->x_eos=1; + } + return (pages); +} + + /* ogg/vorbis decoder setup */ +static int oggcast_vorbis_init(t_oggcast *x) +{ + int err = -1; + + x->x_eos = 0; + x->x_skip = 1; /* assume no resampling */ + /* choose an encoding mode */ + vorbis_info_init(&(x->x_vi)); + + if(x->x_samplerate != sys_getsr()) /* downsampling for Oliver (http://radiostudio.org) */ + { + if(sys_getsr() / x->x_samplerate == 2.0) + { + post("oggcast~: downsampling from %.0f to %d Hz", sys_getsr(), x->x_samplerate); + x->x_skip = 2; + } + else if(sys_getsr() / x->x_samplerate == 4.0) + { + post("oggcast~: downsampling from %.0f to %d Hz", sys_getsr(), x->x_samplerate); + x->x_skip = 4; + } + else if(sys_getsr() / x->x_samplerate == 3.0) + { + post("oggcast~: downsampling from %.0f to %d Hz", sys_getsr(), x->x_samplerate); + x->x_skip = 3; + } else post("oggcast~: warning: resampling from %.0f to %d not supported", sys_getsr(), x->x_samplerate); + } + if(x->x_vbr == 1) + { /* quality based setting */ + if(vorbis_encode_init_vbr(&(x->x_vi), x->x_channels, x->x_samplerate, x->x_quality)) + { + post("oggcast~: ogg/vorbis mode initialisation failed: invalid parameters for quality"); + vorbis_info_clear(&(x->x_vi)); + return (-1); + } + } + else + { /* bitrate based setting */ + if(vorbis_encode_init(&(x->x_vi), x->x_channels, x->x_samplerate, x->x_br_max*1024, x->x_br_nom*1024, x->x_br_min*1024)) + { + post("oggcast~: ogg/vorbis mode initialisation failed: invalid parameters for quality"); + vorbis_info_clear(&(x->x_vi)); + return (-1); + } + } + + /* add a comment */ + vorbis_comment_init(&(x->x_vc)); + vorbis_comment_add_tag(&(x->x_vc),"TITLE", x->x_bcname); + vorbis_comment_add_tag(&(x->x_vc),"ARTIST", x->x_bcartist); + vorbis_comment_add_tag(&(x->x_vc),"GENRE",x->x_bcgenre); + vorbis_comment_add_tag(&(x->x_vc),"DESCRIPTION", x->x_bcdescription); + vorbis_comment_add_tag(&(x->x_vc),"LOCATION",x->x_bclocation); + vorbis_comment_add_tag(&(x->x_vc),"PERFORMER",x->x_bcperformer); + vorbis_comment_add_tag(&(x->x_vc),"COPYRIGHT",x->x_bccopyright); + vorbis_comment_add_tag(&(x->x_vc),"CONTACT",x->x_bccontact); + vorbis_comment_add_tag(&(x->x_vc),"DATE",x->x_bcdate); + vorbis_comment_add_tag(&(x->x_vc),"ENCODER","oggcast~ v0.2 for pure-data"); + + /* set up the analysis state and auxiliary encoding storage */ + vorbis_analysis_init(&(x->x_vd),&(x->x_vi)); + vorbis_block_init(&(x->x_vd),&(x->x_vb)); + + /* set up our packet->stream encoder */ + /* pick a random serial number; that way we can more likely build + chained streams just by concatenation */ + srand(time(NULL)); + ogg_stream_init(&(x->x_os),rand()); + + /* Vorbis streams begin with three headers; the initial header (with + most of the codec setup parameters) which is mandated by the Ogg + bitstream spec. The second header holds any comment fields. The + third header holds the bitstream codebook. We merely need to + make the headers, then pass them to libvorbis one at a time; + libvorbis handles the additional Ogg bitstream constraints */ + + { + ogg_packet header; + ogg_packet header_comm; + ogg_packet header_code; + + vorbis_analysis_headerout(&(x->x_vd),&(x->x_vc),&header,&header_comm,&header_code); + ogg_stream_packetin(&(x->x_os),&header); /* automatically placed in its own page */ + ogg_stream_packetin(&(x->x_os),&header_comm); + ogg_stream_packetin(&(x->x_os),&header_code); + + /* We don't have to write out here, but doing so makes streaming + * much easier, so we do, flushing ALL pages. This ensures the actual + * audio data will start on a new page + * + * IceCast2 server will take this as a first info about our stream + */ + while(!x->x_eos) + { + int result=ogg_stream_flush(&(x->x_os),&(x->x_og)); + if(result==0)break; + err = send(x->x_fd, x->x_og.header, x->x_og.header_len, SEND_OPT); + if(err < 0) + { + error("oggcast~: could not send ogg header to server (%d)", err); + x->x_eos = 1; /* indicate end of stream */ + return (-1); + } + err = send(x->x_fd, x->x_og.body, x->x_og.body_len, SEND_OPT); + if(err < 0) + { + error("oggcast~: could not send ogg body to server (%d)", err); + x->x_eos = 1; /* indicate end of stream */ + return (-1); + } + } + } + return (0); +} + + /* deinit the ogg/vorbis decoder */ +static void oggcast_vorbis_deinit(t_oggcast *x) +{ + vorbis_analysis_wrote(&(x->x_vd),0); + /* get rid of remaining data in encoder, if any */ + while(vorbis_analysis_blockout(&(x->x_vd),&(x->x_vb))==1) + { + vorbis_analysis(&(x->x_vb),NULL); + vorbis_bitrate_addblock(&(x->x_vb)); + + while(vorbis_bitrate_flushpacket(&(x->x_vd),&(x->x_op))) + { + ogg_stream_packetin(&(x->x_os),&(x->x_op)); + oggcast_stream(x, x->x_fd); + } + } + /* clean up and exit. vorbis_info_clear() must be called last */ + ogg_stream_clear(&(x->x_os)); + vorbis_block_clear(&(x->x_vb)); + vorbis_dsp_clear(&(x->x_vd)); + vorbis_comment_clear(&(x->x_vc)); + vorbis_info_clear(&(x->x_vi)); +} + + /* encode ogg/vorbis and stream new data */ +static int oggcast_encode(t_oggcast *x, float *buf, int channels, int fifosize, int fd) +{ + unsigned short i, ch; + int err = 0; + int n, pages = 0; + + /* expose the buffer to submit data */ + float **inbuffer=vorbis_analysis_buffer(&(x->x_vd),READ * channels); + + /* read from buffer */ + for(n = 0; n < READ; n++) /* fill encode buffer */ + { + for(ch = 0; ch < channels; ch++) + { + inbuffer[ch][n] = *buf++; + } + } + /* tell the library how much we actually submitted */ + vorbis_analysis_wrote(&(x->x_vd),n); + + /* vorbis does some data preanalysis, then divvies up blocks for + more involved (potentially parallel) processing. Get a single + block for encoding now */ + while(vorbis_analysis_blockout(&(x->x_vd),&(x->x_vb))==1) + { + /* analysis, assume we want to use bitrate management */ + vorbis_analysis(&(x->x_vb),NULL); + vorbis_bitrate_addblock(&(x->x_vb)); + + while(vorbis_bitrate_flushpacket(&(x->x_vd),&(x->x_op))) + { + /* weld the packet into the bitstream */ + ogg_stream_packetin(&(x->x_os),&(x->x_op)); + err = oggcast_stream(x, fd); /* stream packet to server */ + if(err >= 0) + { + pages += err; /* count pages */ + } + else return (err); + } + } + return (pages); +} + + /* connect to icecast2 server */ +static int oggcast_child_connect(char *hostname, char *mountpoint, t_int portno, + char *passwd, char *bcname, char *bcurl, + char *bcgenre, t_int bcpublic, t_int br_nom, t_int servertype) +{ + struct sockaddr_in server; + struct hostent *hp; + + /* variables used for communication with server */ + const char * buf = 0; + char resp[STRBUF_SIZE]; + unsigned int len; + fd_set fdset; + struct timeval tv; + t_int sockfd; /* our internal handle for the socket */ + t_int ret; + + sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (sockfd < 0) + { + error("oggcast~: internal error while attempting to open socket"); + return (-1); + } + + /* connect socket using hostname provided in command line */ + server.sin_family = AF_INET; + hp = gethostbyname(hostname); + if (hp == 0) + { + post("oggcast~: bad host?"); + sys_closesocket(sockfd); + return (-1); + } + memcpy((char *)&server.sin_addr, (char *)hp->h_addr, hp->h_length); + + /* assign client port number */ + server.sin_port = htons((unsigned short)portno); + + /* try to connect. */ + post("oggcast~: connecting to port %d", portno); + if (connect(sockfd, (struct sockaddr *) &server, sizeof (server)) < 0) + { + error("oggcast~: connection failed!\n"); + sys_closesocket(sockfd); + return (-1); + } + + /* sheck if we can read/write from/to the socket */ + FD_ZERO( &fdset); + FD_SET( sockfd, &fdset); + tv.tv_sec = 0; /* seconds */ + tv.tv_usec = 500; /* microseconds */ + + ret = select(sockfd + 1, &fdset, NULL, NULL, &tv); + if(ret != 0) + { + error("oggcast~: can not read from socket"); + sys_closesocket(sockfd); + return (-1); + } + + post("oggcast~: logging in to IceCast2 server..."); + /* now try to log in at IceCast2 server using ICE/1.0 scheme */ + if(servertype == 0) + { + /* send the request, a string like: "SOURCE / ICE/1.0\n" */ + buf = "SOURCE "; + send(sockfd, buf, strlen(buf), SEND_OPT); + buf = "/"; + send(sockfd, buf, strlen(buf), SEND_OPT); + buf = mountpoint; + send(sockfd, buf, strlen(buf), SEND_OPT); + buf = " ICE/1.0"; + send(sockfd, buf, strlen(buf), SEND_OPT); + /* send the ice headers */ + /* password */ + buf = "\nice-password: "; + send(sockfd, buf, strlen(buf), SEND_OPT); + buf = passwd; + send(sockfd, buf, strlen(buf), SEND_OPT); + /* name */ + buf = "\r\nice-name: "; + send(sockfd, buf, strlen(buf), SEND_OPT); + buf = bcname; + send(sockfd, buf, strlen(buf), SEND_OPT); + /* url */ + buf = "\r\nice-url: "; + send(sockfd, buf, strlen(buf), SEND_OPT); + buf = bcurl; + send(sockfd, buf, strlen(buf), SEND_OPT); + /* genre */ + buf = "\r\nice-genre: "; + send(sockfd, buf, strlen(buf), SEND_OPT); + buf = bcgenre; + send(sockfd, buf, strlen(buf), SEND_OPT); + /* public */ + buf = "\r\nice-public: "; + send(sockfd, buf, strlen(buf), SEND_OPT); + if(bcpublic==0) /* set the public flag for broadcast */ + { + buf = "no"; + } + else + { + buf ="yes"; + } + send(sockfd, buf, strlen(buf), SEND_OPT); + /* bitrate */ + buf = "\r\nice-bitrate: "; + send(sockfd, buf, strlen(buf), SEND_OPT); + if(sprintf(resp, "%d", br_nom) == -1) /* convert int to a string */ + { + error("oggcast~: wrong bitrate"); + } + send(sockfd, resp, strlen(resp), SEND_OPT); + /* description */ + buf = "\r\nice-description: "; + send(sockfd, buf, strlen(buf), SEND_OPT); + buf = "ogg/vorbis streamed from pure-data with oggcast~"; + send(sockfd, buf, strlen(buf), SEND_OPT); + /* end of header */ + buf = "\r\n\r\n"; + send(sockfd, buf, strlen(buf), SEND_OPT); + /* end login for IceCast using ICE/1.0 scheme */ + } + else /* or try to log in at IceCast2 server using HTTP/1.0 base auth scheme */ + { + /* send the request, a string like: "SOURCE / HTTP/1.0\n\r" */ + buf = "SOURCE /"; + send(sockfd, buf, strlen(buf), SEND_OPT); + buf = mountpoint; + send(sockfd, buf, strlen(buf), SEND_OPT); + buf = " HTTP/1.0\r\n"; + send(sockfd, buf, strlen(buf), SEND_OPT); + /* send basic authorization */ + sprintf(resp, "source:%s", passwd); + buf = oggcast_util_base64_encode(resp); + sprintf(resp, "Authorization: Basic %s\r\n", buf); + send(sockfd, resp, strlen(resp), SEND_OPT); + /* send content type */ + buf = "Content-Type: application/x-ogg"; + send(sockfd, buf, strlen(buf), SEND_OPT); + /* send the ice headers */ + /* password */ + buf = "\r\nice-password: "; + send(sockfd, buf, strlen(buf), SEND_OPT); + buf = passwd; + send(sockfd, buf, strlen(buf), SEND_OPT); + /* name */ + buf = "\r\nice-name: "; + send(sockfd, buf, strlen(buf), SEND_OPT); + buf = bcname; + send(sockfd, buf, strlen(buf), SEND_OPT); + /* url */ + buf = "\r\nice-url: "; + send(sockfd, buf, strlen(buf), SEND_OPT); + buf = bcurl; + send(sockfd, buf, strlen(buf), SEND_OPT); + /* genre */ + buf = "\r\nice-genre: "; + send(sockfd, buf, strlen(buf), SEND_OPT); + buf = bcgenre; + send(sockfd, buf, strlen(buf), SEND_OPT); + /* public */ + buf = "\r\nice-public: "; + send(sockfd, buf, strlen(buf), SEND_OPT); + if(bcpublic==0) /* set the public flag for broadcast */ + { + buf = "0"; + } + else + { + buf = "1"; + } + send(sockfd, buf, strlen(buf), SEND_OPT); + /* bitrate */ + buf = "\r\nice-bitrate: "; + send(sockfd, buf, strlen(buf), SEND_OPT); + if(sprintf(resp, "%d", br_nom) == -1) /* convert int to a string */ + { + error("oggcast~: wrong bitrate"); + } + send(sockfd, resp, strlen(resp), SEND_OPT); + /* description */ + buf = "\r\nice-description: "; + send(sockfd, buf, strlen(buf), SEND_OPT); + buf = "Ogg/Vorbis streamed from PureData with oggcast~\r\n"; + send(sockfd, buf, strlen(buf), SEND_OPT); + /* end of header: write an empty line */ + buf = "\r\n"; + send(sockfd, buf, strlen(buf), SEND_OPT); + /* end login for IceCast2 using ICE/1.0 scheme */ + } + + /* check if we can write to server */ + if(oggcast_checkserver(sockfd)!= 0) + { + post("oggcast~: error: server refused to receive data"); + return (-1); + } + + post("oggcast~: logged in to http://%s:%d/%s", hp->h_name, portno, mountpoint); + + return (sockfd); +} + + + +static void oggcast_child_disconnect(t_int fd) +{ + sys_closesocket(fd); + post("oggcast~: connection closed"); +} +/************** the child thread which performs data I/O ***********/ + +#if 0 /* set this to 1 to get debugging output */ +static void pute(char *s) /* debug routine */ +{ + write(2, s, strlen(s)); +} +#else +#define pute(x) +#endif + +#if 1 +#define oggcast_cond_wait pthread_cond_wait +#define oggcast_cond_signal pthread_cond_signal +#else +#include /* debugging version... */ +#include +static void oggcast_fakewait(pthread_mutex_t *b) +{ + struct timeval timout; + timout.tv_sec = 0; + timout.tv_usec = 1000000; + pthread_mutex_unlock(b); + select(0, 0, 0, 0, &timout); + pthread_mutex_lock(b); +} + +void oggcast_banana( void) +{ + struct timeval timout; + timout.tv_sec = 0; + timout.tv_usec = 200000; + pute("banana1\n"); + select(0, 0, 0, 0, &timout); + pute("banana2\n"); +} + + +#define oggcast_cond_wait(a,b) oggcast_fakewait(b) +#define oggcast_cond_signal(a) +#endif + +static void *oggcast_child_main(void *zz) +{ + t_oggcast *x = zz; + time_t now; /* to get the time */ + pute("1\n"); + pthread_mutex_lock(&x->x_mutex); + while (1) + { + int fd, fifotail; + pute("0\n"); + if (x->x_requestcode == REQUEST_NOTHING) + { + pute("wait 2\n"); + oggcast_cond_signal(&x->x_answercondition); + oggcast_cond_wait(&x->x_requestcondition, &x->x_mutex); + pute("3\n"); + } + // connect to Icecast2 server + else if (x->x_requestcode == REQUEST_CONNECT) + { + char boo[100]; + int sysrtn, wantbytes; + + /* copy connect stuff out of the data structure so we can + relinquish the mutex while we're connecting to server. */ + char *hostname = x->x_hostname; + char *mountpoint = x->x_mountpoint; + t_int portno = x->x_port; + char *passwd = x->x_passwd; + char *bcname = x->x_bcname; + char *bcgenre = x->x_bcgenre; + char *bcurl = x->x_bcurl; + t_int bcpublic = x->x_bcpublic; + t_int br_nom = x->x_br_nom; + t_int servertype = x->x_servertype; + /* alter the request code so that an ensuing "open" will get + noticed. */ + pute("4\n"); + x->x_requestcode = REQUEST_BUSY; + x->x_connecterror = 0; + + /* open the socket with the mutex unlocked in case + we're not already connected */ + if(x->x_fd < 0) + { + pthread_mutex_unlock(&x->x_mutex); + fd = oggcast_child_connect(hostname, mountpoint, portno, passwd, bcname, + bcurl, bcgenre, bcpublic, br_nom, servertype); + pthread_mutex_lock(&x->x_mutex); + pute("5\n"); + /* copy back into the instance structure. */ + x->x_connectstate = 1; + clock_delay(x->x_clock_connect, 0); + x->x_fd = fd; + if (fd < 0) + { + x->x_connecterror = fd; + x->x_connectstate = 0; + clock_delay(x->x_clock_connect, 0); + pute("connect failed\n"); + goto lost; + } + else + { + /* get the time for the DATE comment */ + now=time(NULL); + x->x_bcdate = pdogg_strdup(ctime(&now)); /*--moo*/ + x->x_pages = 0; + clock_delay(x->x_clock_pages, 0); + /* initialise the encoder */ + if(oggcast_vorbis_init(x) == 0) + { + post("oggcast~: ogg/vorbis encoder initialised"); + x->x_eos = 0; + x->x_state = STATE_STREAM; + } + else + { + post("oggcast~: could not init encoder"); + oggcast_child_disconnect(fd); + post("oggcast~: connection closed due to initialisation error"); + x->x_fd = -1; + x->x_connectstate = 0; + clock_delay(x->x_clock_connect, 0); + pute("oggcast~: initialisation failed\n"); + goto lost; + } + } + x->x_fifotail = fifotail = 0; + /* set fifosize from bufsize. fifosize must be a + multiple of the number of bytes eaten for each DSP + tick. We pessimistically assume MAXVECSIZE samples + per tick since that could change. There could be a + problem here if the vector size increases while a + stream is being played... */ + x->x_fifosize = x->x_bufsize - (x->x_bufsize % (x->x_channels * READ)); + /* arrange for the "request" condition to be signalled x->x_siginterval + times per buffer */ + sprintf(boo, "fifosize %d\n", x->x_fifosize); + pute(boo); + x->x_sigcountdown = x->x_sigperiod = (x->x_fifosize / (x->x_siginterval * x->x_channels * x->x_vecsize)); + } + /* check if another request has been made; if so, field it */ + if (x->x_requestcode != REQUEST_BUSY) + goto lost; + pute("6\n"); + + while (x->x_requestcode == REQUEST_BUSY) + { + int fifosize = x->x_fifosize, fifotail, channels; + float *buf = x->x_buf; + pute("77\n"); + + /* if the head is < the tail, we can immediately write + from tail to end of fifo to disk; otherwise we hold off + writing until there are at least WRITESIZE bytes in the + buffer */ + if (x->x_fifohead < x->x_fifotail || + x->x_fifohead >= x->x_fifotail + (READ * x->x_channels) + || (x->x_requestcode == REQUEST_CLOSE && + x->x_fifohead != x->x_fifotail)) + { /* encode audio and send to server */ + pute("8\n"); + fifotail = x->x_fifotail; + channels = x->x_channels; + fd = x->x_fd; + pthread_mutex_unlock(&x->x_mutex); + sysrtn = oggcast_encode(x, buf + fifotail, channels, fifosize, fd); + pthread_mutex_lock(&x->x_mutex); + if (x->x_requestcode != REQUEST_BUSY && + x->x_requestcode != REQUEST_CLOSE) + break; + if (sysrtn < 0) + { + post("oggcast~: closing due to error..."); + goto lost; + } + else + { + x->x_fifotail += (READ * x->x_channels); + x->x_pages += sysrtn; + if (x->x_fifotail >= fifosize) + x->x_fifotail = 0; + } + sprintf(boo, "after: head %d, tail %d, pages %d\n", x->x_fifohead, x->x_fifotail, sysrtn); + pute(boo); + } + else /* just wait... */ + { + pute("wait 7a ...\n"); + oggcast_cond_signal(&x->x_answercondition); + pute("signalled\n"); + oggcast_cond_wait(&x->x_requestcondition, + &x->x_mutex); + pute("7a done\n"); + continue; + } + /* signal parent in case it's waiting for data */ + oggcast_cond_signal(&x->x_answercondition); + } + } + + /* reinit encoder (settings have changed) */ + else if (x->x_requestcode == REQUEST_REINIT) + { + pthread_mutex_unlock(&x->x_mutex); + oggcast_vorbis_deinit(x); + oggcast_vorbis_init(x); + pthread_mutex_lock(&x->x_mutex); + post("oggcast~: ogg/vorbis encoder reinitialised"); + x->x_state = STATE_STREAM; + if (x->x_requestcode == REQUEST_REINIT) + x->x_requestcode = REQUEST_CONNECT; + oggcast_cond_signal(&x->x_answercondition); + } + /* close connection to server (disconnect) */ + else if (x->x_requestcode == REQUEST_CLOSE) + { +lost: + x->x_state = STATE_IDLE; + if (x->x_fd >= 0) + { + fd = x->x_fd; + pthread_mutex_unlock(&x->x_mutex); + oggcast_vorbis_deinit(x); + oggcast_child_disconnect(fd); + pthread_mutex_lock(&x->x_mutex); + x->x_fd = -1; + } + if (x->x_requestcode == REQUEST_CLOSE) + x->x_requestcode = REQUEST_NOTHING; + if (x->x_requestcode == REQUEST_BUSY) /* disconnect due to error */ + x->x_requestcode = REQUEST_NOTHING; + x->x_connectstate = 0; + clock_delay(x->x_clock_connect, 0); + x->x_eos = 1; + oggcast_cond_signal(&x->x_answercondition); + } + // quit everything + else if (x->x_requestcode == REQUEST_QUIT) + { + x->x_state = STATE_IDLE; + if (x->x_fd >= 0) + { + fd = x->x_fd; + pthread_mutex_unlock(&x->x_mutex); + oggcast_vorbis_deinit(x); + oggcast_child_disconnect(fd); + pthread_mutex_lock(&x->x_mutex); + x->x_fd = -1; + } + x->x_connectstate = 0; + clock_delay(x->x_clock_connect, 0); + x->x_requestcode = REQUEST_NOTHING; + oggcast_cond_signal(&x->x_answercondition); + break; + } + else + { + pute("13\n"); + } + } + pute("thread exit\n"); + pthread_mutex_unlock(&x->x_mutex); + return (0); +} + +/******** the object proper runs in the calling (parent) thread ****/ + +static void oggcast_tick_connect(t_oggcast *x) +{ + pthread_mutex_lock(&x->x_mutex); + outlet_float(x->x_connection, x->x_connectstate); + pthread_mutex_unlock(&x->x_mutex); +} + +static void oggcast_tick_pages(t_oggcast *x) +{ + /* output new no. of pages if anything changed */ + t_float pages; + pthread_mutex_lock(&x->x_mutex); + pages = x->x_pages; /* get current value with mutex locked */ + pthread_mutex_unlock(&x->x_mutex); + if(pages != x->x_lastpages) + { + outlet_float(x->x_outpages, pages); + x->x_lastpages = pages; + } + clock_delay(x->x_clock_pages, UPDATE_INTERVAL); /* come back again... */ +} + +static void *oggcast_new(t_floatarg fnchannels, t_floatarg fbufsize) +{ + t_oggcast *x; + int nchannels = fnchannels, bufsize = fbufsize * 1024, i; + float *buf; + + if (nchannels < 1) + nchannels = 2; /* two channels as default */ + else if (nchannels > MAXSTREAMCHANS) + nchannels = MAXSTREAMCHANS; + /* check / set buffer size */ + if (bufsize <= 0) bufsize = DEFBUFPERCHAN * nchannels; + else if (bufsize < MINBUFSIZE) + bufsize = MINBUFSIZE; + else if (bufsize > MAXBUFSIZE) + bufsize = MAXBUFSIZE; + buf = getbytes(bufsize*sizeof(t_float)); + if (!buf) return (0); + + x = (t_oggcast *)pd_new(oggcast_class); + + for (i = 1; i < nchannels; i++) + inlet_new (&x->x_obj, &x->x_obj.ob_pd, gensym ("signal"), gensym ("signal")); + x->x_connection = outlet_new(&x->x_obj, gensym("float")); + x->x_outpages = outlet_new(&x->x_obj, gensym("float")); + x->x_ninlets = nchannels; + x->x_outvec = getbytes(nchannels*sizeof(t_sample *)); + x->x_clock_connect = clock_new(x, (t_method)oggcast_tick_connect); + x->x_clock_pages = clock_new(x, (t_method)oggcast_tick_pages); + + pthread_mutex_init(&x->x_mutex, 0); + pthread_cond_init(&x->x_requestcondition, 0); + pthread_cond_init(&x->x_answercondition, 0); + + x->x_vecsize = 2; + x->x_state = STATE_IDLE; + x->x_buf = buf; + x->x_bufsize = bufsize; + x->x_siginterval = 32; /* signal 32 times per buffer */ + /* I found this to be most efficient on my machine */ + x->x_fifosize = x->x_fifohead = x->x_fifotail = x->x_requestcode = 0; + + x->x_connectstate = 0; /* indicating state of connection */ + x->x_outvalue = 0; /* value at output currently is 0 */ + + x->x_samplerate = sys_getsr(); + x->x_resample.upsample = x->x_resample.downsample = 1; /* don't resample */ + + + x->x_fd = -1; + x->x_eos = 0; + x->x_vbr = 1; /* use the vbr setting by default */ + x->x_passwd = "letmein"; + x->x_samplerate = sys_getsr(); /* default to Pd's sampling rate */ + x->x_skip = 1; /* no resampling supported */ + x->x_quality = 0.4; /* quality 0.4 gives roughly 128kbps VBR stream */ + x->x_channels = nchannels; /* stereo */ + x->x_br_max = 144; + x->x_br_nom = 128; + x->x_br_min = 96; + x->x_pages = x->x_lastpages = 0; + x->x_bcname = pdogg_strdup("ogg/vorbis stream"); /*--moo: added strdup() */ + x->x_bcurl = pdogg_strdup("http://www.akustische-kunst.org/puredata/"); + x->x_bcgenre = pdogg_strdup("experimental"); + x->x_bcdescription = pdogg_strdup("ogg/vorbis stream emitted from pure-data with oggcast~"); + x->x_bcartist = pdogg_strdup("Pd and oggcast~ v0.2"); + x->x_bclocation = pdogg_strdup(x->x_bcurl); + x->x_bccopyright = pdogg_strdup(""); + x->x_bcperformer = pdogg_strdup(""); + x->x_bccontact = pdogg_strdup(""); + x->x_bcdate = pdogg_strdup(""); + x->x_bcpublic = 1; + x->x_mountpoint = "puredata.ogg"; + x->x_servertype = 1; /* HTTP/1.0 protocol for Icecast2 */ + + post(oggcast_version); + post("oggcast~: set buffer to %dk bytes", bufsize / 1024); + post("oggcast~: encoding %d channels @ %d Hz", x->x_channels, x->x_samplerate); + + clock_delay(x->x_clock_pages, 0); + pthread_create(&x->x_childthread, 0, oggcast_child_main, x); + return (x); +} + +static t_int *oggcast_perform(t_int *w) +{ + t_oggcast *x = (t_oggcast *)(w[1]); + int vecsize = x->x_vecsize, ninlets = x->x_ninlets, channels = x->x_channels, i, j, skip = x->x_skip; + float *sp = x->x_buf; + pthread_mutex_lock(&x->x_mutex); + if (x->x_state != STATE_IDLE) + { + int wantbytes; + /* get 'wantbytes' bytes from inlet */ + wantbytes = channels * vecsize / skip; /* we'll get vecsize bytes per channel */ + /* check if there is enough space in buffer to write all samples */ + while (x->x_fifotail > x->x_fifohead && + x->x_fifotail < x->x_fifohead + wantbytes + 1) + { + pute("wait...\n"); + oggcast_cond_signal(&x->x_requestcondition); + oggcast_cond_wait(&x->x_answercondition, &x->x_mutex); + pute("done\n"); + } + + /* output audio */ + sp += x->x_fifohead; + + if(ninlets >= channels) + { + for(j = 0; j < vecsize; j += skip) + { + for(i = 0; i < channels; i++) + { + *sp++ = x->x_outvec[i][j]; + } + } + } + else if(channels == ninlets * 2) /* convert mono -> stereo */ + { + for(j = 0; j < vecsize; j += skip) + { + for(i = 0; i < ninlets; i++) + { + *sp++ = x->x_outvec[i][j]; + *sp++ = x->x_outvec[i][j]; + } + } + } + + + x->x_fifohead += wantbytes; + if (x->x_fifohead >= x->x_fifosize) + x->x_fifohead = 0; + /* signal the child thread */ + if ((--x->x_sigcountdown) <= 0) + { + pute("signal 1\n"); + oggcast_cond_signal(&x->x_requestcondition); + x->x_sigcountdown = x->x_sigperiod; + } + } + + pthread_mutex_unlock(&x->x_mutex); + + return (w+2); +} + + +static void oggcast_disconnect(t_oggcast *x) +{ + /* LATER rethink whether you need the mutex just to set a variable? */ + pthread_mutex_lock(&x->x_mutex); + if(x->x_fd >= 0) + { + x->x_state = STATE_IDLE; + x->x_requestcode = REQUEST_CLOSE; + oggcast_cond_signal(&x->x_requestcondition); + } + else post("oggcast~: not connected"); + pthread_mutex_unlock(&x->x_mutex); +} + + + /* connect method. Called as: + connect + */ + +static void oggcast_connect(t_oggcast *x, t_symbol *s, int argc, t_atom *argv) +{ + t_symbol *hostsym = atom_getsymbolarg(0, argc, argv); + t_symbol *mountsym = atom_getsymbolarg(1, argc, argv); + t_float portno = atom_getfloatarg(2, argc, argv); + if (!*hostsym->s_name) /* check for hostname */ + return; + if (!portno) /* check wether the portnumber is specified */ + portno = 8000; /* ...assume port 8000 as standard */ + pthread_mutex_lock(&x->x_mutex); + if(x->x_fd >= 0) + { + post("oggcast~: already connected"); + } + else + { + x->x_requestcode = REQUEST_CONNECT; + x->x_hostname = hostsym->s_name; + x->x_mountpoint = mountsym->s_name; + x->x_port = portno; + + x->x_fifotail = 0; + x->x_fifohead = 0; + // if(x->x_recover != 1)x->x_fifobytes = 0; + + x->x_connecterror = 0; + x->x_state = STATE_STARTUP; + oggcast_cond_signal(&x->x_requestcondition); + } + pthread_mutex_unlock(&x->x_mutex); +} + +static void oggcast_float(t_oggcast *x, t_floatarg f) +{ + if (f != 0) + { + pthread_mutex_lock(&x->x_mutex); + if(x->x_fd >= 0) + { + post("oggcast~: already connected"); + } + else + { + if(x->x_recover != 1) + { + x->x_fifotail = 0; + x->x_fifohead = 0; + } + x->x_requestcode = REQUEST_CONNECT; + + x->x_fifotail = 0; + x->x_fifohead = 0; + + x->x_connecterror = 0; + x->x_state = STATE_STARTUP; + oggcast_cond_signal(&x->x_requestcondition); + } + pthread_mutex_unlock(&x->x_mutex); + } + else oggcast_disconnect(x); +} + +static void oggcast_dsp(t_oggcast *x, t_signal **sp) +{ + int i, ninlets = x->x_ninlets; + pthread_mutex_lock(&x->x_mutex); + x->x_vecsize = sp[0]->s_n; + + x->x_sigperiod = (x->x_fifosize / (x->x_siginterval * ninlets * x->x_vecsize)); + for (i = 0; i < ninlets; i++) + x->x_outvec[i] = sp[i]->s_vec; + pthread_mutex_unlock(&x->x_mutex); + dsp_add(oggcast_perform, 1, x); +} + + /* set password for oggcast server */ +static void oggcast_password(t_oggcast *x, t_symbol *password) +{ + pthread_mutex_lock(&x->x_mutex); + x->x_passwd = password->s_name; + pthread_mutex_unlock(&x->x_mutex); +} + /* set comment fields for header (reads in just anything) */ +static void oggcast_comment(t_oggcast *x, t_symbol *s, t_int argc, t_atom* argv) +{ + t_binbuf *b = binbuf_new(); + char* comment; + int length; + binbuf_add(b, argc, argv); + binbuf_gettext(b, &comment, &length); + + pthread_mutex_lock(&x->x_mutex); + if(strstr(s->s_name, "ARTIST")) + { + if (x->x_bcartist) free(x->x_bcartist); + x->x_bcartist = pdogg_strdup(comment); /*-- moo: added strdup() */ + post("oggcast~: ARTIST = %s", x->x_bcartist); + } + else if(strstr(s->s_name, "GENRE")) + { + free(x->x_bcgenre); + x->x_bcgenre = pdogg_strdup(comment); + post("oggcast~: GENRE = %s", x->x_bcgenre); + } + else if(strstr(s->s_name, "TITLE")) + { + free(x->x_bcname); + x->x_bcname = pdogg_strdup(comment); + post("oggcast~: TITLE = %s", x->x_bcname); + } + else if(strstr(s->s_name, "PERFORMER")) + { + free(x->x_bcperformer); + x->x_bcperformer = pdogg_strdup(comment); + post("oggcast~: PERFORMER = %s",x->x_bcperformer); + } + else if(strstr(s->s_name, "LOCATION")) + { + free(x->x_bclocation); + x->x_bclocation = pdogg_strdup(comment); + post("oggcast~: LOCATION = %s",x->x_bclocation); + } + else if(strstr(s->s_name, "COPYRIGHT")) + { + free(x->x_bccopyright); + x->x_bccopyright = pdogg_strdup(comment); + post("oggcast~: COPYRIGHT = %s", x->x_bccopyright); + } + else if(strstr(s->s_name, "CONTACT")) + { + free(x->x_bccontact); + x->x_bccontact = pdogg_strdup(comment); + post("oggcast~: CONTACT = %s", x->x_bccontact); + } + else if(strstr(s->s_name, "DESCRIPTION")) + { + free(x->x_bcdescription); + x->x_bcdescription = pdogg_strdup(comment); + post("oggcast~: DESCRIPTION = %s", x->x_bcdescription); + } + else if(strstr(s->s_name, "DATE")) + { + free(x->x_bcdate); + x->x_bcdate = pdogg_strdup(comment); + post("oggcast~: DATE = %s", x->x_bcdate); + } + else post("oggcast~: no method for %s", s->s_name); + if(x->x_state == STATE_STREAM) + { + x->x_state = STATE_IDLE; + x->x_requestcode = REQUEST_REINIT; + oggcast_cond_signal(&x->x_requestcondition); + } + pthread_mutex_unlock(&x->x_mutex); + freebytes(comment, strlen(comment)); + binbuf_free(b); +} + /* settings for variable bitrate encoding */ +static void oggcast_vbr(t_oggcast *x, t_floatarg fsr, t_floatarg fchannels, + t_floatarg fquality) +{ + pthread_mutex_lock(&x->x_mutex); + x->x_vbr = 1; + x->x_samplerate = (t_int)fsr; + x->x_quality = fquality; + x->x_channels = (t_int)fchannels; + post("oggcast~: %d channels @ %d Hz, quality %.2f", x->x_channels, x->x_samplerate, x->x_quality); + if(x->x_state == STATE_STREAM) + { + x->x_state = STATE_IDLE; + x->x_requestcode = REQUEST_REINIT; + oggcast_cond_signal(&x->x_requestcondition); + } + pthread_mutex_unlock(&x->x_mutex); +} + + /* settings for bitrate-based vbr encoding */ +static void oggcast_vorbis(t_oggcast *x, t_floatarg fsr, t_floatarg fchannels, + t_floatarg fmax, t_floatarg fnom, t_floatarg fmin) +{ + pthread_mutex_lock(&x->x_mutex); + x->x_vbr = 0; + x->x_samplerate = (t_int)fsr; + x->x_channels = (t_int)fchannels; + x->x_br_max = (t_int)fmax; + x->x_br_nom = (t_int)fnom; + x->x_br_min = (t_int)fmin; + post("oggcast~: %d channels @ %d Hz, bitrates: max. %d / nom. %d / min. %d", + x->x_channels, x->x_samplerate, x->x_br_max, x->x_br_nom, x->x_br_min); + if(x->x_state == STATE_STREAM) + { + x->x_state = STATE_IDLE; + x->x_requestcode = REQUEST_REINIT; + oggcast_cond_signal(&x->x_requestcondition); + } + pthread_mutex_unlock(&x->x_mutex); +} + /* select server type */ +static void oggcast_server(t_oggcast *x, t_floatarg f) +{ + pthread_mutex_lock(&x->x_mutex); + if(f) + { + x->x_servertype = 1; + post("oggcast~: set server type to new Icecast2 (HTTP/1.0 scheme)"); + } + else + { + x->x_servertype = 0; + post("oggcast~: set server type to JRoar (ICE/1.0 scheme)"); + } + pthread_mutex_unlock(&x->x_mutex); +} + /* print settings to pd's console window */ +static void oggcast_print(t_oggcast *x) +{ + pthread_mutex_lock(&x->x_mutex); + if(x->x_servertype) + post("oggcast~: server type is Icecast2"); + else + post("oggcast~: server type is JRoar"); + post("oggcast~: mountpoint at Icecast2: %s", x->x_mountpoint); + if(x->x_vbr == 1) + { + post("oggcast~: Ogg Vorbis encoder: %d channels @ %d Hz, quality %.2f", x->x_channels, x->x_samplerate, x->x_quality); + } + else + { + post("oggcast~: Ogg Vorbis encoder: %d channels @ %d Hz, bitrates: max. %d, nom. %d, min. %d", + x->x_channels, x->x_samplerate, x->x_br_max, x->x_br_nom, x->x_br_min); + } + post("oggcast~: Ogg Vorbis comments:"); + post(" TITLE = %s", x->x_bcname); + post(" ARTIST = %s", x->x_bcartist); + post(" PERFORMER = %s", x->x_bcperformer); + post(" GENRE = %s", x->x_bcgenre); + post(" LOCATION = %s", x->x_bclocation); + post(" COPYRIGHT = %s", x->x_bccopyright); + post(" CONTACT = %s", x->x_bccontact); + post(" DESCRIPTION = %s", x->x_bcdescription); + post(" DATE = %s", x->x_bcdate); + pthread_mutex_unlock(&x->x_mutex); +} + + +static void oggcast_free(t_oggcast *x) +{ + /* request QUIT and wait for acknowledge */ + void *threadrtn; + pthread_mutex_lock(&x->x_mutex); + x->x_requestcode = REQUEST_QUIT; + post("stopping oggcast thread..."); + oggcast_cond_signal(&x->x_requestcondition); + while (x->x_requestcode != REQUEST_NOTHING) + { + post("signalling..."); + oggcast_cond_signal(&x->x_requestcondition); + oggcast_cond_wait(&x->x_answercondition, &x->x_mutex); + } + pthread_mutex_unlock(&x->x_mutex); + if (pthread_join(x->x_childthread, &threadrtn)) + error("oggcast_free: join failed"); + post("... done."); + + pthread_cond_destroy(&x->x_requestcondition); + pthread_cond_destroy(&x->x_answercondition); + pthread_mutex_destroy(&x->x_mutex); + freebytes(x->x_buf, x->x_bufsize*sizeof(t_float)); + freebytes(x->x_outvec, x->x_ninlets*sizeof(t_sample *)); + clock_free(x->x_clock_connect); + clock_free(x->x_clock_pages); + + /*-- moo: free dynamically allocated comment strings --*/ + free(x->x_bcname); + free(x->x_bcurl); + free(x->x_bcgenre); + free(x->x_bcdescription); + free(x->x_bcartist); + free(x->x_bclocation); + free(x->x_bccopyright); + free(x->x_bcperformer); + free(x->x_bccontact); + free(x->x_bcdate); +} + +void oggcast_tilde_setup(void) +{ + oggcast_class = class_new(gensym("oggcast~"), (t_newmethod)oggcast_new, + (t_method)oggcast_free, sizeof(t_oggcast), 0, A_DEFFLOAT, A_DEFFLOAT, 0); + CLASS_MAINSIGNALIN(oggcast_class, t_oggcast, x_f ); + class_addfloat(oggcast_class, (t_method)oggcast_float); + class_addmethod(oggcast_class, (t_method)oggcast_disconnect, gensym("disconnect"), 0); + class_addmethod(oggcast_class, (t_method)oggcast_dsp, gensym("dsp"), 0); + class_addmethod(oggcast_class, (t_method)oggcast_connect, gensym("connect"), A_GIMME, 0); + class_addmethod(oggcast_class, (t_method)oggcast_print, gensym("print"), 0); + class_addmethod(oggcast_class, (t_method)oggcast_password, gensym("passwd"), A_SYMBOL, 0); + class_addmethod(oggcast_class, (t_method)oggcast_vorbis, gensym("vorbis"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0); + class_addmethod(oggcast_class, (t_method)oggcast_vbr, gensym("vbr"), A_FLOAT, A_FLOAT, A_FLOAT, 0); + class_addmethod(oggcast_class, (t_method)oggcast_server, gensym("server"), A_FLOAT, 0); + class_addanything(oggcast_class, oggcast_comment); +} diff --git a/oggcast~/HISTORY b/oggcast~/HISTORY deleted file mode 100644 index 04a7528..0000000 --- a/oggcast~/HISTORY +++ /dev/null @@ -1,83 +0,0 @@ -Version history of oggcast~ external for pure-data - -v 0.2k (29th Sept. 2003): -- fixed a bug in multichannel support -- fixed a memory leak in Icecast2 login - -v 0.2i (13th September 2003): -- updated login scheme to work with latest Icecast2 server - from CVS (tested on Windows and Debian Linux) -- changed handling of comment tags: it's no longer necessarry - to replace spaces with underscores! Just use spaces for spaces. -- fixed (no: avoided!) the bug that deleted underscores from - the Pd patch -- made Icecast2 login the default scheme - -v 0.2h (27th March 2003): -- added HTTP base auth login for (alpha) Icecast2 server: - message 'server 1' switches to Icecast2, 'server 0' back - top JRoar and PeerCast compatibility (which is the default) - -v 0.2g (3rd August 2002): -- finally fixed the bug that made oggcast~ crash after a - while. seems to be realted with output from child thread - using t_clocks but couldn't proove that - -v 0.2f (20st july 2002): -- recompiled with the final 1.0 release of Ogg Vorbis -- changed the URL to the new akustische-kunst.org domain - -v 0.2e (5th july 2002): -- added simple downsampling to stream at lower sample rates - -v 0.2d (21st june 2002 - stable release!): -- cleaned up code a bit -- now clean disconnect in case server dies or closes - socket - -v 0.2c (13th june 2002): -- fixed some small bugs -- buffer size now in kbytes per channel -- some more info-printout - -v 0.2b (12th june 2002): -- completetly rewritten multithreaded version, first - sort-of-stable release -- KNOWN BUG: eats too much CPU power - -v 0.1g (23rd feb. 2002, not for public use!): -- added multithreading functionality: now sending data - to server in a second thread -- now included the static ogg vorbis libraries - no dll's - needed under win any longer -- fixed a bug that sometimes made pd crash - -v 0.1f (11th feb. 2002): -- converted ringbuffer to simple buffer of fixed size - -v 0.1e (10th feb. 2002): -- now really fixed the bug that made pd die when server - disconnected - -v 0.1d (9th feb. 2002): -- fixed a bug in the "vorbis" setting that made on-the-run - changes impossible -- introduced a function to check writeability of the socket -- fixed the bug that crashed pd due to an access violation - in vorbis.dll when send() returned an error (more of a - fast workaround) -- corrected bitrate setting, now it really is _k_bps - - -v 0.1c (9th feb. 2002): -- added support for setting / changing the comment tags in - the ogg/vorbis header, spaces have to be replaced by '=' -- fixed a bug in oggcast_stream() that made Pd crash - now - it's vorbis.dll that makes Pd crash... ;-( - -v 0.1b (not released): -- added support for changing encoder settings when stream - is running (no need to disconnect), seems to be unstable - on linux (with JRoar) - -v 0.1: first public release \ No newline at end of file diff --git a/oggcast~/oggcast~-help.pd b/oggcast~/oggcast~-help.pd deleted file mode 100644 index 17c0999..0000000 --- a/oggcast~/oggcast~-help.pd +++ /dev/null @@ -1,65 +0,0 @@ -#N canvas 298 121 629 593 10; -#X floatatom 18 36 0 40 16000 0 - - -; -#X msg 468 508 \; pd dsp 1; -#X floatatom 18 524 0 0 0 0 - - -; -#X msg 185 250 disconnect; -#X msg 213 352 print; -#X msg 534 508 \; pd dsp 0; -#X obj 468 482 loadbang; -#X floatatom 97 502 10 0 0 0 - - -; -#X text 131 154 connect ; -#X text 272 45 get latest version at; -#X text 168 502 ogg pages; -#X text 48 527 connection state; -#X text 378 337 channels: 1 or 2 (default); -#X text 210 304 vbr ; -#X msg 100 57 passwd letmein; -#X obj 18 63 osc~ 440; -#X text 330 326 quality settings: 0 - 1 (low - hi); -#X text 227 412 possible tags: TITLE \, ARTIST \, PERFORMER \, DESCRIPTION -\,; -#X text 316 425 GENRE \, LOCATION \, COPYRIGHT \, CONTACT \, DATE; -#X msg 163 194 connect 141.53.196.149 puredata.ogg 8000; -#X msg 133 172 connect localhost puredata.ogg 8000; -#X msg 170 220 connect 141.53.3.18 musicforfilms.ogg 8100; -#X obj 18 475 oggcast~ 2 512; -#X text 273 34 written by Olaf Matthes ; -#X text 252 353 print current settings to console window; -#X text 225 373 comment: ; -#X msg 225 390 ARTIST your_name; -#X text 228 444 '_' (underline) or '=' (equal sign) will get replaced -with spaces!; -#X text 240 493 default is 2 channels and 512k; -#X text 241 505 bytes buffer size; -#X text 119 475 creation arguments: oggcast~ -; -#X msg 195 285 vorbis 44100 2 144 128 96; -#X obj 33 110 noise~; -#X text 19 6 oggcast~ 0.2f - send Ogg Vorbis stream to IceCast2 or -JRoar; -#X text 274 59 http://www.akustische-kunst.org/puredata/; -#X text 195 268 vorbis -; -#X msg 210 320 vbr 44100 2 0.6; -#X msg 117 95 server 0; -#X msg 129 120 server 1; -#X text 175 94 choose JRoar or old Icecast2 as server type (default) -; -#X text 188 121 choose new Icecast2 (final) as server type; -#X connect 0 0 15 0; -#X connect 3 0 22 0; -#X connect 4 0 22 0; -#X connect 6 0 1 0; -#X connect 14 0 22 0; -#X connect 15 0 22 0; -#X connect 19 0 22 0; -#X connect 20 0 22 0; -#X connect 21 0 22 0; -#X connect 22 0 2 0; -#X connect 22 1 7 0; -#X connect 26 0 22 0; -#X connect 31 0 22 0; -#X connect 32 0 22 1; -#X connect 36 0 22 0; -#X connect 37 0 22 0; -#X connect 38 0 22 0; diff --git a/oggcast~/oggcast~.c b/oggcast~/oggcast~.c deleted file mode 100644 index ebd21a1..0000000 --- a/oggcast~/oggcast~.c +++ /dev/null @@ -1,1466 +0,0 @@ -/* ------------------------- oggcast~ ----------------------------------------- */ -/* */ -/* Tilde object to send an Ogg Vorbis stream from to IceCast2 server. */ -/* Written by Olaf Matthes (olaf.matthes@gmx.de) */ -/* Get source at http://www.akustische-kunst.org/puredata/pdogg/ */ -/* */ -/* This library is free software; you can redistribute it and/or */ -/* modify it under the terms of the GNU Lesser General Public */ -/* License as published by the Free Software Foundation; either */ -/* version 2 of the License, or (at your option) any later version. */ -/* */ -/* This library is distributed in the hope that it will be useful, */ -/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ -/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */ -/* Lesser General Public License for more details. */ -/* */ -/* You should have received a copy of the GNU Lesser General Public */ -/* License along with this library; if not, write to the */ -/* Free Software Foundation, Inc., 59 Temple Place - Suite 330, */ -/* Boston, MA 02111-1307, USA. */ -/* */ -/* Based on PureData by Miller Puckette and others. */ -/* Uses the Ogg Vorbis decoding library which can be found at */ -/* http://www.vorbis.com/ */ -/* */ -/* ---------------------------------------------------------------------------- */ - - /* Pd includes */ -#include "m_pd.h" -#include "g_canvas.h" - /* Vorbis includes */ -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#ifdef WIN32 -# include /* for 'write' in pute-function only */ -# include -# include -#else -# include -# include -# include -# include -# include -# include -# include -# define SOCKET_ERROR -1 -#endif - -#ifdef _MSC_VER -# pragma warning( disable : 4244 ) -# pragma warning( disable : 4305 ) -#endif - -#ifdef WIN32 -# define sys_closesocket closesocket -# define pdogg_strdup(s) _strdup(s) -#else -# define sys_closesocket close -# define pdogg_strdup(s) strdup(s) -#endif - -/************************* oggcast~ object ******************************/ - -/* Each instance of oggcast~ owns a "child" thread for doing the data -transfer. The parent thread signals the child each time: - (1) a connection wants opening or closing; - (2) we've eaten another 1/16 of the shared buffer (so that the - child thread should check if it's time to receive some more.) -The child signals the parent whenever a receive has completed. Signalling -is done by setting "conditions" and putting data in mutex-controlled common -areas. -*/ - - -#define REQUEST_NOTHING 0 -#define REQUEST_CONNECT 1 -#define REQUEST_CLOSE 2 -#define REQUEST_QUIT 3 -#define REQUEST_BUSY 4 -#define REQUEST_DATA 5 -#define REQUEST_REINIT 6 - -#define STATE_IDLE 0 -#define STATE_STARTUP 1 /* connecting and filling the buffer */ -#define STATE_STREAM 2 /* streaming aund audio output */ - -#define READ 4096 /* amount of data we pass on to encoder */ -#define DEFBUFPERCHAN 262144 /* audio output buffer by default: 256k */ -#define MINBUFSIZE 65536 -#define MAXBUFSIZE 16777216 /* arbitrary; just don't want to hang malloc */ -#define STRBUF_SIZE 1024 /* char received from server on startup */ -#define MAXSTREAMCHANS 256 /* maximum number of channels: restricted by Pd? */ -#define UPDATE_INTERVAL 250 /* time in milliseconds between updates of output values */ - -#ifdef __linux__ // 'real' linux only, not for OS X ! -#define SEND_OPT MSG_DONTWAIT|MSG_NOSIGNAL -#else -#define SEND_OPT 0 -#endif - - -static char *oggcast_version = "oggcast~: ogg/vorbis streaming client version 0.2k, written by Olaf Matthes"; - -static t_class *oggcast_class; - -typedef struct _oggcast -{ - t_object x_obj; - t_float *x_f; - t_clock *x_clock_connect; - t_clock *x_clock_pages; - t_outlet *x_connection; /* outlet for connection state */ - t_outlet *x_outpages; /* outlet for no. of ogg pages */ - - t_float *x_buf; /* audio data buffer */ - t_int x_bufsize; /* buffer size in bytes */ - t_int x_ninlets; /* number of audio outlets */ - t_sample **x_outvec; /* audio vectors */ - t_int x_vecsize; /* vector size for transfers */ - t_int x_state; /* opened, running, or idle */ - - /* parameters to communicate with subthread */ - t_int x_requestcode; /* pending request from parent to I/O thread */ - t_int x_connecterror; /* slot for "errno" return */ - - /* buffer stuff */ - t_int x_fifosize; /* buffer size appropriately rounded down */ - t_int x_fifohead; /* index of next byte to get from file */ - t_int x_fifotail; /* index of next byte the ugen will read */ - t_int x_sigcountdown; /* counter for signalling child for more data */ - t_int x_sigperiod; /* number of ticks per signal */ - t_int x_siginterval; /* number of times per buffer (depends on data rate) */ - - /* ogg/vorbis related stuff */ - ogg_stream_state x_os; /* take physical pages, weld into a logical stream of packets */ - ogg_page x_og; /* one Ogg bitstream page. Vorbis packets are inside */ - ogg_packet x_op; /* one raw packet of data for decode */ - vorbis_info x_vi; /* struct that stores all the static vorbis bitstream settings */ - vorbis_comment x_vc; /* struct that stores all the user comments */ - vorbis_dsp_state x_vd; /* central working state for the packet->PCM decoder */ - vorbis_block x_vb; /* local working space for packet->PCM decode */ - - t_int x_eos; /* end of stream */ - t_float x_pages; /* number of pages that have been output to server */ - t_float x_lastpages; - - /* ringbuffer stuff */ - t_float *x_buffer; /* data to be buffered (ringbuffer)*/ - t_int x_bytesbuffered; /* number of unprocessed bytes in buffer */ - - /* ogg/vorbis format stuff */ - t_int x_samplerate; /* samplerate of stream (default = getsr() ) */ - t_int x_skip; /* samples from input to skip (for resampling) */ - t_float x_quality; /* desired quality level from 0.0 to 1.0 (lo to hi) */ - t_int x_br_max; /* max. bitrate of ogg/vorbis stream */ - t_int x_br_nom; /* nom. bitrate of ogg/vorbis stream */ - t_int x_br_min; /* min. bitrate of ogg/vorbis stream */ - t_int x_channels; /* number of channels (1 or 2) */ - t_int x_vbr; - - /* IceCast server stuff */ - char* x_passwd; /* password for server */ - char* x_bcname; /* name of broadcast */ - char* x_bcurl; /* url of broadcast */ - char* x_bcgenre; /* genre of broadcast */ - char* x_bcdescription; /* description */ - char* x_bcartist; /* artist */ - char* x_bclocation; - char* x_bccopyright; - char* x_bcperformer; - char* x_bccontact; - char* x_bcdate; /* system date when broadcast started */ - char* x_hostname; /* name or IP of host to connect to */ - char* x_mountpoint; /* mountpoint for IceCast server */ - t_float x_port; /* port number on which the connection is made */ - t_int x_bcpublic; /* do(n't) publish broadcast on www.oggcast.com */ - t_int x_servertype; /* type of server: 0 = JRoar or old Icecast2; 1 = new Icecast2 */ - - - - - t_int x_connectstate; /* indicates to state of socket connection */ - t_int x_outvalue; /* value that has last been output via outlet */ - t_int x_fd; /* the socket number */ - t_resample x_resample; /* resampling unit */ - t_int x_recover; /* indicate how to behave on buffer underruns */ - - /* thread stuff */ - pthread_mutex_t x_mutex; - pthread_cond_t x_requestcondition; - pthread_cond_t x_answercondition; - pthread_t x_childthread; -} t_oggcast; - - -static char base64table[65] = { - 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P', - 'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f', - 'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v', - 'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/', -}; - - /* This isn't efficient, but it doesn't need to be */ -char *oggcast_util_base64_encode(char *data) -{ - int len = strlen(data); - char *out = (char *)getbytes(len*4/3 + 4); - char *result = out; - int chunk; - - while(len > 0) { - chunk = (len >3)?3:len; - *out++ = base64table[(*data & 0xFC)>>2]; - *out++ = base64table[((*data & 0x03)<<4) | ((*(data+1) & 0xF0) >> 4)]; - - switch(chunk) { - case 3: - *out++ = base64table[((*(data+1) & 0x0F)<<2) | ((*(data+2) & 0xC0)>>6)]; - *out++ = base64table[(*(data+2)) & 0x3F]; - break; - case 2: - *out++ = base64table[((*(data+1) & 0x0F)<<2)]; - *out++ = '='; - break; - case 1: - *out++ = '='; - *out++ = '='; - break; - } - data += chunk; - len -= chunk; - } - *out = 0; - - return result; -} - - /* check server for writeability */ -static int oggcast_checkserver(t_int sock) -{ - fd_set fdset; - struct timeval ztout; - fd_set writeset; - fd_set exceptset; - - FD_ZERO(&writeset); - FD_ZERO(&exceptset); - FD_SET(sock, &writeset ); - FD_SET(sock, &exceptset ); - - if(select(sock+1, NULL, &writeset, &exceptset, &ztout) > 0) - { - if(!FD_ISSET(sock, &writeset)) - { - post("oggcast~: can not write data to the server, quitting"); - return -1; - } - if(FD_ISSET(sock, &exceptset)) - { - post("oggcast~: socket returned an error, quitting"); - return -1; - } - } - return 0; -} - - /* stream ogg/vorbis to IceCast2 server */ -static int oggcast_stream(t_oggcast *x, t_int fd) -{ - int err = -1; /* error return code */ - int pages = 0; - - /* write out pages (if any) */ - while(!x->x_eos) - { - int result=ogg_stream_pageout(&(x->x_os),&(x->x_og)); - if(result==0)break; - err = send(fd, x->x_og.header, x->x_og.header_len, SEND_OPT); - if(err < 0) - { - error("oggcast~: could not send ogg header to server (%d)", err); - x->x_eos = 1; /* indicate (artificial) end of stream */ - return err; - } - err = send(fd, x->x_og.body, x->x_og.body_len, SEND_OPT); - if(err < 0) - { - error("oggcast~: could not send ogg body to server (%d)", err); - x->x_eos = 1; /* indicate (artificial) end of stream */ - return err; - } - pages++; /* count number of pages */ - /* there might be more than one pages we have to send */ - if(ogg_page_eos(&(x->x_og)))x->x_eos=1; - } - return (pages); -} - - /* ogg/vorbis decoder setup */ -static int oggcast_vorbis_init(t_oggcast *x) -{ - int err = -1; - - x->x_eos = 0; - x->x_skip = 1; /* assume no resampling */ - /* choose an encoding mode */ - vorbis_info_init(&(x->x_vi)); - - if(x->x_samplerate != sys_getsr()) /* downsampling for Oliver (http://radiostudio.org) */ - { - if(sys_getsr() / x->x_samplerate == 2.0) - { - post("oggcast~: downsampling from %.0f to %d Hz", sys_getsr(), x->x_samplerate); - x->x_skip = 2; - } - else if(sys_getsr() / x->x_samplerate == 4.0) - { - post("oggcast~: downsampling from %.0f to %d Hz", sys_getsr(), x->x_samplerate); - x->x_skip = 4; - } - else if(sys_getsr() / x->x_samplerate == 3.0) - { - post("oggcast~: downsampling from %.0f to %d Hz", sys_getsr(), x->x_samplerate); - x->x_skip = 3; - } else post("oggcast~: warning: resampling from %.0f to %d not supported", sys_getsr(), x->x_samplerate); - } - if(x->x_vbr == 1) - { /* quality based setting */ - if(vorbis_encode_init_vbr(&(x->x_vi), x->x_channels, x->x_samplerate, x->x_quality)) - { - post("oggcast~: ogg/vorbis mode initialisation failed: invalid parameters for quality"); - vorbis_info_clear(&(x->x_vi)); - return (-1); - } - } - else - { /* bitrate based setting */ - if(vorbis_encode_init(&(x->x_vi), x->x_channels, x->x_samplerate, x->x_br_max*1024, x->x_br_nom*1024, x->x_br_min*1024)) - { - post("oggcast~: ogg/vorbis mode initialisation failed: invalid parameters for quality"); - vorbis_info_clear(&(x->x_vi)); - return (-1); - } - } - - /* add a comment */ - vorbis_comment_init(&(x->x_vc)); - vorbis_comment_add_tag(&(x->x_vc),"TITLE", x->x_bcname); - vorbis_comment_add_tag(&(x->x_vc),"ARTIST", x->x_bcartist); - vorbis_comment_add_tag(&(x->x_vc),"GENRE",x->x_bcgenre); - vorbis_comment_add_tag(&(x->x_vc),"DESCRIPTION", x->x_bcdescription); - vorbis_comment_add_tag(&(x->x_vc),"LOCATION",x->x_bclocation); - vorbis_comment_add_tag(&(x->x_vc),"PERFORMER",x->x_bcperformer); - vorbis_comment_add_tag(&(x->x_vc),"COPYRIGHT",x->x_bccopyright); - vorbis_comment_add_tag(&(x->x_vc),"CONTACT",x->x_bccontact); - vorbis_comment_add_tag(&(x->x_vc),"DATE",x->x_bcdate); - vorbis_comment_add_tag(&(x->x_vc),"ENCODER","oggcast~ v0.2 for pure-data"); - - /* set up the analysis state and auxiliary encoding storage */ - vorbis_analysis_init(&(x->x_vd),&(x->x_vi)); - vorbis_block_init(&(x->x_vd),&(x->x_vb)); - - /* set up our packet->stream encoder */ - /* pick a random serial number; that way we can more likely build - chained streams just by concatenation */ - srand(time(NULL)); - ogg_stream_init(&(x->x_os),rand()); - - /* Vorbis streams begin with three headers; the initial header (with - most of the codec setup parameters) which is mandated by the Ogg - bitstream spec. The second header holds any comment fields. The - third header holds the bitstream codebook. We merely need to - make the headers, then pass them to libvorbis one at a time; - libvorbis handles the additional Ogg bitstream constraints */ - - { - ogg_packet header; - ogg_packet header_comm; - ogg_packet header_code; - - vorbis_analysis_headerout(&(x->x_vd),&(x->x_vc),&header,&header_comm,&header_code); - ogg_stream_packetin(&(x->x_os),&header); /* automatically placed in its own page */ - ogg_stream_packetin(&(x->x_os),&header_comm); - ogg_stream_packetin(&(x->x_os),&header_code); - - /* We don't have to write out here, but doing so makes streaming - * much easier, so we do, flushing ALL pages. This ensures the actual - * audio data will start on a new page - * - * IceCast2 server will take this as a first info about our stream - */ - while(!x->x_eos) - { - int result=ogg_stream_flush(&(x->x_os),&(x->x_og)); - if(result==0)break; - err = send(x->x_fd, x->x_og.header, x->x_og.header_len, SEND_OPT); - if(err < 0) - { - error("oggcast~: could not send ogg header to server (%d)", err); - x->x_eos = 1; /* indicate end of stream */ - return (-1); - } - err = send(x->x_fd, x->x_og.body, x->x_og.body_len, SEND_OPT); - if(err < 0) - { - error("oggcast~: could not send ogg body to server (%d)", err); - x->x_eos = 1; /* indicate end of stream */ - return (-1); - } - } - } - return (0); -} - - /* deinit the ogg/vorbis decoder */ -static void oggcast_vorbis_deinit(t_oggcast *x) -{ - vorbis_analysis_wrote(&(x->x_vd),0); - /* get rid of remaining data in encoder, if any */ - while(vorbis_analysis_blockout(&(x->x_vd),&(x->x_vb))==1) - { - vorbis_analysis(&(x->x_vb),NULL); - vorbis_bitrate_addblock(&(x->x_vb)); - - while(vorbis_bitrate_flushpacket(&(x->x_vd),&(x->x_op))) - { - ogg_stream_packetin(&(x->x_os),&(x->x_op)); - oggcast_stream(x, x->x_fd); - } - } - /* clean up and exit. vorbis_info_clear() must be called last */ - ogg_stream_clear(&(x->x_os)); - vorbis_block_clear(&(x->x_vb)); - vorbis_dsp_clear(&(x->x_vd)); - vorbis_comment_clear(&(x->x_vc)); - vorbis_info_clear(&(x->x_vi)); -} - - /* encode ogg/vorbis and stream new data */ -static int oggcast_encode(t_oggcast *x, float *buf, int channels, int fifosize, int fd) -{ - unsigned short i, ch; - int err = 0; - int n, pages = 0; - - /* expose the buffer to submit data */ - float **inbuffer=vorbis_analysis_buffer(&(x->x_vd),READ * channels); - - /* read from buffer */ - for(n = 0; n < READ; n++) /* fill encode buffer */ - { - for(ch = 0; ch < channels; ch++) - { - inbuffer[ch][n] = *buf++; - } - } - /* tell the library how much we actually submitted */ - vorbis_analysis_wrote(&(x->x_vd),n); - - /* vorbis does some data preanalysis, then divvies up blocks for - more involved (potentially parallel) processing. Get a single - block for encoding now */ - while(vorbis_analysis_blockout(&(x->x_vd),&(x->x_vb))==1) - { - /* analysis, assume we want to use bitrate management */ - vorbis_analysis(&(x->x_vb),NULL); - vorbis_bitrate_addblock(&(x->x_vb)); - - while(vorbis_bitrate_flushpacket(&(x->x_vd),&(x->x_op))) - { - /* weld the packet into the bitstream */ - ogg_stream_packetin(&(x->x_os),&(x->x_op)); - err = oggcast_stream(x, fd); /* stream packet to server */ - if(err >= 0) - { - pages += err; /* count pages */ - } - else return (err); - } - } - return (pages); -} - - /* connect to icecast2 server */ -static int oggcast_child_connect(char *hostname, char *mountpoint, t_int portno, - char *passwd, char *bcname, char *bcurl, - char *bcgenre, t_int bcpublic, t_int br_nom, t_int servertype) -{ - struct sockaddr_in server; - struct hostent *hp; - - /* variables used for communication with server */ - const char * buf = 0; - char resp[STRBUF_SIZE]; - unsigned int len; - fd_set fdset; - struct timeval tv; - t_int sockfd; /* our internal handle for the socket */ - t_int ret; - - sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - if (sockfd < 0) - { - error("oggcast~: internal error while attempting to open socket"); - return (-1); - } - - /* connect socket using hostname provided in command line */ - server.sin_family = AF_INET; - hp = gethostbyname(hostname); - if (hp == 0) - { - post("oggcast~: bad host?"); - sys_closesocket(sockfd); - return (-1); - } - memcpy((char *)&server.sin_addr, (char *)hp->h_addr, hp->h_length); - - /* assign client port number */ - server.sin_port = htons((unsigned short)portno); - - /* try to connect. */ - post("oggcast~: connecting to port %d", portno); - if (connect(sockfd, (struct sockaddr *) &server, sizeof (server)) < 0) - { - error("oggcast~: connection failed!\n"); - sys_closesocket(sockfd); - return (-1); - } - - /* sheck if we can read/write from/to the socket */ - FD_ZERO( &fdset); - FD_SET( sockfd, &fdset); - tv.tv_sec = 0; /* seconds */ - tv.tv_usec = 500; /* microseconds */ - - ret = select(sockfd + 1, &fdset, NULL, NULL, &tv); - if(ret != 0) - { - error("oggcast~: can not read from socket"); - sys_closesocket(sockfd); - return (-1); - } - - post("oggcast~: logging in to IceCast2 server..."); - /* now try to log in at IceCast2 server using ICE/1.0 scheme */ - if(servertype == 0) - { - /* send the request, a string like: "SOURCE / ICE/1.0\n" */ - buf = "SOURCE "; - send(sockfd, buf, strlen(buf), SEND_OPT); - buf = "/"; - send(sockfd, buf, strlen(buf), SEND_OPT); - buf = mountpoint; - send(sockfd, buf, strlen(buf), SEND_OPT); - buf = " ICE/1.0"; - send(sockfd, buf, strlen(buf), SEND_OPT); - /* send the ice headers */ - /* password */ - buf = "\nice-password: "; - send(sockfd, buf, strlen(buf), SEND_OPT); - buf = passwd; - send(sockfd, buf, strlen(buf), SEND_OPT); - /* name */ - buf = "\r\nice-name: "; - send(sockfd, buf, strlen(buf), SEND_OPT); - buf = bcname; - send(sockfd, buf, strlen(buf), SEND_OPT); - /* url */ - buf = "\r\nice-url: "; - send(sockfd, buf, strlen(buf), SEND_OPT); - buf = bcurl; - send(sockfd, buf, strlen(buf), SEND_OPT); - /* genre */ - buf = "\r\nice-genre: "; - send(sockfd, buf, strlen(buf), SEND_OPT); - buf = bcgenre; - send(sockfd, buf, strlen(buf), SEND_OPT); - /* public */ - buf = "\r\nice-public: "; - send(sockfd, buf, strlen(buf), SEND_OPT); - if(bcpublic==0) /* set the public flag for broadcast */ - { - buf = "no"; - } - else - { - buf ="yes"; - } - send(sockfd, buf, strlen(buf), SEND_OPT); - /* bitrate */ - buf = "\r\nice-bitrate: "; - send(sockfd, buf, strlen(buf), SEND_OPT); - if(sprintf(resp, "%d", br_nom) == -1) /* convert int to a string */ - { - error("oggcast~: wrong bitrate"); - } - send(sockfd, resp, strlen(resp), SEND_OPT); - /* description */ - buf = "\r\nice-description: "; - send(sockfd, buf, strlen(buf), SEND_OPT); - buf = "ogg/vorbis streamed from pure-data with oggcast~"; - send(sockfd, buf, strlen(buf), SEND_OPT); - /* end of header */ - buf = "\r\n\r\n"; - send(sockfd, buf, strlen(buf), SEND_OPT); - /* end login for IceCast using ICE/1.0 scheme */ - } - else /* or try to log in at IceCast2 server using HTTP/1.0 base auth scheme */ - { - /* send the request, a string like: "SOURCE / HTTP/1.0\n\r" */ - buf = "SOURCE /"; - send(sockfd, buf, strlen(buf), SEND_OPT); - buf = mountpoint; - send(sockfd, buf, strlen(buf), SEND_OPT); - buf = " HTTP/1.0\r\n"; - send(sockfd, buf, strlen(buf), SEND_OPT); - /* send basic authorization */ - sprintf(resp, "source:%s", passwd); - buf = oggcast_util_base64_encode(resp); - sprintf(resp, "Authorization: Basic %s\r\n", buf); - send(sockfd, resp, strlen(resp), SEND_OPT); - /* send content type */ - buf = "Content-Type: application/x-ogg"; - send(sockfd, buf, strlen(buf), SEND_OPT); - /* send the ice headers */ - /* password */ - buf = "\r\nice-password: "; - send(sockfd, buf, strlen(buf), SEND_OPT); - buf = passwd; - send(sockfd, buf, strlen(buf), SEND_OPT); - /* name */ - buf = "\r\nice-name: "; - send(sockfd, buf, strlen(buf), SEND_OPT); - buf = bcname; - send(sockfd, buf, strlen(buf), SEND_OPT); - /* url */ - buf = "\r\nice-url: "; - send(sockfd, buf, strlen(buf), SEND_OPT); - buf = bcurl; - send(sockfd, buf, strlen(buf), SEND_OPT); - /* genre */ - buf = "\r\nice-genre: "; - send(sockfd, buf, strlen(buf), SEND_OPT); - buf = bcgenre; - send(sockfd, buf, strlen(buf), SEND_OPT); - /* public */ - buf = "\r\nice-public: "; - send(sockfd, buf, strlen(buf), SEND_OPT); - if(bcpublic==0) /* set the public flag for broadcast */ - { - buf = "0"; - } - else - { - buf = "1"; - } - send(sockfd, buf, strlen(buf), SEND_OPT); - /* bitrate */ - buf = "\r\nice-bitrate: "; - send(sockfd, buf, strlen(buf), SEND_OPT); - if(sprintf(resp, "%d", br_nom) == -1) /* convert int to a string */ - { - error("oggcast~: wrong bitrate"); - } - send(sockfd, resp, strlen(resp), SEND_OPT); - /* description */ - buf = "\r\nice-description: "; - send(sockfd, buf, strlen(buf), SEND_OPT); - buf = "Ogg/Vorbis streamed from PureData with oggcast~\r\n"; - send(sockfd, buf, strlen(buf), SEND_OPT); - /* end of header: write an empty line */ - buf = "\r\n"; - send(sockfd, buf, strlen(buf), SEND_OPT); - /* end login for IceCast2 using ICE/1.0 scheme */ - } - - /* check if we can write to server */ - if(oggcast_checkserver(sockfd)!= 0) - { - post("oggcast~: error: server refused to receive data"); - return (-1); - } - - post("oggcast~: logged in to http://%s:%d/%s", hp->h_name, portno, mountpoint); - - return (sockfd); -} - - - -static void oggcast_child_disconnect(t_int fd) -{ - sys_closesocket(fd); - post("oggcast~: connection closed"); -} -/************** the child thread which performs data I/O ***********/ - -#if 0 /* set this to 1 to get debugging output */ -static void pute(char *s) /* debug routine */ -{ - write(2, s, strlen(s)); -} -#else -#define pute(x) -#endif - -#if 1 -#define oggcast_cond_wait pthread_cond_wait -#define oggcast_cond_signal pthread_cond_signal -#else -#include /* debugging version... */ -#include -static void oggcast_fakewait(pthread_mutex_t *b) -{ - struct timeval timout; - timout.tv_sec = 0; - timout.tv_usec = 1000000; - pthread_mutex_unlock(b); - select(0, 0, 0, 0, &timout); - pthread_mutex_lock(b); -} - -void oggcast_banana( void) -{ - struct timeval timout; - timout.tv_sec = 0; - timout.tv_usec = 200000; - pute("banana1\n"); - select(0, 0, 0, 0, &timout); - pute("banana2\n"); -} - - -#define oggcast_cond_wait(a,b) oggcast_fakewait(b) -#define oggcast_cond_signal(a) -#endif - -static void *oggcast_child_main(void *zz) -{ - t_oggcast *x = zz; - time_t now; /* to get the time */ - pute("1\n"); - pthread_mutex_lock(&x->x_mutex); - while (1) - { - int fd, fifotail; - pute("0\n"); - if (x->x_requestcode == REQUEST_NOTHING) - { - pute("wait 2\n"); - oggcast_cond_signal(&x->x_answercondition); - oggcast_cond_wait(&x->x_requestcondition, &x->x_mutex); - pute("3\n"); - } - // connect to Icecast2 server - else if (x->x_requestcode == REQUEST_CONNECT) - { - char boo[100]; - int sysrtn, wantbytes; - - /* copy connect stuff out of the data structure so we can - relinquish the mutex while we're connecting to server. */ - char *hostname = x->x_hostname; - char *mountpoint = x->x_mountpoint; - t_int portno = x->x_port; - char *passwd = x->x_passwd; - char *bcname = x->x_bcname; - char *bcgenre = x->x_bcgenre; - char *bcurl = x->x_bcurl; - t_int bcpublic = x->x_bcpublic; - t_int br_nom = x->x_br_nom; - t_int servertype = x->x_servertype; - /* alter the request code so that an ensuing "open" will get - noticed. */ - pute("4\n"); - x->x_requestcode = REQUEST_BUSY; - x->x_connecterror = 0; - - /* open the socket with the mutex unlocked in case - we're not already connected */ - if(x->x_fd < 0) - { - pthread_mutex_unlock(&x->x_mutex); - fd = oggcast_child_connect(hostname, mountpoint, portno, passwd, bcname, - bcurl, bcgenre, bcpublic, br_nom, servertype); - pthread_mutex_lock(&x->x_mutex); - pute("5\n"); - /* copy back into the instance structure. */ - x->x_connectstate = 1; - clock_delay(x->x_clock_connect, 0); - x->x_fd = fd; - if (fd < 0) - { - x->x_connecterror = fd; - x->x_connectstate = 0; - clock_delay(x->x_clock_connect, 0); - pute("connect failed\n"); - goto lost; - } - else - { - /* get the time for the DATE comment */ - now=time(NULL); - x->x_bcdate = pdogg_strdup(ctime(&now)); /*--moo*/ - x->x_pages = 0; - clock_delay(x->x_clock_pages, 0); - /* initialise the encoder */ - if(oggcast_vorbis_init(x) == 0) - { - post("oggcast~: ogg/vorbis encoder initialised"); - x->x_eos = 0; - x->x_state = STATE_STREAM; - } - else - { - post("oggcast~: could not init encoder"); - oggcast_child_disconnect(fd); - post("oggcast~: connection closed due to initialisation error"); - x->x_fd = -1; - x->x_connectstate = 0; - clock_delay(x->x_clock_connect, 0); - pute("oggcast~: initialisation failed\n"); - goto lost; - } - } - x->x_fifotail = fifotail = 0; - /* set fifosize from bufsize. fifosize must be a - multiple of the number of bytes eaten for each DSP - tick. We pessimistically assume MAXVECSIZE samples - per tick since that could change. There could be a - problem here if the vector size increases while a - stream is being played... */ - x->x_fifosize = x->x_bufsize - (x->x_bufsize % (x->x_channels * READ)); - /* arrange for the "request" condition to be signalled x->x_siginterval - times per buffer */ - sprintf(boo, "fifosize %d\n", x->x_fifosize); - pute(boo); - x->x_sigcountdown = x->x_sigperiod = (x->x_fifosize / (x->x_siginterval * x->x_channels * x->x_vecsize)); - } - /* check if another request has been made; if so, field it */ - if (x->x_requestcode != REQUEST_BUSY) - goto lost; - pute("6\n"); - - while (x->x_requestcode == REQUEST_BUSY) - { - int fifosize = x->x_fifosize, fifotail, channels; - float *buf = x->x_buf; - pute("77\n"); - - /* if the head is < the tail, we can immediately write - from tail to end of fifo to disk; otherwise we hold off - writing until there are at least WRITESIZE bytes in the - buffer */ - if (x->x_fifohead < x->x_fifotail || - x->x_fifohead >= x->x_fifotail + (READ * x->x_channels) - || (x->x_requestcode == REQUEST_CLOSE && - x->x_fifohead != x->x_fifotail)) - { /* encode audio and send to server */ - pute("8\n"); - fifotail = x->x_fifotail; - channels = x->x_channels; - fd = x->x_fd; - pthread_mutex_unlock(&x->x_mutex); - sysrtn = oggcast_encode(x, buf + fifotail, channels, fifosize, fd); - pthread_mutex_lock(&x->x_mutex); - if (x->x_requestcode != REQUEST_BUSY && - x->x_requestcode != REQUEST_CLOSE) - break; - if (sysrtn < 0) - { - post("oggcast~: closing due to error..."); - goto lost; - } - else - { - x->x_fifotail += (READ * x->x_channels); - x->x_pages += sysrtn; - if (x->x_fifotail >= fifosize) - x->x_fifotail = 0; - } - sprintf(boo, "after: head %d, tail %d, pages %d\n", x->x_fifohead, x->x_fifotail, sysrtn); - pute(boo); - } - else /* just wait... */ - { - pute("wait 7a ...\n"); - oggcast_cond_signal(&x->x_answercondition); - pute("signalled\n"); - oggcast_cond_wait(&x->x_requestcondition, - &x->x_mutex); - pute("7a done\n"); - continue; - } - /* signal parent in case it's waiting for data */ - oggcast_cond_signal(&x->x_answercondition); - } - } - - /* reinit encoder (settings have changed) */ - else if (x->x_requestcode == REQUEST_REINIT) - { - pthread_mutex_unlock(&x->x_mutex); - oggcast_vorbis_deinit(x); - oggcast_vorbis_init(x); - pthread_mutex_lock(&x->x_mutex); - post("oggcast~: ogg/vorbis encoder reinitialised"); - x->x_state = STATE_STREAM; - if (x->x_requestcode == REQUEST_REINIT) - x->x_requestcode = REQUEST_CONNECT; - oggcast_cond_signal(&x->x_answercondition); - } - /* close connection to server (disconnect) */ - else if (x->x_requestcode == REQUEST_CLOSE) - { -lost: - x->x_state = STATE_IDLE; - if (x->x_fd >= 0) - { - fd = x->x_fd; - pthread_mutex_unlock(&x->x_mutex); - oggcast_vorbis_deinit(x); - oggcast_child_disconnect(fd); - pthread_mutex_lock(&x->x_mutex); - x->x_fd = -1; - } - if (x->x_requestcode == REQUEST_CLOSE) - x->x_requestcode = REQUEST_NOTHING; - if (x->x_requestcode == REQUEST_BUSY) /* disconnect due to error */ - x->x_requestcode = REQUEST_NOTHING; - x->x_connectstate = 0; - clock_delay(x->x_clock_connect, 0); - x->x_eos = 1; - oggcast_cond_signal(&x->x_answercondition); - } - // quit everything - else if (x->x_requestcode == REQUEST_QUIT) - { - x->x_state = STATE_IDLE; - if (x->x_fd >= 0) - { - fd = x->x_fd; - pthread_mutex_unlock(&x->x_mutex); - oggcast_vorbis_deinit(x); - oggcast_child_disconnect(fd); - pthread_mutex_lock(&x->x_mutex); - x->x_fd = -1; - } - x->x_connectstate = 0; - clock_delay(x->x_clock_connect, 0); - x->x_requestcode = REQUEST_NOTHING; - oggcast_cond_signal(&x->x_answercondition); - break; - } - else - { - pute("13\n"); - } - } - pute("thread exit\n"); - pthread_mutex_unlock(&x->x_mutex); - return (0); -} - -/******** the object proper runs in the calling (parent) thread ****/ - -static void oggcast_tick_connect(t_oggcast *x) -{ - pthread_mutex_lock(&x->x_mutex); - outlet_float(x->x_connection, x->x_connectstate); - pthread_mutex_unlock(&x->x_mutex); -} - -static void oggcast_tick_pages(t_oggcast *x) -{ - /* output new no. of pages if anything changed */ - t_float pages; - pthread_mutex_lock(&x->x_mutex); - pages = x->x_pages; /* get current value with mutex locked */ - pthread_mutex_unlock(&x->x_mutex); - if(pages != x->x_lastpages) - { - outlet_float(x->x_outpages, pages); - x->x_lastpages = pages; - } - clock_delay(x->x_clock_pages, UPDATE_INTERVAL); /* come back again... */ -} - -static void *oggcast_new(t_floatarg fnchannels, t_floatarg fbufsize) -{ - t_oggcast *x; - int nchannels = fnchannels, bufsize = fbufsize * 1024, i; - float *buf; - - if (nchannels < 1) - nchannels = 2; /* two channels as default */ - else if (nchannels > MAXSTREAMCHANS) - nchannels = MAXSTREAMCHANS; - /* check / set buffer size */ - if (bufsize <= 0) bufsize = DEFBUFPERCHAN * nchannels; - else if (bufsize < MINBUFSIZE) - bufsize = MINBUFSIZE; - else if (bufsize > MAXBUFSIZE) - bufsize = MAXBUFSIZE; - buf = getbytes(bufsize*sizeof(t_float)); - if (!buf) return (0); - - x = (t_oggcast *)pd_new(oggcast_class); - - for (i = 1; i < nchannels; i++) - inlet_new (&x->x_obj, &x->x_obj.ob_pd, gensym ("signal"), gensym ("signal")); - x->x_connection = outlet_new(&x->x_obj, gensym("float")); - x->x_outpages = outlet_new(&x->x_obj, gensym("float")); - x->x_ninlets = nchannels; - x->x_outvec = getbytes(nchannels*sizeof(t_sample *)); - x->x_clock_connect = clock_new(x, (t_method)oggcast_tick_connect); - x->x_clock_pages = clock_new(x, (t_method)oggcast_tick_pages); - - pthread_mutex_init(&x->x_mutex, 0); - pthread_cond_init(&x->x_requestcondition, 0); - pthread_cond_init(&x->x_answercondition, 0); - - x->x_vecsize = 2; - x->x_state = STATE_IDLE; - x->x_buf = buf; - x->x_bufsize = bufsize; - x->x_siginterval = 32; /* signal 32 times per buffer */ - /* I found this to be most efficient on my machine */ - x->x_fifosize = x->x_fifohead = x->x_fifotail = x->x_requestcode = 0; - - x->x_connectstate = 0; /* indicating state of connection */ - x->x_outvalue = 0; /* value at output currently is 0 */ - - x->x_samplerate = sys_getsr(); - x->x_resample.upsample = x->x_resample.downsample = 1; /* don't resample */ - - - x->x_fd = -1; - x->x_eos = 0; - x->x_vbr = 1; /* use the vbr setting by default */ - x->x_passwd = "letmein"; - x->x_samplerate = sys_getsr(); /* default to Pd's sampling rate */ - x->x_skip = 1; /* no resampling supported */ - x->x_quality = 0.4; /* quality 0.4 gives roughly 128kbps VBR stream */ - x->x_channels = nchannels; /* stereo */ - x->x_br_max = 144; - x->x_br_nom = 128; - x->x_br_min = 96; - x->x_pages = x->x_lastpages = 0; - x->x_bcname = pdogg_strdup("ogg/vorbis stream"); /*--moo: added strdup() */ - x->x_bcurl = pdogg_strdup("http://www.akustische-kunst.org/puredata/"); - x->x_bcgenre = pdogg_strdup("experimental"); - x->x_bcdescription = pdogg_strdup("ogg/vorbis stream emitted from pure-data with oggcast~"); - x->x_bcartist = pdogg_strdup("Pd and oggcast~ v0.2"); - x->x_bclocation = pdogg_strdup(x->x_bcurl); - x->x_bccopyright = pdogg_strdup(""); - x->x_bcperformer = pdogg_strdup(""); - x->x_bccontact = pdogg_strdup(""); - x->x_bcdate = pdogg_strdup(""); - x->x_bcpublic = 1; - x->x_mountpoint = "puredata.ogg"; - x->x_servertype = 1; /* HTTP/1.0 protocol for Icecast2 */ - - post(oggcast_version); - post("oggcast~: set buffer to %dk bytes", bufsize / 1024); - post("oggcast~: encoding %d channels @ %d Hz", x->x_channels, x->x_samplerate); - - clock_delay(x->x_clock_pages, 0); - pthread_create(&x->x_childthread, 0, oggcast_child_main, x); - return (x); -} - -static t_int *oggcast_perform(t_int *w) -{ - t_oggcast *x = (t_oggcast *)(w[1]); - int vecsize = x->x_vecsize, ninlets = x->x_ninlets, channels = x->x_channels, i, j, skip = x->x_skip; - float *sp = x->x_buf; - pthread_mutex_lock(&x->x_mutex); - if (x->x_state != STATE_IDLE) - { - int wantbytes; - /* get 'wantbytes' bytes from inlet */ - wantbytes = channels * vecsize / skip; /* we'll get vecsize bytes per channel */ - /* check if there is enough space in buffer to write all samples */ - while (x->x_fifotail > x->x_fifohead && - x->x_fifotail < x->x_fifohead + wantbytes + 1) - { - pute("wait...\n"); - oggcast_cond_signal(&x->x_requestcondition); - oggcast_cond_wait(&x->x_answercondition, &x->x_mutex); - pute("done\n"); - } - - /* output audio */ - sp += x->x_fifohead; - - if(ninlets >= channels) - { - for(j = 0; j < vecsize; j += skip) - { - for(i = 0; i < channels; i++) - { - *sp++ = x->x_outvec[i][j]; - } - } - } - else if(channels == ninlets * 2) /* convert mono -> stereo */ - { - for(j = 0; j < vecsize; j += skip) - { - for(i = 0; i < ninlets; i++) - { - *sp++ = x->x_outvec[i][j]; - *sp++ = x->x_outvec[i][j]; - } - } - } - - - x->x_fifohead += wantbytes; - if (x->x_fifohead >= x->x_fifosize) - x->x_fifohead = 0; - /* signal the child thread */ - if ((--x->x_sigcountdown) <= 0) - { - pute("signal 1\n"); - oggcast_cond_signal(&x->x_requestcondition); - x->x_sigcountdown = x->x_sigperiod; - } - } - - pthread_mutex_unlock(&x->x_mutex); - - return (w+2); -} - - -static void oggcast_disconnect(t_oggcast *x) -{ - /* LATER rethink whether you need the mutex just to set a variable? */ - pthread_mutex_lock(&x->x_mutex); - if(x->x_fd >= 0) - { - x->x_state = STATE_IDLE; - x->x_requestcode = REQUEST_CLOSE; - oggcast_cond_signal(&x->x_requestcondition); - } - else post("oggcast~: not connected"); - pthread_mutex_unlock(&x->x_mutex); -} - - - /* connect method. Called as: - connect - */ - -static void oggcast_connect(t_oggcast *x, t_symbol *s, int argc, t_atom *argv) -{ - t_symbol *hostsym = atom_getsymbolarg(0, argc, argv); - t_symbol *mountsym = atom_getsymbolarg(1, argc, argv); - t_float portno = atom_getfloatarg(2, argc, argv); - if (!*hostsym->s_name) /* check for hostname */ - return; - if (!portno) /* check wether the portnumber is specified */ - portno = 8000; /* ...assume port 8000 as standard */ - pthread_mutex_lock(&x->x_mutex); - if(x->x_fd >= 0) - { - post("oggcast~: already connected"); - } - else - { - x->x_requestcode = REQUEST_CONNECT; - x->x_hostname = hostsym->s_name; - x->x_mountpoint = mountsym->s_name; - x->x_port = portno; - - x->x_fifotail = 0; - x->x_fifohead = 0; - // if(x->x_recover != 1)x->x_fifobytes = 0; - - x->x_connecterror = 0; - x->x_state = STATE_STARTUP; - oggcast_cond_signal(&x->x_requestcondition); - } - pthread_mutex_unlock(&x->x_mutex); -} - -static void oggcast_float(t_oggcast *x, t_floatarg f) -{ - if (f != 0) - { - pthread_mutex_lock(&x->x_mutex); - if(x->x_fd >= 0) - { - post("oggcast~: already connected"); - } - else - { - if(x->x_recover != 1) - { - x->x_fifotail = 0; - x->x_fifohead = 0; - } - x->x_requestcode = REQUEST_CONNECT; - - x->x_fifotail = 0; - x->x_fifohead = 0; - - x->x_connecterror = 0; - x->x_state = STATE_STARTUP; - oggcast_cond_signal(&x->x_requestcondition); - } - pthread_mutex_unlock(&x->x_mutex); - } - else oggcast_disconnect(x); -} - -static void oggcast_dsp(t_oggcast *x, t_signal **sp) -{ - int i, ninlets = x->x_ninlets; - pthread_mutex_lock(&x->x_mutex); - x->x_vecsize = sp[0]->s_n; - - x->x_sigperiod = (x->x_fifosize / (x->x_siginterval * ninlets * x->x_vecsize)); - for (i = 0; i < ninlets; i++) - x->x_outvec[i] = sp[i]->s_vec; - pthread_mutex_unlock(&x->x_mutex); - dsp_add(oggcast_perform, 1, x); -} - - /* set password for oggcast server */ -static void oggcast_password(t_oggcast *x, t_symbol *password) -{ - pthread_mutex_lock(&x->x_mutex); - x->x_passwd = password->s_name; - pthread_mutex_unlock(&x->x_mutex); -} - /* set comment fields for header (reads in just anything) */ -static void oggcast_comment(t_oggcast *x, t_symbol *s, t_int argc, t_atom* argv) -{ - t_binbuf *b = binbuf_new(); - char* comment; - int length; - binbuf_add(b, argc, argv); - binbuf_gettext(b, &comment, &length); - - pthread_mutex_lock(&x->x_mutex); - if(strstr(s->s_name, "ARTIST")) - { - if (x->x_bcartist) free(x->x_bcartist); - x->x_bcartist = pdogg_strdup(comment); /*-- moo: added strdup() */ - post("oggcast~: ARTIST = %s", x->x_bcartist); - } - else if(strstr(s->s_name, "GENRE")) - { - free(x->x_bcgenre); - x->x_bcgenre = pdogg_strdup(comment); - post("oggcast~: GENRE = %s", x->x_bcgenre); - } - else if(strstr(s->s_name, "TITLE")) - { - free(x->x_bcname); - x->x_bcname = pdogg_strdup(comment); - post("oggcast~: TITLE = %s", x->x_bcname); - } - else if(strstr(s->s_name, "PERFORMER")) - { - free(x->x_bcperformer); - x->x_bcperformer = pdogg_strdup(comment); - post("oggcast~: PERFORMER = %s",x->x_bcperformer); - } - else if(strstr(s->s_name, "LOCATION")) - { - free(x->x_bclocation); - x->x_bclocation = pdogg_strdup(comment); - post("oggcast~: LOCATION = %s",x->x_bclocation); - } - else if(strstr(s->s_name, "COPYRIGHT")) - { - free(x->x_bccopyright); - x->x_bccopyright = pdogg_strdup(comment); - post("oggcast~: COPYRIGHT = %s", x->x_bccopyright); - } - else if(strstr(s->s_name, "CONTACT")) - { - free(x->x_bccontact); - x->x_bccontact = pdogg_strdup(comment); - post("oggcast~: CONTACT = %s", x->x_bccontact); - } - else if(strstr(s->s_name, "DESCRIPTION")) - { - free(x->x_bcdescription); - x->x_bcdescription = pdogg_strdup(comment); - post("oggcast~: DESCRIPTION = %s", x->x_bcdescription); - } - else if(strstr(s->s_name, "DATE")) - { - free(x->x_bcdate); - x->x_bcdate = pdogg_strdup(comment); - post("oggcast~: DATE = %s", x->x_bcdate); - } - else post("oggcast~: no method for %s", s->s_name); - if(x->x_state == STATE_STREAM) - { - x->x_state = STATE_IDLE; - x->x_requestcode = REQUEST_REINIT; - oggcast_cond_signal(&x->x_requestcondition); - } - pthread_mutex_unlock(&x->x_mutex); - freebytes(comment, strlen(comment)); - binbuf_free(b); -} - /* settings for variable bitrate encoding */ -static void oggcast_vbr(t_oggcast *x, t_floatarg fsr, t_floatarg fchannels, - t_floatarg fquality) -{ - pthread_mutex_lock(&x->x_mutex); - x->x_vbr = 1; - x->x_samplerate = (t_int)fsr; - x->x_quality = fquality; - x->x_channels = (t_int)fchannels; - post("oggcast~: %d channels @ %d Hz, quality %.2f", x->x_channels, x->x_samplerate, x->x_quality); - if(x->x_state == STATE_STREAM) - { - x->x_state = STATE_IDLE; - x->x_requestcode = REQUEST_REINIT; - oggcast_cond_signal(&x->x_requestcondition); - } - pthread_mutex_unlock(&x->x_mutex); -} - - /* settings for bitrate-based vbr encoding */ -static void oggcast_vorbis(t_oggcast *x, t_floatarg fsr, t_floatarg fchannels, - t_floatarg fmax, t_floatarg fnom, t_floatarg fmin) -{ - pthread_mutex_lock(&x->x_mutex); - x->x_vbr = 0; - x->x_samplerate = (t_int)fsr; - x->x_channels = (t_int)fchannels; - x->x_br_max = (t_int)fmax; - x->x_br_nom = (t_int)fnom; - x->x_br_min = (t_int)fmin; - post("oggcast~: %d channels @ %d Hz, bitrates: max. %d / nom. %d / min. %d", - x->x_channels, x->x_samplerate, x->x_br_max, x->x_br_nom, x->x_br_min); - if(x->x_state == STATE_STREAM) - { - x->x_state = STATE_IDLE; - x->x_requestcode = REQUEST_REINIT; - oggcast_cond_signal(&x->x_requestcondition); - } - pthread_mutex_unlock(&x->x_mutex); -} - /* select server type */ -static void oggcast_server(t_oggcast *x, t_floatarg f) -{ - pthread_mutex_lock(&x->x_mutex); - if(f) - { - x->x_servertype = 1; - post("oggcast~: set server type to new Icecast2 (HTTP/1.0 scheme)"); - } - else - { - x->x_servertype = 0; - post("oggcast~: set server type to JRoar (ICE/1.0 scheme)"); - } - pthread_mutex_unlock(&x->x_mutex); -} - /* print settings to pd's console window */ -static void oggcast_print(t_oggcast *x) -{ - pthread_mutex_lock(&x->x_mutex); - if(x->x_servertype) - post("oggcast~: server type is Icecast2"); - else - post("oggcast~: server type is JRoar"); - post("oggcast~: mountpoint at Icecast2: %s", x->x_mountpoint); - if(x->x_vbr == 1) - { - post("oggcast~: Ogg Vorbis encoder: %d channels @ %d Hz, quality %.2f", x->x_channels, x->x_samplerate, x->x_quality); - } - else - { - post("oggcast~: Ogg Vorbis encoder: %d channels @ %d Hz, bitrates: max. %d, nom. %d, min. %d", - x->x_channels, x->x_samplerate, x->x_br_max, x->x_br_nom, x->x_br_min); - } - post("oggcast~: Ogg Vorbis comments:"); - post(" TITLE = %s", x->x_bcname); - post(" ARTIST = %s", x->x_bcartist); - post(" PERFORMER = %s", x->x_bcperformer); - post(" GENRE = %s", x->x_bcgenre); - post(" LOCATION = %s", x->x_bclocation); - post(" COPYRIGHT = %s", x->x_bccopyright); - post(" CONTACT = %s", x->x_bccontact); - post(" DESCRIPTION = %s", x->x_bcdescription); - post(" DATE = %s", x->x_bcdate); - pthread_mutex_unlock(&x->x_mutex); -} - - -static void oggcast_free(t_oggcast *x) -{ - /* request QUIT and wait for acknowledge */ - void *threadrtn; - pthread_mutex_lock(&x->x_mutex); - x->x_requestcode = REQUEST_QUIT; - post("stopping oggcast thread..."); - oggcast_cond_signal(&x->x_requestcondition); - while (x->x_requestcode != REQUEST_NOTHING) - { - post("signalling..."); - oggcast_cond_signal(&x->x_requestcondition); - oggcast_cond_wait(&x->x_answercondition, &x->x_mutex); - } - pthread_mutex_unlock(&x->x_mutex); - if (pthread_join(x->x_childthread, &threadrtn)) - error("oggcast_free: join failed"); - post("... done."); - - pthread_cond_destroy(&x->x_requestcondition); - pthread_cond_destroy(&x->x_answercondition); - pthread_mutex_destroy(&x->x_mutex); - freebytes(x->x_buf, x->x_bufsize*sizeof(t_float)); - freebytes(x->x_outvec, x->x_ninlets*sizeof(t_sample *)); - clock_free(x->x_clock_connect); - clock_free(x->x_clock_pages); - - /*-- moo: free dynamically allocated comment strings --*/ - free(x->x_bcname); - free(x->x_bcurl); - free(x->x_bcgenre); - free(x->x_bcdescription); - free(x->x_bcartist); - free(x->x_bclocation); - free(x->x_bccopyright); - free(x->x_bcperformer); - free(x->x_bccontact); - free(x->x_bcdate); -} - -void oggcast_tilde_setup(void) -{ - oggcast_class = class_new(gensym("oggcast~"), (t_newmethod)oggcast_new, - (t_method)oggcast_free, sizeof(t_oggcast), 0, A_DEFFLOAT, A_DEFFLOAT, 0); - CLASS_MAINSIGNALIN(oggcast_class, t_oggcast, x_f ); - class_addfloat(oggcast_class, (t_method)oggcast_float); - class_addmethod(oggcast_class, (t_method)oggcast_disconnect, gensym("disconnect"), 0); - class_addmethod(oggcast_class, (t_method)oggcast_dsp, gensym("dsp"), 0); - class_addmethod(oggcast_class, (t_method)oggcast_connect, gensym("connect"), A_GIMME, 0); - class_addmethod(oggcast_class, (t_method)oggcast_print, gensym("print"), 0); - class_addmethod(oggcast_class, (t_method)oggcast_password, gensym("passwd"), A_SYMBOL, 0); - class_addmethod(oggcast_class, (t_method)oggcast_vorbis, gensym("vorbis"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0); - class_addmethod(oggcast_class, (t_method)oggcast_vbr, gensym("vbr"), A_FLOAT, A_FLOAT, A_FLOAT, 0); - class_addmethod(oggcast_class, (t_method)oggcast_server, gensym("server"), A_FLOAT, 0); - class_addanything(oggcast_class, oggcast_comment); -} diff --git a/oggcast~/oggcast~.libs b/oggcast~/oggcast~.libs deleted file mode 100644 index 28114c5..0000000 --- a/oggcast~/oggcast~.libs +++ /dev/null @@ -1 +0,0 @@ --logg -lvorbis -lvorbisenc -lvorbisfile diff --git a/oggcast~/readme b/oggcast~/readme deleted file mode 100644 index b3090ec..0000000 --- a/oggcast~/readme +++ /dev/null @@ -1,116 +0,0 @@ -Version 0.2 -copyright (c) 2002 by Olaf Matthes - -oggcast~ is a ogg/vorbis streaming external for pd (by Miller -Puckette) that connects to an IceCast2 server. - - -To run oggcast~ place the file oggcast~.dll for win or oggcast~.pd_linux -in the directory of our patch or start pd with '-lib oggcast~' flag. - -To compile oggcast~ on Linux get the ogg/vorbice library from -http://www.vorbis.com/. -You have to modify the makefile to make it point to the place where the -ogg/vorbis library is. - - -This software is published under LGPL terms. - -This is software with ABSOLUTELY NO WARRANTY. -Use it at your OWN RISK. It's possible to damage e.g. hardware or your hearing -due to a bug or for other reasons. - -***************************************************************************** - -oggcast~ uses the ogg/vorbice library to encode audio data. -The latest version of ogg/vorbis can be found at http://www.vorbice.com/ - -Below is the original copyright information taken from the ogg/vorbis library: - - -Copyright (c) 2001, Xiphophorus - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -- Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - -- Redistributions in binary form must reproduce the above copyright -notice, this list of conditions and the following disclaimer in the -documentation and/or other materials provided with the distribution. - -- Neither the name of the Xiphophorus nor the names of its contributors -may be used to endorse or promote products derived from this software -without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR -CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -***************************************************************************** - -Usage: - -Use message "vbr " to set the vorbis -encoding parameters. Resampling is currently not supported, so 'samplerate' -should be the one pd is running at. 'channels' specyfies the number of channels -to stream. This can be set to 2 (default) or 1 which means mono stream taking -the leftmost audio input only. 'quality' can be a value between 0.0 and 1.0 -giving the quality of the stream. 0.4 (default) results in a stream that's -about 128kbps. - -Since Vorbis uses VBR encoding, bitrates vary depending on the type of audio -signal to be encoded. A pure sine [osc~] results in the smalest stream, com- -plex audio signals will increase this value significantly. To test the maximum -bitrate that might occur for a quality setting use noise~ as signal input. - -Use message "vorbis " to set encoding quality on the basis of bitrates. -When setting all three bitrate parameters to the same value one gets a -constant bitrate stream. Values are in kbps! - -Message "connect " connects to the IceCast2 server. -Note that no response about succesfull connection is send by the server. All -messages in the pd console window about connection status depend on the ability -to send data to the server. -The mountpoint should end with '.ogg' to indiocate to the player/client that -it is an ogg/vorbis encoded stream. - -Use "passwd " to set your password (default is 'letmein') and -"disconnect" to disconnect from the server. "print" prints out the current -vorbis encoder settings. - -To set the comment tags in the ogg/vorbis header (which can be displayed by -the receiving client) use message " ". Supported tags are: -TITLE, ARTIST, GENRE, PERFORMER, LOCATION, COPYRIGHT, CONTACT, DESCRIPTION and -DATE (which is automatically set to the date/time the broadcast started). To -get spaces use '=' or '_' instead. Note that under Win2k '=' sometimes get lost -from the patch after saving!!! - - -Listening to it: - -To listen to ogg/vorbis encoded livestreams many player need an extra plug-in. -Have a look at http://www.vorbis.com/ to find the appropiate plug-in for your -player. -To play back the stream just open lacation http://:/. - -Note that changing encoding parameters or header comments while oggcast~ is -streaming to the server might result in audible dropouts. - - - -Latest version can be found at: -http://www.akustische-kunst.de/puredata/ - -Please report any bugs to olaf.matthes@gmx.de! \ No newline at end of file diff --git a/oggread~-help.pd b/oggread~-help.pd new file mode 100644 index 0000000..4d353f7 --- /dev/null +++ b/oggread~-help.pd @@ -0,0 +1,44 @@ +#N canvas 280 107 572 464 12; +#X obj 27 411 dac~; +#X floatatom 75 412 8 0 0 0 - - -; +#X obj 36 343 oggread~; +#X msg 71 170 start; +#X msg 110 226 stop; +#X msg 118 287 resume; +#X msg 36 45 open myfile.ogg; +#X text 119 168 play file from beginning; +#X text 152 226 stop (pause) playing; +#X text 179 288 resume playing at current position; +#X msg 396 394 \; pd dsp 1; +#X msg 472 394 \; pd dsp 0; +#X obj 396 368 loadbang; +#X text 185 52 get latest version at; +#X text 152 411 position in file (seconds); +#X text 186 38 written by Olaf Matthes ; +#X text 17 16 oggread~ version 0.2 - Ogg Vorbis file player; +#X msg 126 250 0; +#X msg 95 195 1; +#X text 230 318 set new playing position (in seconds); +#X obj 95 375 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1 +-1; +#X text 121 375 bang at end of file; +#X text 186 67 http://www.akustische-kunst.org/puredata/; +#X obj 55 102 openpanel; +#X msg 55 74 bang; +#X msg 55 129 open \$1; +#X msg 149 317 seek 60; +#X connect 2 0 0 0; +#X connect 2 1 0 1; +#X connect 2 2 1 0; +#X connect 2 3 20 0; +#X connect 3 0 2 0; +#X connect 4 0 2 0; +#X connect 5 0 2 0; +#X connect 6 0 2 0; +#X connect 12 0 10 0; +#X connect 17 0 2 0; +#X connect 18 0 2 0; +#X connect 23 0 25 0; +#X connect 24 0 23 0; +#X connect 25 0 2 0; +#X connect 26 0 2 0; diff --git a/oggread~.c b/oggread~.c new file mode 100644 index 0000000..af1a3a0 --- /dev/null +++ b/oggread~.c @@ -0,0 +1,416 @@ +/* ------------------------- oggread~ ------------------------------------------ */ +/* */ +/* Tilde object to read and play back Ogg Vorbis files. */ +/* Written by Olaf Matthes (olaf.matthes@gmx.de) */ +/* Get source at http://www.akustische-kunst.de/puredata/ */ +/* */ +/* This library is free software; you can redistribute it and/or */ +/* modify it under the terms of the GNU Lesser General Public */ +/* License as published by the Free Software Foundation; either */ +/* version 2 of the License, or (at your option) any later version. */ +/* */ +/* This library is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */ +/* Lesser General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU Lesser General Public */ +/* License along with this library; if not, write to the */ +/* Free Software Foundation, Inc., 59 Temple Place - Suite 330, */ +/* Boston, MA 02111-1307, USA. */ +/* */ +/* Based on PureData by Miller Puckette and others. */ +/* Uses the Ogg Vorbis decoding library which can be found at */ +/* http://www.vorbis.com/ */ +/* */ +/* ---------------------------------------------------------------------------- */ + +#include "m_pd.h" +#include "s_stuff.h" +#include +#include + +#include +#include +#include +#include +#include +#ifdef WIN32 +#include +#include +#else +#include +#include +#include +#include +#include +#include +#include +#define SOCKET_ERROR -1 +#endif + +#ifdef _MSC_VER +#pragma warning( disable : 4244 ) +#pragma warning( disable : 4305 ) +#endif + +#define READ 4096 /* amount of data we pass on to decoder */ +#define MIN_AUDIO_INPUT READ*8 /* this is completely guessed! we just fill half the buffer */ +#define OUTPUT_BUFFER_SIZE 65536 /* audio output buffer: 64k */ + +static char *oggread_version = "oggread~: ogg/vorbis file reader version 0.2c, written by Olaf Matthes"; + +/* ------------------------ oggread~ ----------------------------- */ + +static t_class *oggread_class; + +typedef struct _oggread +{ + t_object x_obj; + t_clock *x_clock; + /* ogg/vorbis related stuff */ + OggVorbis_File x_ov; + ogg_stream_state x_os; /* take physical pages, weld into a logical stream of packets */ + ogg_sync_state x_oy; /* sync and verify incoming physical bitstream */ + ogg_page x_og; /* one Ogg bitstream page. Vorbis packets are inside */ + ogg_packet x_op; /* one raw packet of data for decode */ + vorbis_info *x_vi; /* struct that stores all the static vorbis bitstream settings */ + vorbis_comment x_vc; /* struct that stores all the user comments */ + vorbis_dsp_state x_vd; /* central working state for the packet->PCM decoder */ + vorbis_block x_vb; /* local working space for packet->PCM decode */ + t_int x_eos; /* end of stream */ + char *x_buffer;/* buffer used to pass on data to ogg/vorbis */ + + t_float x_position; /* current playing position */ + t_outlet *x_out_position; /* output to send them to */ + t_outlet *x_out_end; /* signal end of file */ + + t_outlet *x_connection; + t_int x_fd; /* the file handle */ + FILE *x_file; + int x_current_section; + t_int x_blocksize; /* size of a dsp block */ + t_int x_decoded; /* number of samples we got from decoder on last call */ + + t_float *x_outbuffer; /* buffer to store audio decoded data */ + t_int x_outwriteposition; + t_int x_outreadposition; + t_int x_outunread; + t_int x_outbuffersize; + + t_int x_samplerate; /* pd's samplerate, might differ from stream */ + t_int x_stream; /* indicates if a stream gets output */ +} t_oggread; + + /* output playing position */ +static void oggread_tick(t_oggread *x) +{ + outlet_float(x->x_out_position, x->x_position); + clock_delay(x->x_clock, 250); +} + +static int oggread_decode_input(t_oggread *x) +{ + long ret; /* bytes per channel returned by decoder */ + int i; + float **pcm; + + x->x_vi = ov_info(&x->x_ov, x->x_current_section); + + while(!x->x_eos) + { + ret = ov_read_float(&x->x_ov, &pcm, READ, &x->x_current_section); + if (ret == 0) + { + /* EOF */ + x->x_eos = 1; + x->x_stream = 0; + clock_unset(x->x_clock); + // post("oggread~: end of file detected, stopping"); + outlet_bang(x->x_out_end); + } + else if (ret < 0) + { + /* error in the stream. Not a problem, just reporting it in + case we (the app) cares. In this case, we don't. */ + } + else + { + /* we don't bother dealing with sample rate changes, etc, but + you'll have to */ + long j; + for(j = 0; j < ret; j++) + { + for(i = 0; i < x->x_vi->channels; i++) + { + x->x_outbuffer[x->x_outwriteposition] = pcm[i][j]; + x->x_outwriteposition = (x->x_outwriteposition + 1)%x->x_outbuffersize; + } + } + x->x_outunread += (t_int)ret * x->x_vi->channels; + + } + break; + } + x->x_decoded = (t_int)ret * x->x_vi->channels; /* num. of samples we got from decoder */ + + x->x_position = (t_float)ov_time_tell(&x->x_ov); + + /* exit decoding 'loop' here, we'll get called again by perform() */ + return 1; +} + + +static t_int *oggread_perform(t_int *w) +{ + t_oggread *x = (t_oggread*) (w[1]); + t_float *out1 = (t_float *)(w[2]); + t_float *out2 = (t_float *)(w[3]); + int n = (int)(w[4]); + int ret; + int i = 0; + + x->x_blocksize = n; + + while( n-- ) + { /* check that the stream provides enough data */ + if((x->x_stream == 1) && (x->x_outunread > (x->x_blocksize * x->x_vi->channels))) + { + if(x->x_vi->channels != 1) /* play stereo */ + { + *out1++=*(x->x_outbuffer+x->x_outreadposition); + x->x_outreadposition = (x->x_outreadposition + 1)%x->x_outbuffersize; + *out2++=*(x->x_outbuffer+x->x_outreadposition); + x->x_outreadposition = (x->x_outreadposition + 1)%x->x_outbuffersize; + x->x_outunread-=2; + } + else /* play mono on both sides */ + { + *out1++=*(x->x_outbuffer+x->x_outreadposition); + *out2++=*(x->x_outbuffer+x->x_outreadposition); + x->x_outreadposition = (x->x_outreadposition + 1)%x->x_outbuffersize; + x->x_outunread--; + } + } + else /* silence in case of buffer underrun */ + { + *out1++=0.0; + *out2++=0.0; + } + } + + /* decode data whenever we used up some samples from outbuffer */ + if((x->x_fd > 0) && (x->x_stream) /* only go when file is open and ready */ + && (x->x_outunread < (MIN_AUDIO_INPUT - x->x_decoded))) /* we used up data from last decode */ + { + // post("oggread~: decoding..."); + if(oggread_decode_input(x) != 1) + { + post("oggread~: decoder error"); + } + // else post("oggread~: decoder returned %d samples", x->x_decoded); + } + return (w+5); +} + +static void oggread_dsp(t_oggread *x, t_signal **sp) +{ + dsp_add(oggread_perform, 4, x, sp[1]->s_vec, sp[2]->s_vec, sp[1]->s_n); +} + + + /* start playing */ +static void oggread_start(t_oggread *x) +{ + if(x->x_fd > 0) + { + if(ov_time_seek(&x->x_ov, 0) < 0) + { + post("oggread~: could not rewind file to beginning"); + } + post("oggread~: START"); + x->x_eos = 0; + x->x_outreadposition = 0; + x->x_outwriteposition = 0; + x->x_outunread = 0; + x->x_position = 0; + clock_delay(x->x_clock, 0); + x->x_stream = 1; + } + else post("oggread~: no file open (ignored)"); +} + + /* resume file reading */ +static void oggread_resume(t_oggread *x) +{ + if(x->x_fd > 0) + { + x->x_stream = 1; + clock_delay(x->x_clock, 0); + post("oggread~: RESUME"); + } + else post("oggread~: encoder not initialised"); +} + + /* seek in file */ +static void oggread_seek(t_oggread *x, t_floatarg f) +{ + if(x->x_fd > 0) + if(ov_time_seek(&x->x_ov, f) < 0) + { + post("oggread~: could not set playing position to %g seconds", f); + } + else post("oggread~: playing position set to %g seconds", f); +} + + /* stop playing */ +static void oggread_stop(t_oggread *x) +{ + if(x->x_stream)post("oggread~: STOP"); + x->x_stream = 0; + clock_unset(x->x_clock); +} + +static void oggread_float(t_oggread *x, t_floatarg f) +{ + if(f == 0) oggread_stop(x); + else oggread_start(x); +} + + /* open ogg/vorbis file */ +static void oggread_open(t_oggread *x, t_symbol *filename) +{ + int i; + + x->x_stream = 0; + /* first close previous file */ + if(x->x_fd > 0) + { + ov_clear(&x->x_ov); + post("oggread~: previous file closed"); + } + /* open file for reading */ +#ifdef WIN32 + if((x->x_file = fopen(filename->s_name, "rb")) < 0) +#else + if((x->x_file = fopen(filename->s_name, "r")) < 0) +#endif + { + post("oggread~: could not open file \"%s\"", filename->s_name); + x->x_eos = 1; + x->x_fd = -1; + } + else + { + x->x_stream = 0; + x->x_eos = 0; + x->x_fd = 1; + x->x_outreadposition = 0; + x->x_outwriteposition = 0; + x->x_outunread = 0; + post("oggread~: file \"%s\" opened", filename->s_name); + outlet_float( x->x_out_position, 0); + + /* try to open as ogg vorbis file */ + if(ov_open(x->x_file, &x->x_ov, NULL, -1) < 0) + { /* an error occured (no ogg vorbis file ?) */ + post("oggread~: error: could not open \"%s\" as an OggVorbis file", filename->s_name); + ov_clear(&x->x_ov); + post("oggread~: file closed due to error"); + x->x_fd=-1; + x->x_eos=1; + return; + } + + /* print details about each logical bitstream in the input */ + if(ov_seekable(&x->x_ov)) + { + post("oggread~: input bitstream contained %ld logical bitstream section(s)", ov_streams(&x->x_ov)); + post("oggread~: total bitstream playing time: %ld seconds", (long)ov_time_total(&x->x_ov,-1)); + post("oggread~: encoded by: %s\n",ov_comment(&x->x_ov,-1)->vendor); + } + else + { + post("oggread~: file \"%s\" was not seekable\n" + "oggread~: first logical bitstream information:", filename->s_name); + } + + for(i = 0; i < ov_streams(&x->x_ov); i++) + { + x->x_vi = ov_info(&x->x_ov,i); + post("\tlogical bitstream section %d information:",i+1); + post("\t\t%ldHz %d channels bitrate %ldkbps serial number=%ld", + x->x_vi->rate,x->x_vi->channels,ov_bitrate(&x->x_ov,i)/1000, ov_serialnumber(&x->x_ov,i)); + post("\t\theader length: %ld bytes",(long) + (x->x_ov.dataoffsets[i] - x->x_ov.offsets[i])); + post("\t\tcompressed length: %ld bytes",(long)(ov_raw_total(&x->x_ov,i))); + post("\t\tplay time: %ld seconds\n",(long)ov_time_total(&x->x_ov,i)); + } + + } +} + + +static void oggread_free(t_oggread *x) +{ + if (x->x_fd > 0) { + post( "oggread~: closing file" ); + ov_clear(&x->x_ov); + x->x_fd = -1; + } + freebytes(x->x_outbuffer, OUTPUT_BUFFER_SIZE*sizeof(t_float)); + clock_free(x->x_clock); +} + +static void *oggread_new(t_floatarg fdographics) +{ + t_oggread *x = NULL; + + x = (t_oggread *)pd_new(oggread_class); + outlet_new(&x->x_obj, gensym("signal")); + outlet_new(&x->x_obj, gensym("signal")); + x->x_out_position = outlet_new(&x->x_obj, gensym("float")); + x->x_out_end = outlet_new(&x->x_obj, gensym("bang")); + x->x_clock = clock_new(x, (t_method)oggread_tick); + + x->x_fd = -1; + x->x_eos = 1; + x->x_stream = 0; + x->x_position = 0; + x->x_samplerate = sys_getsr(); + + x->x_outbuffersize = OUTPUT_BUFFER_SIZE; + x->x_outbuffer = (t_float*) getbytes(OUTPUT_BUFFER_SIZE*sizeof(t_float)); + + if(!x->x_outbuffer) + { + post( "oggread~: could not allocate buffer" ); + return NULL; + } + memset(x->x_outbuffer, 0x0, OUTPUT_BUFFER_SIZE); + + x->x_outreadposition = 0; + x->x_outwriteposition = 0; + x->x_outunread = 0; + x->x_decoded = 0; + + post(oggread_version); + + return (x); +} + + +void oggread_tilde_setup(void) +{ + oggread_class = class_new(gensym("oggread~"), + (t_newmethod) oggread_new, (t_method) oggread_free, + sizeof(t_oggread), 0, A_DEFFLOAT, A_NULL); + class_addfloat(oggread_class, (t_method)oggread_float); + class_addmethod(oggread_class, nullfn, gensym("signal"), 0); + class_addmethod(oggread_class, (t_method)oggread_dsp, gensym("dsp"), 0); + class_addmethod(oggread_class, (t_method)oggread_open, gensym("open"), A_SYMBOL, 0); + class_addmethod(oggread_class, (t_method)oggread_start, gensym("start"), 0); + class_addmethod(oggread_class, (t_method)oggread_resume, gensym("resume"), 0); + class_addmethod(oggread_class, (t_method)oggread_seek, gensym("seek"), A_DEFFLOAT, 0); + class_addmethod(oggread_class, (t_method)oggread_stop, gensym("stop"), 0); +} diff --git a/oggread~/oggread~-help.pd b/oggread~/oggread~-help.pd deleted file mode 100644 index 4d353f7..0000000 --- a/oggread~/oggread~-help.pd +++ /dev/null @@ -1,44 +0,0 @@ -#N canvas 280 107 572 464 12; -#X obj 27 411 dac~; -#X floatatom 75 412 8 0 0 0 - - -; -#X obj 36 343 oggread~; -#X msg 71 170 start; -#X msg 110 226 stop; -#X msg 118 287 resume; -#X msg 36 45 open myfile.ogg; -#X text 119 168 play file from beginning; -#X text 152 226 stop (pause) playing; -#X text 179 288 resume playing at current position; -#X msg 396 394 \; pd dsp 1; -#X msg 472 394 \; pd dsp 0; -#X obj 396 368 loadbang; -#X text 185 52 get latest version at; -#X text 152 411 position in file (seconds); -#X text 186 38 written by Olaf Matthes ; -#X text 17 16 oggread~ version 0.2 - Ogg Vorbis file player; -#X msg 126 250 0; -#X msg 95 195 1; -#X text 230 318 set new playing position (in seconds); -#X obj 95 375 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1 --1; -#X text 121 375 bang at end of file; -#X text 186 67 http://www.akustische-kunst.org/puredata/; -#X obj 55 102 openpanel; -#X msg 55 74 bang; -#X msg 55 129 open \$1; -#X msg 149 317 seek 60; -#X connect 2 0 0 0; -#X connect 2 1 0 1; -#X connect 2 2 1 0; -#X connect 2 3 20 0; -#X connect 3 0 2 0; -#X connect 4 0 2 0; -#X connect 5 0 2 0; -#X connect 6 0 2 0; -#X connect 12 0 10 0; -#X connect 17 0 2 0; -#X connect 18 0 2 0; -#X connect 23 0 25 0; -#X connect 24 0 23 0; -#X connect 25 0 2 0; -#X connect 26 0 2 0; diff --git a/oggread~/oggread~.c b/oggread~/oggread~.c deleted file mode 100644 index af1a3a0..0000000 --- a/oggread~/oggread~.c +++ /dev/null @@ -1,416 +0,0 @@ -/* ------------------------- oggread~ ------------------------------------------ */ -/* */ -/* Tilde object to read and play back Ogg Vorbis files. */ -/* Written by Olaf Matthes (olaf.matthes@gmx.de) */ -/* Get source at http://www.akustische-kunst.de/puredata/ */ -/* */ -/* This library is free software; you can redistribute it and/or */ -/* modify it under the terms of the GNU Lesser General Public */ -/* License as published by the Free Software Foundation; either */ -/* version 2 of the License, or (at your option) any later version. */ -/* */ -/* This library is distributed in the hope that it will be useful, */ -/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ -/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */ -/* Lesser General Public License for more details. */ -/* */ -/* You should have received a copy of the GNU Lesser General Public */ -/* License along with this library; if not, write to the */ -/* Free Software Foundation, Inc., 59 Temple Place - Suite 330, */ -/* Boston, MA 02111-1307, USA. */ -/* */ -/* Based on PureData by Miller Puckette and others. */ -/* Uses the Ogg Vorbis decoding library which can be found at */ -/* http://www.vorbis.com/ */ -/* */ -/* ---------------------------------------------------------------------------- */ - -#include "m_pd.h" -#include "s_stuff.h" -#include -#include - -#include -#include -#include -#include -#include -#ifdef WIN32 -#include -#include -#else -#include -#include -#include -#include -#include -#include -#include -#define SOCKET_ERROR -1 -#endif - -#ifdef _MSC_VER -#pragma warning( disable : 4244 ) -#pragma warning( disable : 4305 ) -#endif - -#define READ 4096 /* amount of data we pass on to decoder */ -#define MIN_AUDIO_INPUT READ*8 /* this is completely guessed! we just fill half the buffer */ -#define OUTPUT_BUFFER_SIZE 65536 /* audio output buffer: 64k */ - -static char *oggread_version = "oggread~: ogg/vorbis file reader version 0.2c, written by Olaf Matthes"; - -/* ------------------------ oggread~ ----------------------------- */ - -static t_class *oggread_class; - -typedef struct _oggread -{ - t_object x_obj; - t_clock *x_clock; - /* ogg/vorbis related stuff */ - OggVorbis_File x_ov; - ogg_stream_state x_os; /* take physical pages, weld into a logical stream of packets */ - ogg_sync_state x_oy; /* sync and verify incoming physical bitstream */ - ogg_page x_og; /* one Ogg bitstream page. Vorbis packets are inside */ - ogg_packet x_op; /* one raw packet of data for decode */ - vorbis_info *x_vi; /* struct that stores all the static vorbis bitstream settings */ - vorbis_comment x_vc; /* struct that stores all the user comments */ - vorbis_dsp_state x_vd; /* central working state for the packet->PCM decoder */ - vorbis_block x_vb; /* local working space for packet->PCM decode */ - t_int x_eos; /* end of stream */ - char *x_buffer;/* buffer used to pass on data to ogg/vorbis */ - - t_float x_position; /* current playing position */ - t_outlet *x_out_position; /* output to send them to */ - t_outlet *x_out_end; /* signal end of file */ - - t_outlet *x_connection; - t_int x_fd; /* the file handle */ - FILE *x_file; - int x_current_section; - t_int x_blocksize; /* size of a dsp block */ - t_int x_decoded; /* number of samples we got from decoder on last call */ - - t_float *x_outbuffer; /* buffer to store audio decoded data */ - t_int x_outwriteposition; - t_int x_outreadposition; - t_int x_outunread; - t_int x_outbuffersize; - - t_int x_samplerate; /* pd's samplerate, might differ from stream */ - t_int x_stream; /* indicates if a stream gets output */ -} t_oggread; - - /* output playing position */ -static void oggread_tick(t_oggread *x) -{ - outlet_float(x->x_out_position, x->x_position); - clock_delay(x->x_clock, 250); -} - -static int oggread_decode_input(t_oggread *x) -{ - long ret; /* bytes per channel returned by decoder */ - int i; - float **pcm; - - x->x_vi = ov_info(&x->x_ov, x->x_current_section); - - while(!x->x_eos) - { - ret = ov_read_float(&x->x_ov, &pcm, READ, &x->x_current_section); - if (ret == 0) - { - /* EOF */ - x->x_eos = 1; - x->x_stream = 0; - clock_unset(x->x_clock); - // post("oggread~: end of file detected, stopping"); - outlet_bang(x->x_out_end); - } - else if (ret < 0) - { - /* error in the stream. Not a problem, just reporting it in - case we (the app) cares. In this case, we don't. */ - } - else - { - /* we don't bother dealing with sample rate changes, etc, but - you'll have to */ - long j; - for(j = 0; j < ret; j++) - { - for(i = 0; i < x->x_vi->channels; i++) - { - x->x_outbuffer[x->x_outwriteposition] = pcm[i][j]; - x->x_outwriteposition = (x->x_outwriteposition + 1)%x->x_outbuffersize; - } - } - x->x_outunread += (t_int)ret * x->x_vi->channels; - - } - break; - } - x->x_decoded = (t_int)ret * x->x_vi->channels; /* num. of samples we got from decoder */ - - x->x_position = (t_float)ov_time_tell(&x->x_ov); - - /* exit decoding 'loop' here, we'll get called again by perform() */ - return 1; -} - - -static t_int *oggread_perform(t_int *w) -{ - t_oggread *x = (t_oggread*) (w[1]); - t_float *out1 = (t_float *)(w[2]); - t_float *out2 = (t_float *)(w[3]); - int n = (int)(w[4]); - int ret; - int i = 0; - - x->x_blocksize = n; - - while( n-- ) - { /* check that the stream provides enough data */ - if((x->x_stream == 1) && (x->x_outunread > (x->x_blocksize * x->x_vi->channels))) - { - if(x->x_vi->channels != 1) /* play stereo */ - { - *out1++=*(x->x_outbuffer+x->x_outreadposition); - x->x_outreadposition = (x->x_outreadposition + 1)%x->x_outbuffersize; - *out2++=*(x->x_outbuffer+x->x_outreadposition); - x->x_outreadposition = (x->x_outreadposition + 1)%x->x_outbuffersize; - x->x_outunread-=2; - } - else /* play mono on both sides */ - { - *out1++=*(x->x_outbuffer+x->x_outreadposition); - *out2++=*(x->x_outbuffer+x->x_outreadposition); - x->x_outreadposition = (x->x_outreadposition + 1)%x->x_outbuffersize; - x->x_outunread--; - } - } - else /* silence in case of buffer underrun */ - { - *out1++=0.0; - *out2++=0.0; - } - } - - /* decode data whenever we used up some samples from outbuffer */ - if((x->x_fd > 0) && (x->x_stream) /* only go when file is open and ready */ - && (x->x_outunread < (MIN_AUDIO_INPUT - x->x_decoded))) /* we used up data from last decode */ - { - // post("oggread~: decoding..."); - if(oggread_decode_input(x) != 1) - { - post("oggread~: decoder error"); - } - // else post("oggread~: decoder returned %d samples", x->x_decoded); - } - return (w+5); -} - -static void oggread_dsp(t_oggread *x, t_signal **sp) -{ - dsp_add(oggread_perform, 4, x, sp[1]->s_vec, sp[2]->s_vec, sp[1]->s_n); -} - - - /* start playing */ -static void oggread_start(t_oggread *x) -{ - if(x->x_fd > 0) - { - if(ov_time_seek(&x->x_ov, 0) < 0) - { - post("oggread~: could not rewind file to beginning"); - } - post("oggread~: START"); - x->x_eos = 0; - x->x_outreadposition = 0; - x->x_outwriteposition = 0; - x->x_outunread = 0; - x->x_position = 0; - clock_delay(x->x_clock, 0); - x->x_stream = 1; - } - else post("oggread~: no file open (ignored)"); -} - - /* resume file reading */ -static void oggread_resume(t_oggread *x) -{ - if(x->x_fd > 0) - { - x->x_stream = 1; - clock_delay(x->x_clock, 0); - post("oggread~: RESUME"); - } - else post("oggread~: encoder not initialised"); -} - - /* seek in file */ -static void oggread_seek(t_oggread *x, t_floatarg f) -{ - if(x->x_fd > 0) - if(ov_time_seek(&x->x_ov, f) < 0) - { - post("oggread~: could not set playing position to %g seconds", f); - } - else post("oggread~: playing position set to %g seconds", f); -} - - /* stop playing */ -static void oggread_stop(t_oggread *x) -{ - if(x->x_stream)post("oggread~: STOP"); - x->x_stream = 0; - clock_unset(x->x_clock); -} - -static void oggread_float(t_oggread *x, t_floatarg f) -{ - if(f == 0) oggread_stop(x); - else oggread_start(x); -} - - /* open ogg/vorbis file */ -static void oggread_open(t_oggread *x, t_symbol *filename) -{ - int i; - - x->x_stream = 0; - /* first close previous file */ - if(x->x_fd > 0) - { - ov_clear(&x->x_ov); - post("oggread~: previous file closed"); - } - /* open file for reading */ -#ifdef WIN32 - if((x->x_file = fopen(filename->s_name, "rb")) < 0) -#else - if((x->x_file = fopen(filename->s_name, "r")) < 0) -#endif - { - post("oggread~: could not open file \"%s\"", filename->s_name); - x->x_eos = 1; - x->x_fd = -1; - } - else - { - x->x_stream = 0; - x->x_eos = 0; - x->x_fd = 1; - x->x_outreadposition = 0; - x->x_outwriteposition = 0; - x->x_outunread = 0; - post("oggread~: file \"%s\" opened", filename->s_name); - outlet_float( x->x_out_position, 0); - - /* try to open as ogg vorbis file */ - if(ov_open(x->x_file, &x->x_ov, NULL, -1) < 0) - { /* an error occured (no ogg vorbis file ?) */ - post("oggread~: error: could not open \"%s\" as an OggVorbis file", filename->s_name); - ov_clear(&x->x_ov); - post("oggread~: file closed due to error"); - x->x_fd=-1; - x->x_eos=1; - return; - } - - /* print details about each logical bitstream in the input */ - if(ov_seekable(&x->x_ov)) - { - post("oggread~: input bitstream contained %ld logical bitstream section(s)", ov_streams(&x->x_ov)); - post("oggread~: total bitstream playing time: %ld seconds", (long)ov_time_total(&x->x_ov,-1)); - post("oggread~: encoded by: %s\n",ov_comment(&x->x_ov,-1)->vendor); - } - else - { - post("oggread~: file \"%s\" was not seekable\n" - "oggread~: first logical bitstream information:", filename->s_name); - } - - for(i = 0; i < ov_streams(&x->x_ov); i++) - { - x->x_vi = ov_info(&x->x_ov,i); - post("\tlogical bitstream section %d information:",i+1); - post("\t\t%ldHz %d channels bitrate %ldkbps serial number=%ld", - x->x_vi->rate,x->x_vi->channels,ov_bitrate(&x->x_ov,i)/1000, ov_serialnumber(&x->x_ov,i)); - post("\t\theader length: %ld bytes",(long) - (x->x_ov.dataoffsets[i] - x->x_ov.offsets[i])); - post("\t\tcompressed length: %ld bytes",(long)(ov_raw_total(&x->x_ov,i))); - post("\t\tplay time: %ld seconds\n",(long)ov_time_total(&x->x_ov,i)); - } - - } -} - - -static void oggread_free(t_oggread *x) -{ - if (x->x_fd > 0) { - post( "oggread~: closing file" ); - ov_clear(&x->x_ov); - x->x_fd = -1; - } - freebytes(x->x_outbuffer, OUTPUT_BUFFER_SIZE*sizeof(t_float)); - clock_free(x->x_clock); -} - -static void *oggread_new(t_floatarg fdographics) -{ - t_oggread *x = NULL; - - x = (t_oggread *)pd_new(oggread_class); - outlet_new(&x->x_obj, gensym("signal")); - outlet_new(&x->x_obj, gensym("signal")); - x->x_out_position = outlet_new(&x->x_obj, gensym("float")); - x->x_out_end = outlet_new(&x->x_obj, gensym("bang")); - x->x_clock = clock_new(x, (t_method)oggread_tick); - - x->x_fd = -1; - x->x_eos = 1; - x->x_stream = 0; - x->x_position = 0; - x->x_samplerate = sys_getsr(); - - x->x_outbuffersize = OUTPUT_BUFFER_SIZE; - x->x_outbuffer = (t_float*) getbytes(OUTPUT_BUFFER_SIZE*sizeof(t_float)); - - if(!x->x_outbuffer) - { - post( "oggread~: could not allocate buffer" ); - return NULL; - } - memset(x->x_outbuffer, 0x0, OUTPUT_BUFFER_SIZE); - - x->x_outreadposition = 0; - x->x_outwriteposition = 0; - x->x_outunread = 0; - x->x_decoded = 0; - - post(oggread_version); - - return (x); -} - - -void oggread_tilde_setup(void) -{ - oggread_class = class_new(gensym("oggread~"), - (t_newmethod) oggread_new, (t_method) oggread_free, - sizeof(t_oggread), 0, A_DEFFLOAT, A_NULL); - class_addfloat(oggread_class, (t_method)oggread_float); - class_addmethod(oggread_class, nullfn, gensym("signal"), 0); - class_addmethod(oggread_class, (t_method)oggread_dsp, gensym("dsp"), 0); - class_addmethod(oggread_class, (t_method)oggread_open, gensym("open"), A_SYMBOL, 0); - class_addmethod(oggread_class, (t_method)oggread_start, gensym("start"), 0); - class_addmethod(oggread_class, (t_method)oggread_resume, gensym("resume"), 0); - class_addmethod(oggread_class, (t_method)oggread_seek, gensym("seek"), A_DEFFLOAT, 0); - class_addmethod(oggread_class, (t_method)oggread_stop, gensym("stop"), 0); -} diff --git a/oggread~/oggread~.libs b/oggread~/oggread~.libs deleted file mode 100644 index 28114c5..0000000 --- a/oggread~/oggread~.libs +++ /dev/null @@ -1 +0,0 @@ --logg -lvorbis -lvorbisenc -lvorbisfile diff --git a/oggread~/readme b/oggread~/readme deleted file mode 100644 index 149af81..0000000 --- a/oggread~/readme +++ /dev/null @@ -1,75 +0,0 @@ -oggread~ version 0.2c -copyright (c) 2002 by Olaf Matthes - -oggread~ is an ogg/vorbis file player external for pd (by Miller -Puckette). - - -To run oggread~ place the file oggread~.dll for win or oggread~.pd_linux -in the directory of our patch or start pd with '-lib oggread~' flag. - -To compile oggread~ on Linux get the ogg/vorbice library from -http://www.vorbis.com/. -You have to modify the makefile to make it point to the place where the -ogg/vorbis library is. - - -This software is published under LGPL terms. - -This is software with ABSOLUTELY NO WARRANTY. -Use it at your OWN RISK. It's possible to damage e.g. hardware or your hearing -due to a bug or for other reasons. - -***************************************************************************** - -oggread~ uses the ogg/vorbice library to encode audio data. -The latest version of ogg/vorbis can be found at http://www.vorbice.com/ - -Below is the original copyright information taken from the ogg/vorbis library: - - -Copyright (c) 2001, Xiphophorus - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -- Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - -- Redistributions in binary form must reproduce the above copyright -notice, this list of conditions and the following disclaimer in the -documentation and/or other materials provided with the distribution. - -- Neither the name of the Xiphophorus nor the names of its contributors -may be used to endorse or promote products derived from this software -without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR -CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -***************************************************************************** - -Listening to it: - -To listen to ogg/vorbis encoded audio files many player need an extra plug-in. -Have a look at http://www.vorbis.com/ to find the appropiate plug-in for your -player. - - - - - -Latest version can be found at: -http://www.akustische-kunst.de/puredata/ - -Please report any bugs to olaf.matthes@gmx.de! \ No newline at end of file diff --git a/oggwrite~-help.pd b/oggwrite~-help.pd new file mode 100644 index 0000000..86bbd91 --- /dev/null +++ b/oggwrite~-help.pd @@ -0,0 +1,59 @@ +#N canvas 286 47 631 513 10; +#X obj 40 415 oggwrite~; +#X obj 40 64 osc~ 440; +#X floatatom 40 33 5 0 0; +#X msg 120 52 open myfile.ogg; +#X msg 175 125 start; +#X msg 185 148 stop; +#X msg 155 78 append; +#X msg 166 99 truncate; +#X floatatom 89 441 5 0 0; +#X floatatom 40 470 5 0 0; +#X msg 204 259 print; +#X text 189 180 vorbis +; +#X text 373 251 channels: 1 or 2 (default); +#X text 204 216 vbr ; +#X msg 203 232 vbr 44100 2 0.4; +#X text 324 238 quality settings: 0 - 1 (low - hi); +#X text 325 267 resampling currently not supported!; +#X text 202 290 comment ; +#X text 204 329 possible tags: TITLE \, ARTIST \, PERFORMER \, DESCRIPTION +\,; +#X text 293 342 GENRE \, LOCATION \, COPYRIGHT \, CONTACT \, DATE; +#X msg 202 307 ARTIST your=name; +#X msg 189 197 vorbis 44100 2 144 128 96; +#X text 136 441 ogg pages written to file; +#X msg 481 455 \; pd dsp 1; +#X msg 547 455 \; pd dsp 0; +#X obj 481 429 loadbang; +#X text 354 9 written by Olaf Matthes (olaf.matthes@gmx.de); +#X text 353 21 get latest version at; +#X text 354 34 http://www.akustische-kunst.de/puredata/; +#X text 209 76 append data at end of file; +#X text 226 97 overwrite previously recorded data; +#X text 221 145 stop recording; +#X text 225 50 open a file first!; +#X text 240 407 might result in audible dropouts!; +#X text 202 395 note: changing settings while recording; +#X text 85 470 file state (1 = open \; 0 = closed); +#X text 5 9 oggwrite~ version 0.1 - write Ogg Vorbis stream to file +; +#X msg 204 361 COPYRIGHT (c)=2002=Olaf=Matthes; +#X text 220 123 start recording; +#X connect 0 0 9 0; +#X connect 0 1 8 0; +#X connect 1 0 0 0; +#X connect 1 0 0 1; +#X connect 2 0 1 0; +#X connect 3 0 0 0; +#X connect 4 0 0 0; +#X connect 5 0 0 0; +#X connect 6 0 0 0; +#X connect 7 0 0 0; +#X connect 10 0 0 0; +#X connect 14 0 0 0; +#X connect 20 0 0 0; +#X connect 21 0 0 0; +#X connect 25 0 23 0; +#X connect 37 0 0 0; diff --git a/oggwrite~.c b/oggwrite~.c new file mode 100644 index 0000000..8f91c7f --- /dev/null +++ b/oggwrite~.c @@ -0,0 +1,751 @@ +/* -------------------------- oggwrite~ ---------------------------------------- */ +/* */ +/* Tilde object to send ogg/vorbis encoded stream to icecast2 server. */ +/* Written by Olaf Matthes (olaf.matthes@gmx.de). */ +/* Get source at http://www.akustische-kunst.de/puredata/ */ +/* */ +/* This library is free software; you can redistribute it and/or */ +/* modify it under the terms of the GNU Lesser General Public */ +/* License as published by the Free Software Foundation; either */ +/* version 2 of the License, or (at your option) any later version. */ +/* */ +/* This library is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */ +/* Lesser General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU Lesser General Public */ +/* License along with this library; if not, write to the */ +/* Free Software Foundation, Inc., 59 Temple Place - Suite 330, */ +/* Boston, MA 02111-1307, USA. */ +/* */ +/* Based on PureData by Miller Puckette and others. */ +/* Uses the Ogg/Vorbis encoding library which can be found at */ +/* http://www.vorbis.org */ +/* */ +/* ---------------------------------------------------------------------------- */ + + + +#ifdef _MSC_VER +#pragma warning( disable : 4244 ) +#pragma warning( disable : 4305 ) +#endif + +#include +#include +#include +#include +#include +#include +#include +#ifndef __APPLE__ +#include +#endif +#include +#include +#ifdef WIN32 +#include +#include +#include +#include +#else +#include +#include +#include +#include +#include +#define SOCKET_ERROR -1 +#endif + +#include "m_pd.h" /* standard pd stuff */ +#include /* vorbis encoder stuff */ + +#define READ 1024 /* number of samples send to encoder at each call */ + /* has to be even multiple of 64 */ + +static char *oggwrite_version = "oggwrite~: ogg/vorbis recorder version 0.1c, written by Olaf Matthes"; + +static t_class *oggwrite_class; + +typedef struct _oggwrite +{ + t_object x_obj; + /* ogg/vorbis related stuff */ + ogg_stream_state x_os; /* take physical pages, weld into a logical stream of packets */ + ogg_page x_og; /* one Ogg bitstream page. Vorbis packets are inside */ + ogg_packet x_op; /* one raw packet of data for decode */ + vorbis_info x_vi; /* struct that stores all the static vorbis bitstream settings */ + vorbis_comment x_vc; /* struct that stores all the user comments */ + vorbis_dsp_state x_vd; /* central working state for the packet->PCM decoder */ + vorbis_block x_vb; /* local working space for packet->PCM decode */ + + t_int x_eos; /* end of stream */ + t_int x_vorbis; /* info about encoder status */ + t_float x_pages; /* number of pages that have been output to server */ + t_outlet *x_outpages; /* output to send them to */ + + /* ringbuffer stuff */ + t_float *x_buffer; /* data to be buffered (ringbuffer)*/ + t_int x_bytesbuffered; /* number of unprocessed bytes in buffer */ + + /* ogg/vorbis format stuff */ + t_int x_samplerate; /* samplerate of stream (default = getsr() ) */ + t_float x_quality; /* desired quality level from 0.0 to 1.0 (lo to hi) */ + t_int x_br_max; /* max. bitrate of ogg/vorbis stream */ + t_int x_br_nom; /* nom. bitrate of ogg/vorbis stream */ + t_int x_br_min; /* min. bitrate of ogg/vorbis stream */ + t_int x_channels; /* number of channels (1 or 2) */ + t_int x_vbr; + + /* IceCast server stuff */ + char* x_passwd; /* password for server */ + char* x_bcname; /* name of broadcast */ + char* x_bcurl; /* url of broadcast */ + char* x_bcgenre; /* genre of broadcast */ + char* x_bcdescription; /* description */ + char* x_bcartist; /* artist */ + char* x_bclocation; + char* x_bccopyright; + char* x_bcperformer; + char* x_bccontact; + char* x_bcdate; /* system date when broadcast started */ + char* x_mountpoint; /* mountpoint for IceCast server */ + t_int x_bcpublic; /* do(n't) publish broadcast on www.oggwrite.com */ + + /* recording stuff */ + t_int x_fd; /* file descriptor of the mp3 output */ + t_int x_file_open_mode;/* file opening mode */ + t_int x_byteswritten; /* number of bytes written */ + t_int x_recflag; /* recording flag toggled by messages "start" and "stop" */ + + t_float x_f; /* float needed for signal input */ +} t_oggwrite; + +/* Utility functions */ + +static void sys_closesocket(int fd) +{ +#ifdef WIN32 + closesocket(fd); +#else + close(fd); +#endif +} + /* prototypes */ +static void oggwrite_vorbis_deinit(t_oggwrite *x); + +/* ----------------------------------------- oggwrite~ ---------------------------------- */ + /* write ogg/vorbis to file */ +static int oggwrite_write(t_oggwrite *x) +{ + int err = -1; /* error return code */ + + /* write out pages (if any) */ + while(!x->x_eos) + { + int result=ogg_stream_pageout(&(x->x_os),&(x->x_og)); + if(result==0)break; +#ifdef WIN32 + err = _write(x->x_fd, x->x_og.header, x->x_og.header_len); +#else + err = write(x->x_fd, x->x_og.header, x->x_og.header_len); +#endif + if(err < 0) + { + error("oggwrite~: could not send ogg header to server (%d)", err); + x->x_eos = 1; /* indicate (artificial) end of stream */ + return err; + } +#ifdef WIN32 + err = _write(x->x_fd, x->x_og.body, x->x_og.body_len); +#else + err = write(x->x_fd, x->x_og.body, x->x_og.body_len); +#endif + if(err < 0) + { + error("oggwrite~: could not send ogg body to server (%d)", err); + x->x_eos = 1; /* indicate (artificial) end of stream */ + return err; + } + x->x_pages++; /* count number of pages */ + /* there might be more than one pages we have to send */ + if(ogg_page_eos(&(x->x_og)))x->x_eos=1; + } + outlet_float(x->x_outpages, x->x_pages); /* update info */ + return 1; +} + + + /* encode data to ogg/vorbis stream */ +static void oggwrite_encode(t_oggwrite *x) +{ + unsigned short i, ch; + int err = 1; + int n; + int channel = x->x_channels; /* make lokal copy of num. of channels */ + + /* expose the buffer to submit data */ + float **inbuffer=vorbis_analysis_buffer(&(x->x_vd),READ); + + /* read from buffer */ + for(n = 0; n < READ / channel; n++) /* fill encode buffer */ + { + for(ch = 0; ch < channel; ch++) + { + inbuffer[ch][n] = (float)x->x_buffer[n * channel + ch]; + } + } + /* tell the library how much we actually submitted */ + vorbis_analysis_wrote(&(x->x_vd),n); + + /* vorbis does some data preanalysis, then divvies up blocks for + more involved (potentially parallel) processing. Get a single + block for encoding now */ + while(vorbis_analysis_blockout(&(x->x_vd),&(x->x_vb))==1) + { + /* analysis, assume we want to use bitrate management */ + vorbis_analysis(&(x->x_vb),NULL); + vorbis_bitrate_addblock(&(x->x_vb)); + + while(vorbis_bitrate_flushpacket(&(x->x_vd),&(x->x_op))) + { + /* weld the packet into the bitstream */ + ogg_stream_packetin(&(x->x_os),&(x->x_op)); + err = oggwrite_write(x); /* stream packet to server */ + } + } + /* check for errors */ + if(err < 0) + { + if(x->x_fd > 0) + { +#ifdef WIN32 + if(_close(x->x_fd) < 0) +#else + if(close(x->x_fd) < 0) +#endif + { + post( "oggwrite~: file closed due to an error" ); + outlet_float(x->x_obj.ob_outlet, 0); + } + } + } +} + + /* buffer data as channel interleaved floats */ +static t_int *oggwrite_perform(t_int *w) +{ + t_float *in1 = (t_float *)(w[1]); /* left audio inlet */ + t_float *in2 = (t_float *)(w[2]); /* right audio inlet */ + t_oggwrite *x = (t_oggwrite *)(w[3]); + int n = (int)(w[4]); /* number of samples */ + int i; + t_float in; + + /* copy the data into the buffer */ + if(x->x_channels != 1) /* everything but mono */ + { + n *= 2; /* two channels go into one buffer */ + + for(i = 0; i < n; i++) + { + if(i%2) + { + in = *(in2++); /* right inlet */ + } + else + { + in = *(in1++); /* left inlet */ + } + if (in > 1.0) { in = 1.0; } + if (in < -1.0) { in = -1.0; } + x->x_buffer[i + x->x_bytesbuffered] = in; + } + } + else /* mono encoding -> just take left signal inlet 'in1' */ + { + for(i = 0; i < n; i++) + { + in = *(in1++); + if (in > 1.0) { in = 1.0; } + if (in < -1.0) { in = -1.0; } + x->x_buffer[i + x->x_bytesbuffered] = in; + } + } + /* count, encode and send ogg/vorbis */ + if((x->x_fd >= 0)&&(x->x_recflag)) + { + /* count buffered samples when connected */ + x->x_bytesbuffered += n; + + /* encode and send to server */ + if((x->x_bytesbuffered >= READ)&&(x->x_vorbis >= 0)) + { + oggwrite_encode(x); /* encode data */ + x->x_bytesbuffered = 0; /* assume we got rid of all of them */ + } + } + return (w+5); +} + +static void oggwrite_dsp(t_oggwrite *x, t_signal **sp) +{ + dsp_add(oggwrite_perform, 4, sp[0]->s_vec, sp[1]->s_vec, x, sp[0]->s_n); +} + + /* initialize the vorbisenc library */ +static void oggwrite_vorbis_init(t_oggwrite *x) +{ + int err = -1; + + x->x_vorbis = -1; /* indicate that encoder is not available right now */ + + /* choose an encoding mode */ + vorbis_info_init(&(x->x_vi)); + + if(x->x_samplerate != sys_getsr())post("oggwrite~: warning: resampling from %d to %.0f not supported", x->x_samplerate, sys_getsr()); + if(x->x_vbr == 1) + { /* quality based setting */ + if(vorbis_encode_init_vbr(&(x->x_vi), x->x_channels, x->x_samplerate, x->x_quality)) + { + post("oggwrite~: ogg/vorbis mode initialisation failed: invalid parameters for quality"); + vorbis_info_clear(&(x->x_vi)); + return; + } + } + else + { /* bitrate based setting */ + if(vorbis_encode_init(&(x->x_vi), x->x_channels, x->x_samplerate, x->x_br_max*1024, x->x_br_nom*1024, x->x_br_min*1024)) + { + post("oggwrite~: ogg/vorbis mode initialisation failed: invalid parameters for quality"); + vorbis_info_clear(&(x->x_vi)); + return; + } + } + + /* add a comment */ + vorbis_comment_init(&(x->x_vc)); + vorbis_comment_add_tag(&(x->x_vc),"TITLE", x->x_bcname); + vorbis_comment_add_tag(&(x->x_vc),"ARTIST", x->x_bcartist); + vorbis_comment_add_tag(&(x->x_vc),"GENRE",x->x_bcgenre); + vorbis_comment_add_tag(&(x->x_vc),"DESCRIPTION", x->x_bcdescription); + vorbis_comment_add_tag(&(x->x_vc),"LOCATION",x->x_bclocation); + vorbis_comment_add_tag(&(x->x_vc),"PERFORMER",x->x_bcperformer); + vorbis_comment_add_tag(&(x->x_vc),"COPYRIGHT",x->x_bccopyright); + vorbis_comment_add_tag(&(x->x_vc),"CONTACT",x->x_bccontact); + vorbis_comment_add_tag(&(x->x_vc),"DATE",x->x_bcdate); + vorbis_comment_add_tag(&(x->x_vc),"ENCODER","oggwrite~ v0.1b for pure-data"); + + /* set up the analysis state and auxiliary encoding storage */ + vorbis_analysis_init(&(x->x_vd),&(x->x_vi)); + vorbis_block_init(&(x->x_vd),&(x->x_vb)); + + /* set up our packet->stream encoder */ + /* pick a random serial number; that way we can more likely build + chained streams just by concatenation */ + srand(time(NULL)); + ogg_stream_init(&(x->x_os),rand()); + + /* Vorbis streams begin with three headers; the initial header (with + most of the codec setup parameters) which is mandated by the Ogg + bitstream spec. The second header holds any comment fields. The + third header holds the bitstream codebook. We merely need to + make the headers, then pass them to libvorbis one at a time; + libvorbis handles the additional Ogg bitstream constraints */ + + { + ogg_packet header; + ogg_packet header_comm; + ogg_packet header_code; + + vorbis_analysis_headerout(&(x->x_vd),&(x->x_vc),&header,&header_comm,&header_code); + ogg_stream_packetin(&(x->x_os),&header); /* automatically placed in its own page */ + ogg_stream_packetin(&(x->x_os),&header_comm); + ogg_stream_packetin(&(x->x_os),&header_code); + + /* We don't have to write out here, but doing so makes streaming + * much easier, so we do, flushing ALL pages. This ensures the actual + * audio data will start on a new page + * + * IceCast2 server will take this as a first info about our stream + */ + while(!x->x_eos) + { + int result=ogg_stream_flush(&(x->x_os),&(x->x_og)); + if(result==0)break; +#ifdef WIN32 + err = _write(x->x_fd, x->x_og.header, x->x_og.header_len); +#else + err = write(x->x_fd, x->x_og.header, x->x_og.header_len); +#endif + if(err < 0) + { + error("oggwrite~: could not write ogg header to file (%d)", err); + x->x_eos = 1; /* indicate end of stream */ + x->x_vorbis = -1; /* stop encoding instantly */ + if(x->x_fd > 0) + { +#ifdef WIN32 + if(_close(x->x_fd) < 0) +#else + if(close(x->x_fd) < 0) +#endif + { + post( "oggwrite~: file closed due to an error" ); + outlet_float(x->x_obj.ob_outlet, 0); + } + } + return; + } +#ifdef WIN32 + err = _write(x->x_fd, x->x_og.body, x->x_og.body_len); +#else + err = write(x->x_fd, x->x_og.body, x->x_og.body_len); +#endif + if(err < 0) + { + error("oggwrite~: could not write ogg body to file (%d)", err); + x->x_eos = 1; /* indicate end of stream */ + x->x_vorbis = -1; /* stop encoding instantly */ + if(x->x_fd > 0) + { +#ifdef WIN32 + if(_close(x->x_fd) < 0) +#else + if(close(x->x_fd) < 0) +#endif + { + post( "oggwrite~: file closed due to an error" ); + outlet_float(x->x_obj.ob_outlet, 0); + } + } + return; + } + } + } + x->x_vorbis = 1; /* vorbis encoder initialised */ + post("oggwrite~: ogg/vorbis encoder (re)initialised"); +} + + /* initialize the vorbisenc library */ +static void oggwrite_vorbis_deinit(t_oggwrite *x) +{ + x->x_vorbis = -1; + vorbis_analysis_wrote(&(x->x_vd),0); + /* clean up and exit. vorbis_info_clear() must be called last */ + ogg_stream_clear(&(x->x_os)); + vorbis_block_clear(&(x->x_vb)); + vorbis_dsp_clear(&(x->x_vd)); + vorbis_comment_clear(&(x->x_vc)); + vorbis_info_clear(&(x->x_vi)); + post("oggwrite~: ogg/vorbis encoder closed"); +} + + /* connect to oggwrite server */ +static void oggwrite_open(t_oggwrite *x, t_symbol *sfile) +{ + time_t now; /* to get the time */ + + /* closing previous file descriptor */ + if(x->x_fd > 0) + { +#ifdef WIN32 + if(_close(x->x_fd) < 0) +#else + if(close(x->x_fd) < 0) +#endif + { + error( "oggwrite~: file closed" ); + outlet_float(x->x_obj.ob_outlet, 0); + } + } + + if(x->x_recflag) + { + x->x_recflag = 0; + } + +#ifdef WIN32 + if((x->x_fd = _open( sfile->s_name, x->x_file_open_mode, _S_IREAD|_S_IWRITE)) < 0) +#else + if((x->x_fd = open( sfile->s_name, x->x_file_open_mode, S_IRWXU|S_IRWXG|S_IRWXO )) < 0) +#endif + { + error( "oggwrite~: can not open \"%s\"", sfile->s_name); + x->x_fd=-1; + return; + } + x->x_byteswritten = 0; + post( "oggwrite~: \"%s \" opened", sfile->s_name); + outlet_float(x->x_obj.ob_outlet, 1); + + /* get the time for the DATE comment, then init encoder */ + now=time(NULL); + x->x_bcdate = ctime(&now); + + x->x_eos = 0; + oggwrite_vorbis_init(x); +} + + /* setting file write mode to append */ +static void oggwrite_append(t_oggwrite *x) +{ +#ifdef WIN32 + x->x_file_open_mode = _O_CREAT|_O_WRONLY|_O_APPEND|_O_BINARY; +#else + x->x_file_open_mode = O_CREAT|O_WRONLY|O_APPEND|O_NONBLOCK; +#endif + if(x->x_fd>=0)post("oggwrite~: mode set to append: open a new file to make changes take effect"); +} + + /* setting file write mode to truncate */ +static void oggwrite_truncate(t_oggwrite *x) +{ +#ifdef WIN32 + x->x_file_open_mode = _O_CREAT|_O_WRONLY|_O_TRUNC|_O_BINARY; +#else + x->x_file_open_mode = O_CREAT|O_WRONLY|O_TRUNC|O_NONBLOCK; +#endif + if(x->x_fd>=0)post("oggwrite~: mode set to truncate: open a new file to make changes take effect"); +} + + /* start recording */ +static void oggwrite_start(t_oggwrite *x) +{ + if ( x->x_fd < 0 ) { + post("oggwrite~: no file selected"); + return; + } + + if ( x->x_recflag == 1 ) { + post("oggwrite~: already recording"); + return; + } + if(x->x_vorbis < 0) + { + oggwrite_vorbis_init(x); + } + + x->x_recflag = 1; + post("oggwrite~: start recording"); +} + + /* stop recording */ +static void oggwrite_stop(t_oggwrite *x) +{ + int err = -1; + + /* first stop recording / buffering and so on, than do the rest */ + x->x_recflag = 0; + post("oggwrite~: recording stoped"); + if(x->x_vorbis >= 0) + { + oggwrite_vorbis_deinit(x); + } +} + + /* set comment fields for header (reads in just anything) */ +static void oggwrite_comment(t_oggwrite *x, t_symbol *s, t_int argc, t_atom* argv) +{ + int i = argc; + char *comment = NULL; + int len = strlen(atom_gensym(argv)->s_name); + + comment = atom_gensym(argv)->s_name; + + while (len--) + { + if(*(comment + len) == '=')*(comment + len) = ' '; + } + + if(strstr(s->s_name, "ARTIST")) + { + x->x_bcartist = comment; + post("oggwrite~: ARTIST = %s", x->x_bcartist); + } + else if(strstr(s->s_name, "GENRE")) + { + x->x_bcgenre = comment; + post("oggwrite~: GENRE = %s", x->x_bcgenre); + } + else if(strstr(s->s_name, "TITLE")) + { + x->x_bcname = comment; + post("oggwrite~: TITLE = %s", x->x_bcname); + } + else if(strstr(s->s_name, "PERFORMER")) + { + x->x_bcperformer = comment; + post("oggwrite~: PERFORMER = %s", x->x_bcperformer); + } + else if(strstr(s->s_name, "LOCATION")) + { + x->x_bclocation = comment; + post("oggwrite~: LOCATION = %s", x->x_bclocation); + } + else if(strstr(s->s_name, "COPYRIGHT")) + { + x->x_bccopyright = comment; + post("oggwrite~: COPYRIGHT = %s", x->x_bccopyright); + } + else if(strstr(s->s_name, "CONTACT")) + { + x->x_bccontact = comment; + post("oggwrite~: CONTACT = %s", x->x_bccontact); + } + else if(strstr(s->s_name, "DESCRIPTION")) + { + x->x_bcdescription = comment; + post("oggwrite~: DESCRIPTION = %s", x->x_bcdescription); + } + else if(strstr(s->s_name, "DATE")) + { + x->x_bcdate = comment; + post("oggwrite~: DATE=%s", x->x_bcdate); + } + else post("oggwrite~: no method for %s", s->s_name); + if(x->x_vorbis >=0) + { + oggwrite_vorbis_deinit(x); + oggwrite_vorbis_init(x); + } +} + + /* settings for variable bitrate encoding */ +static void oggwrite_vbr(t_oggwrite *x, t_floatarg fsr, t_floatarg fchannels, + t_floatarg fquality) +{ + x->x_vbr = 1; + x->x_samplerate = (t_int)fsr; + x->x_quality = fquality; + x->x_channels = (t_int)fchannels; + post("oggwrite~: %d channels @ %d Hz, quality %.2f", x->x_channels, x->x_samplerate, x->x_quality); + if(x->x_vorbis >=0) + { + oggwrite_vorbis_deinit(x); + oggwrite_vorbis_init(x); + } +} + + /* settings for bitrate-based vbr encoding */ +static void oggwrite_vorbis(t_oggwrite *x, t_floatarg fsr, t_floatarg fchannels, + t_floatarg fmax, t_floatarg fnom, t_floatarg fmin) +{ + x->x_vbr = 0; + x->x_samplerate = (t_int)fsr; + x->x_channels = (t_int)fchannels; + x->x_br_max = (t_int)fmax; + x->x_br_nom = (t_int)fnom; + x->x_br_min = (t_int)fmin; + post("oggwrite~: %d channels @ %d Hz, bitrates: max. %d / nom. %d / min. %d", + x->x_channels, x->x_samplerate, x->x_br_max, x->x_br_nom, x->x_br_min); + if(x->x_vorbis >=0) + { + oggwrite_vorbis_deinit(x); + oggwrite_vorbis_init(x); + } +} + + /* print settings to pd's console window */ +static void oggwrite_print(t_oggwrite *x) +{ + if(x->x_vbr == 1) + { + post("oggwrite~: Ogg Vorbis encoder: %d channels @ %d Hz, quality %.2f", x->x_channels, x->x_samplerate, x->x_quality); + } + else + { + post("oggwrite~: Ogg Vorbis encoder: %d channels @ %d Hz, bitrates: max. %d, nom. %d, min. %d", + x->x_channels, x->x_samplerate, x->x_br_max, x->x_br_nom, x->x_br_min); + } + post("oggwrite~: Ogg Vorbis comments:"); + post(" TITLE = %s", x->x_bcname); + post(" ARTIST = %s", x->x_bcartist); + post(" PERFORMER = %s", x->x_bcperformer); + post(" GENRE = %s", x->x_bcgenre); + post(" LOCATION = %s", x->x_bclocation); + post(" COPYRIGHT = %s", x->x_bccopyright); + post(" CONTACT = %s", x->x_bccontact); + post(" DESCRIPTION = %s", x->x_bcdescription); + post(" DATE = %s", x->x_bcdate); +} + + + /* clean up */ +static void oggwrite_free(t_oggwrite *x) +{ + if(x->x_vorbis >= 0) /* close encoder */ + { + oggwrite_vorbis_deinit(x); + } + if(x->x_fd >= 0) + { /* close file */ +#ifdef WIN32 + _close(x->x_fd); +#else + close(x->x_fd); +#endif + outlet_float(x->x_obj.ob_outlet, 0); + } + freebytes(x->x_buffer, READ*sizeof(t_float)); +} + +static void *oggwrite_new(void) +{ + t_oggwrite *x = (t_oggwrite *)pd_new(oggwrite_class); + inlet_new (&x->x_obj, &x->x_obj.ob_pd, gensym ("signal"), gensym ("signal")); + outlet_new(&x->x_obj, gensym("float")); + x->x_outpages = outlet_new(&x->x_obj, gensym("float")); + x->x_fd = -1; +#ifdef WIN32 + x->x_file_open_mode = _O_CREAT|_O_WRONLY|_O_APPEND|_O_BINARY; +#else + x->x_file_open_mode = O_CREAT|O_WRONLY|O_APPEND|O_NONBLOCK; +#endif + x->x_vorbis = -1; + x->x_eos = 0; + x->x_vbr = 1; /* use the vbr setting by default */ + x->x_samplerate = sys_getsr(); /* no resampling supported, sorry */ + x->x_quality = 0.4; /* quality 0.4 gives roughly 128kbps VBR stream */ + x->x_channels = 2; /* stereo */ + x->x_br_max = 144; + x->x_br_nom = 128; + x->x_br_min = 96; + x->x_buffer = getbytes(READ*sizeof(t_float)); + if (!x->x_buffer) /* check buffer */ + { + error("out of memory!"); + } + x->x_bytesbuffered = 0; + x->x_pages = 0; + x->x_bcname = "ogg/vorbis stream"; + x->x_bcurl = "http://www.pure-data.info/"; + x->x_bcgenre = "experimental"; + x->x_bcdescription = "ogg/vorbis stream recorded with pure-data using oggwrite~"; + x->x_bcartist = "pure-data"; + x->x_bclocation = x->x_bcurl; + x->x_bccopyright = ""; + x->x_bcperformer = ""; + x->x_bccontact = ""; + x->x_bcdate = ""; + post(oggwrite_version); + return(x); +} + +void oggwrite_tilde_setup(void) +{ + oggwrite_class = class_new(gensym("oggwrite~"), (t_newmethod)oggwrite_new, (t_method)oggwrite_free, + sizeof(t_oggwrite), 0, 0); + CLASS_MAINSIGNALIN(oggwrite_class, t_oggwrite, x_f ); + class_addmethod(oggwrite_class, (t_method)oggwrite_dsp, gensym("dsp"), 0); + class_addmethod(oggwrite_class, (t_method)oggwrite_open, gensym("open"), A_SYMBOL, 0); + class_addmethod(oggwrite_class, (t_method)oggwrite_start, gensym("start"), 0); + class_addmethod(oggwrite_class, (t_method)oggwrite_stop, gensym("stop"), 0); + class_addmethod(oggwrite_class, (t_method)oggwrite_append, gensym("append"), 0); + class_addmethod(oggwrite_class, (t_method)oggwrite_truncate, gensym("truncate"), 0); + class_addmethod(oggwrite_class, (t_method)oggwrite_vorbis, gensym("vorbis"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0); + class_addmethod(oggwrite_class, (t_method)oggwrite_vbr, gensym("vbr"), A_FLOAT, A_FLOAT, A_FLOAT, 0); + class_addmethod(oggwrite_class, (t_method)oggwrite_print, gensym("print"), 0); + class_addanything(oggwrite_class, oggwrite_comment); +} diff --git a/oggwrite~/oggwrite~-help.pd b/oggwrite~/oggwrite~-help.pd deleted file mode 100644 index 86bbd91..0000000 --- a/oggwrite~/oggwrite~-help.pd +++ /dev/null @@ -1,59 +0,0 @@ -#N canvas 286 47 631 513 10; -#X obj 40 415 oggwrite~; -#X obj 40 64 osc~ 440; -#X floatatom 40 33 5 0 0; -#X msg 120 52 open myfile.ogg; -#X msg 175 125 start; -#X msg 185 148 stop; -#X msg 155 78 append; -#X msg 166 99 truncate; -#X floatatom 89 441 5 0 0; -#X floatatom 40 470 5 0 0; -#X msg 204 259 print; -#X text 189 180 vorbis -; -#X text 373 251 channels: 1 or 2 (default); -#X text 204 216 vbr ; -#X msg 203 232 vbr 44100 2 0.4; -#X text 324 238 quality settings: 0 - 1 (low - hi); -#X text 325 267 resampling currently not supported!; -#X text 202 290 comment ; -#X text 204 329 possible tags: TITLE \, ARTIST \, PERFORMER \, DESCRIPTION -\,; -#X text 293 342 GENRE \, LOCATION \, COPYRIGHT \, CONTACT \, DATE; -#X msg 202 307 ARTIST your=name; -#X msg 189 197 vorbis 44100 2 144 128 96; -#X text 136 441 ogg pages written to file; -#X msg 481 455 \; pd dsp 1; -#X msg 547 455 \; pd dsp 0; -#X obj 481 429 loadbang; -#X text 354 9 written by Olaf Matthes (olaf.matthes@gmx.de); -#X text 353 21 get latest version at; -#X text 354 34 http://www.akustische-kunst.de/puredata/; -#X text 209 76 append data at end of file; -#X text 226 97 overwrite previously recorded data; -#X text 221 145 stop recording; -#X text 225 50 open a file first!; -#X text 240 407 might result in audible dropouts!; -#X text 202 395 note: changing settings while recording; -#X text 85 470 file state (1 = open \; 0 = closed); -#X text 5 9 oggwrite~ version 0.1 - write Ogg Vorbis stream to file -; -#X msg 204 361 COPYRIGHT (c)=2002=Olaf=Matthes; -#X text 220 123 start recording; -#X connect 0 0 9 0; -#X connect 0 1 8 0; -#X connect 1 0 0 0; -#X connect 1 0 0 1; -#X connect 2 0 1 0; -#X connect 3 0 0 0; -#X connect 4 0 0 0; -#X connect 5 0 0 0; -#X connect 6 0 0 0; -#X connect 7 0 0 0; -#X connect 10 0 0 0; -#X connect 14 0 0 0; -#X connect 20 0 0 0; -#X connect 21 0 0 0; -#X connect 25 0 23 0; -#X connect 37 0 0 0; diff --git a/oggwrite~/oggwrite~.c b/oggwrite~/oggwrite~.c deleted file mode 100644 index 8f91c7f..0000000 --- a/oggwrite~/oggwrite~.c +++ /dev/null @@ -1,751 +0,0 @@ -/* -------------------------- oggwrite~ ---------------------------------------- */ -/* */ -/* Tilde object to send ogg/vorbis encoded stream to icecast2 server. */ -/* Written by Olaf Matthes (olaf.matthes@gmx.de). */ -/* Get source at http://www.akustische-kunst.de/puredata/ */ -/* */ -/* This library is free software; you can redistribute it and/or */ -/* modify it under the terms of the GNU Lesser General Public */ -/* License as published by the Free Software Foundation; either */ -/* version 2 of the License, or (at your option) any later version. */ -/* */ -/* This library is distributed in the hope that it will be useful, */ -/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ -/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */ -/* Lesser General Public License for more details. */ -/* */ -/* You should have received a copy of the GNU Lesser General Public */ -/* License along with this library; if not, write to the */ -/* Free Software Foundation, Inc., 59 Temple Place - Suite 330, */ -/* Boston, MA 02111-1307, USA. */ -/* */ -/* Based on PureData by Miller Puckette and others. */ -/* Uses the Ogg/Vorbis encoding library which can be found at */ -/* http://www.vorbis.org */ -/* */ -/* ---------------------------------------------------------------------------- */ - - - -#ifdef _MSC_VER -#pragma warning( disable : 4244 ) -#pragma warning( disable : 4305 ) -#endif - -#include -#include -#include -#include -#include -#include -#include -#ifndef __APPLE__ -#include -#endif -#include -#include -#ifdef WIN32 -#include -#include -#include -#include -#else -#include -#include -#include -#include -#include -#define SOCKET_ERROR -1 -#endif - -#include "m_pd.h" /* standard pd stuff */ -#include /* vorbis encoder stuff */ - -#define READ 1024 /* number of samples send to encoder at each call */ - /* has to be even multiple of 64 */ - -static char *oggwrite_version = "oggwrite~: ogg/vorbis recorder version 0.1c, written by Olaf Matthes"; - -static t_class *oggwrite_class; - -typedef struct _oggwrite -{ - t_object x_obj; - /* ogg/vorbis related stuff */ - ogg_stream_state x_os; /* take physical pages, weld into a logical stream of packets */ - ogg_page x_og; /* one Ogg bitstream page. Vorbis packets are inside */ - ogg_packet x_op; /* one raw packet of data for decode */ - vorbis_info x_vi; /* struct that stores all the static vorbis bitstream settings */ - vorbis_comment x_vc; /* struct that stores all the user comments */ - vorbis_dsp_state x_vd; /* central working state for the packet->PCM decoder */ - vorbis_block x_vb; /* local working space for packet->PCM decode */ - - t_int x_eos; /* end of stream */ - t_int x_vorbis; /* info about encoder status */ - t_float x_pages; /* number of pages that have been output to server */ - t_outlet *x_outpages; /* output to send them to */ - - /* ringbuffer stuff */ - t_float *x_buffer; /* data to be buffered (ringbuffer)*/ - t_int x_bytesbuffered; /* number of unprocessed bytes in buffer */ - - /* ogg/vorbis format stuff */ - t_int x_samplerate; /* samplerate of stream (default = getsr() ) */ - t_float x_quality; /* desired quality level from 0.0 to 1.0 (lo to hi) */ - t_int x_br_max; /* max. bitrate of ogg/vorbis stream */ - t_int x_br_nom; /* nom. bitrate of ogg/vorbis stream */ - t_int x_br_min; /* min. bitrate of ogg/vorbis stream */ - t_int x_channels; /* number of channels (1 or 2) */ - t_int x_vbr; - - /* IceCast server stuff */ - char* x_passwd; /* password for server */ - char* x_bcname; /* name of broadcast */ - char* x_bcurl; /* url of broadcast */ - char* x_bcgenre; /* genre of broadcast */ - char* x_bcdescription; /* description */ - char* x_bcartist; /* artist */ - char* x_bclocation; - char* x_bccopyright; - char* x_bcperformer; - char* x_bccontact; - char* x_bcdate; /* system date when broadcast started */ - char* x_mountpoint; /* mountpoint for IceCast server */ - t_int x_bcpublic; /* do(n't) publish broadcast on www.oggwrite.com */ - - /* recording stuff */ - t_int x_fd; /* file descriptor of the mp3 output */ - t_int x_file_open_mode;/* file opening mode */ - t_int x_byteswritten; /* number of bytes written */ - t_int x_recflag; /* recording flag toggled by messages "start" and "stop" */ - - t_float x_f; /* float needed for signal input */ -} t_oggwrite; - -/* Utility functions */ - -static void sys_closesocket(int fd) -{ -#ifdef WIN32 - closesocket(fd); -#else - close(fd); -#endif -} - /* prototypes */ -static void oggwrite_vorbis_deinit(t_oggwrite *x); - -/* ----------------------------------------- oggwrite~ ---------------------------------- */ - /* write ogg/vorbis to file */ -static int oggwrite_write(t_oggwrite *x) -{ - int err = -1; /* error return code */ - - /* write out pages (if any) */ - while(!x->x_eos) - { - int result=ogg_stream_pageout(&(x->x_os),&(x->x_og)); - if(result==0)break; -#ifdef WIN32 - err = _write(x->x_fd, x->x_og.header, x->x_og.header_len); -#else - err = write(x->x_fd, x->x_og.header, x->x_og.header_len); -#endif - if(err < 0) - { - error("oggwrite~: could not send ogg header to server (%d)", err); - x->x_eos = 1; /* indicate (artificial) end of stream */ - return err; - } -#ifdef WIN32 - err = _write(x->x_fd, x->x_og.body, x->x_og.body_len); -#else - err = write(x->x_fd, x->x_og.body, x->x_og.body_len); -#endif - if(err < 0) - { - error("oggwrite~: could not send ogg body to server (%d)", err); - x->x_eos = 1; /* indicate (artificial) end of stream */ - return err; - } - x->x_pages++; /* count number of pages */ - /* there might be more than one pages we have to send */ - if(ogg_page_eos(&(x->x_og)))x->x_eos=1; - } - outlet_float(x->x_outpages, x->x_pages); /* update info */ - return 1; -} - - - /* encode data to ogg/vorbis stream */ -static void oggwrite_encode(t_oggwrite *x) -{ - unsigned short i, ch; - int err = 1; - int n; - int channel = x->x_channels; /* make lokal copy of num. of channels */ - - /* expose the buffer to submit data */ - float **inbuffer=vorbis_analysis_buffer(&(x->x_vd),READ); - - /* read from buffer */ - for(n = 0; n < READ / channel; n++) /* fill encode buffer */ - { - for(ch = 0; ch < channel; ch++) - { - inbuffer[ch][n] = (float)x->x_buffer[n * channel + ch]; - } - } - /* tell the library how much we actually submitted */ - vorbis_analysis_wrote(&(x->x_vd),n); - - /* vorbis does some data preanalysis, then divvies up blocks for - more involved (potentially parallel) processing. Get a single - block for encoding now */ - while(vorbis_analysis_blockout(&(x->x_vd),&(x->x_vb))==1) - { - /* analysis, assume we want to use bitrate management */ - vorbis_analysis(&(x->x_vb),NULL); - vorbis_bitrate_addblock(&(x->x_vb)); - - while(vorbis_bitrate_flushpacket(&(x->x_vd),&(x->x_op))) - { - /* weld the packet into the bitstream */ - ogg_stream_packetin(&(x->x_os),&(x->x_op)); - err = oggwrite_write(x); /* stream packet to server */ - } - } - /* check for errors */ - if(err < 0) - { - if(x->x_fd > 0) - { -#ifdef WIN32 - if(_close(x->x_fd) < 0) -#else - if(close(x->x_fd) < 0) -#endif - { - post( "oggwrite~: file closed due to an error" ); - outlet_float(x->x_obj.ob_outlet, 0); - } - } - } -} - - /* buffer data as channel interleaved floats */ -static t_int *oggwrite_perform(t_int *w) -{ - t_float *in1 = (t_float *)(w[1]); /* left audio inlet */ - t_float *in2 = (t_float *)(w[2]); /* right audio inlet */ - t_oggwrite *x = (t_oggwrite *)(w[3]); - int n = (int)(w[4]); /* number of samples */ - int i; - t_float in; - - /* copy the data into the buffer */ - if(x->x_channels != 1) /* everything but mono */ - { - n *= 2; /* two channels go into one buffer */ - - for(i = 0; i < n; i++) - { - if(i%2) - { - in = *(in2++); /* right inlet */ - } - else - { - in = *(in1++); /* left inlet */ - } - if (in > 1.0) { in = 1.0; } - if (in < -1.0) { in = -1.0; } - x->x_buffer[i + x->x_bytesbuffered] = in; - } - } - else /* mono encoding -> just take left signal inlet 'in1' */ - { - for(i = 0; i < n; i++) - { - in = *(in1++); - if (in > 1.0) { in = 1.0; } - if (in < -1.0) { in = -1.0; } - x->x_buffer[i + x->x_bytesbuffered] = in; - } - } - /* count, encode and send ogg/vorbis */ - if((x->x_fd >= 0)&&(x->x_recflag)) - { - /* count buffered samples when connected */ - x->x_bytesbuffered += n; - - /* encode and send to server */ - if((x->x_bytesbuffered >= READ)&&(x->x_vorbis >= 0)) - { - oggwrite_encode(x); /* encode data */ - x->x_bytesbuffered = 0; /* assume we got rid of all of them */ - } - } - return (w+5); -} - -static void oggwrite_dsp(t_oggwrite *x, t_signal **sp) -{ - dsp_add(oggwrite_perform, 4, sp[0]->s_vec, sp[1]->s_vec, x, sp[0]->s_n); -} - - /* initialize the vorbisenc library */ -static void oggwrite_vorbis_init(t_oggwrite *x) -{ - int err = -1; - - x->x_vorbis = -1; /* indicate that encoder is not available right now */ - - /* choose an encoding mode */ - vorbis_info_init(&(x->x_vi)); - - if(x->x_samplerate != sys_getsr())post("oggwrite~: warning: resampling from %d to %.0f not supported", x->x_samplerate, sys_getsr()); - if(x->x_vbr == 1) - { /* quality based setting */ - if(vorbis_encode_init_vbr(&(x->x_vi), x->x_channels, x->x_samplerate, x->x_quality)) - { - post("oggwrite~: ogg/vorbis mode initialisation failed: invalid parameters for quality"); - vorbis_info_clear(&(x->x_vi)); - return; - } - } - else - { /* bitrate based setting */ - if(vorbis_encode_init(&(x->x_vi), x->x_channels, x->x_samplerate, x->x_br_max*1024, x->x_br_nom*1024, x->x_br_min*1024)) - { - post("oggwrite~: ogg/vorbis mode initialisation failed: invalid parameters for quality"); - vorbis_info_clear(&(x->x_vi)); - return; - } - } - - /* add a comment */ - vorbis_comment_init(&(x->x_vc)); - vorbis_comment_add_tag(&(x->x_vc),"TITLE", x->x_bcname); - vorbis_comment_add_tag(&(x->x_vc),"ARTIST", x->x_bcartist); - vorbis_comment_add_tag(&(x->x_vc),"GENRE",x->x_bcgenre); - vorbis_comment_add_tag(&(x->x_vc),"DESCRIPTION", x->x_bcdescription); - vorbis_comment_add_tag(&(x->x_vc),"LOCATION",x->x_bclocation); - vorbis_comment_add_tag(&(x->x_vc),"PERFORMER",x->x_bcperformer); - vorbis_comment_add_tag(&(x->x_vc),"COPYRIGHT",x->x_bccopyright); - vorbis_comment_add_tag(&(x->x_vc),"CONTACT",x->x_bccontact); - vorbis_comment_add_tag(&(x->x_vc),"DATE",x->x_bcdate); - vorbis_comment_add_tag(&(x->x_vc),"ENCODER","oggwrite~ v0.1b for pure-data"); - - /* set up the analysis state and auxiliary encoding storage */ - vorbis_analysis_init(&(x->x_vd),&(x->x_vi)); - vorbis_block_init(&(x->x_vd),&(x->x_vb)); - - /* set up our packet->stream encoder */ - /* pick a random serial number; that way we can more likely build - chained streams just by concatenation */ - srand(time(NULL)); - ogg_stream_init(&(x->x_os),rand()); - - /* Vorbis streams begin with three headers; the initial header (with - most of the codec setup parameters) which is mandated by the Ogg - bitstream spec. The second header holds any comment fields. The - third header holds the bitstream codebook. We merely need to - make the headers, then pass them to libvorbis one at a time; - libvorbis handles the additional Ogg bitstream constraints */ - - { - ogg_packet header; - ogg_packet header_comm; - ogg_packet header_code; - - vorbis_analysis_headerout(&(x->x_vd),&(x->x_vc),&header,&header_comm,&header_code); - ogg_stream_packetin(&(x->x_os),&header); /* automatically placed in its own page */ - ogg_stream_packetin(&(x->x_os),&header_comm); - ogg_stream_packetin(&(x->x_os),&header_code); - - /* We don't have to write out here, but doing so makes streaming - * much easier, so we do, flushing ALL pages. This ensures the actual - * audio data will start on a new page - * - * IceCast2 server will take this as a first info about our stream - */ - while(!x->x_eos) - { - int result=ogg_stream_flush(&(x->x_os),&(x->x_og)); - if(result==0)break; -#ifdef WIN32 - err = _write(x->x_fd, x->x_og.header, x->x_og.header_len); -#else - err = write(x->x_fd, x->x_og.header, x->x_og.header_len); -#endif - if(err < 0) - { - error("oggwrite~: could not write ogg header to file (%d)", err); - x->x_eos = 1; /* indicate end of stream */ - x->x_vorbis = -1; /* stop encoding instantly */ - if(x->x_fd > 0) - { -#ifdef WIN32 - if(_close(x->x_fd) < 0) -#else - if(close(x->x_fd) < 0) -#endif - { - post( "oggwrite~: file closed due to an error" ); - outlet_float(x->x_obj.ob_outlet, 0); - } - } - return; - } -#ifdef WIN32 - err = _write(x->x_fd, x->x_og.body, x->x_og.body_len); -#else - err = write(x->x_fd, x->x_og.body, x->x_og.body_len); -#endif - if(err < 0) - { - error("oggwrite~: could not write ogg body to file (%d)", err); - x->x_eos = 1; /* indicate end of stream */ - x->x_vorbis = -1; /* stop encoding instantly */ - if(x->x_fd > 0) - { -#ifdef WIN32 - if(_close(x->x_fd) < 0) -#else - if(close(x->x_fd) < 0) -#endif - { - post( "oggwrite~: file closed due to an error" ); - outlet_float(x->x_obj.ob_outlet, 0); - } - } - return; - } - } - } - x->x_vorbis = 1; /* vorbis encoder initialised */ - post("oggwrite~: ogg/vorbis encoder (re)initialised"); -} - - /* initialize the vorbisenc library */ -static void oggwrite_vorbis_deinit(t_oggwrite *x) -{ - x->x_vorbis = -1; - vorbis_analysis_wrote(&(x->x_vd),0); - /* clean up and exit. vorbis_info_clear() must be called last */ - ogg_stream_clear(&(x->x_os)); - vorbis_block_clear(&(x->x_vb)); - vorbis_dsp_clear(&(x->x_vd)); - vorbis_comment_clear(&(x->x_vc)); - vorbis_info_clear(&(x->x_vi)); - post("oggwrite~: ogg/vorbis encoder closed"); -} - - /* connect to oggwrite server */ -static void oggwrite_open(t_oggwrite *x, t_symbol *sfile) -{ - time_t now; /* to get the time */ - - /* closing previous file descriptor */ - if(x->x_fd > 0) - { -#ifdef WIN32 - if(_close(x->x_fd) < 0) -#else - if(close(x->x_fd) < 0) -#endif - { - error( "oggwrite~: file closed" ); - outlet_float(x->x_obj.ob_outlet, 0); - } - } - - if(x->x_recflag) - { - x->x_recflag = 0; - } - -#ifdef WIN32 - if((x->x_fd = _open( sfile->s_name, x->x_file_open_mode, _S_IREAD|_S_IWRITE)) < 0) -#else - if((x->x_fd = open( sfile->s_name, x->x_file_open_mode, S_IRWXU|S_IRWXG|S_IRWXO )) < 0) -#endif - { - error( "oggwrite~: can not open \"%s\"", sfile->s_name); - x->x_fd=-1; - return; - } - x->x_byteswritten = 0; - post( "oggwrite~: \"%s \" opened", sfile->s_name); - outlet_float(x->x_obj.ob_outlet, 1); - - /* get the time for the DATE comment, then init encoder */ - now=time(NULL); - x->x_bcdate = ctime(&now); - - x->x_eos = 0; - oggwrite_vorbis_init(x); -} - - /* setting file write mode to append */ -static void oggwrite_append(t_oggwrite *x) -{ -#ifdef WIN32 - x->x_file_open_mode = _O_CREAT|_O_WRONLY|_O_APPEND|_O_BINARY; -#else - x->x_file_open_mode = O_CREAT|O_WRONLY|O_APPEND|O_NONBLOCK; -#endif - if(x->x_fd>=0)post("oggwrite~: mode set to append: open a new file to make changes take effect"); -} - - /* setting file write mode to truncate */ -static void oggwrite_truncate(t_oggwrite *x) -{ -#ifdef WIN32 - x->x_file_open_mode = _O_CREAT|_O_WRONLY|_O_TRUNC|_O_BINARY; -#else - x->x_file_open_mode = O_CREAT|O_WRONLY|O_TRUNC|O_NONBLOCK; -#endif - if(x->x_fd>=0)post("oggwrite~: mode set to truncate: open a new file to make changes take effect"); -} - - /* start recording */ -static void oggwrite_start(t_oggwrite *x) -{ - if ( x->x_fd < 0 ) { - post("oggwrite~: no file selected"); - return; - } - - if ( x->x_recflag == 1 ) { - post("oggwrite~: already recording"); - return; - } - if(x->x_vorbis < 0) - { - oggwrite_vorbis_init(x); - } - - x->x_recflag = 1; - post("oggwrite~: start recording"); -} - - /* stop recording */ -static void oggwrite_stop(t_oggwrite *x) -{ - int err = -1; - - /* first stop recording / buffering and so on, than do the rest */ - x->x_recflag = 0; - post("oggwrite~: recording stoped"); - if(x->x_vorbis >= 0) - { - oggwrite_vorbis_deinit(x); - } -} - - /* set comment fields for header (reads in just anything) */ -static void oggwrite_comment(t_oggwrite *x, t_symbol *s, t_int argc, t_atom* argv) -{ - int i = argc; - char *comment = NULL; - int len = strlen(atom_gensym(argv)->s_name); - - comment = atom_gensym(argv)->s_name; - - while (len--) - { - if(*(comment + len) == '=')*(comment + len) = ' '; - } - - if(strstr(s->s_name, "ARTIST")) - { - x->x_bcartist = comment; - post("oggwrite~: ARTIST = %s", x->x_bcartist); - } - else if(strstr(s->s_name, "GENRE")) - { - x->x_bcgenre = comment; - post("oggwrite~: GENRE = %s", x->x_bcgenre); - } - else if(strstr(s->s_name, "TITLE")) - { - x->x_bcname = comment; - post("oggwrite~: TITLE = %s", x->x_bcname); - } - else if(strstr(s->s_name, "PERFORMER")) - { - x->x_bcperformer = comment; - post("oggwrite~: PERFORMER = %s", x->x_bcperformer); - } - else if(strstr(s->s_name, "LOCATION")) - { - x->x_bclocation = comment; - post("oggwrite~: LOCATION = %s", x->x_bclocation); - } - else if(strstr(s->s_name, "COPYRIGHT")) - { - x->x_bccopyright = comment; - post("oggwrite~: COPYRIGHT = %s", x->x_bccopyright); - } - else if(strstr(s->s_name, "CONTACT")) - { - x->x_bccontact = comment; - post("oggwrite~: CONTACT = %s", x->x_bccontact); - } - else if(strstr(s->s_name, "DESCRIPTION")) - { - x->x_bcdescription = comment; - post("oggwrite~: DESCRIPTION = %s", x->x_bcdescription); - } - else if(strstr(s->s_name, "DATE")) - { - x->x_bcdate = comment; - post("oggwrite~: DATE=%s", x->x_bcdate); - } - else post("oggwrite~: no method for %s", s->s_name); - if(x->x_vorbis >=0) - { - oggwrite_vorbis_deinit(x); - oggwrite_vorbis_init(x); - } -} - - /* settings for variable bitrate encoding */ -static void oggwrite_vbr(t_oggwrite *x, t_floatarg fsr, t_floatarg fchannels, - t_floatarg fquality) -{ - x->x_vbr = 1; - x->x_samplerate = (t_int)fsr; - x->x_quality = fquality; - x->x_channels = (t_int)fchannels; - post("oggwrite~: %d channels @ %d Hz, quality %.2f", x->x_channels, x->x_samplerate, x->x_quality); - if(x->x_vorbis >=0) - { - oggwrite_vorbis_deinit(x); - oggwrite_vorbis_init(x); - } -} - - /* settings for bitrate-based vbr encoding */ -static void oggwrite_vorbis(t_oggwrite *x, t_floatarg fsr, t_floatarg fchannels, - t_floatarg fmax, t_floatarg fnom, t_floatarg fmin) -{ - x->x_vbr = 0; - x->x_samplerate = (t_int)fsr; - x->x_channels = (t_int)fchannels; - x->x_br_max = (t_int)fmax; - x->x_br_nom = (t_int)fnom; - x->x_br_min = (t_int)fmin; - post("oggwrite~: %d channels @ %d Hz, bitrates: max. %d / nom. %d / min. %d", - x->x_channels, x->x_samplerate, x->x_br_max, x->x_br_nom, x->x_br_min); - if(x->x_vorbis >=0) - { - oggwrite_vorbis_deinit(x); - oggwrite_vorbis_init(x); - } -} - - /* print settings to pd's console window */ -static void oggwrite_print(t_oggwrite *x) -{ - if(x->x_vbr == 1) - { - post("oggwrite~: Ogg Vorbis encoder: %d channels @ %d Hz, quality %.2f", x->x_channels, x->x_samplerate, x->x_quality); - } - else - { - post("oggwrite~: Ogg Vorbis encoder: %d channels @ %d Hz, bitrates: max. %d, nom. %d, min. %d", - x->x_channels, x->x_samplerate, x->x_br_max, x->x_br_nom, x->x_br_min); - } - post("oggwrite~: Ogg Vorbis comments:"); - post(" TITLE = %s", x->x_bcname); - post(" ARTIST = %s", x->x_bcartist); - post(" PERFORMER = %s", x->x_bcperformer); - post(" GENRE = %s", x->x_bcgenre); - post(" LOCATION = %s", x->x_bclocation); - post(" COPYRIGHT = %s", x->x_bccopyright); - post(" CONTACT = %s", x->x_bccontact); - post(" DESCRIPTION = %s", x->x_bcdescription); - post(" DATE = %s", x->x_bcdate); -} - - - /* clean up */ -static void oggwrite_free(t_oggwrite *x) -{ - if(x->x_vorbis >= 0) /* close encoder */ - { - oggwrite_vorbis_deinit(x); - } - if(x->x_fd >= 0) - { /* close file */ -#ifdef WIN32 - _close(x->x_fd); -#else - close(x->x_fd); -#endif - outlet_float(x->x_obj.ob_outlet, 0); - } - freebytes(x->x_buffer, READ*sizeof(t_float)); -} - -static void *oggwrite_new(void) -{ - t_oggwrite *x = (t_oggwrite *)pd_new(oggwrite_class); - inlet_new (&x->x_obj, &x->x_obj.ob_pd, gensym ("signal"), gensym ("signal")); - outlet_new(&x->x_obj, gensym("float")); - x->x_outpages = outlet_new(&x->x_obj, gensym("float")); - x->x_fd = -1; -#ifdef WIN32 - x->x_file_open_mode = _O_CREAT|_O_WRONLY|_O_APPEND|_O_BINARY; -#else - x->x_file_open_mode = O_CREAT|O_WRONLY|O_APPEND|O_NONBLOCK; -#endif - x->x_vorbis = -1; - x->x_eos = 0; - x->x_vbr = 1; /* use the vbr setting by default */ - x->x_samplerate = sys_getsr(); /* no resampling supported, sorry */ - x->x_quality = 0.4; /* quality 0.4 gives roughly 128kbps VBR stream */ - x->x_channels = 2; /* stereo */ - x->x_br_max = 144; - x->x_br_nom = 128; - x->x_br_min = 96; - x->x_buffer = getbytes(READ*sizeof(t_float)); - if (!x->x_buffer) /* check buffer */ - { - error("out of memory!"); - } - x->x_bytesbuffered = 0; - x->x_pages = 0; - x->x_bcname = "ogg/vorbis stream"; - x->x_bcurl = "http://www.pure-data.info/"; - x->x_bcgenre = "experimental"; - x->x_bcdescription = "ogg/vorbis stream recorded with pure-data using oggwrite~"; - x->x_bcartist = "pure-data"; - x->x_bclocation = x->x_bcurl; - x->x_bccopyright = ""; - x->x_bcperformer = ""; - x->x_bccontact = ""; - x->x_bcdate = ""; - post(oggwrite_version); - return(x); -} - -void oggwrite_tilde_setup(void) -{ - oggwrite_class = class_new(gensym("oggwrite~"), (t_newmethod)oggwrite_new, (t_method)oggwrite_free, - sizeof(t_oggwrite), 0, 0); - CLASS_MAINSIGNALIN(oggwrite_class, t_oggwrite, x_f ); - class_addmethod(oggwrite_class, (t_method)oggwrite_dsp, gensym("dsp"), 0); - class_addmethod(oggwrite_class, (t_method)oggwrite_open, gensym("open"), A_SYMBOL, 0); - class_addmethod(oggwrite_class, (t_method)oggwrite_start, gensym("start"), 0); - class_addmethod(oggwrite_class, (t_method)oggwrite_stop, gensym("stop"), 0); - class_addmethod(oggwrite_class, (t_method)oggwrite_append, gensym("append"), 0); - class_addmethod(oggwrite_class, (t_method)oggwrite_truncate, gensym("truncate"), 0); - class_addmethod(oggwrite_class, (t_method)oggwrite_vorbis, gensym("vorbis"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0); - class_addmethod(oggwrite_class, (t_method)oggwrite_vbr, gensym("vbr"), A_FLOAT, A_FLOAT, A_FLOAT, 0); - class_addmethod(oggwrite_class, (t_method)oggwrite_print, gensym("print"), 0); - class_addanything(oggwrite_class, oggwrite_comment); -} diff --git a/oggwrite~/oggwrite~.libs b/oggwrite~/oggwrite~.libs deleted file mode 100644 index 28114c5..0000000 --- a/oggwrite~/oggwrite~.libs +++ /dev/null @@ -1 +0,0 @@ --logg -lvorbis -lvorbisenc -lvorbisfile diff --git a/oggwrite~/readme b/oggwrite~/readme deleted file mode 100644 index 4945d0d..0000000 --- a/oggwrite~/readme +++ /dev/null @@ -1,110 +0,0 @@ -oggwrite~ version 0.1b -copyright (c) 2002 by Olaf Matthes - -oggwrite~ is an ogg/vorbis file writing external for pd (by Miller -Puckette). - - -To run oggwrite~ place the file oggwrite~.dll for win or oggwrite~.pd_linux -in the directory of our patch or start pd with '-lib oggwrite~' flag. - -To compile oggwrite~ on Linux get the ogg/vorbice library from -http://www.vorbis.com/. -You have to modify the makefile to make it point to the place where the -ogg/vorbis library is. - - -This software is published under LGPL terms. - -This is software with ABSOLUTELY NO WARRANTY. -Use it at your OWN RISK. It's possible to damage e.g. hardware or your hearing -due to a bug or for other reasons. - -***************************************************************************** - -oggwrite~ uses the ogg/vorbice library to encode audio data. -The latest version of ogg/vorbis can be found at http://www.vorbice.com/ - -Below is the original copyright information taken from the ogg/vorbis library: - - -Copyright (c) 2001, Xiphophorus - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -- Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - -- Redistributions in binary form must reproduce the above copyright -notice, this list of conditions and the following disclaimer in the -documentation and/or other materials provided with the distribution. - -- Neither the name of the Xiphophorus nor the names of its contributors -may be used to endorse or promote products derived from this software -without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR -CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -***************************************************************************** - -Usage: - -Use message "vbr " to set the vorbis -encoding parameters. Resampling is currently not supported, so 'samplerate' -should be the one pd is running at. 'channels' specyfies the number of channels -to stream. This can be set to 2 (default) or 1 which means mono stream taking -the leftmost audio input only. 'quality' can be a value between 0.0 and 1.0 -giving the quality of the stream. 0.4 (default) results in a stream that's -about 128kbps. - -Since Vorbis uses VBR encoding, bitrates vary depending on the type of audio -signal to be encoded. A pure sine [osc~] results in the smalest stream, com- -plex audio signals will increase this value significantly. To test the maximum -bitrate that might occur for a quality setting use noise~ as signal input. - -Use message "vorbis " to set encoding quality on the basis of bitrates. -When setting all three bitrate parameters to the same value one gets a -constant bitrate stream. Values are in kbps! - -Message "open " opens an existing file. In case the file does not -exist oggwrite~ will create it. Previously opened files will be closed. To -determine how data should be written to the file choose "append" (default) -or "truncate" (in case you want to overwrite data in your file). - -To set the comment tags in the ogg/vorbis header (which can be displayed by -the receiving client) use message " ". Supported tags are: -TITLE, ARTIST, GENRE, PERFORMER, LOCATION, COPYRIGHT, CONTACT, DESCRIPTION and -DATE (which is automatically set to the date/time the broadcast started). To -get spaces use '=' instead. - -Note that changing encoding parameters or header comments while oggwrite~ is -recording to file might result in audible dropouts. - - -Listening to it: - -To listen to ogg/vorbis encoded audio files many player need an extra plug-in. -Have a look at http://www.vorbis.com/ to find the appropiate plug-in for your -player. - - - - - -Latest version can be found at: -http://www.akustische-kunst.de/puredata/ - -Please report any bugs to olaf.matthes@gmx.de! \ No newline at end of file diff --git a/pdogg-meta.pd b/pdogg-meta.pd new file mode 100644 index 0000000..7295eb9 --- /dev/null +++ b/pdogg-meta.pd @@ -0,0 +1,8 @@ +#N canvas 10 10 200 200 10; +#N canvas 20 20 420 300 META 0; +#X text 10 30 NAME pdogg; +#X text 10 50 AUTHOR Olaf Matthes; +#X text 10 70 DESCRIPTION objects for reading, writing, and streaming ogg; +#X text 10 90 LICENSE LGPL; +#X text 10 110 VERSION 0.25; +#X restore 10 10 pd META; diff --git a/readme b/readme deleted file mode 100644 index 03a6f59..0000000 --- a/readme +++ /dev/null @@ -1,79 +0,0 @@ -Version 0.2 -copyright (c) 2002 by Olaf Matthes - -pdogg~ is a collection of ogg/vorbis externals for pd (by Miller -Puckette). - -It includes: -- oggamp~ : streaming client -- oggcast~ : streamer (for Icecast2) -- oggread~ : reads files from disk -- oggwrite~ : writes files to disk - - -To use pdogg~ start pd with '-lib path\to\pdogg' flag. -On Win32 systems Pd 0.35 test 17 or later is necessary to get it working! - -To compile pdogg~ you need the ogg/vorbice library from -http://www.vorbis.com/ and under win additionally Pthreads-win32 from -http://sources.redhat.com/pthreads-win32/. -You have to modify the makefile to make it point to the place where the -libraries can be found on your system. - - -This software is published under LGPL terms. - -This is software with ABSOLUTELY NO WARRANTY. -Use it at your OWN RISK. It's possible to damage e.g. hardware or your hearing -due to a bug or for other reasons. - -***************************************************************************** - -pdogg~ uses the ogg/vorbice library to encode audio data. -The latest version of ogg/vorbis can be found at http://www.vorbice.com/ - -Below is the original copyright information taken from the ogg/vorbis library: - - -Copyright (c) 2001, Xiphophorus - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -- Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - -- Redistributions in binary form must reproduce the above copyright -notice, this list of conditions and the following disclaimer in the -documentation and/or other materials provided with the distribution. - -- Neither the name of the Xiphophorus nor the names of its contributors -may be used to endorse or promote products derived from this software -without specific prior written permission. - - -Below the copyright notice for Pthreads-win32: - - Pthreads-win32 is open software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public License - as published by the Free Software Foundation version 2.1 of the - License. - Pthreads-win32 is several binary link libraries, several modules, - associated interface definition files and scripts used to control - its compilation and installation. - Pthreads-win32 is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - -***************************************************************************** - -For information about the included externals see the README files provided -in the subdirectories. - - -Latest version can be found at: -http://www.akustische-kunst.de/puredata/ - -Please report any bugs to olaf.matthes@gmx.de! \ No newline at end of file -- cgit v1.2.1