pdogg-0.25.1/0000755002537200234200000000000011553251230013142 5ustar zmoelnigiemuserspdogg-0.25.1/Makefile0000644002537200234200000003227511553247330014621 0ustar zmoelnigiemusers## Pd library template version 1.0.9 # For instructions on how to use this template, see: # http://puredata.info/docs/developer/MakefileTemplate # # the name of this library # must not contain any spaces or weird characters (as it's used for # filenames,...) LIBRARY_NAME = pdogg # add your .c source files, one object per file, to the SOURCES # variable, help files will be included automatically, and for GUI # objects, the matching .tcl file too SOURCES = oggamp~.c oggcast~.c oggread~.c oggwrite~.c # 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, test patches, # documentation, etc. README.txt and LICENSE.txt are required and therefore # automatically included EXTRA_DIST = pdogg.c makefile.msvc HISTORY # special case: pthread lib was added below #------------------------------------------------------------------------------# # # things you might need to edit if you are using other C libraries # #------------------------------------------------------------------------------# # -I"$(PD_INCLUDE)/pd" supports the header location for 0.43 ALL_CFLAGS = -I"$(PD_INCLUDE)" ALL_LDFLAGS = ALL_LIBS = $(shell pkg-config --libs vorbis vorbisenc vorbisfile) #------------------------------------------------------------------------------# # # you shouldn't need to edit anything below here, if we did it right :) # #------------------------------------------------------------------------------# # these can be set from outside without (usually) breaking the build CFLAGS = -Wall -W -g LDFLAGS= LIBS= # 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) ALL_CFLAGS += -DPD -DVERSION='"$(LIBRARY_VERSION)"' PD_INCLUDE = $(PD_PATH)/include/pd # where to install the library, overridden below depending on platform prefix = /usr/local libdir = $(prefix)/lib pkglibdir = $(libdir)/pd-externals objectsdir = $(pkglibdir) INSTALL = install INSTALL_PROGRAM = $(INSTALL) -p -m 644 INSTALL_DATA = $(INSTALL) -p -m 644 INSTALL_DIR = $(INSTALL) -p -m 755 -d ALLSOURCES := $(SOURCES) $(SOURCES_android) $(SOURCES_cygwin) $(SOURCES_macosx) \ $(SOURCES_iphoneos) $(SOURCES_linux) $(SOURCES_windows) DISTDIR=$(LIBRARY_NAME)-$(LIBRARY_VERSION) ORIGDIR=pd-$(LIBRARY_NAME:~=)_$(LIBRARY_VERSION) UNAME := $(shell uname -s) ifeq ($(UNAME),Darwin) CPU := $(shell uname -p) ifeq ($(CPU),arm) # iPhone/iPod Touch SOURCES += $(SOURCES_iphoneos) EXTENSION = pd_darwin OS = iphoneos PD_PATH = /Applications/Pd-extended.app/Contents/Resources 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 ALL_CFLAGS := $(IPHONE_CFLAGS) $(ALL_CFLAGS) ALL_LDFLAGS += -arch armv6 -bundle -undefined dynamic_lookup $(ISYSROOT) ALL_LIBS += -lc STRIP = strip -x DISTBINDIR=$(DISTDIR)-$(OS) else # Mac OS X SOURCES += $(SOURCES_macosx) EXTENSION = pd_darwin OS = macosx PD_PATH = /Applications/Pd-extended.app/Contents/Resources OPT_CFLAGS = -ftree-vectorize -ftree-vectorizer-verbose=2 -fast # build universal 32-bit on 10.4 and 32/64 on newer ifeq ($(shell uname -r | sed 's|\([0-9][0-9]*\)\.[0-9][0-9]*\.[0-9][0-9]*|\1|'), 8) FAT_FLAGS = -arch ppc -arch i386 -mmacosx-version-min=10.4 else FAT_FLAGS = -arch ppc -arch i386 -arch x86_64 -mmacosx-version-min=10.4 SOURCES += $(SOURCES_iphoneos) endif ALL_CFLAGS += $(FAT_FLAGS) -fPIC -I/sw/include ALL_LDFLAGS += $(FAT_FLAGS) -bundle -undefined dynamic_lookup -L/sw/lib # if the 'pd' binary exists, check the linking against it to aid with stripping ALL_LDFLAGS += $(shell test -e $(PD_PATH)/bin/pd && echo -bundle_loader $(PD_PATH)/bin/pd) ALL_LIBS += -lc STRIP = strip -x DISTBINDIR=$(DISTDIR)-$(OS) # install into ~/Library/Pd on Mac OS X since /usr/local isn't used much pkglibdir=$(HOME)/Library/Pd endif endif # Tho Android uses Linux, we use this fake uname to provide an easy way to # setup all this things needed to cross-compile for Android using the NDK ifeq ($(UNAME),ANDROID) CPU := arm SOURCES += $(SOURCES_android) EXTENSION = pd_linux OS = android PD_PATH = /usr NDK_BASE := /usr/local/android-ndk NDK_PLATFORM_VERSION := 5 NDK_SYSROOT=$(NDK_BASE)/platforms/android-$(NDK_PLATFORM_VERSION)/arch-arm NDK_UNAME := $(shell uname -s | tr '[A-Z]' '[a-z]') NDK_TOOLCHAIN_BASE=$(NDK_BASE)/toolchains/arm-linux-androideabi-4.4.3/prebuilt/$(NDK_UNAME)-x86 CC := $(NDK_TOOLCHAIN_BASE)/bin/arm-linux-androideabi-gcc --sysroot=$(NDK_SYSROOT) OPT_CFLAGS = -O6 -funroll-loops -fomit-frame-pointer CFLAGS += LDFLAGS += -Wl,--export-dynamic -shared LIBS += -lc STRIP := $(NDK_TOOLCHAIN_BASE)/bin/arm-linux-androideabi-strip \ --strip-unneeded -R .note -R .comment DISTBINDIR=$(DISTDIR)-$(OS)-$(shell uname -m) endif ifeq ($(UNAME),Linux) CPU := $(shell uname -m) SOURCES += $(SOURCES_linux) EXTENSION = pd_linux OS = linux PD_PATH = /usr OPT_CFLAGS = -O6 -funroll-loops -fomit-frame-pointer ALL_CFLAGS += -fPIC ALL_LDFLAGS += -Wl,--export-dynamic -shared -fPIC ALL_LIBS += -lc STRIP = strip --strip-unneeded -R .note -R .comment DISTBINDIR=$(DISTDIR)-$(OS)-$(shell uname -m) endif ifeq ($(UNAME),GNU) # GNU/Hurd, should work like GNU/Linux for basically all externals CPU := $(shell uname -m) SOURCES += $(SOURCES_linux) EXTENSION = pd_linux OS = linux PD_PATH = /usr OPT_CFLAGS = -O6 -funroll-loops -fomit-frame-pointer ALL_CFLAGS += -fPIC ALL_LDFLAGS += -Wl,--export-dynamic -shared -fPIC ALL_LIBS += -lc STRIP = strip --strip-unneeded -R .note -R .comment DISTBINDIR=$(DISTDIR)-$(OS)-$(shell uname -m) endif ifeq ($(UNAME),GNU/kFreeBSD) # Debian GNU/kFreeBSD, should work like GNU/Linux for basically all externals CPU := $(shell uname -m) SOURCES += $(SOURCES_linux) EXTENSION = pd_linux OS = linux PD_PATH = /usr OPT_CFLAGS = -O6 -funroll-loops -fomit-frame-pointer ALL_CFLAGS += -fPIC ALL_LDFLAGS += -Wl,--export-dynamic -shared -fPIC ALL_LIBS += -lc STRIP = strip --strip-unneeded -R .note -R .comment DISTBINDIR=$(DISTDIR)-$(OS)-$(shell uname -m) endif ifeq (CYGWIN,$(findstring CYGWIN,$(UNAME))) CPU := $(shell uname -m) SOURCES += $(SOURCES_cygwin) EXTENSION = dll OS = cygwin PD_PATH = $(cygpath $(PROGRAMFILES))/pd OPT_CFLAGS = -O6 -funroll-loops -fomit-frame-pointer ALL_CFLAGS += ALL_LDFLAGS += -Wl,--export-dynamic -shared -L"$(PD_PATH)/src" -L"$(PD_PATH)/bin" ALL_LIBS += -lc -lpd STRIP = strip --strip-unneeded -R .note -R .comment DISTBINDIR=$(DISTDIR)-$(OS) endif ifeq (MINGW,$(findstring MINGW,$(UNAME))) CPU := $(shell uname -m) SOURCES += $(SOURCES_windows) EXTENSION = dll OS = windows PD_PATH = $(shell cd "$(PROGRAMFILES)"/pd && pwd) OPT_CFLAGS = -O3 -funroll-loops -fomit-frame-pointer ALL_CFLAGS += -mms-bitfields ALL_LDFLAGS += -s -shared -Wl,--enable-auto-import ALL_LIBS += -L"$(PD_PATH)/src" -L"$(PD_PATH)/bin" -L"$(PD_PATH)/obj" -lpd -lwsock32 -lkernel32 -luser32 -lgdi32 -lpthreadGC2 STRIP = strip --strip-unneeded -R .note -R .comment DISTBINDIR=$(DISTDIR)-$(OS) endif # in case somebody manually set the HELPPATCHES above HELPPATCHES ?= $(SOURCES:.c=-help.pd) $(PDOBJECTS:.pd=-help.pd) CFLAGS += $(OPT_CFLAGS) ALL_CFLAGS := $(ALL_CFLAGS) $(CFLAGS) ALL_LDFLAGS := $(LDFLAGS) $(ALL_LDFLAGS) ALL_LIBS := $(LIBS) $(ALL_LIBS) .PHONY = install libdir_install single_install install-doc install-exec install-examples install-manual clean dist etags $(LIBRARY_NAME) all: $(SOURCES:.c=.$(EXTENSION)) %.o: %.c $(CC) $(ALL_CFLAGS) -o "$*.o" -c "$*.c" %.$(EXTENSION): %.o $(CC) $(ALL_LDFLAGS) -o "$*.$(EXTENSION)" "$*.o" $(ALL_LIBS) chmod a-x "$*.$(EXTENSION)" # this links everything into a single binary file $(LIBRARY_NAME): $(SOURCES:.c=.o) $(LIBRARY_NAME).o $(CC) $(ALL_LDFLAGS) -o $(LIBRARY_NAME).$(EXTENSION) $(SOURCES:.c=.o) $(LIBRARY_NAME).o $(ALL_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_DATA) $(LIBRARY_NAME)-meta.pd \ $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME) test -z "$(strip $(SOURCES))" || (\ $(INSTALL_PROGRAM) $(SOURCES:.c=.$(EXTENSION)) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME) && \ $(STRIP) $(addprefix $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/,$(SOURCES:.c=.$(EXTENSION)))) test -z "$(strip $(shell ls $(SOURCES:.c=.tcl)))" || \ $(INSTALL_DATA) $(shell ls $(SOURCES:.c=.tcl)) \ $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME) test -z "$(strip $(PDOBJECTS))" || \ $(INSTALL_DATA) $(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_PROGRAM) $(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) $(PDOBJECTS))" || \ $(INSTALL_DATA) $(HELPPATCHES) \ $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME) $(INSTALL_DATA) README.txt $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/README.txt $(INSTALL_DATA) 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_DATA) 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_DATA) manual/$$file $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/manual; \ done clean: -rm -f -- $(SOURCES:.c=.o) $(SOURCES_LIB:.c=.o) -rm -f -- $(SOURCES:.c=.$(EXTENSION)) -rm -f -- $(LIBRARY_NAME).o -rm -f -- $(LIBRARY_NAME).$(EXTENSION) distclean: clean -rm -f -- $(DISTBINDIR).tar.gz -rm -rf -- $(DISTBINDIR) -rm -f -- $(DISTDIR).tar.gz -rm -rf -- $(DISTDIR) -rm -f -- $(ORIGDIR).tar.gz -rm -rf -- $(ORIGDIR) $(DISTBINDIR): $(INSTALL_DIR) $(DISTBINDIR) libdir: all $(DISTBINDIR) $(INSTALL_DATA) $(LIBRARY_NAME)-meta.pd $(DISTBINDIR) $(INSTALL_DATA) $(SOURCES) $(DISTBINDIR) $(INSTALL_DATA) $(HELPPATCHES) $(DISTBINDIR) test -z "$(strip $(EXTRA_DIST))" || \ $(INSTALL_DATA) $(EXTRA_DIST) $(DISTBINDIR) # tar --exclude-vcs -czpf $(DISTBINDIR).tar.gz $(DISTBINDIR) $(DISTDIR): $(INSTALL_DIR) $(DISTDIR) $(ORIGDIR): $(INSTALL_DIR) $(ORIGDIR) dist: $(DISTDIR) $(INSTALL_DATA) Makefile $(DISTDIR) $(INSTALL_DATA) README.txt $(DISTDIR) $(INSTALL_DATA) LICENSE.txt $(DISTDIR) $(INSTALL_DATA) $(LIBRARY_NAME)-meta.pd $(DISTDIR) test -z "$(strip $(ALLSOURCES))" || \ $(INSTALL_DATA) $(ALLSOURCES) $(DISTDIR) test -z "$(strip $(shell ls $(ALLSOURCES:.c=.tcl)))" || \ $(INSTALL_DATA) $(shell ls $(ALLSOURCES:.c=.tcl)) $(DISTDIR) test -z "$(strip $(PDOBJECTS))" || \ $(INSTALL_DATA) $(PDOBJECTS) $(DISTDIR) test -z "$(strip $(HELPPATCHES))" || \ $(INSTALL_DATA) $(HELPPATCHES) $(DISTDIR) test -z "$(strip $(EXTRA_DIST))" || \ $(INSTALL_DATA) $(EXTRA_DIST) $(DISTDIR) test -z "$(strip $(EXAMPLES))" || \ $(INSTALL_DIR) $(DISTDIR)/examples && \ for file in $(EXAMPLES); do \ $(INSTALL_DATA) examples/$$file $(DISTDIR)/examples; \ done test -z "$(strip $(MANUAL))" || \ $(INSTALL_DIR) $(DISTDIR)/manual && \ for file in $(MANUAL); do \ $(INSTALL_DATA) manual/$$file $(DISTDIR)/manual; \ done tar --exclude-vcs -czpf $(DISTDIR).tar.gz $(DISTDIR) # make a Debian source package dpkg-source: debclean make distclean dist mv $(DISTDIR) $(ORIGDIR) tar --exclude-vcs -czpf ../$(ORIGDIR).orig.tar.gz $(ORIGDIR) rm -f -- $(DISTDIR).tar.gz rm -rf -- $(DISTDIR) $(ORIGDIR) cd .. && dpkg-source -b $(LIBRARY_NAME) etags: etags *.h $(SOURCES) ../../pd/src/*.[ch] /usr/include/*.h /usr/include/*/*.h showsetup: @echo "CFLAGS: $(CFLAGS)" @echo "LDFLAGS: $(LDFLAGS)" @echo "LIBS: $(LIBS)" @echo "ALL_CFLAGS: $(ALL_CFLAGS)" @echo "ALL_LDFLAGS: $(ALL_LDFLAGS)" @echo "ALL_LIBS: $(ALL_LIBS)" @echo "PD_INCLUDE: $(PD_INCLUDE)" @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)" @echo "pkglibdir: $(pkglibdir)" @echo "DISTDIR: $(DISTDIR)" @echo "ORIGDIR: $(ORIGDIR)" pdogg-0.25.1/README.txt0000644002537200234200000001131711553250547014655 0ustar zmoelnigiemusersVersion 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. pdogg-0.25.1/LICENSE.txt0000644002537200234200000006126111363524141014776 0ustar zmoelnigiemusers 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! pdogg-0.25.1/pdogg-meta.pd0000644002537200234200000000043511553251151015517 0ustar zmoelnigiemusers#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.1; #X restore 10 10 pd META; pdogg-0.25.1/oggamp~.c0000644002537200234200000013136511363524141014772 0ustar zmoelnigiemusers/* ------------------------- 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); } pdogg-0.25.1/oggcast~.c0000644002537200234200000013756111363524141015153 0ustar zmoelnigiemusers/* ------------------------- 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); } pdogg-0.25.1/oggread~.c0000644002537200234200000003277711363524141015137 0ustar zmoelnigiemusers/* ------------------------- 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); } pdogg-0.25.1/oggwrite~.c0000644002537200234200000006021011363524141015335 0ustar zmoelnigiemusers/* -------------------------- 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); } pdogg-0.25.1/oggamp~-help.pd0000644002537200234200000000441411533231642016073 0ustar zmoelnigiemusers#N canvas 1 53 678 484 10; #X obj 15 420 dac~; #X floatatom 102 419 5 0 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 22 332 5 0 0 0 - - -; #X obj 22 354 / 100; #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; #N canvas 458 284 494 344 META 0; #X text 12 145 HELP_PATCH_AUTHORS "pd meta" information added by Jonathan Wilkes for Pd version 0.42.; #X text 12 125 AUTHOR Olaf Matthes; #X text 12 25 LICENSE LGPL; #X text 12 45 DESCRIPTION version 0.2 - Ogg Vorbis streaming client ; #X text 12 5 KEYWORDS signal soundfile network; #X text 12 65 INLET_0 connect disconnect print recover; #X text 12 85 OUTLET_N signal; #X text 12 105 OUTLET_R float; #X restore 622 453 pd META; #X text 57 7 oggamp~ version 0.2 - Ogg Vorbis streaming client; #X connect 3 0 27 0; #X connect 4 0 27 0; #X connect 5 0 27 0; #X connect 6 0 27 0; #X connect 7 0 27 0; #X connect 8 0 27 0; #X connect 9 0 27 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 14 0 27 0; #X connect 25 0 23 0; #X connect 26 0 27 0; #X connect 27 0 10 0; #X connect 27 1 11 0; #X connect 27 2 1 0; pdogg-0.25.1/oggcast~-help.pd0000644002537200234200000000543511533231642016254 0ustar zmoelnigiemusers#N canvas 1 53 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 99 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 47 524 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 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; #N canvas 493 108 494 344 META 0; #X text 12 195 HELP_PATCH_AUTHORS "pd meta" information added by Jonathan Wilkes for Pd version 0.42.; #X text 12 175 AUTHOR Olaf Matthes; #X text 12 25 LICENSE LGPL; #X text 12 5 KEYWORDS signal soundfile network; #X text 12 45 DESCRIPTION send Ogg Vorbis stream to IceCast2 or JRoar ; #X text 12 65 INLET_0 signal passwd server connect disconnect vorbis vbr print TITLE ARTIST PERFORMER DESCRIPTION GENRE LOCATION COPYRIGHT CONTACT DATE; #X text 12 115 INLET_1 signal; #X text 12 135 OUTLET_0 float; #X text 12 155 OUTLET_1 float; #X restore 568 562 pd META; #X obj 18 475 oggcast~ 2 512; #X connect 0 0 15 0; #X connect 3 0 41 0; #X connect 4 0 41 0; #X connect 6 0 1 0; #X connect 14 0 41 0; #X connect 15 0 41 0; #X connect 19 0 41 0; #X connect 20 0 41 0; #X connect 21 0 41 0; #X connect 25 0 41 0; #X connect 30 0 41 0; #X connect 31 0 41 1; #X connect 35 0 41 0; #X connect 36 0 41 0; #X connect 37 0 41 0; #X connect 41 0 2 0; #X connect 41 1 7 0; pdogg-0.25.1/oggread~-help.pd0000644002537200234200000000340111533231642016224 0ustar zmoelnigiemusers#N canvas 1 53 576 474 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; #N canvas 457 255 494 344 META 0; #X text 12 185 HELP_PATCH_AUTHORS "pd meta" information added by Jonathan Wilkes for Pd version 0.42.; #X text 12 85 OUTLET_0 signal; #X text 12 165 AUTHOR Olaf Matthes; #X text 12 25 LICENSE LGPL; #X text 12 45 DESCRIPTION version 0.2 - Ogg Vorbis file player; #X text 12 65 INLET_0 float open start stop resume seek; #X text 12 105 OUTLET_1 signal; #X text 12 125 OUTLET_2 float; #X text 12 145 OUTLET_3 bang; #X text 12 5 KEYWORDS signal soundfile; #X restore 514 447 pd META; #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; pdogg-0.25.1/oggwrite~-help.pd0000644002537200234200000000501411533231642016445 0ustar zmoelnigiemusers#N canvas 1 53 631 537 10; #X obj 40 415 oggwrite~; #X obj 40 64 osc~ 440; #X floatatom 40 33 5 0 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 91 441 5 0 0 0 - - -; #X floatatom 40 470 5 0 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 138 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; #N canvas 391 212 494 344 META 0; #X text 12 195 HELP_PATCH_AUTHORS "pd meta" information added by Jonathan Wilkes for Pd version 0.42.; #X text 12 175 AUTHOR Olaf Matthes; #X text 12 25 LICENSE LGPL; #X text 12 5 KEYWORDS signal filesystem soundfile; #X text 12 45 DESCRIPTION version 0.1 - write Ogg Vorbis stream to file; #X text 12 65 INLET_0 signal open append truncate start stop vorbis vbr print TITLE ARTIST PERFORMER DESCRIPTION GENRE LOCATION COPYRIGHT CONTACT DATE; #X text 12 115 INLET_1 signal; #X text 12 135 OUTLET_0 float; #X text 12 155 OUTLET_1 float; #X restore 572 509 pd META; #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; pdogg-0.25.1/pdogg.c0000644002537200234200000000220610754271266014423 0ustar zmoelnigiemusers#ifndef VERSION #define VERSION "0.25" #endif #include #ifndef __DATE__ #define __DATE__ "without using a gnu compiler" #endif typedef struct _pdogg { t_object x_obj; } t_pdogg; static t_class* pdogg_class; // tilde objects void oggamp_tilde_setup(); void oggcast_tilde_setup(); void oggread_tilde_setup(); void oggwrite_tilde_setup(); static void* pdogg_new(t_symbol* s) { t_pdogg *x = (t_pdogg *)pd_new(pdogg_class); return (x); } void pdogg_setup(void) { pdogg_class = class_new(gensym("pdogg"), (t_newmethod)pdogg_new, 0, sizeof(t_pdogg), 0,0); oggamp_tilde_setup(); oggcast_tilde_setup(); oggread_tilde_setup(); oggwrite_tilde_setup(); post("\n pdogg :: Ogg Vorbis library for pure-data"); post(" written by Olaf Matthes "); post(" version: "VERSION); post(" compiled: "__DATE__", using Ogg Vorbis library 1.0"); post(" home: http://www.akustische-kunst.org/puredata/pdogg/"); post(" including: oggamp~0.2f, oggcast~0.2h, oggread~0.2c, oggwrite~0.1c\n"); } pdogg-0.25.1/makefile.msvc0000644002537200234200000000611411363524141015616 0ustar zmoelnigiemusersNAME=pdogg CSYM=pdogg current: pd_nt pd_darwin pd_linux # ----------------------- NT ----------------------- pd_nt: $(NAME).dll .SUFFIXES: .dll PDNTCFLAGS = /W3 /WX /MD /O2 /G6 /DNT /DPD /nologo VC = "C:\Programme\Microsoft Visual Studio\VC98" PDNTINCLUDE = /I. /Ic:\pd\tcl\include /Ic:\pd\src /I$(VC)\include /Iinclude PDNTLDIR = $(VC)\Lib PDNTLIB = $(PDNTLDIR)\msvcrt.lib \ $(PDNTLDIR)\oldnames.lib \ $(PDNTLDIR)\kernel32.lib \ $(PDNTLDIR)\user32.lib \ $(PDNTLDIR)\uuid.lib \ $(PDNTLDIR)\ws2_32.lib \ $(PDNTLDIR)\pthreadVC.lib \ lib\ogg_static.lib \ lib\vorbis_static.lib \ lib\vorbisenc_static.lib \ lib\vorbisfile_static.lib \ c:\pd\bin\pd.lib EXTERNALS = oggamp~.obj oggcast~.obj oggread~.obj oggwrite~.obj .c.dll: cl $(PDNTCFLAGS) $(PDNTINCLUDE) /c oggamp~\oggamp~.c cl $(PDNTCFLAGS) $(PDNTINCLUDE) /c oggcast~\oggcast~.c cl $(PDNTCFLAGS) $(PDNTINCLUDE) /c oggread~\oggread~.c cl $(PDNTCFLAGS) $(PDNTINCLUDE) /c oggwrite~\oggwrite~.c cl $(PDNTCFLAGS) $(PDNTINCLUDE) /c $*.c link /dll /NODEFAULTLIB:libcmt.lib /NODEFAULTLIB:libc.lib /export:$(CSYM)_setup $*.obj \ $(EXTERNALS) $(PDNTLIB) # ----------------------- Mac OS X ----------------------- pd_darwin: $(NAME).pd_darwin .SUFFIXES: .pd_darwin DARWINCFLAGS = -DPD -DUNIX -DMACOSX -O3 \ -Wall -W -Wshadow -Wstrict-prototypes \ -Wno-unused -Wno-parentheses -Wno-switch # where is your m_pd.h ??? DARWININCLUDE = -I../../src -I. -Iinclude DARWINEXTERNALS = oggamp~.o oggcast~.o oggread~.o oggwrite~.o .c.pd_darwin: cc $(DARWINCFLAGS) $(DARWININCLUDE) -c oggamp~/oggamp~.c cc $(DARWINCFLAGS) $(DARWININCLUDE) -c oggcast~/oggcast~.c cc $(DARWINCFLAGS) $(DARWININCLUDE) -c oggread~/oggread~.c cc $(DARWINCFLAGS) $(DARWININCLUDE) -c oggwrite~/oggwrite~.c cc $(DARWINCFLAGS) $(DARWININCLUDE) -c $*.c cc -bundle -undefined suppress -flat_namespace -o $*.pd_darwin $*.o $(DARWINEXTERNALS) \ -Llib -lvorbisfile -lvorbisenc -lvorbis -logg rm -f $*.o ../$*.pd_darwin ln -s $*/$*.pd_darwin .. # ----------------------- LINUX i386 ----------------------- pd_linux: $(NAME).pd_linux .SUFFIXES: .pd_linux LINUXCFLAGS = -DPD -DUNIX -DICECAST -O2 -funroll-loops -fomit-frame-pointer \ -Wall -W -Wshadow -Wno-unused -Wno-parentheses -Wno-switch LINUXINCLUDE = -I../../src -I ../../pd/src LINUXEXTERNALS = oggamp~.o oggcast~.o oggread~.o oggwrite~.o .c.pd_linux: cc $(LINUXCFLAGS) $(LINUXINCLUDE) -c oggamp~/oggamp~.c cc $(LINUXCFLAGS) $(LINUXINCLUDE) -c oggcast~/oggcast~.c cc $(LINUXCFLAGS) $(LINUXINCLUDE) -c oggread~/oggread~.c cc $(LINUXCFLAGS) $(LINUXINCLUDE) -c oggwrite~/oggwrite~.c cc $(LINUXCFLAGS) $(LINUXINCLUDE) -c $*.c ld --export-dynamic -shared -o $*.pd_linux $*.o $(LINUXEXTERNALS) -lc \ -lm -L/usr/local/lib -lvorbisfile -lvorbisenc -lvorbis -logg strip --strip-unneeded $*.pd_linux # ---------------------------------------------------------- PDDIR=/usr/lib/pd install: install -d $(PDDIR)/doc/5.reference/pdogg cp *-help.pd ../../doc/5.reference/pdogg clean: rm -f *.o *.pd_* so_locations pdogg-0.25.1/HISTORY0000644002537200234200000001044411363524141014234 0ustar zmoelnigiemusersVersion 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