summaryrefslogtreecommitdiffstats
path: root/artsc
diff options
context:
space:
mode:
Diffstat (limited to 'artsc')
-rw-r--r--artsc/Makefile.am32
-rwxr-xr-xartsc/artsc-config.in56
-rw-r--r--artsc/artsc.c226
-rw-r--r--artsc/artsc.h246
-rw-r--r--artsc/artsc_export.h.in52
-rw-r--r--artsc/artscbackend.cc805
-rw-r--r--artsc/artsdsp.c675
-rwxr-xr-xartsc/artsdsp.in106
-rw-r--r--artsc/stdioemu.c96
9 files changed, 2294 insertions, 0 deletions
diff --git a/artsc/Makefile.am b/artsc/Makefile.am
new file mode 100644
index 0000000..7487633
--- /dev/null
+++ b/artsc/Makefile.am
@@ -0,0 +1,32 @@
+AM_CFLAGS = -DARTSC_BACKEND='"$(libdir)/libartscbackend.la"'
+AM_CPPFLAGS = -DCOMPILING_ARTSC
+lib_LTLIBRARIES = libartsc.la libartscbackend.la libartsdsp.la libartsdsp_st.la
+FLOWLIBS = $(top_builddir)/flow/libartsflow.la
+
+INCLUDES = -I$(top_srcdir)/mcop -I$(top_builddir)/mcop -I$(top_srcdir)/flow \
+ -I$(top_builddir)/flow -I$(top_builddir)/soundserver \
+ -I$(top_srcdir)/libltdl $(all_includes)
+
+bin_SCRIPTS = artsc-config artsdsp
+
+libartsdsp_la_SOURCES = artsdsp.c
+libartsdsp_la_LDFLAGS = -no-undefined -module $(all_libraries)
+libartsdsp_la_LIBADD = libartsc.la
+
+libartsdsp_st_la_SOURCES = artsc.c artsdsp.c
+libartsdsp_st_la_LDFLAGS = -no-undefined -module $(all_libraries)
+libartsdsp_st_la_LIBADD = $(top_builddir)/libltdl/libltdlc.la
+
+libartsc_la_SOURCES = artsc.c
+libartsc_la_LDFLAGS = -no-undefined $(USE_THREADS) $(all_libraries)
+libartsc_la_LIBADD = $(top_builddir)/libltdl/libltdlc.la $(LIBPTHREAD)
+
+libartscbackend_la_SOURCES = artscbackend.cc
+libartscbackend_la_LDFLAGS = -no-undefined -module $(KDE_RPATH) $(all_libraries)
+libartscbackend_la_LIBADD = $(FLOWLIBS) \
+ $(top_builddir)/soundserver/libsoundserver_idl.la
+
+artscincludedir = $(includedir)/artsc
+artscinclude_HEADERS = artsc.h artsc_export.h
+
+artscbackend.lo: $(top_builddir)/soundserver/soundserver.h ../flow/artsflow.h
diff --git a/artsc/artsc-config.in b/artsc/artsc-config.in
new file mode 100755
index 0000000..8b9b578
--- /dev/null
+++ b/artsc/artsc-config.in
@@ -0,0 +1,56 @@
+#!/bin/sh
+
+usage()
+{
+ echo "usage: $0 [OPTIONS]"
+cat << EOH
+
+options:
+ [--libs]
+ [--cflags]
+ [--version]
+ [--arts-version]
+ [--arts-prefix]
+EOH
+ exit 1;
+}
+
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+libdl=@LIBDL@
+
+flags=""
+
+while test $# -gt 0
+do
+ case $1 in
+ --libs)
+ flags="$flags -L$libdir $libdl -lartsc @USE_THREADS@ @LIBPTHREAD@ @GLIB_LDFLAGS@ @GLIB_LIBADD@"
+ ;;
+ --cflags)
+ flags="$flags -I$includedir/artsc @GLIB_CFLAGS@"
+ ;;
+ --version)
+ echo 0.9.5
+ ;;
+ --arts-version)
+ echo @ARTS_VERSION@
+ ;;
+ --arts-prefix)
+ echo $prefix
+ ;;
+ *)
+ echo "$0: unknown option $1"
+ echo
+ usage
+ ;;
+ esac
+ shift
+done
+
+if test -n "$flags"
+then
+ echo $flags
+fi
diff --git a/artsc/artsc.c b/artsc/artsc.c
new file mode 100644
index 0000000..61a02b6
--- /dev/null
+++ b/artsc/artsc.c
@@ -0,0 +1,226 @@
+ /*
+
+ Copyright (C) 2000 Stefan Westerfeld
+ stefan@space.twc.de
+
+ 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; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include "artsc.h"
+#include "ltdl.h"
+#include <assert.h>
+
+typedef int (*backend_init_ptr)();
+typedef int (*backend_suspend_ptr)();
+typedef int (*backend_suspended_ptr)();
+typedef void (*backend_free_ptr)();
+typedef arts_stream_t (*backend_play_stream_ptr)(int,int,int,const char*);
+typedef arts_stream_t (*backend_record_stream_ptr)(int,int,int,const char*);
+typedef void (*backend_close_stream_ptr)(arts_stream_t);
+typedef int (*backend_read_ptr)(arts_stream_t,void*,int);
+typedef int (*backend_write_ptr)(arts_stream_t,const void*,int);
+typedef int (*backend_stream_set_ptr)(arts_stream_t, arts_parameter_t, int);
+typedef int (*backend_stream_get_ptr)(arts_stream_t, arts_parameter_t);
+
+static struct arts_backend {
+ int available;
+ int refcnt;
+ lt_dlhandle handle;
+
+ backend_init_ptr init;
+ backend_suspend_ptr suspend;
+ backend_suspended_ptr suspended;
+ backend_free_ptr free;
+ backend_play_stream_ptr play_stream;
+ backend_record_stream_ptr record_stream;
+ backend_close_stream_ptr close_stream;
+ backend_read_ptr read;
+ backend_write_ptr write;
+ backend_stream_set_ptr stream_set;
+ backend_stream_get_ptr stream_get;
+} backend = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
+
+static void arts_backend_ref()
+{
+ if(backend.refcnt == 0)
+ {
+ lt_dlinit();
+ backend.handle = lt_dlopen(ARTSC_BACKEND);
+
+ if(backend.handle)
+ {
+ backend.init = (backend_init_ptr)
+ lt_dlsym(backend.handle, "arts_backend_init");
+ backend.suspend = (backend_suspend_ptr)
+ lt_dlsym(backend.handle, "arts_backend_suspend");
+ backend.suspended = (backend_suspended_ptr)
+ lt_dlsym(backend.handle, "arts_backend_suspended");
+ backend.free = (backend_free_ptr)
+ lt_dlsym(backend.handle, "arts_backend_free");
+ backend.play_stream = (backend_play_stream_ptr)
+ lt_dlsym(backend.handle, "arts_backend_play_stream");
+ backend.record_stream = (backend_record_stream_ptr)
+ lt_dlsym(backend.handle, "arts_backend_record_stream");
+ backend.close_stream = (backend_close_stream_ptr)
+ lt_dlsym(backend.handle, "arts_backend_close_stream");
+ backend.write = (backend_write_ptr)
+ lt_dlsym(backend.handle, "arts_backend_write");
+ backend.read = (backend_read_ptr)
+ lt_dlsym(backend.handle, "arts_backend_read");
+ backend.stream_set = (backend_stream_set_ptr)
+ lt_dlsym(backend.handle, "arts_backend_stream_set");
+ backend.stream_get = (backend_stream_get_ptr)
+ lt_dlsym(backend.handle, "arts_backend_stream_get");
+ }
+
+ if(backend.handle && backend.init && backend.free && backend.play_stream
+ && backend.record_stream && backend.close_stream && backend.write
+ && backend.read && backend.stream_set && backend.stream_get
+ && backend.suspend)
+ backend.available = 1;
+ else
+ backend.available = 0;
+ }
+ backend.refcnt++;
+}
+
+static void arts_backend_release()
+{
+ assert(backend.refcnt > 0);
+ backend.refcnt--;
+ if(backend.refcnt == 0)
+ {
+ if(backend.available)
+ {
+ backend.available = 0;
+
+ if(backend.handle) lt_dlclose(backend.handle);
+ }
+ lt_dlexit();
+ }
+}
+
+int arts_init()
+{
+ int rc = ARTS_E_NOBACKEND;
+
+ arts_backend_ref();
+ if(backend.available) rc = backend.init();
+
+ /* init failed: the user may not call other arts_xxx functions now */
+ if(rc < 0) arts_backend_release();
+
+ return rc;
+}
+
+int arts_suspend()
+{
+ int rc = ARTS_E_NOBACKEND;
+
+ if(backend.available) rc = backend.suspend();
+ return rc;
+}
+
+int arts_suspended()
+{
+ int rc = ARTS_E_NOBACKEND;
+
+ if(backend.available && backend.suspended) rc = backend.suspended();
+ return rc;
+}
+
+void arts_free()
+{
+ if(backend.available)
+ {
+ backend.free();
+ arts_backend_release();
+ }
+}
+
+arts_stream_t arts_play_stream(int rate, int bits, int channels, const char *name)
+{
+ arts_stream_t rc = 0;
+
+ if(backend.available) rc = backend.play_stream(rate,bits,channels,name);
+ return rc;
+}
+
+arts_stream_t arts_record_stream(int rate, int bits, int channels, const char *name)
+{
+ arts_stream_t rc = 0;
+
+ if(backend.available) rc = backend.record_stream(rate,bits,channels,name);
+ return rc;
+}
+
+void arts_close_stream(arts_stream_t stream)
+{
+ if(backend.available) backend.close_stream(stream);
+}
+
+int arts_read(arts_stream_t stream, void *buffer, int count)
+{
+ int rc = ARTS_E_NOBACKEND;
+
+ if(backend.available) rc = backend.read(stream,buffer,count);
+ return rc;
+}
+
+int arts_write(arts_stream_t stream, const void *buffer, int count)
+{
+ int rc = ARTS_E_NOBACKEND;
+
+ if(backend.available) rc = backend.write(stream,buffer,count);
+ return rc;
+}
+
+int arts_stream_set(arts_stream_t stream, arts_parameter_t param, int value)
+{
+ int rc = ARTS_E_NOBACKEND;
+
+ if(backend.available) rc = backend.stream_set(stream,param,value);
+ return rc;
+}
+
+int arts_stream_get(arts_stream_t stream, arts_parameter_t param)
+{
+ int rc = ARTS_E_NOBACKEND;
+
+ if(backend.available) rc = backend.stream_get(stream,param);
+ return rc;
+}
+
+const char *arts_error_text(int errorcode)
+{
+ switch(errorcode) {
+ case 0:
+ return "success";
+ case ARTS_E_NOSERVER:
+ return "can't connect to aRts soundserver";
+ case ARTS_E_NOBACKEND:
+ return "loading the aRts backend \""
+ ARTSC_BACKEND "\" failed";
+ case ARTS_E_NOIMPL:
+ return "this aRts function is not yet implemented";
+ case ARTS_E_NOINIT:
+ return "need to use arts_init() before using other functions";
+ case ARTS_E_NOSTREAM:
+ return "you passed no valid aRts stream to a function";
+ }
+ return "unknown arts error happened";
+}
diff --git a/artsc/artsc.h b/artsc/artsc.h
new file mode 100644
index 0000000..5cdf8f1
--- /dev/null
+++ b/artsc/artsc.h
@@ -0,0 +1,246 @@
+ /*
+
+ Copyright (C) 2000 Stefan Westerfeld
+ stefan@space.twc.de
+
+ 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; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#ifndef ARTSC_H
+#define ARTSC_H
+
+#include "artsc_export.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @libdoc aRts plain C API
+ *
+ * The aRts plain C API aims at easily writing/porting plain C apps to the
+ * arts sound server. What is provided is streaming functionality, in a
+ * blocking way. So for most apps, you simply remove the few system calls
+ * that deal with your audio device, and replace them with the appropriate
+ * arts calls.
+ */
+
+/**
+ * the type of streams (simply treat these as black boxes)
+ */
+typedef void *arts_stream_t;
+
+/* error codes */
+
+#define ARTS_E_NOSERVER ( -1 )
+#define ARTS_E_NOBACKEND ( -2 )
+#define ARTS_E_NOSTREAM ( -3 )
+#define ARTS_E_NOINIT ( -4 )
+#define ARTS_E_NOIMPL ( -5 )
+
+/**
+ * the values for stream parameters
+ *
+ * @see arts_parameter_t
+ */
+enum arts_parameter_t_enum {
+ ARTS_P_BUFFER_SIZE = 1,
+ ARTS_P_BUFFER_TIME = 2,
+ ARTS_P_BUFFER_SPACE = 3,
+ ARTS_P_SERVER_LATENCY = 4,
+ ARTS_P_TOTAL_LATENCY = 5,
+ ARTS_P_BLOCKING = 6,
+ ARTS_P_PACKET_SIZE = 7,
+ ARTS_P_PACKET_COUNT = 8,
+ ARTS_P_PACKET_SETTINGS = 9
+};
+
+/**
+ * parameters for streams
+ *
+ * @li ARTS_P_BUFFER_SIZE (rw)
+ * The size of the internal buffers used for streaming to the server - this
+ * directly affects the latency that will occur. If you never set it
+ * explicitly, this value defaults to at least 65536 (64kb). Generally,
+ * it is important to know that the server itself gives some constraints
+ * which makes buffer sizes below a certain value impossible. So setting
+ * this parameter will always result in either getting what you wanted,
+ * or a larger streaming buffer due to server limitations.
+ *
+ * @li ARTS_P_BUFFER_TIME (rw)
+ * The time the buffer used for streaming to the server takes to play in
+ * milliseconds. This is just a more human readable method to set the buffer
+ * size, so setting ARTS_P_BUFFER_SIZE affects this parameter and the other
+ * way round. As aRts chooses reasonable buffer sizes for streaming (rather
+ * 3*16kb than 40234 bytes), setting this parameter will often end up with
+ * a slightly larger value than you requested.
+ *
+ * @li ARTS_P_BUFFER_SPACE (r)
+ * The amount of bytes that can be read/written without blocking (depending
+ * whether this is a record or play stream). As requesting this parameter
+ * does a few system calls (but no remote invocation) to verify that it is
+ * up-to-date, don't overuse it.
+ *
+ * @li ARTS_P_SERVER_LATENCY (r)
+ * The amount of latency the server creates (due to hardware buffering)
+ * in milliseconds.
+ *
+ * @li ARTS_P_TOTAL_LATENCY (r)
+ * The overall latency in milliseconds it takes (at most), from the time
+ * when you write a byte into a stream, until it gets played on the
+ * soundcard. This is simply a shortcut to the sum of ARTS_P_BUFFER_TIME
+ * and ARTS_P_SERVER_LATENCY.
+ *
+ * @li ARTS_P_BLOCKING (rw)
+ * If this parameter is 1 (the default), arts_read/arts_write will block
+ * when not all data can be read/written successfully, and wait until it
+ * works. If this parameter is 0, arts_read/arts_write will return
+ * the number of successfully read/written bytes immediately.
+ *
+ * @li ARTS_P_PACKET_SIZE (r)
+ * This returns the size of the packets used for buffering. The optimal
+ * size for arts_stream_write is always writing one packet. The buffering of
+ * streams works with audio packets. So the ARTS_P_BUFFER_SIZE parameter of
+ * streams (which specifies how many bytes of a stream are prebuffered),
+ * really consists of (ARTS_P_PACKET_SIZE) * (ARTS_P_PACKET_COUNT).
+ *
+ * @li ARTS_P_PACKET_COUNT (r)
+ * This returns the number of the packets are used for buffering. See
+ * ARTS_P_PACKET_SIZE for more.
+ *
+ * @li ARTS_P_PACKET_SETTINGS (rw)
+ * This is a way to configure packet size & packet count at the same time.
+ * The format is 0xCCCCSSSS, where 2^SSSS is the packet size, and CCCC is
+ * the packet count. Note that when writing this, you don't necessarily
+ * get the settings you requested.
+ */
+typedef enum arts_parameter_t_enum arts_parameter_t;
+
+/**
+ * initializes the aRts C API, and connects to the sound server
+ *
+ * @return 0 if everything is all right, an error code otherwise
+ */
+
+ARTSC_EXPORT int arts_init(void);
+
+/**
+ * disconnects from the sound server and frees the aRts C API internals
+ */
+ARTSC_EXPORT void arts_free(void);
+
+/**
+ * asks aRtsd to free the DSP device and return 1 if it was successful,
+ * 0 if there were active non-suspendable modules
+ */
+ARTSC_EXPORT int arts_suspend(void);
+
+/**
+ * asks aRtsd if the DSP device is free and return 1 if it is,
+ * 0 if not
+ */
+ARTSC_EXPORT int arts_suspended(void);
+
+
+/**
+ * converts an error code to a human readable error message
+ *
+ * @param errorcode the errorcode (from another arts function that failed)
+ * @returns a text string with the error message
+ */
+ARTSC_EXPORT const char *arts_error_text(int errorcode);
+
+/**
+ * open a stream for playing
+ *
+ * @param rate the sampling rate (something like 44100)
+ * @param bits how many bits each sample has (8 or 16)
+ * @param channels how many channels, 1 is mono, 2 is stereo
+ * @param name the name of the stream (these will be used so that the user can
+ * assign streams to effects/mixer channels and similar)
+ *
+ * @return a stream
+ */
+ARTSC_EXPORT arts_stream_t arts_play_stream(int rate, int bits, int channels, const char *name);
+
+/**
+ * open a stream for recording
+ *
+ * @param rate the sampling rate (something like 44100)
+ * @param bits how many bits each sample has (8 or 16)
+ * @param channels how many channels, 1 is mono, 2 is stereo
+ * @param name the name of the stream (these will be used so that the user can
+ * assign streams to effects/mixer channels and similar)
+ *
+ * @return a stream
+ */
+ARTSC_EXPORT arts_stream_t arts_record_stream(int rate, int bits, int channels, const char *name);
+
+/**
+ * close a stream
+ */
+ARTSC_EXPORT void arts_close_stream(arts_stream_t stream);
+
+/**
+ * read samples from stream
+ *
+ * @param stream a previously opened record stream
+ * @param buffer a buffer with sample data
+ * @param count the number of bytes contained in the buffer
+ *
+ * @returns number of read bytes on success or error code
+ */
+ARTSC_EXPORT int arts_read(arts_stream_t stream, void *buffer, int count);
+
+/**
+ * write samples to to stream
+ *
+ * @param stream a previously opened play stream
+ * @param buffer a buffer with sample data
+ * @param count the number of bytes contained in the buffer
+ *
+ * @returns number of written bytes on success or error code
+ */
+ARTSC_EXPORT int arts_write(arts_stream_t stream, const void *buffer, int count);
+
+/**
+ * configure a parameter of a stream
+ *
+ * @param stream an opened record or play stream
+ * @param parameter the parameter you want to modify
+ * @param value the new value
+ *
+ * @returns the new value of the parameter (which may or may not be the value
+ * you wanted to have), or an error code if something went wrong
+ */
+ARTSC_EXPORT int arts_stream_set(arts_stream_t stream, arts_parameter_t param, int value);
+
+/**
+ * query a parameter of a stream
+ *
+ * @param stream an opened record or play stream
+ * @param parameter the parameter you want to query
+ *
+ * @returns the value of the parameter, or an error code
+ */
+ARTSC_EXPORT int arts_stream_get(arts_stream_t stream, arts_parameter_t param);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ARTSC_H */
diff --git a/artsc/artsc_export.h.in b/artsc/artsc_export.h.in
new file mode 100644
index 0000000..85fd6ef
--- /dev/null
+++ b/artsc/artsc_export.h.in
@@ -0,0 +1,52 @@
+/* This file is part of the KDE libraries
+ Copyright (c) 2002-2003 KDE Team
+
+ 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; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+#ifndef ARTSC_EXPORT_H
+#define ARTSC_EXPORT_H
+
+#undef __KDE_HAVE_GCC_VISIBILITY
+/**
+ * The ARTS_NO_EXPORT macro marks the symbol of the given variable
+ * to be hidden. A hidden symbol is stripped during the linking step,
+ * so it can't be used from outside the resulting library, which is similar
+ * to static. However, static limits the visibility to the current
+ * compilation unit. hidden symbols can still be used in multiple compilation
+ * units.
+ *
+ * \code
+ * int ARTSC_NO_EXPORT foo;
+ * int ARTSC_EXPORT bar;
+ * \end
+ */
+
+#if defined(__KDE_HAVE_GCC_VISIBILITY)
+/* Visibility is available for GCC newer than 3.4.
+ * See: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=9283
+ */
+#define ARTSC_NO_EXPORT __attribute__ ((visibility("hidden")))
+#define ARTSC_EXPORT __attribute__ ((visibility("default")))
+#elif defined(_WIN32)
+#define ARTSC_NO_EXPORT
+#define ARTSC_EXPORT __declspec(dllexport)
+#else
+#define ARTSC_NO_EXPORT
+#define ARTSC_EXPORT
+#endif
+
+#endif /* ARTSC_EXPORTS */
diff --git a/artsc/artscbackend.cc b/artsc/artscbackend.cc
new file mode 100644
index 0000000..a9f3a91
--- /dev/null
+++ b/artsc/artscbackend.cc
@@ -0,0 +1,805 @@
+ /*
+
+ Copyright (C) 2000 Stefan Westerfeld
+ stefan@space.twc.de
+ 2001 Matthias Kretz
+ kretz@kde.org
+
+ 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; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+#include "artsc.h"
+#include "soundserver.h"
+#include "stdsynthmodule.h"
+
+#include <iostream>
+#include <algorithm>
+#include <queue>
+
+#include <stdio.h>
+#include <cstring>
+#include <unistd.h>
+#include <fcntl.h>
+#include <math.h>
+#include <assert.h>
+#include "arts_export.h"
+
+#define arts_backend_debug(x) ;
+
+using namespace std;
+using namespace Arts;
+
+/**
+ * Base class for streams
+ */
+class Stream
+{
+protected:
+ SoundServer server;
+ float serverBufferTime;
+
+ bool _finished, isAttached;
+ int _samplingRate, _bits, _channels, pos;
+ string _name;
+ queue< DataPacket<mcopbyte>* > streamqueue;
+
+ int packetCount, packetCapacity;
+ int blockingIO;
+
+ /**
+ * returns the amount of bytes that will be played in a given amount of
+ * time in milliseconds
+ */
+ int timeToBytes(float time)
+ {
+ float playSpeed = _channels * _samplingRate * _bits / 8;
+ return (int)(playSpeed * (time / 1000.0));
+ }
+
+ /**
+ * returns the time in milliseconds it takes with the current parameters
+ * to play a given amount of bytes
+ */
+ float bytesToTime(int size)
+ {
+ float playSpeed = _channels * _samplingRate * _bits / 8;
+ return (1000.0 * ((float)size) / playSpeed);
+ }
+
+ int bufferSize() {
+ return packetCount * packetCapacity;
+ }
+
+ float bufferTime() {
+ return bytesToTime(bufferSize());
+ }
+
+ int bufferSpace() {
+ int space = 0;
+
+ attach();
+
+ /* make sure that our information is up-to-date */
+ Dispatcher::the()->ioManager()->processOneEvent(false);
+
+ if(!streamqueue.empty())
+ {
+ space += packetCapacity - pos; /* the first, half filled packet */
+
+ if(streamqueue.size() > 1) /* and the other, empty packets */
+ space += (streamqueue.size()-1)*packetCapacity;
+ }
+ return space;
+ }
+
+ int setBufferSize(int size)
+ {
+ /* don't change sizes when already streaming */
+ if(isAttached)
+ return ARTS_E_NOIMPL;
+
+ /*
+ * these parameters are usually a bad idea ;-) however we have to start
+ * somewhere, and maybe in two years, with a highly optimized kernel
+ * this is possible - for now, don't request the impossible or don't
+ * complain if it doesn't work
+ */
+ packetCount = 3;
+ packetCapacity = 128;
+
+ /*
+ * - do not configure stream buffers smaller than the server
+ * recommended value
+ * - try to get more or less close to the value the application
+ * wants
+ */
+ int needSize = max(size, timeToBytes(server.minStreamBufferTime()));
+
+ while(bufferSize() < needSize)
+ {
+ packetCount++;
+ if(packetCount == 8)
+ {
+ packetCount /= 2;
+ packetCapacity *= 2;
+ }
+ }
+
+ return bufferSize();
+ }
+
+ int packetSettings()
+ {
+ int settings = 0;
+
+ int cap = packetCapacity;
+ while(cap > 1)
+ {
+ settings++;
+ cap /= 2;
+ }
+
+ settings |= packetCount << 16;
+ return settings;
+ }
+
+ int setPacketSettings(int settings)
+ {
+ /* don't change sizes when already streaming */
+ if(isAttached)
+ return ARTS_E_NOIMPL;
+
+ packetCount = settings >> 16;
+
+ packetCapacity = 1;
+ int c = settings & 0xffff;
+ while(c > 0) {
+ packetCapacity *= 2;
+ c--;
+ }
+
+ /*
+ * - do not configure stream buffers smaller than the server
+ * recommended value
+ * - keep the packetSize the applications specified
+ */
+ int needSize = timeToBytes(server.minStreamBufferTime());
+
+ while(bufferSize() < needSize)
+ packetCount++;
+
+ return packetSettings();
+ }
+
+ /**
+ * the stream has to attach itself
+ */
+ virtual void attach() = 0;
+
+public:
+ Stream(SoundServer server, int rate, int bits, int channels,
+ string name) : server(server), _finished(false), isAttached(false),
+ _samplingRate(rate), _bits(bits), _channels(channels), pos(0),
+ _name(name)
+ {
+ serverBufferTime = server.serverBufferTime();
+ stream_set(ARTS_P_BUFFER_SIZE,64*1024);
+ stream_set(ARTS_P_BLOCKING,1);
+ }
+ virtual ~Stream()
+ {
+ //
+ }
+
+ virtual int stream_set(arts_parameter_t param, int value)
+ {
+ int result;
+
+ switch(param) {
+ case ARTS_P_BUFFER_SIZE:
+ return setBufferSize(value);
+
+ case ARTS_P_BUFFER_TIME:
+ result = setBufferSize(timeToBytes(value));
+ if(result < 0) return result;
+ return (int)bufferTime();
+
+ case ARTS_P_PACKET_SETTINGS:
+ return setPacketSettings(value);
+
+ case ARTS_P_BLOCKING:
+ if(value != 0 && value != 1) return ARTS_E_NOIMPL;
+
+ blockingIO = value;
+ return blockingIO;
+ /*
+ * maybe ARTS_P_TOTAL_LATENCY _could_ be made writeable, the
+ * others are of course useless
+ */
+ case ARTS_P_BUFFER_SPACE:
+ case ARTS_P_SERVER_LATENCY:
+ case ARTS_P_TOTAL_LATENCY:
+ case ARTS_P_PACKET_SIZE:
+ case ARTS_P_PACKET_COUNT:
+ return ARTS_E_NOIMPL;
+ }
+ return ARTS_E_NOIMPL;
+ }
+
+ virtual int stream_get(arts_parameter_t param)
+ {
+ switch(param) {
+ case ARTS_P_BUFFER_SIZE:
+ return bufferSize();
+
+ case ARTS_P_BUFFER_TIME:
+ return (int)bufferTime();
+
+ case ARTS_P_BUFFER_SPACE:
+ return bufferSpace();
+
+ case ARTS_P_PACKET_SETTINGS:
+ return packetSettings();
+
+ case ARTS_P_SERVER_LATENCY:
+ return (int)serverBufferTime;
+
+ case ARTS_P_TOTAL_LATENCY:
+ return stream_get(ARTS_P_SERVER_LATENCY)
+ + stream_get(ARTS_P_BUFFER_TIME);
+
+ case ARTS_P_BLOCKING:
+ return blockingIO;
+
+ case ARTS_P_PACKET_SIZE:
+ return packetCapacity;
+
+ case ARTS_P_PACKET_COUNT:
+ return packetCount;
+ }
+ return ARTS_E_NOIMPL;
+ }
+
+ virtual int write(const mcopbyte * /*data*/, int /*size*/)
+ {
+ return ARTS_E_NOIMPL;
+ }
+
+ virtual int read(mcopbyte * /*data*/, int /*size*/)
+ {
+ return ARTS_E_NOIMPL;
+ }
+
+ virtual void close() = 0;
+
+ int suspend()
+ {
+ if(isAttached)
+ {
+ return server.suspend();
+ }
+ return 0;
+ }
+
+ int suspended()
+ {
+ if(isAttached)
+ {
+ return 0;
+ }
+ return server.suspended();
+ }
+};
+
+class Receiver : public ByteSoundReceiver_skel,
+ public StdSynthModule,
+ virtual public Stream
+{
+ /*
+ * FIXME: bsWrapper is a more or less ugly trick to be able to use
+ * this object although not using smartwrappers to access it
+ */
+ ByteSoundReceiver bsWrapper;
+
+protected:
+ virtual void attach()
+ {
+ if(!isAttached)
+ {
+ isAttached = true;
+
+ server.attachRecorder(bsWrapper);
+ start();
+
+ /*
+ * TODO: this processOneEvent looks a bit strange here... it is
+ * there since StdIOManager does block 5 seconds on the first
+ * arts_write if it isn't - although notifications are pending
+ *
+ * Probably the real solution is to rewrite the
+ * StdIOManager::processOneEvent function. (And maybe drop the
+ * assumption that aRts will not block when an infinite amount
+ * of notifications is pending - I mean: will it ever happen?)
+ */
+ Dispatcher::the()->ioManager()->processOneEvent(false);
+ }
+ }
+
+public:
+ Receiver(SoundServer server, int rate, int bits, int channels,
+ string name) : Stream( server, rate, bits, channels, name)
+ {
+ bsWrapper = ByteSoundReceiver::_from_base(this);
+ }
+
+ virtual ~Receiver() {
+ //
+ }
+
+ long samplingRate() { return _samplingRate; }
+ long channels() { return _channels; }
+ long bits() { return _bits; }
+ bool finished() { return _finished; }
+ string title() { return _name; }
+
+ void process_indata(DataPacket<mcopbyte> *packet)
+ {
+ streamqueue.push(packet);
+ }
+
+ void close()
+ {
+ if(isAttached)
+ {
+ /* remove all packets from the streamqueue */
+ while(!streamqueue.empty())
+ {
+ DataPacket<mcopbyte> *packet = streamqueue.front();
+ packet->processed();
+ streamqueue.pop();
+ }
+
+ server.detachRecorder(bsWrapper);
+ }
+ // similar effect like "delete this;"
+ bsWrapper = ByteSoundReceiver::null();
+ }
+
+ int read(mcopbyte *data, int size)
+ {
+ attach();
+
+ int remaining = size;
+ while(remaining)
+ {
+ if(blockingIO)
+ {
+ /* C API blocking style read */
+ while(streamqueue.empty())
+ Dispatcher::the()->ioManager()->processOneEvent(true);
+ }
+ else
+ {
+ /* non blocking I/O */
+ if(streamqueue.empty())
+ Dispatcher::the()->ioManager()->processOneEvent(false);
+
+ /* still no more packets to read? */
+ if(streamqueue.empty())
+ return size - remaining;
+ }
+
+ /* get a packet */
+ DataPacket<mcopbyte> *packet = streamqueue.front();
+
+ /* copy some data from there */
+ int tocopy = min(remaining,packet->size-pos);
+ memcpy(data,&packet->contents[pos],tocopy);
+ pos += tocopy;
+ data += tocopy;
+ remaining -= tocopy;
+
+ /* have we read the whole packet? then get rid of it */
+ if(pos == packet->size)
+ {
+ packet->processed();
+ streamqueue.pop();
+ pos = 0;
+ }
+ }
+
+ /* no possible error conditions */
+ return size;
+ }
+};
+
+class Sender : public ByteSoundProducerV2_skel,
+ public StdSynthModule,
+ virtual public Stream
+{
+ /*
+ * FIXME: bsWrapper is a more or less ugly trick to be able to use
+ * this object although not using smartwrappers to access it
+ */
+ ByteSoundProducerV2 bsWrapper;
+
+protected:
+ virtual void attach()
+ {
+ if(!isAttached)
+ {
+ isAttached = true;
+
+ server.attach(bsWrapper);
+ start();
+
+ /*
+ * TODO: this processOneEvent looks a bit strange here... it is
+ * there since StdIOManager does block 5 seconds on the first
+ * arts_write if it isn't - although notifications are pending
+ *
+ * Probably the real solution is to rewrite the
+ * StdIOManager::processOneEvent function. (And maybe drop the
+ * assumption that aRts will not block when an infinite amount
+ * of notifications is pending - I mean: will it ever happen?)
+ */
+ Dispatcher::the()->ioManager()->processOneEvent(false);
+ }
+ }
+
+public:
+ Sender(SoundServer server, int rate, int bits, int channels,
+ string name) : Stream( server, rate, bits, channels, name)
+ {
+ bsWrapper = ByteSoundProducerV2::_from_base(this);
+ }
+
+ virtual ~Sender() {
+ //
+ }
+
+ long samplingRate() { return _samplingRate; }
+ long channels() { return _channels; }
+ long bits() { return _bits; }
+ bool finished() { return _finished; }
+ string title() { return _name; }
+
+ void streamStart()
+ {
+ /*
+ * start streaming
+ */
+ outdata.setPull(packetCount, packetCapacity);
+ }
+
+ void request_outdata(DataPacket<mcopbyte> *packet)
+ {
+ streamqueue.push(packet);
+ }
+
+ void close()
+ {
+ if(isAttached)
+ {
+ if(pos != 0)
+ {
+ /* send the last half-filled packet */
+ DataPacket<mcopbyte> *packet = streamqueue.front();
+ packet->size = pos;
+ packet->send();
+ streamqueue.pop();
+ }
+ outdata.endPull();
+
+ /* remove all packets from the streamqueue */
+ while(!streamqueue.empty())
+ {
+ DataPacket<mcopbyte> *packet = streamqueue.front();
+ packet->size = 0;
+ packet->send();
+ streamqueue.pop();
+ }
+
+ server.detach(bsWrapper);
+ }
+ // similar effect like "delete this;"
+ Arts::ByteSoundProducerV2_base* x = _copy();
+ bsWrapper = ByteSoundProducerV2::null();
+ x->_release();
+ }
+
+ int write(const mcopbyte *data, int size)
+ {
+ attach();
+
+ int remaining = size;
+ while(remaining)
+ {
+ if(blockingIO)
+ {
+ /* C API blocking style write */
+ /* we're not waiting for any data here, but rather for
+ * DataPackets that we can fill */
+ while(streamqueue.empty())
+ Dispatcher::the()->ioManager()->processOneEvent(true);
+ }
+ else
+ {
+ /* non blocking I/O */
+ if(streamqueue.empty())
+ Dispatcher::the()->ioManager()->processOneEvent(false);
+
+ /* still no more space to write? */
+ if(streamqueue.empty())
+ return size - remaining;
+ }
+
+ /* get a packet */
+ DataPacket<mcopbyte> *packet = streamqueue.front();
+
+ /* copy some data there */
+ int tocopy = min(remaining,packetCapacity-pos);
+ memcpy(&packet->contents[pos],data,tocopy);
+ pos += tocopy;
+ data += tocopy;
+ remaining -= tocopy;
+
+ /* have we filled up the packet? then send it */
+ if(pos == packetCapacity)
+ {
+ packet->size = packetCapacity;
+ packet->send();
+ streamqueue.pop();
+ pos = 0;
+ }
+ }
+
+ /* no possible error conditions */
+ return size;
+ }
+};
+
+class ArtsCApi {
+protected:
+ static ArtsCApi *instance;
+ int refcnt;
+
+ Dispatcher dispatcher;
+ SoundServer server;
+
+ ArtsCApi() : refcnt(1), server(Reference("global:Arts_SoundServer"))
+ {
+ //
+ }
+
+public:
+// C Api commands
+ int init() {
+ if(server.isNull())
+ return ARTS_E_NOSERVER;
+
+ return 0;
+ }
+
+ int suspend() {
+ if(!server.isNull())
+ return server.suspend()? 1:0;
+ return ARTS_E_NOSERVER;
+ }
+
+ int suspended() {
+ if(!server.isNull())
+ return server.suspended()? 1:0;
+ return ARTS_E_NOSERVER;
+ }
+
+ void free() {
+ // nothing to do
+ }
+
+ arts_stream_t play_stream(int rate, int bits, int channels, const char *name)
+ {
+ if(server.isNull())
+ return 0;
+
+ return (arts_stream_t)static_cast<Stream *>(new Sender(server,rate,bits,channels,name));
+ }
+
+ arts_stream_t record_stream(int rate, int bits, int channels, const char *name)
+ {
+ if(server.isNull())
+ return 0;
+
+ return (arts_stream_t)static_cast<Stream *>(new Receiver(server,rate,bits,channels,name));
+ }
+
+ void close_stream(arts_stream_t stream)
+ {
+ if(server.isNull())
+ return;
+
+ if(!stream)
+ return;
+
+ static_cast<Stream *>(stream)->close();
+ }
+
+ int write(arts_stream_t stream, const void *data, int size)
+ {
+ if(server.isNull())
+ return ARTS_E_NOSERVER;
+
+ if(!stream)
+ return ARTS_E_NOSTREAM;
+
+ return static_cast<Stream *>(stream)->write((const mcopbyte *)data,size);
+ }
+
+ int read(arts_stream_t stream, void *data, int size)
+ {
+ if(server.isNull())
+ return ARTS_E_NOSERVER;
+
+ if(!stream)
+ return ARTS_E_NOSTREAM;
+
+ return static_cast<Stream *>(stream)->read((mcopbyte *)data,size);
+ }
+
+ int stream_set(arts_stream_t stream, arts_parameter_t param, int value)
+ {
+ if(server.isNull())
+ return ARTS_E_NOSERVER;
+
+ if(!stream)
+ return ARTS_E_NOSTREAM;
+
+ return static_cast<Stream *>(stream)->stream_set(param,value);
+ }
+
+ int stream_get(arts_stream_t stream, arts_parameter_t param)
+ {
+ if(server.isNull())
+ return ARTS_E_NOSERVER;
+
+ if(!stream)
+ return ARTS_E_NOSTREAM;
+
+ return static_cast<Stream *>(stream)->stream_get(param);
+ }
+
+// allocation and freeing of the class
+ static ArtsCApi *the() {
+ return instance;
+ }
+ static void ref() {
+ if(!instance)
+ instance = new ArtsCApi();
+ else
+ instance->refcnt++;
+ }
+ static void release() {
+ assert(instance);
+ assert(instance->refcnt > 0);
+ instance->refcnt--;
+ if(instance->refcnt == 0)
+ {
+ delete instance;
+ instance = 0;
+ }
+ }
+};
+
+//----------------------------- static members -------------------------------
+
+ArtsCApi *ArtsCApi::instance = 0;
+
+//------------------ wrappers from C to C++ class ----------------------------
+
+extern "C" ARTSC_EXPORT int arts_backend_init()
+{
+ arts_backend_debug("arts_backend_init");
+ ArtsCApi::ref();
+
+ // if init fails, don't expect free, and don't expect that the user
+ // continues using other API functions
+ int rc = ArtsCApi::the()->init();
+ if(rc < 0) ArtsCApi::release();
+ return rc;
+}
+
+extern "C" ARTSC_EXPORT int arts_backend_suspend()
+{
+ if(!ArtsCApi::the()) return ARTS_E_NOINIT;
+ arts_backend_debug("arts_backend_suspend");
+ return ArtsCApi::the()->suspend();
+}
+
+extern "C" ARTSC_EXPORT int arts_backend_suspended()
+{
+ if(!ArtsCApi::the()) return ARTS_E_NOINIT;
+ arts_backend_debug("arts_backend_suspended");
+ return ArtsCApi::the()->suspended();
+}
+
+extern "C" ARTSC_EXPORT void arts_backend_free()
+{
+ if(!ArtsCApi::the()) return;
+
+ arts_backend_debug("arts_backend_free");
+ ArtsCApi::the()->free();
+ ArtsCApi::release();
+}
+
+extern "C" ARTSC_EXPORT arts_stream_t arts_backend_play_stream(int rate, int bits, int channels, const char *name)
+{
+ if(!ArtsCApi::the()) return 0;
+
+ arts_backend_debug("arts_backend_play_stream");
+ return ArtsCApi::the()->play_stream(rate,bits,channels,name);
+}
+
+extern "C" ARTSC_EXPORT arts_stream_t arts_backend_record_stream(int rate, int bits, int channels, const char *name)
+{
+ if(!ArtsCApi::the()) return 0;
+
+ arts_backend_debug("arts_backend_record_stream");
+ return ArtsCApi::the()->record_stream(rate,bits,channels,name);
+}
+
+extern "C" ARTSC_EXPORT void arts_backend_close_stream(arts_stream_t stream)
+{
+ if(!ArtsCApi::the()) return;
+
+ arts_backend_debug("arts_backend_close_stream");
+ ArtsCApi::the()->close_stream(stream);
+}
+
+extern "C" ARTSC_EXPORT int arts_backend_read(arts_stream_t stream, void *buffer, int count)
+{
+ if(!ArtsCApi::the()) return ARTS_E_NOINIT;
+
+ arts_backend_debug("arts_backend_read");
+ return ArtsCApi::the()->read(stream,buffer,count);
+}
+
+extern "C" ARTSC_EXPORT int arts_backend_write(arts_stream_t stream, const void *buffer,
+ int count)
+{
+ if(!ArtsCApi::the()) return ARTS_E_NOINIT;
+
+ arts_backend_debug("arts_backend_write");
+ return ArtsCApi::the()->write(stream,buffer,count);
+}
+
+extern "C" ARTSC_EXPORT int arts_backend_stream_set(arts_stream_t stream,
+ arts_parameter_t param, int value)
+{
+ if(!ArtsCApi::the()) return ARTS_E_NOINIT;
+
+ arts_backend_debug("arts_stream_set");
+ return ArtsCApi::the()->stream_set(stream,param,value);
+}
+
+extern "C" ARTSC_EXPORT int arts_backend_stream_get(arts_stream_t stream,
+ arts_parameter_t param)
+{
+ if(!ArtsCApi::the()) return ARTS_E_NOINIT;
+
+ arts_backend_debug("arts_stream_get");
+ return ArtsCApi::the()->stream_get(stream,param);
+}
diff --git a/artsc/artsdsp.c b/artsc/artsdsp.c
new file mode 100644
index 0000000..368b15f
--- /dev/null
+++ b/artsc/artsdsp.c
@@ -0,0 +1,675 @@
+/* Evil evil evil hack to get OSS apps to cooperate with artsd
+ * This is based on the original esddsp, which esd uses to do the same.
+ *
+ * Copyright (C) 1998 Manish Singh <yosh@gimp.org>
+ * Copyright (C) 2000 Stefan Westerfeld <stefan@space.twc.de> (aRts port)
+ *
+ * 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#define _GNU_SOURCE 1
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_SYS_SOUNDCARD_H
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/soundcard.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+/* #include <sys/mman.h> */
+#include <fcntl.h>
+
+#include <artsc.h>
+#include <dlfcn.h>
+
+#include "arts_export.h"
+/*
+ * NOTE:
+ *
+ * To truly support non-blocking I/O, there is some stuff missing. Namely,
+ * select should be trapped and redirected, as well as the means for making
+ * a stream non-blocking and so on. Maybe poll, too.
+ *
+ * Currently, only apps that are doing blocking I/O will probably work right.
+ */
+
+/**
+ * the stream status: sndfd is -1 when unused, otherwise it is a useless fd
+ * which points to /dev/null, to ensure compatibility with more weird
+ * operations on streams
+ *
+ * settings contains what has already been set (speed, bits, channels), and
+ * is 7 when all of these are true
+ *
+ * stream contains an aRts stream or 0
+ */
+static int sndfd = -1;
+static int settings;
+static int arts_init_done = 0;
+static arts_stream_t stream = 0;
+static arts_stream_t record_stream = 0;
+static int bits = 0;
+static int speed = 0;
+static int channels = 0;
+static int frags;
+
+#if defined(HAVE_IOCTL_INT_INT_DOTS)
+typedef int ioctl_request_t;
+#elif defined(HAVE_IOCTL_INT_ULONG_DOTS)
+typedef unsigned long ioctl_request_t;
+#elif defined(HAVE_IOCTL_INT_ULONGINT_DOTS)
+typedef unsigned long int ioctl_request_t;
+#else
+#error "unknown ioctl type (check config.h, adapt configure test)..."
+#endif
+
+/*
+ * memory mapping emulation
+ */
+static int mmapemu = 0;
+static count_info mmapemu_ocount;
+static char *mmapemu_obuffer = 0;
+static int mmapemu_osize = 0;
+
+/*
+ * original C library functions
+ */
+typedef int (*orig_open_ptr)(const char *pathname, int flags, ...);
+typedef int (*orig_close_ptr)(int fd);
+typedef int (*orig_ioctl_ptr)(int fd, ioctl_request_t request, ...);
+typedef ssize_t (*orig_write_ptr)(int fd, const void *buf, size_t count);
+typedef ssize_t (*orig_read_ptr)(int fd, void *buf, size_t count);
+typedef void* (*orig_mmap_ptr)(void *start, size_t length, int prot,
+ int flags, int fd, off_t offset);
+typedef int (*orig_munmap_ptr)(void *start, size_t length);
+typedef FILE* (*orig_fopen_ptr)(const char *path, const char *mode);
+typedef int (*orig_access_ptr)(const char *pathname, int mode);
+
+static orig_open_ptr orig_open;
+static orig_close_ptr orig_close;
+static orig_ioctl_ptr orig_ioctl;
+static orig_write_ptr orig_write;
+static orig_read_ptr orig_read;
+static orig_mmap_ptr orig_mmap;
+static orig_munmap_ptr orig_munmap;
+static orig_fopen_ptr orig_fopen;
+static orig_access_ptr orig_access;
+
+static int artsdsp_debug = 0;
+static int artsdsp_init = 0;
+
+void *mmap(void *start, size_t length, int prot, int flags,
+ int fd, off_t offset);
+int munmap(void *start, size_t length);
+#define CHECK_INIT() if(!artsdsp_init) artsdsp_doinit();
+
+/*
+ * Initialization - maybe this should be either be a startup only called
+ * routine, or use pthread locks to prevent strange effects in multithreaded
+ * use (however it seems highly unlikely that an application would create
+ * multiple threads before even using one of redirected the system functions
+ * once).
+ */
+
+static void artsdsp_doinit()
+{
+ const char *env;
+ artsdsp_init = 1;
+
+ /* debugging? */
+ env = getenv("ARTSDSP_VERBOSE");
+ artsdsp_debug = env && !strcmp(env,"1");
+
+ /* mmap emulation? */
+ env = getenv("ARTSDSP_MMAP");
+ mmapemu = env && !strcmp(env,"1");
+
+ /* resolve original symbols */
+ orig_open = (orig_open_ptr)dlsym(RTLD_NEXT,"open");
+ orig_close = (orig_close_ptr)dlsym(RTLD_NEXT,"close");
+ orig_write = (orig_write_ptr)dlsym(RTLD_NEXT,"write");
+ orig_read = (orig_read_ptr)dlsym(RTLD_NEXT,"read");
+ orig_ioctl = (orig_ioctl_ptr)dlsym(RTLD_NEXT,"ioctl");
+ orig_mmap = (orig_mmap_ptr)dlsym(RTLD_NEXT,"mmap");
+ orig_munmap = (orig_munmap_ptr)dlsym(RTLD_NEXT,"munmap");
+ orig_fopen = (orig_fopen_ptr)dlsym(RTLD_NEXT,"fopen");
+ orig_access = (orig_access_ptr)dlsym(RTLD_NEXT,"access");
+}
+
+static void
+#ifdef __GNUC__
+ __attribute__( ( format ( printf, 1, 2 ) ) )
+#endif
+artsdspdebug(const char *fmt,...)
+{
+ CHECK_INIT();
+
+ if(artsdsp_debug)
+ {
+ va_list ap;
+ va_start(ap, fmt);
+ (void) vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ }
+}
+
+static void mmapemu_flush()
+{
+ int space;
+
+ if (mmapemu_osize == 0) {
+ return;
+ }
+
+ space = arts_stream_get(stream, ARTS_P_BUFFER_SPACE);
+ artsdspdebug("space = %d\n",space);
+ while(space >= 4096)
+ {
+ arts_write(stream,&mmapemu_obuffer[mmapemu_ocount.ptr],4096);
+ space -= 4096;
+ mmapemu_ocount.ptr += 4096;
+ mmapemu_ocount.ptr %= mmapemu_osize;
+ mmapemu_ocount.blocks++;
+ mmapemu_ocount.bytes += 4096;
+ }
+}
+
+/* returns 1 if the filename points to a sound device */
+static int is_sound_device(const char *pathname)
+{
+ if(!pathname) return 0;
+ if(strcmp(pathname,"/dev/dsp") == 0) return 1;
+ if(strcmp(pathname,"/dev/sound/dsp") == 0) return 1;
+ return 0;
+}
+
+int open (const char *pathname, int flags, ...)
+{
+ va_list args;
+ mode_t mode = 0;
+
+ CHECK_INIT();
+
+ /*
+ * After the documentation, va_arg is not safe if there is no argument to
+ * get "random errors will occur", so only get it in case O_CREAT is set,
+ * and hope that passing 0 to the orig_open function in all other cases
+ * will work.
+ */
+ va_start(args,flags);
+ if(flags & O_CREAT) {
+ /* The compiler will select one of these at compile-tyime if -O is used.
+ * Otherwise, it may be deferred until runtime.
+ */
+ if (sizeof(int) >= sizeof(mode_t)) {
+ mode = va_arg(args, int);
+ } else {
+ mode = va_arg(args, mode_t);
+ }
+ }
+ va_end(args);
+
+ if (!is_sound_device(pathname)) /* original open for anything but sound */
+ return orig_open (pathname, flags, mode);
+
+ settings = 0;
+ frags = 0;
+ stream = 0;
+ record_stream = 0;
+
+ artsdspdebug ("aRts: hijacking /dev/dsp open...\n");
+
+ sndfd = orig_open("/dev/null",flags,mode);
+ if(sndfd >= 0)
+ {
+ if(!arts_init_done) {
+ int rc = arts_init();
+ if(rc < 0) {
+ artsdspdebug("error on aRts init: %s\n", arts_error_text(rc));
+ orig_close(sndfd);
+ sndfd = -1;
+ return orig_open (pathname, flags, mode);
+ }
+ else arts_init_done = 1;
+ }
+ }
+
+ /* success */
+ return sndfd;
+}
+
+int ioctl (int fd, ioctl_request_t request, ...)
+{
+ int space, size, latency, odelay;
+
+ /*
+ * FreeBSD needs ioctl with varargs. However I have no idea how to "forward"
+ * the variable args ioctl to the orig_ioctl routine. So I expect the ioctl
+ * to have exactly one pointer-like parameter and forward that, hoping that
+ * it works
+ */
+ va_list args;
+ void *argp;
+ va_start(args,request);
+ argp = va_arg(args, void *);
+ va_end(args);
+
+ CHECK_INIT();
+
+ if (fd != sndfd )
+ return orig_ioctl (fd, request, argp);
+ else if (sndfd != -1)
+ {
+ int *arg = (int *) argp;
+ artsdspdebug("aRts: hijacking /dev/dsp ioctl (%d : %x - %p)\n",
+ ( int ) fd, ( int ) request, ( void* ) argp);
+
+ switch (request)
+ {
+ struct audio_buf_info *audiop;
+#ifdef SNDCTL_DSP_RESET
+ case SNDCTL_DSP_RESET: /* _SIO ('P', 0) */
+ artsdspdebug("aRts: SNDCTL_DSP_RESET unsupported\n");
+ break;
+#endif
+
+#ifdef SNDCTL_DSP_SYNC
+ case SNDCTL_DSP_SYNC: /* _SIO ('P', 1) */
+ artsdspdebug("aRts: SNDCTL_DSP_SYNC unsupported\n");
+ break;
+#endif
+
+#ifdef SNDCTL_DSP_SPEED
+ case SNDCTL_DSP_SPEED: /* _SIOWR('P', 2, int) */
+ speed = *arg;
+ settings |= 2;
+ break;
+#endif
+
+#ifdef SNDCTL_DSP_STEREO
+ case SNDCTL_DSP_STEREO: /* _SIOWR('P', 3, int) */
+ channels = (*arg)?2:1;
+ settings |= 4;
+ break;
+#endif
+
+#ifdef SNDCTL_DSP_GETBLKSIZE
+ case SNDCTL_DSP_GETBLKSIZE: /* _SIOWR('P', 4, int) */
+ if(mmapemu)
+ *arg = 4096;
+ else if(stream)
+ *arg = arts_stream_get(stream,ARTS_P_PACKET_SIZE);
+ else
+ {
+ int fragSize = frags & 0x7fff;
+ if(fragSize > 1 && fragSize < 16)
+ *arg = (1 << fragSize);
+ else
+ *arg = 16384; /* no idea ;-) */
+ }
+ break;
+#endif
+
+#ifdef SNDCTL_DSP_SETFMT
+ case SNDCTL_DSP_SETFMT: /* _SIOWR('P',5, int) */
+ bits = (*arg & 0x30) ? 16 : 8;
+ settings |= 1;
+ break;
+#endif
+
+#ifdef SNDCTL_DSP_CHANNELS
+ case SNDCTL_DSP_CHANNELS: /* _SIOWR('P', 6, int) */
+ channels = (*arg);
+ settings |= 4;
+ break;
+#endif
+
+#ifdef SOUND_PCM_WRITE_FILTER
+ case SOUND_PCM_WRITE_FILTER: /* _SIOWR('P', 7, int) */
+ artsdspdebug("aRts: SNDCTL_DSP_WRITE_FILTER(%d) unsupported\n",*arg);
+ break;
+#endif
+
+#ifdef SNDCTL_DSP_POST
+ case SNDCTL_DSP_POST: /* _SIO ('P', 8) */
+ artsdspdebug("aRts: SNDCTL_DSP_POST unsupported\n");
+ break;
+#endif
+
+#ifdef SNDCTL_DSP_SUBDIVIDE
+ case SNDCTL_DSP_SUBDIVIDE: /* _SIOWR('P', 9, int) */
+ artsdspdebug("aRts: SNDCTL_DSP_SUBDIVIDE(%d) unsupported\n",*arg);
+ break;
+#endif
+
+#ifdef SNDCTL_DSP_SETFRAGMENT
+ case SNDCTL_DSP_SETFRAGMENT: /* _SIOWR('P',10, int) */
+ artsdspdebug("aRts: SNDCTL_DSP_SETFRAGMENT(%x) partially supported\n",
+ *arg);
+ frags = *arg;
+ break;
+#endif
+
+#ifdef SNDCTL_DSP_GETFMTS
+ case SNDCTL_DSP_GETFMTS: /* _SIOR ('P',11, int) */
+ *arg = 8 | 16;
+ break;
+#endif
+
+#if defined(SNDCTL_DSP_GETOSPACE) && defined(SNDCTL_DSP_GETISPACE)
+ case SNDCTL_DSP_GETOSPACE: /* _SIOR ('P',12, audio_buf_info) */
+ case SNDCTL_DSP_GETISPACE: /* _SIOR ('P',13, audio_buf_info) */
+ audiop = argp;
+ if(mmapemu)
+ {
+ audiop->fragstotal = 16;
+ audiop->fragsize = 4096;
+ audiop->bytes = 0; /* FIXME: is this right? */
+ audiop->fragments = 0;
+ }
+ else
+ {
+ audiop->fragstotal =
+ stream?arts_stream_get(stream, ARTS_P_PACKET_COUNT):10;
+ audiop->fragsize =
+ stream?arts_stream_get(stream, ARTS_P_PACKET_SIZE):16384;
+ audiop->bytes =
+ stream?arts_stream_get(stream, ARTS_P_BUFFER_SPACE):16384;
+ audiop->fragments = audiop->bytes / audiop->fragsize;
+ }
+ break;
+#endif
+
+#ifdef SNDCTL_DSP_NONBLOCK
+ case SNDCTL_DSP_NONBLOCK: /* _SIO ('P',14) */
+ artsdspdebug("aRts: SNDCTL_DSP_NONBLOCK unsupported\n");
+ break;
+#endif
+
+#ifdef SNDCTL_DSP_GETCAPS
+ case SNDCTL_DSP_GETCAPS: /* _SIOR ('P',15, int) */
+ if(mmapemu)
+ *arg = DSP_CAP_MMAP | DSP_CAP_TRIGGER | DSP_CAP_REALTIME | DSP_CAP_DUPLEX;
+ else
+ *arg = DSP_CAP_DUPLEX;
+ break;
+#endif
+
+#ifdef SNDCTL_DSP_GETTRIGGER
+ case SNDCTL_DSP_GETTRIGGER: /* _SIOR ('P',16, int) */
+ artsdspdebug("aRts: SNDCTL_DSP_GETTRIGGER unsupported\n");
+ break;
+#endif
+
+#ifdef SNDCTL_DSP_SETTRIGGER
+ case SNDCTL_DSP_SETTRIGGER: /* _SIOW ('P',16, int) */
+ artsdspdebug("aRts: SNDCTL_DSP_SETTRIGGER unsupported\n");
+ break;
+#endif
+
+#ifdef SNDCTL_DSP_GETIPTR
+ case SNDCTL_DSP_GETIPTR: /* _SIOR ('P',17, count_info) */
+ artsdspdebug("aRts: SNDCTL_DSP_GETIPTR unsupported\n");
+ break;
+#endif
+
+#ifdef SNDCTL_DSP_GETOPTR
+ case SNDCTL_DSP_GETOPTR: /* _SIOR ('P',18, count_info) */
+ if(mmapemu)
+ {
+ mmapemu_flush();
+ *((count_info *)arg) = mmapemu_ocount;
+ mmapemu_ocount.blocks = 0;
+ }
+ else
+ {
+ artsdspdebug("aRts: SNDCTL_DSP_GETOPTR unsupported\n");
+ }
+ break;
+#endif
+
+#ifdef SNDCTL_DSP_MAPINBUF
+ case SNDCTL_DSP_MAPINBUF: /* _SIOR ('P', 19, buffmem_desc) */
+ artsdspdebug("aRts: SNDCTL_DSP_MAPINBUF unsupported\n");
+ break;
+#endif
+
+#ifdef SNDCTL_DSP_MAPOUTBUF
+ case SNDCTL_DSP_MAPOUTBUF: /* _SIOR ('P', 20, buffmem_desc) */
+ artsdspdebug("aRts: SNDCTL_DSP_MAPOUTBUF unsupported\n");
+ break;
+#endif
+
+#ifdef SNDCTL_DSP_SETSYNCRO
+ case SNDCTL_DSP_SETSYNCRO: /* _SIO ('P', 21) */
+ artsdspdebug("aRts: SNDCTL_DSP_SETSYNCHRO unsupported\n");
+ break;
+#endif
+
+#ifdef SNDCTL_DSP_SETDUPLEX
+ case SNDCTL_DSP_SETDUPLEX: /* _SIO ('P', 22) */
+ artsdspdebug("aRts: SNDCTL_DSP_SETDUPLEX unsupported\n");
+ break;
+#endif
+
+#ifdef SNDCTL_DSP_GETODELAY
+ case SNDCTL_DSP_GETODELAY: /* _SIOR ('P', 23, int) */
+ space = arts_stream_get(stream, ARTS_P_BUFFER_SPACE);
+ size = arts_stream_get(stream, ARTS_P_BUFFER_SIZE);
+ latency = arts_stream_get(stream, ARTS_P_SERVER_LATENCY);
+ odelay = size - space + (latency * speed * bits * channels) / 8000;
+ artsdspdebug("aRts: SNDCTL_DSP_GETODELAY returning %d\n", odelay);
+ *arg = odelay;
+ break;
+#endif
+
+ default:
+ artsdspdebug("aRts: unhandled /dev/dsp ioctl (%lx - %p)\n",request, argp);
+ break;
+ }
+
+ if (settings == 7 && !stream)
+ {
+ const char *name = getenv("ARTSDSP_NAME");
+ int fragSize = (frags & 0x7fff);
+ int fragCount = (frags & 0x7fff0000) >> 16;
+
+ artsdspdebug ("aRts: creating stream...\n");
+ stream = arts_play_stream(speed,bits,channels,name?name:"artsdsp");
+
+ if(fragSize > 1 && fragSize < 16)
+ {
+ /*
+ * if fragment settings are way too large (unrealistic), we
+ * will assume that the user didn't mean it, and let the C API
+ * choose a convenient number >= 3
+ */
+ if(fragCount < 2 || fragCount > 8192
+ || (fragCount * (1 << fragSize)) > 128*1024)
+ {
+ frags = 0x00030000+fragSize;
+ }
+
+ arts_stream_set(stream,ARTS_P_PACKET_SETTINGS,frags);
+ }
+ if(mmapemu)
+ {
+ arts_stream_set(stream,ARTS_P_PACKET_SETTINGS,0x0002000c);
+ mmapemu_ocount.ptr=mmapemu_ocount.blocks=mmapemu_ocount.bytes=0;
+ artsdspdebug("aRts: total latency = %dms, buffer latency = %dms\n",
+ arts_stream_get(stream,ARTS_P_TOTAL_LATENCY),
+ arts_stream_get(stream, ARTS_P_BUFFER_TIME));
+ }
+ }
+
+ return 0;
+ }
+
+ return 0;
+}
+
+int close(int fd)
+{
+ CHECK_INIT();
+
+ if (fd != sndfd)
+ return orig_close (fd);
+ else if (sndfd != -1)
+ {
+ artsdspdebug ("aRts: /dev/dsp close...\n");
+ if(stream)
+ {
+ arts_close_stream(stream);
+ stream = 0;
+ }
+ if (record_stream)
+ {
+ arts_close_stream(record_stream);
+ record_stream = 0;
+ }
+ if(mmapemu && mmapemu_obuffer)
+ {
+ free(mmapemu_obuffer);
+ mmapemu_obuffer = 0;
+ }
+
+ /*
+ * there are problems with library unloading in conjunction with X11,
+ * (Arts::X11GlobalComm), so we don't unload stuff again here
+ */
+
+ /* arts_free(); */
+
+ orig_close(sndfd);
+ sndfd = -1;
+ }
+ return 0;
+}
+
+int access(const char *pathname, int mode)
+{
+ CHECK_INIT();
+
+ if (!is_sound_device(pathname)) /* original open for anything but sound */
+ return orig_access (pathname, mode);
+ else
+ artsdspdebug ("aRts: /dev/dsp access...\n");
+ return 0;
+}
+
+ssize_t write (int fd, const void *buf, size_t count)
+{
+ CHECK_INIT();
+
+ if(fd != sndfd)
+ return orig_write(fd,buf,count);
+ else if(sndfd != -1)
+ {
+ artsdspdebug ("aRts: /dev/dsp write...\n");
+ if(stream != 0)
+ {
+ return arts_write(stream,buf,count);
+ }
+ }
+ return 0;
+}
+
+ssize_t read (int fd, void *buf, size_t count)
+{
+ CHECK_INIT();
+
+ if (fd != sndfd)
+ return orig_read(fd,buf,count);
+ else if (sndfd == -1)
+ return 0;
+
+ if (!record_stream)
+ record_stream = arts_record_stream(speed, bits, channels, "artsdsp");
+ artsdspdebug ( "aRts: /dev/dsp read...\n" );
+ return arts_read(record_stream, buf, count);
+}
+
+void *mmap(void *start, size_t length, int prot, int flags,
+ int fd, off_t offset)
+{
+ CHECK_INIT();
+
+ if(fd != sndfd || sndfd == -1)
+ return orig_mmap(start,length,prot,flags,fd,offset);
+ else
+ {
+ artsdspdebug ("aRts: mmap - start = %p, length = %zd, prot = %d\n",
+ start, length, prot);
+ artsdspdebug (" flags = %d, fd = %d, offset = %ld\n",flags, fd,offset);
+
+ if(mmapemu && length > 0)
+ {
+ mmapemu_osize = length;
+ mmapemu_obuffer = malloc(length);
+ mmapemu_ocount.ptr = mmapemu_ocount.blocks = mmapemu_ocount.bytes = 0;
+ return mmapemu_obuffer;
+ }
+ else artsdspdebug ("aRts: /dev/dsp mmap (unsupported, try -m option)...\n");
+ }
+ return (void *)-1;
+}
+
+int munmap(void *start, size_t length)
+{
+ CHECK_INIT();
+
+ if(start != mmapemu_obuffer || mmapemu_obuffer == 0)
+ return orig_munmap(start,length);
+
+ artsdspdebug ("aRts: /dev/dsp munmap...\n");
+ mmapemu_obuffer = 0;
+ free(start);
+
+ return 0;
+}
+
+#ifdef HAVE_ARTSDSP_STDIO_EMU
+
+/*
+ * use #include rather than linking, because we want to keep everything
+ * static inside artsdsp to be sure that we don't override application symbols
+ */
+#include "stdioemu.c"
+
+FILE* fopen(const char *path, const char *mode)
+{
+ CHECK_INIT();
+
+ if (!is_sound_device(path)) /* original open for anything but sound */
+ return orig_fopen (path, mode);
+
+ artsdspdebug ("aRts: hijacking /dev/dsp fopen...\n");
+
+ return fake_fopen(path, mode);
+}
+#endif
+
+#endif
+/*
+ * vim:ts=4
+ */
diff --git a/artsc/artsdsp.in b/artsc/artsdsp.in
new file mode 100755
index 0000000..084a716
--- /dev/null
+++ b/artsc/artsdsp.in
@@ -0,0 +1,106 @@
+#!/bin/sh
+# artsdsp - wrapper script to allow *some* binary only programs to use artsd
+# based on the esddsp script
+
+# keep this in sync with artsversion.h
+version="@ARTS_VERSION@"
+
+# default values for script variables
+verbose=0
+set_name=0
+single_thread=0
+
+# check for artsdsp options
+while test $# -gt 0; do
+
+ case "$1" in
+
+ -h|--help)
+ echo "artsdsp - attempt to reroute audio device to artsd"
+ echo " "
+ echo "artsdsp [options] application arguments"
+ echo " "
+ echo "options:"
+ echo "-h, --help show brief help"
+ echo "-n, --name=NAME use name to identify player to artsd"
+ echo "-m, --mmap emulate memory mapping (i.e. for quake)"
+ echo "-s, --single-threaded use the single-threaded version"
+ echo "-v, --verbose show parameters"
+ echo "-V, --version show version"
+ exit 0
+ ;;
+
+ -n)
+ shift
+ if test $# -gt 0; then
+ export ARTSDSP_NAME=$1
+ else
+ echo "no player name specified"
+ exit 1
+ fi
+ shift
+ set_name=1
+ ;;
+
+ --name*)
+ export ARTSDSP_NAME=`echo $1 | sed -e 's/^[^=]*=//g'`
+ set_name=1
+ shift
+ ;;
+
+ -v|--verbose)
+ verbose=1
+ shift
+ ;;
+
+ -V|--version)
+ echo "artsdsp $version"
+ exit
+ ;;
+
+ -m|--mmap)
+ export ARTSDSP_MMAP=1
+ shift
+ ;;
+
+ -s|--single-threaded)
+ single_thread=1
+ shift
+ ;;
+
+ *)
+ # no more artsdsp options, get on with life
+ break
+ ;;
+ esac
+done
+
+# echo options if verbose specified
+if test "$verbose" = 1; then
+ ARTSDSP_VERBOSE=1
+ export ARTSDSP_VERBOSE
+ echo "artsdsp: $version"
+ echo "name: $ARTSDSP_NAME"
+ echo "command line: $@"
+ if test "$single_thread" = 1; then
+ echo "threaded: no"
+ else
+ echo "threaded: yes"
+ fi
+fi
+
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+
+if test "$single_thread" = 1; then
+ LD_PRELOAD=${exec_prefix}/\$LIB/libartsdsp_st.so.0
+else
+ LD_PRELOAD=${exec_prefix}/\$LIB/libartsdsp.so.0:${exec_prefix}/\$LIB/libartsc.so.0
+fi
+if test -f ${exec_prefix}/\$LIB/libdl.so.2; then
+ LD_PRELOAD=$LD_PRELOAD:${exec_prefix}/\$LIB/libdl.so.2
+fi
+export LD_PRELOAD
+
+# invoke the program with the args given
+exec "$@"
diff --git a/artsc/stdioemu.c b/artsc/stdioemu.c
new file mode 100644
index 0000000..2b3bc1c
--- /dev/null
+++ b/artsc/stdioemu.c
@@ -0,0 +1,96 @@
+ /*
+
+ Copyright (C) 2000 Stefan Westerfeld
+ stefan@space.twc.de
+
+ 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; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ */
+
+/*
+ * This source only exists because some very special programs think that
+ * it is a very special idea to access /dev/dsp by the means of stdio, so
+ * we need to fake FILE* access for artsdsp as well.
+ *
+ * To do so, it relies on glibc internals, so that it will probably not work
+ * on other systems - but then again, it might not be necessary on other
+ * systems, when fopen properly calls open, it might as well work unchanged.
+ */
+
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <stdio.h>
+#include <libio.h>
+
+struct fd_cookie {
+ int fd;
+};
+
+static ssize_t fdc_read (void *cookie, char *buffer, size_t size)
+{
+ struct fd_cookie *fdc = (struct fd_cookie *)cookie;
+ return read(fdc->fd, buffer, size);
+}
+
+static ssize_t fdc_write (void *cookie, const char *buffer, size_t size)
+{
+ struct fd_cookie *fdc = (struct fd_cookie *)cookie;
+ return write(fdc->fd, buffer, size);
+}
+
+static int fdc_seek (void* cookie, off64_t* position, int whence)
+{
+ return -1;
+}
+
+static int fdc_clean (void *cookie)
+{
+ struct fd_cookie *fdc = (struct fd_cookie *)cookie;
+ int result = close(fdc->fd);
+ free(cookie);
+ return result;
+}
+
+static FILE *fake_fopen(const char *path, const char *mode)
+{
+ cookie_io_functions_t fns = { fdc_read, fdc_write, fdc_seek, fdc_clean };
+ struct fd_cookie *fdc =
+ (struct fd_cookie *)malloc(sizeof(struct fd_cookie));
+ const char *mptr;
+ int open_mode = 0;
+ FILE *result = 0;
+
+ for(mptr = mode; *mptr; mptr++)
+ {
+ if(*mptr == 'r') open_mode |= 1; /* 1 = read */
+ if(*mptr == 'w') open_mode |= 2; /* 2 = write */
+ if(*mptr == '+') open_mode |= 3; /* 3 = readwrite */
+ if(*mptr == 'a') open_mode |= 2; /* append -> write */
+ }
+ if(open_mode == 1) fdc->fd = open(path,O_RDONLY,0666);
+ if(open_mode == 2) fdc->fd = open(path,O_WRONLY,0666);
+ if(open_mode == 3) fdc->fd = open(path,O_RDWR,0666);
+
+ if(open_mode && fdc->fd > 0)
+ {
+ result = fopencookie (fdc,"w", fns);
+ result->_fileno = fdc->fd; /* ugly patchy slimy kludgy hack */
+ }
+ return result;
+}