summaryrefslogtreecommitdiffstats
path: root/soundserver
diff options
context:
space:
mode:
authortpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-01-05 00:01:18 +0000
committertpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-01-05 00:01:18 +0000
commit42995d7bf396933ee60c5f89c354ea89cf13df0d (patch)
treecfdcea0ac57420e7baf570bfe435e107bb842541 /soundserver
downloadarts-42995d7bf396933ee60c5f89c354ea89cf13df0d.tar.gz
arts-42995d7bf396933ee60c5f89c354ea89cf13df0d.zip
Copy of aRts for Trinity modifications
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/dependencies/arts@1070145 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'soundserver')
-rw-r--r--soundserver/FileInputStream.mcopclass5
-rw-r--r--soundserver/GSLPlayObject.mcopclass7
-rw-r--r--soundserver/Makefile.am122
-rw-r--r--soundserver/WavPlayObject.mcopclass7
-rw-r--r--soundserver/artscat.cc244
-rw-r--r--soundserver/artsd.cc376
-rw-r--r--soundserver/artsplay.cc77
-rw-r--r--soundserver/artsrec.cc194
-rw-r--r--soundserver/artsshell.cc671
-rw-r--r--soundserver/artsversion-new.h.in31
-rw-r--r--soundserver/artswrapper.c120
-rw-r--r--soundserver/cpuusage.cc137
-rw-r--r--soundserver/cpuusage.h53
-rw-r--r--soundserver/crashhandler.cc236
-rw-r--r--soundserver/crashhandler.h131
-rw-r--r--soundserver/fileinputstream_impl.cc193
-rw-r--r--soundserver/gslplayobject_impl.cc235
-rw-r--r--soundserver/kmedia2.idl242
-rw-r--r--soundserver/samplestorage_impl.cc202
-rw-r--r--soundserver/simplesoundserver_impl.cc453
-rw-r--r--soundserver/simplesoundserver_impl.h148
-rw-r--r--soundserver/soundserver.idl294
-rw-r--r--soundserver/soundserver_impl.cc90
-rw-r--r--soundserver/soundserver_impl.h46
-rw-r--r--soundserver/soundserverstartup_impl.cc99
-rw-r--r--soundserver/soundserverstartup_impl.h49
-rw-r--r--soundserver/soundserverv2_impl.cc386
-rw-r--r--soundserver/soundserverv2_impl.h71
-rw-r--r--soundserver/stdoutwriter_impl.cc52
-rw-r--r--soundserver/tradercheck.cc213
-rw-r--r--soundserver/tradercheck.h50
-rw-r--r--soundserver/wavplayobject_impl.cc172
32 files changed, 5406 insertions, 0 deletions
diff --git a/soundserver/FileInputStream.mcopclass b/soundserver/FileInputStream.mcopclass
new file mode 100644
index 0000000..c7b7a20
--- /dev/null
+++ b/soundserver/FileInputStream.mcopclass
@@ -0,0 +1,5 @@
+Interface=Arts::FileInputStream,Arts::InputStream,Arts::SynthModule,Arts::Object
+Author="Stefan Westerfeld <[email protected]>"
+URL="http://www.arts-project.org"
+Language=C++
+Library=libkmedia2.la
diff --git a/soundserver/GSLPlayObject.mcopclass b/soundserver/GSLPlayObject.mcopclass
new file mode 100644
index 0000000..2f7ca0a
--- /dev/null
+++ b/soundserver/GSLPlayObject.mcopclass
@@ -0,0 +1,7 @@
+Interface=Arts::GSLPlayObject,Arts::PitchablePlayObject,Arts::PlayObject,Arts::PlayObject_private,Arts::SynthModule,Arts::Object
+Author="Hans Meine <[email protected]>"
+URL="http://www.arts-project.org/"
+Extension=wav,mp3,ogg
+Language=C++
+Library=libartsgslplayobject.la
+MimeType=audio/wav,audio/x-wav,audio/x-mp3,audio/x-mp1,audio/x-mp2,audio/vorbis,application/ogg
diff --git a/soundserver/Makefile.am b/soundserver/Makefile.am
new file mode 100644
index 0000000..eee67b4
--- /dev/null
+++ b/soundserver/Makefile.am
@@ -0,0 +1,122 @@
+# $Id: Makefile.am 434809 2005-07-15 13:20:54Z mueller $
+
+# necessary for artswrapper
+artsdpath = $(bindir)/artsd
+
+INCLUDES = -I$(top_srcdir)/mcop -I$(top_builddir)/mcop -I$(top_srcdir)/flow \
+ -I$(top_builddir)/flow -DEXECUTE=\"$(artsdpath)\" $(all_includes)
+MCOPINC = -I$(srcdir) -I$(top_srcdir)/flow -I$(top_srcdir)/mcop
+FLOWLIBS = $(top_builddir)/flow/libartsflow.la
+
+####### install idl files
+
+artsincludedir = $(includedir)/arts
+artsinclude_HEADERS = soundserver.h soundserver.idl kmedia2.h kmedia2.idl \
+ artsversion.h
+
+####### libsoundserver_idl, libkmedia2_idl (idl files as library)
+
+lib_LTLIBRARIES = libkmedia2_idl.la libsoundserver_idl.la \
+ libartsgslplayobject.la libartswavplayobject.la libkmedia2.la
+
+AM_LDFLAGS = $(LDFLAGS_AS_NEEDED)
+
+libsoundserver_idl_la_LIBADD = libkmedia2_idl.la \
+ $(top_builddir)/flow/libartsflow_idl.la
+libsoundserver_idl_la_LDFLAGS = -no-undefined -version-info 1:0 $(all_libraries)
+libsoundserver_idl_la_SOURCES = soundserver.cc
+libsoundserver_idl_la_COMPILE_FIRST = soundserver.h
+
+libkmedia2_idl_la_SOURCES = kmedia2.cc
+libkmedia2_idl_la_COMPILE_FIRST = kmedia2.h
+libkmedia2_idl_la_LIBADD = $(top_builddir)/flow/libartsflow.la
+libkmedia2_idl_la_LDFLAGS = -no-undefined -version-info 1:0 $(all_libraries)
+
+libkmedia2_la_SOURCES = fileinputstream_impl.cc stdoutwriter_impl.cc
+libkmedia2_la_LIBADD = libkmedia2_idl.la $(FLOWLIBS)
+libkmedia2_la_LDFLAGS = -no-undefined -version-info 1:0 $(all_libraries)
+libkmedia2_la_COMPILE_FIRST = kmedia2.h ../flow/artsflow.h
+
+libartswavplayobject_la_SOURCES = wavplayobject_impl.cc
+libartswavplayobject_la_LIBADD = $(top_builddir)/mcop/libmcop.la \
+ libsoundserver_idl.la $(FLOWLIBS)
+libartswavplayobject_la_LDFLAGS = -no-undefined -module $(all_libraries)
+libartswavplayobject_la_COMPILE_FIRST = soundserver.h ../flow/artsflow.h
+
+libartsgslplayobject_la_SOURCES = gslplayobject_impl.cc
+libartsgslplayobject_la_LIBADD = $(top_builddir)/mcop/libmcop.la \
+ libsoundserver_idl.la $(FLOWLIBS)
+libartsgslplayobject_la_LDFLAGS = -no-undefined -module $(all_libraries)
+libartsgslplayobject_la_COMPILE_FIRST = soundserver.h ../flow/artsflow.h
+
+###### "real" programs
+
+bin_PROGRAMS = artsd artsplay artscat artswrapper artsshell artsrec
+
+artsd_LDADD = libsoundserver_idl.la $(FLOWLIBS) \
+ $(top_builddir)/mcop_mt/libmcop_mt.la
+artsd_LDFLAGS = $(USE_THREADS)
+artsd_SOURCES = soundserverv2_impl.cc soundserver_impl.cc simplesoundserver_impl.cc artsd.cc cpuusage.cc samplestorage_impl.cc crashhandler.cc soundserverstartup_impl.cc
+artsd_COMPILE_FIRST = soundserver.h artsversion.h
+
+artscat_LDADD = libsoundserver_idl.la $(FLOWLIBS) $(LIBPTHREAD)
+artscat_LDFLAGS = $(USE_THREADS)
+artscat_SOURCES = artscat.cc
+artscat_COMPILE_FIRST = soundserver.h artsversion.h
+
+artsrec_LDADD = libsoundserver_idl.la $(FLOWLIBS) $(LIBPTHREAD)
+artsrec_LDFLAGS = $(USE_THREADS)
+artsrec_SOURCES = artsrec.cc
+artsrec_COMPILE_FIRST = soundserver.h artsversion.h
+
+artsplay_LDADD = libsoundserver_idl.la $(LIBPTHREAD)
+artsplay_LDFLAGS = $(USE_THREADS)
+artsplay_SOURCES = artsplay.cc
+artsplay_COMPILE_FIRST = soundserver.h artsversion.h
+
+artswrapper_SOURCES = artswrapper.c
+artswrapper_CFLAGS = $(KDE_USE_FPIE)
+artswrapper_INCLUDES = -I$(top_builddir) -DEXECUTE=\"$(artsdpath)\"
+artswrapper_LDADD = $(LIBPOSIX4)
+artswrapper_LDFLAGS = $(KDE_USE_PIE)
+
+artsshell_LDADD = libsoundserver_idl.la $(LIBPTHREAD)
+artsshell_LDFLAGS = $(USE_THREADS)
+artsshell_SOURCES = artsshell.cc tradercheck.cc
+artsshell_COMPILE_FIRST = soundserver.h artsversion.h ../flow/artsflow.h
+
+DISTCLEANFILES = soundserver.cc soundserver.h soundserver.mcopclass \
+ soundserver.mcoptype kmedia2.h kmedia2.cc kmedia2.mcopclass kmedia2.mcoptype artsversion.h
+
+####### Build rules
+soundserver.mcoptype: soundserver.h
+soundserver.mcopclass: soundserver.h
+soundserver.cc soundserver.h: $(top_srcdir)/soundserver/soundserver.idl kmedia2.h $(MCOPIDL) ../flow/artsflow.h
+ $(MCOPIDL) -t $(MCOPINC) $(top_srcdir)/soundserver/soundserver.idl
+
+kmedia2.mcoptype: kmedia2.h
+kmedia2.mcopclass: kmedia2.h
+kmedia2.cc kmedia2.h: $(top_srcdir)/soundserver/kmedia2.idl $(MCOPIDL) ../flow/artsflow.h
+ $(MCOPIDL) -t $(MCOPINC) $(top_srcdir)/soundserver/kmedia2.idl
+
+
+artsversion.h: artsversion-new.h
+ (cmp -s artsversion-new.h artsversion.h \
+ || cp artsversion-new.h artsversion.h)
+
+if HAVE_WAVSUPPORT
+WAV_MCOPCLASS = WavPlayObject.mcopclass
+endif
+
+mcopclassdir = $(libdir)/mcop/Arts
+mcopclass_DATA = FileInputStream.mcopclass $(WAV_MCOPCLASS) GSLPlayObject.mcopclass
+
+#install-exec-hook:
+# @(chown root $(DESTDIR)$(bindir)/artswrapper && chmod 4755 $(DESTDIR)$(bindir)/artswrapper) \
+# || echo "please make $(DESTDIR)$(bindir)/artswrapper suid root"
+
+######## install idl typeinfo files
+
+mcoptypedir = $(libdir)/mcop
+mcoptype_DATA = soundserver.mcoptype soundserver.mcopclass \
+ kmedia2.mcoptype kmedia2.mcopclass
diff --git a/soundserver/WavPlayObject.mcopclass b/soundserver/WavPlayObject.mcopclass
new file mode 100644
index 0000000..1f25805
--- /dev/null
+++ b/soundserver/WavPlayObject.mcopclass
@@ -0,0 +1,7 @@
+Interface=Arts::WavPlayObject,Arts::PlayObject,Arts::PlayObject_private,Arts::SynthModule,Arts::Object
+Author="Stefan Westerfeld <[email protected]>"
+URL="http://www.arts-project.org"
+Extension=wav
+Language=C++
+Library=libartswavplayobject.la
+MimeType=audio/wav,audio/x-wav
diff --git a/soundserver/artscat.cc b/soundserver/artscat.cc
new file mode 100644
index 0000000..28b1aaf
--- /dev/null
+++ b/soundserver/artscat.cc
@@ -0,0 +1,244 @@
+ /*
+
+ Copyright (C) 2000 Stefan Westerfeld
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ Permission is also granted to link this program with the Qt
+ library, treating Qt like a library that normally accompanies the
+ operating system kernel, whether or not that is in fact the case.
+
+ */
+
+#include "soundserver.h"
+#include "stdsynthmodule.h"
+#include "artsversion.h"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <iostream>
+#include <cstdlib>
+
+using namespace std;
+using namespace Arts;
+
+int cfgSamplingRate = 44100;
+int cfgBits = 16;
+int cfgChannels = 2;
+string cfgTitle = "artscat";
+
+class Sender : public ByteSoundProducerV2_skel,
+ public StdSynthModule,
+ public IONotify
+{
+protected:
+ FILE *pfile;
+ int pfd;
+ int packets;
+ bool waiting;
+ queue< DataPacket<mcopbyte>* > wqueue;
+public:
+ Sender(FILE *input,float minStreamBufferTime) : pfile(input), waiting(false)
+ {
+ pfd = fileno(pfile);
+
+ int rc = fcntl(pfd, F_SETFL, O_NONBLOCK);
+ assert(rc != -1);
+
+ /*
+ * calculate stream buffer parameters
+ */
+ float streamBufferTime;
+ packets = 7;
+ do {
+ packets++;
+ streamBufferTime = (float)(packets * packetCapacity * 1000)
+ / (float)(samplingRate() * channels() * 2);
+ } while(streamBufferTime < minStreamBufferTime);
+ }
+
+ ~Sender()
+ {
+ if(waiting) Dispatcher::the()->ioManager()->remove(this,IOType::read);
+ }
+
+ long samplingRate() { return cfgSamplingRate; }
+ long channels() { return cfgChannels; }
+ long bits() { return cfgBits; }
+ bool finished() { return (pfile == 0); }
+ string title() { return cfgTitle; }
+
+ void streamStart()
+ {
+ /*
+ * start streaming
+ */
+ outdata.setPull(packets, packetCapacity);
+ }
+
+ enum { packetCapacity = 4096 };
+ void handle_eof()
+ {
+ /*
+ * cleanup
+ */
+ outdata.endPull();
+ pclose(pfile);
+ pfile = 0;
+ pfd = 0;
+
+ /*
+ * remove all packets from the wqueue
+ */
+ while(!wqueue.empty())
+ {
+ DataPacket<mcopbyte> *packet = wqueue.front();
+ packet->size = 0;
+ packet->send();
+ wqueue.pop();
+ }
+
+ /*
+ * go out of the waiting mode
+ */
+ if(waiting)
+ {
+ Dispatcher::the()->ioManager()->remove(this,IOType::read);
+ waiting = false;
+ }
+
+ /*
+ * terminate dispatcher to end the program
+ */
+ Dispatcher::the()->terminate();
+ }
+ void request_outdata(DataPacket<mcopbyte> *packet)
+ {
+ if(!waiting)
+ {
+ packet->size = read(pfd, packet->contents, packetCapacity);
+ if(packet->size > 0)
+ {
+ // got something - done here
+ packet->send();
+ return;
+ }
+ Dispatcher::the()->ioManager()->watchFD(pfd,IOType::read,this);
+ waiting = true;
+ }
+
+ wqueue.push(packet);
+ }
+
+ void notifyIO(int,int)
+ {
+ assert(waiting);
+
+ DataPacket<mcopbyte> *packet = wqueue.front();
+ packet->size = read(pfd, packet->contents, packetCapacity);
+ assert(packet->size >= 0);
+ if(packet->size == 0) {
+ handle_eof();
+ return;
+ }
+ packet->send();
+
+ wqueue.pop();
+
+ if(wqueue.empty())
+ {
+ Dispatcher::the()->ioManager()->remove(this,IOType::read);
+ waiting = false;
+ }
+ }
+};
+
+static void exitUsage(const char *progname)
+{
+ fprintf(stderr,"usage: %s [ options ] [ <filename> ]\n",progname);
+ fprintf(stderr,"-r <samplingrate> set samplingrate to use\n");
+ fprintf(stderr,"-b <bits> set number of bits (8 or 16)\n");
+ fprintf(stderr,"-c <channels> set number of channels (1 or 2)\n");
+ fprintf(stderr,"-t <title> set stream title\n");
+ fprintf(stderr,"-v show version\n");
+ fprintf(stderr,"-h display this help and exit\n");
+ exit(1);
+}
+
+int main(int argc, char **argv)
+{
+ bool titleSetByUser = false;
+ int optch;
+ while((optch = getopt(argc,argv,"r:b:c:t:hv")) > 0)
+ {
+ switch(optch)
+ {
+ case 'r':
+ cfgSamplingRate = atoi(optarg);
+ break;
+ case 'b':
+ cfgBits = atoi(optarg);
+ break;
+ case 'c':
+ cfgChannels = atoi(optarg);
+ break;
+ case 't':
+ cfgTitle = optarg;
+ titleSetByUser = true;
+ break;
+ case 'v':
+ printf("artscat %s\n",ARTS_VERSION);
+ exit(0);
+ break;
+ case 'h':
+ default:
+ exitUsage(argc?argv[0]:"artscat");
+ break;
+ }
+ }
+
+ FILE *infile = stdin;
+
+ if (optind < argc)
+ {
+ string filename = argv[optind];
+ if(filename != "-")
+ {
+ infile = fopen(filename.c_str(),"r");
+ if(!infile)
+ {
+ cerr << "Can't open file '" << argv[optind] << "'." << endl;
+ exit(1);
+ }
+ if(!titleSetByUser) cfgTitle = filename;
+ }
+ }
+ Dispatcher dispatcher;
+ SimpleSoundServer server(Reference("global:Arts_SimpleSoundServer"));
+
+ if(server.isNull())
+ {
+ cerr << "Can't connect to sound server" << endl;
+ return 1;
+ }
+
+ ByteSoundProducerV2 sender = ByteSoundProducerV2::_from_base(new Sender(infile,server.minStreamBufferTime()));
+ server.attach(sender);
+ sender.start();
+ dispatcher.run();
+ server.detach(sender);
+}
diff --git a/soundserver/artsd.cc b/soundserver/artsd.cc
new file mode 100644
index 0000000..e02529e
--- /dev/null
+++ b/soundserver/artsd.cc
@@ -0,0 +1,376 @@
+ /*
+
+ Copyright (C) 2000-2001 Stefan Westerfeld
+ 2003 Arnold Krille <[email protected]
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ Permission is also granted to link this program with the Qt
+ library, treating Qt like a library that normally accompanies the
+ operating system kernel, whether or not that is in fact the case.
+
+ */
+
+#include "mcoputils.h"
+#include <signal.h>
+#include <math.h>
+#include <iostream>
+#include <cstring>
+#include <cstdlib>
+#include <stdio.h>
+#include "soundserver.h"
+#include "audiosubsys.h"
+#include "audioio.h"
+#include "tcpserver.h"
+#include "cpuusage.h"
+#include "debug.h"
+#include "artsversion.h"
+#include "crashhandler.h"
+
+using namespace std;
+using namespace Arts;
+
+extern "C" void stopServer(int)
+{
+ Dispatcher::the()->terminate();
+}
+
+static void initSignals()
+{
+ signal(SIGHUP ,stopServer);
+ signal(SIGINT ,stopServer);
+ signal(SIGTERM,stopServer);
+}
+
+static void exitUsage(const char *progname)
+{
+ fprintf(stderr,"usage: %s [ options ]\n",progname);
+ fprintf(stderr,"\n");
+ fprintf(stderr,"server/network options:\n");
+ fprintf(stderr,"-n enable network transparency\n");
+ fprintf(stderr,"-p <port> set TCP port to use (implies -n)\n");
+ fprintf(stderr,"-u public, no authentication (dangerous!)\n");
+ fprintf(stderr,"-N use larger network buffers\n");
+ fprintf(stderr,"-w <n> increase network buffers by factor of <n>\n");
+ fprintf(stderr,"\n");
+ fprintf(stderr,"audio options:\n");
+ fprintf(stderr,"-a <audioiomethod> select audio i/o method (oss, alsa, ...)\n");
+ fprintf(stderr,"-r <samplingrate> set samplingrate to use\n");
+ fprintf(stderr,"-b <bits> set number of bits (8 or 16)\n");
+ fprintf(stderr,"-d enable full duplex operation\n");
+ fprintf(stderr,"-V <volume>[dB] set output volume\n");
+ fprintf(stderr,"-D <devicename> audio device (usually /dev/dsp)\n");
+ fprintf(stderr,"-F <fragments> number of fragments\n");
+ fprintf(stderr,"-S <size> fragment size in bytes\n");
+ fprintf(stderr,"-s <seconds> auto-suspend time in seconds\n");
+ fprintf(stderr,"-f force starting artsd (if no soundcard is there, uses the null output device)\n");
+ fprintf(stderr,"\n");
+ fprintf(stderr,"misc options:\n");
+ fprintf(stderr,"-h display this help and exit\n");
+ fprintf(stderr,"-A list possible audio i/o methods (for -a)\n");
+ fprintf(stderr,"-v show version\n");
+ fprintf(stderr,"-l <level> information level\n");
+ fprintf(stderr," 3: quiet, 2: warnings, 1: info, 0: debug\n");
+ fprintf(stderr,"-m <appName> application to display messages\n");
+ fprintf(stderr,"-c <appName> application to display crash dialogs\n");
+ exit(1);
+}
+
+static void exitListAudioIO()
+{
+ fprintf(stderr,"possible choices for the audio i/o method:\n");
+ fprintf(stderr,"\n");
+
+ for(int i = 0; i < AudioIO::queryAudioIOCount(); i++)
+ {
+ fprintf(stderr, " %-10s%s\n",
+ AudioIO::queryAudioIOParamStr(i, AudioIO::name),
+ AudioIO::queryAudioIOParamStr(i, AudioIO::fullName));
+ }
+ fprintf(stderr,"\n");
+ exit(0);
+}
+
+static Dispatcher::StartServer cfgServers = Dispatcher::startUnixServer;
+static int cfgSamplingRate = 0;
+static int cfgBits = 0;
+static int cfgFragmentCount= 0;
+static int cfgFragmentSize = 0;
+static int cfgPort = 0;
+static int cfgDebugLevel = 2;
+static const char *cfgDebugApp = 0;
+static const char *cfgCrashApp = 0;
+static bool cfgFullDuplex = 0;
+static bool cfgForceStart = 0;
+static const char *cfgDeviceName = 0;
+#if defined(__osf__)
+// osf/1 does not have sound devices so avoid the default auto-detect
+static const char *cfgAudioIO = "null";
+#else
+static const char *cfgAudioIO = 0;
+#endif
+static int cfgAutoSuspend = 0;
+static int cfgBuffers = 0;
+static float cfgVolume = 0.0;
+
+static bool cmdListAudioIO = false;
+
+static void handleArgs(int argc, char **argv)
+{
+ int optch;
+ while((optch = getopt(argc,argv,"r:p:nuF:S:hD:dl:a:Ab:s:m:vNw:fV:c:")) > 0)
+ {
+ switch(optch)
+ {
+ case 'p': cfgPort = atoi(optarg); // setting a port => network transparency
+ case 'n': cfgServers = static_cast<Dispatcher::StartServer>( cfgServers | Dispatcher::startTCPServer);
+ break;
+ case 'a': cfgAudioIO = optarg;
+ break;
+ case 'r': cfgSamplingRate = atoi(optarg);
+ break;
+ case 'b': cfgBits = atoi(optarg);
+ break;
+ case 's': cfgAutoSuspend = atoi(optarg);
+ break;
+ case 'F': cfgFragmentCount = atoi(optarg);
+ break;
+ case 'S': cfgFragmentSize = atoi(optarg);
+ break;
+ case 'D': cfgDeviceName = optarg;
+ break;
+ case 'd': cfgFullDuplex = true;
+ break;
+ case 'l': cfgDebugLevel = atoi(optarg);
+ break;
+ case 'm': cfgDebugApp = optarg;
+ break;
+ case 'c': cfgCrashApp = optarg;
+ break;
+ case 'u': cfgServers = static_cast<Dispatcher::StartServer>( cfgServers | Dispatcher::noAuthentication);
+ break;
+ case 'A': cmdListAudioIO = true;
+ break;
+ case 'v': printf("artsd %s\n",ARTS_VERSION);
+ exit(0);
+ break;
+ case 'N': cfgBuffers = 5;
+ break;
+ case 'w': cfgBuffers = atoi(optarg);
+ break;
+ case 'f': cfgForceStart = true;
+ break;
+ case 'V':
+ if ( strstr( optarg,"dB" )&& strlen( strstr( optarg,"dB" ) )==2 ) {
+ char* val = ( char* )calloc( strlen( optarg )-1, sizeof( char ) );
+ strncpy( val, optarg, strlen( optarg )-2 );
+ cfgVolume = pow( 10, atof( val )/20.0 );
+ free( val );
+ } else cfgVolume = atof( optarg );
+ break;
+ case 'h':
+ default:
+ exitUsage(argc?argv[0]:"artsd");
+ break;
+ }
+ }
+}
+
+static bool publishReferences(SoundServerV2 server,
+ AudioManager audioManager,
+ bool silent)
+{
+ ObjectManager *om = ObjectManager::the();
+ bool result;
+
+ result=om->addGlobalReference(server,"Arts_SoundServerV2")
+ && om->addGlobalReference(server,"Arts_SoundServer")
+ && om->addGlobalReference(server,"Arts_SimpleSoundServer")
+ && om->addGlobalReference(server,"Arts_PlayObjectFactory")
+ && om->addGlobalReference(audioManager,"Arts_AudioManager");
+
+ if(!result && !silent)
+ {
+ cerr <<
+"Error: Can't add object reference (probably artsd is already running)."
+ << endl <<
+" If you are sure it is not already running, remove the relevant files:"
+ << endl << endl <<
+" "<< MCOPUtils::createFilePath("Arts_SoundServerV2") << endl <<
+" "<< MCOPUtils::createFilePath("Arts_SoundServer") << endl <<
+" "<< MCOPUtils::createFilePath("Arts_SimpleSoundServer") << endl <<
+" "<< MCOPUtils::createFilePath("Arts_PlayObjectFactory") << endl <<
+" "<< MCOPUtils::createFilePath("Arts_AudioManager") << endl << endl;
+ }
+ return result;
+}
+
+static int cleanReference(const string& reference)
+{
+ Object test;
+ test = Reference("global:"+reference);
+ if(test.isNull())
+ {
+ Dispatcher::the()->globalComm().erase(reference);
+ return 1;
+ }
+ else
+ return 0;
+}
+
+static void cleanUnusedReferences()
+{
+ int i = 0;
+
+ cerr << "There are already artsd objects registered, "
+ "looking if they are active..." << endl;
+
+ sleep(1); // maybe an artsd process has just started (give it some time)
+
+ i += cleanReference("Arts_SoundServerV2");
+ i += cleanReference("Arts_SoundServer");
+ i += cleanReference("Arts_SimpleSoundServer");
+ i += cleanReference("Arts_PlayObjectFactory");
+ i += cleanReference("Arts_AudioManager");
+
+ if(i)
+ cerr << "... cleaned " <<i<< " unused mcop global references." << endl;
+ cerr << endl;
+}
+
+int main(int argc, char **argv)
+{
+ handleArgs(argc, argv);
+
+ Debug::init("[artsd]", static_cast<Debug::Level>(cfgDebugLevel));
+
+ arts_debug("artsd version is %s",ARTS_VERSION);
+ if (cfgDebugApp)
+ Debug::messageApp(cfgDebugApp);
+ if (cfgCrashApp)
+ {
+ CrashHandler::setCrashHandler(CrashHandler::defaultCrashHandler);
+ CrashHandler::setCrashApp(cfgCrashApp);
+ CrashHandler::setApplicationName("artsd");
+ CrashHandler::setApplicationPath(argc ? argv[0] : "artsd");
+ CrashHandler::setApplicationVersion (ARTS_VERSION);
+ CrashHandler::setProgramName("Soundserver");
+ CrashHandler::setBugAddress("[email protected]");
+ }
+
+ if(cfgPort) TCPServer::setPort(cfgPort);
+
+ CPUUsage cpuUsage;
+ Dispatcher dispatcher(0,cfgServers);
+ string warnNullDevice;
+
+ initSignals();
+
+ /* execute commands, if any */
+ if(cmdListAudioIO) exitListAudioIO();
+
+ /* apply configuration */
+ if(cfgAudioIO) AudioSubSystem::the()->audioIO(cfgAudioIO);
+ if(cfgSamplingRate) AudioSubSystem::the()->samplingRate(cfgSamplingRate);
+ if(cfgFragmentCount) AudioSubSystem::the()->fragmentCount(cfgFragmentCount);
+ if(cfgFragmentSize) AudioSubSystem::the()->fragmentSize(cfgFragmentSize);
+ if(cfgFullDuplex) AudioSubSystem::the()->fullDuplex(cfgFullDuplex);
+ if(cfgDeviceName) AudioSubSystem::the()->deviceName(cfgDeviceName);
+ if(cfgBits) AudioSubSystem::the()->format(cfgBits);
+
+ SoundServerStartup startup;
+ startup.lock();
+
+ if(cfgForceStart && !AudioSubSystem::the()->check())
+ {
+ warnNullDevice = "Error while initializing the sound driver:\n";
+ warnNullDevice += AudioSubSystem::the()->error();
+ warnNullDevice += "\n\nThe sound server will continue, using the null output device.";
+
+ AudioSubSystem::the()->audioIO("null");
+ }
+
+ if(!AudioSubSystem::the()->check())
+ {
+ string msg = "Error while initializing the sound driver:\n";
+ msg += AudioSubSystem::the()->error();
+ arts_fatal("%s", msg.c_str());
+ exit(1);
+ }
+
+ /* start sound server implementation */
+ SoundServerV2 server;
+ AudioManager audioManager;
+
+ if (fabs(cfgVolume) > 1e-10)
+ server.outVolume().scaleFactor(cfgVolume);
+
+ if (cfgAutoSuspend)
+ server.autoSuspendSeconds(cfgAutoSuspend);
+
+ if (cfgBuffers)
+ server.bufferSizeMultiplier(cfgBuffers);
+
+ /* make global MCOP references available */
+ if(!publishReferences(server,audioManager,true))
+ {
+ cleanUnusedReferences();
+ if(!publishReferences(server,audioManager,false)) return 1;
+ }
+
+ startup.unlock();
+
+ /* warn if we are using the null device */
+ if (!warnNullDevice.empty())
+ {
+ /* hack to get this message to the user in any case */
+ Debug::init("[artsd]", static_cast<Debug::Level>(1));
+ arts_info("%s", warnNullDevice.c_str());
+ Debug::init("[artsd]", static_cast<Debug::Level>(cfgDebugLevel));
+ }
+
+ /* warn if there was a problem with artswrapper */
+ char *wrapper = getenv("STARTED_THROUGH_ARTSWRAPPER");
+ if (wrapper && !strcmp(wrapper, "2"))
+ arts_warning(
+ "Can't set real-time scheduling priority.\n"
+ "You need to run artswrapper as root or\n"
+ "setuid root. This means that you will\n"
+ "likely not be able to produce acceptable\n"
+ "sound (i.e. without clicks and breaks).");
+
+ if (wrapper && !strcmp(wrapper, "3"))
+ arts_warning(
+ "This system has no support for real-time\n"
+ "scheduling priority. This means that you\n"
+ "will likely not be able to produce acceptable\n"
+ "sound (i.e. without clicks and breaks).");
+
+ dispatcher.run();
+ return 0;
+}
+
+#ifdef __SUNPRO_CC
+/* See bottom of simplesoundserver_impl.cc for the reason this is here. */
+#include "simplesoundserver_impl.h"
+REGISTER_IMPLEMENTATION(SimpleSoundServer_impl);
+#include "soundserver_impl.h"
+REGISTER_IMPLEMENTATION(SoundServer_impl);
+#include "soundserverv2_impl.h"
+REGISTER_IMPLEMENTATION(SoundServerV2_impl);
+#include "soundserverstartup_impl.h"
+REGISTER_IMPLEMENTATION(SoundServerStartup_impl);
+#endif
diff --git a/soundserver/artsplay.cc b/soundserver/artsplay.cc
new file mode 100644
index 0000000..22b500a
--- /dev/null
+++ b/soundserver/artsplay.cc
@@ -0,0 +1,77 @@
+ /*
+
+ Copyright (C) 1999 Stefan Westerfeld
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ Permission is also granted to link this program with the Qt
+ library, treating Qt like a library that normally accompanies the
+ operating system kernel, whether or not that is in fact the case.
+ */
+
+#include "soundserver.h"
+#include "artsversion.h"
+
+#include <sys/param.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <vector>
+#include <string>
+#include <cstring>
+#include <iostream>
+
+using namespace std;
+using namespace Arts;
+
+/* returns the absolute path to a filename */
+static string absolutePath(const string& path)
+{
+ if(path[0] == '/') return path;
+
+ char buffer[PATH_MAX];
+ getcwd(buffer,PATH_MAX);
+
+ if(buffer[strlen(buffer)-1] == '/')
+ return buffer + path;
+ else
+ return string(buffer) + '/' + path;
+}
+
+int main(int argc, char **argv)
+{
+ if(argc != 2)
+ {
+ cerr << "usage: " << argv[0] << " <filename>" << endl;
+ return 1;
+ }
+ if(strcmp(argv[1],"-v") == 0)
+ {
+ cout << "artsplay " << ARTS_VERSION << endl;
+ return 0;
+ }
+
+ Dispatcher dispatcher;
+ SimpleSoundServer server;
+ server = Arts::Reference("global:Arts_SimpleSoundServer");
+
+ if(server.isNull())
+ {
+ cerr << "Can't connect to sound server" << endl;
+ return 1;
+ }
+ return server.play(absolutePath(argv[1])) != 0;
+}
diff --git a/soundserver/artsrec.cc b/soundserver/artsrec.cc
new file mode 100644
index 0000000..81e01fe
--- /dev/null
+++ b/soundserver/artsrec.cc
@@ -0,0 +1,194 @@
+ /*
+
+ Copyright (C) 2000 Stefan Westerfeld
+ 2001 Matthias Kretz
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ Permission is also granted to link this program with the Qt
+ library, treating Qt like a library that normally accompanies the
+ operating system kernel, whether or not that is in fact the case.
+
+ */
+
+#include "soundserver.h"
+#include "stdsynthmodule.h"
+#include "artsversion.h"
+#include "debug.h"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <iostream>
+#include <cstdlib>
+
+using namespace std;
+using namespace Arts;
+
+int cfgSamplingRate = 44100;
+int cfgBits = 16;
+int cfgChannels = 2;
+
+class Receiver : public ByteSoundReceiver_skel,
+ public StdSynthModule,
+ public IONotify
+{
+protected:
+ FILE *pfile;
+ int pfd;
+ int packets;
+ bool waiting;
+ queue< DataPacket<mcopbyte>* > wqueue;
+public:
+ Receiver(FILE *input,float minStreamBufferTime) : pfile(input), waiting(false)
+ {
+ pfd = fileno(pfile);
+
+ int rc = fcntl(pfd, F_SETFL, O_NONBLOCK);
+ assert(rc != -1);
+
+ /*
+ * calculate stream buffer parameters
+ */
+ float streamBufferTime;
+ packets = 7;
+ do {
+ packets++;
+ streamBufferTime = (float)(packets * packetCapacity * 1000)
+ / (float)(samplingRate() * channels() * 2);
+ } while(streamBufferTime < minStreamBufferTime);
+ }
+
+ ~Receiver()
+ {
+ if(waiting) Dispatcher::the()->ioManager()->remove(this,IOType::write);
+ }
+
+ long samplingRate() { return cfgSamplingRate; }
+ long channels() { return cfgChannels; }
+ long bits() { return cfgBits; }
+ bool finished() { return (pfile == 0); }
+ string title() { return "artsrec"; }
+
+ enum { packetCapacity = 4096 };
+ void process_indata(DataPacket<mcopbyte> *packet)
+ {
+ if(!waiting)
+ {
+ int size = write( pfd, packet->contents, packet->size );
+ if( size != packet->size ) {
+ arts_debug( "Not written enough" );
+ //Dispatcher::the()->ioManager()->watchFD(pfd,IOType::write,this);
+ //waiting = true;
+ }
+ packet->processed();
+ return;
+ }
+
+ wqueue.push(packet);
+ }
+
+ void notifyIO(int,int)
+ {
+#if 0
+ assert(waiting);
+
+ DataPacket<mcopbyte> *packet = wqueue.front();
+ packet->size = read(pfd, packet->contents, packetCapacity);
+ assert(packet->size >= 0);
+ if(packet->size == 0) {
+ return;
+ }
+ packet->send();
+
+ wqueue.pop();
+
+ if(wqueue.empty())
+ {
+ Dispatcher::the()->ioManager()->remove(this,IOType::write);
+ waiting = false;
+ }
+#endif
+ }
+};
+
+static void exitUsage(const char *progname)
+{
+ fprintf(stderr,"usage: %s [ options ] [ <filename> ]\n",progname);
+ fprintf(stderr,"-r <samplingrate> set samplingrate to use\n");
+ fprintf(stderr,"-b <bits> set number of bits (8 or 16)\n");
+ fprintf(stderr,"-c <channels> set number of channels (1 or 2)\n");
+ fprintf(stderr,"-v show version\n");
+ fprintf(stderr,"-h display this help and exit\n");
+ exit(1);
+}
+
+int main(int argc, char **argv)
+{
+ int optch;
+ while((optch = getopt(argc,argv,"r:b:c:hv")) > 0)
+ {
+ switch(optch)
+ {
+ case 'r': cfgSamplingRate = atoi(optarg);
+ break;
+ case 'b': cfgBits = atoi(optarg);
+ break;
+ case 'c': cfgChannels = atoi(optarg);
+ break;
+ case 'v':
+ printf("artsrec %s\n",ARTS_VERSION);
+ exit(0);
+ break;
+ case 'h':
+ default:
+ exitUsage(argc?argv[0]:"artsrec");
+ break;
+ }
+ }
+
+ FILE *outfile = stdout;
+
+ if (optind < argc)
+ {
+ string filename = argv[optind];
+ if(filename != "-")
+ {
+ outfile = fopen(filename.c_str(),"w");
+ if(!outfile)
+ {
+ cerr << "Can't open file '" << argv[optind] << "'." << endl;
+ exit(1);
+ }
+ }
+ }
+ Dispatcher dispatcher;
+ SimpleSoundServer server(Reference("global:Arts_SimpleSoundServer"));
+
+ if(server.isNull())
+ {
+ cerr << "Can't connect to sound server" << endl;
+ return 1;
+ }
+
+ ByteSoundReceiver receiver = ByteSoundReceiver::_from_base(new Receiver(outfile,server.minStreamBufferTime()));
+ server.attachRecorder(receiver);
+ receiver.start();
+ dispatcher.run();
+ receiver.stop();
+ server.detachRecorder(receiver);
+}
diff --git a/soundserver/artsshell.cc b/soundserver/artsshell.cc
new file mode 100644
index 0000000..21257b2
--- /dev/null
+++ b/soundserver/artsshell.cc
@@ -0,0 +1,671 @@
+/*
+
+ Copyright (C) 2000-2001 Jeff Tranter
+
+ Stefan Westerfeld
+
+ 2002 Matthias Kretz <[email protected]>
+ 2003 Arnold Krille <[email protected]>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+------------------------------------------------------------------------
+
+ artsshell - command line access to aRts functions
+
+This is a utility (similar to what was in aRts 0.3) to provide command
+line access to aRts functions.
+
+usage:
+
+artsshell [options] <command> [command-options]
+
+Options:
+
+-q - suppress all output.
+-h - display command usage.
+-v - show version.
+
+Commands:
+
+help, ?
+
+Show brief summary of commands.
+
+version
+
+Display aRts version.
+
+suspend
+
+This suspends the sound server if it is not in use. It can be useful
+when running programs that are not artsd-enabled, although in most
+cases you should try to use artsdsp first.
+
+Returns zero status if successful or if the sound server was already
+suspended, 1 if unable to connect to the sound server (e.g. it is not
+running), and 2 if unable to suspend the sound server (e.g. it is
+being used by an application). The sound server will attempt to start
+again if it receives any requests.
+
+status
+
+Display status information about the server, i.e. is it running, is it
+suspended, real-time status, etc.
+
+terminate
+
+Terminate the sound server.
+
+autosuspend <seconds>
+
+Sets the autosuspend time for the specified number of seconds. A value
+of zero disables the autosuspend feature.
+
+networkbuffers <n>
+
+When running artsd over a network connection a large buffer size is
+desirable to avoid dropouts. This command allows increasing the buffer
+size by a factor of <n> from the default.
+
+stereoeffect insert [top|bottom] <module name>
+stereoeffect remove <id>
+stereoeffect list
+
+Inserts or removes a stereo effect into the stereo effect stack. The
+list option lists all stereo effects. When inserting, returns an
+identifier that can be used for removing it. It can be installed at
+the top or the bottom (the default).
+
+e.g. stereoeffect insert bottom Arts::Synth_FREEVERB
+ stereoeffect remove 1
+ stereoeffect list
+
+midi (future)
+
+When the MIDI manager functionality is all implemented this would be a
+good place to implement the commands. Possible options include:
+
+midi list
+midi route <from> <to>
+
+execute (future)
+
+Being able to load a module into the sound server from the command
+line would be useful. The relevant code could be taken from
+artbuilder. Suggested command format:
+
+execute <module.arts>
+
+*/
+
+#include <unistd.h>
+#include <iostream>
+#include "soundserver.h"
+#include "dispatcher.h"
+#include "artsversion.h"
+#include "tradercheck.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <cstring>
+#include <math.h>
+
+bool quiet = false;
+const char *filename = 0;
+
+using namespace std;
+
+const char* commandsmessage = "\
+Commands:\n\
+ suspend - suspend sound server\n\
+ status - display sound server status information\n\
+ terminate - terminate sound server (might confuse/kill apps using it)\n\
+ autosuspend <secs> - set autosuspend time\n\
+ networkbuffers <n> - increase network buffers by a factor of <n>\n\
+ volume [<volume>] - display/set the volume of the soundserver\n\
+ volumedb [<volume>] - display/set the volume in dB (decibel).\n\
+ 6dB means amplification by 2\n\
+ Note: If you want to use negative values from the commandline\n\
+ you have to use:\n\
+ artsshell -- volumedb -NN\n\
+ since otherwise the negative value gets lost...\n\
+ stereoeffect insert [top|bottom] <name>\n\
+ - insert stereo effect\n\
+ stereoeffect remove <id>\n\
+ - remove stereo effect\n\
+ stereoeffect list - list available effects\n\
+ tradercheck - check consistency of the trader data (information\n\
+ stored in mcopclass/mcoptype files)\n\
+ traderquery [ <requirement1> <requirement2> ... <requirementN> ]\n\
+ - query the trader for implementations with given requirements\n\
+ version - show sound server version\n\
+ help, ? - show commands\n";
+
+void help()
+{
+ cout << commandsmessage << endl;
+}
+
+// Display command usage and exit
+void usage()
+{
+ cerr <<
+"usage: artsshell [options] <command> [command-options]\n\
+\n\
+Options:\n\
+ -q - suppress all output\n\
+ -h - display command usage\n\
+ -v - show version\n\
+ -f <filename> - execute commands from <filename>\n\n" << commandsmessage << endl;
+ exit(0);
+}
+
+// Parse command line options
+void parseOptions(int argc, char **argv)
+{
+ int optch;
+
+ if (argc == 0)
+ usage();
+
+ while((optch = getopt(argc, argv, "qhvf:1234567890")) > 0)
+ {
+ switch(optch)
+ {
+ case 'q': quiet = true;
+ break;
+ case 'v':
+ printf("artsshell %s\n", ARTS_VERSION);
+ exit(0);
+ break;
+ case 'h':
+ usage();
+ break;
+ case 'f':
+ filename = optarg;
+ break;
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case '0':
+ break;
+ default:
+ usage();
+ break;
+ }
+ }
+}
+
+
+// Suspend sound server, if possible
+int suspend(Arts::SoundServer server)
+{
+ switch (server.secondsUntilSuspend())
+ {
+ case 0:
+ if (!quiet)
+ cerr << "sound server was already suspended" << endl;
+ return 0;
+ break;
+
+ case -1:
+ if (!quiet)
+ cerr << "sound server is busy" << endl;
+ return 2;
+ break;
+
+ default:
+ if (server.suspend() == true)
+ {
+ if (!quiet)
+ cerr << "sound server suspended" << endl;
+ return 0;
+ } else {
+ if (!quiet)
+ cerr << "unable to suspend sound server" << endl;
+ return 2;
+ }
+ }
+ return 0;
+}
+
+
+// Display server status information
+void status(Arts::SoundServerV2 server)
+{
+ Arts::RealtimeStatus rtStatus = server.realtimeStatus();
+ long seconds = server.secondsUntilSuspend();
+
+ cout << "server status: ";
+ switch (seconds)
+ {
+ case -1:
+ cout << "busy" << endl;
+ break;
+ case 0:
+ cout << "suspended" << endl;
+ break;
+ case -2:
+ cout << "running, autosuspend disabled" << endl;
+ break;
+ default:
+ cout << "running, will suspend in " << seconds << " s" << endl;
+ }
+
+ cout << "real-time status: ";
+ switch (rtStatus)
+ {
+ case Arts::rtRealtime:
+ cout << "real-time" << endl;
+ break;
+ case Arts::rtNoSupport:
+ cout << "no real-time support" << endl;
+ break;
+ case Arts::rtNoWrapper:
+ cout << "not started through real-time wrapper" << endl;
+ break;
+ case Arts::rtNoRealtime:
+ cout << "not real-time" << endl;
+ break;
+ default:
+ cout << "unknown" << endl;
+ break;
+ }
+
+ cout << "server buffer time: " << server.serverBufferTime() << " ms" << endl;
+ cout << "buffer size multiplier: " << server.bufferSizeMultiplier() << endl;
+ cout << "minimum stream buffer time: " << server.minStreamBufferTime() << " ms" << endl;
+ cout << "auto suspend time: " << server.autoSuspendSeconds() << " s" << endl;
+ cout << "audio method: " << server.audioMethod() << endl;
+ cout << "sampling rate: " << server.samplingRate() << endl;
+ cout << "channels: " << server.channels() << endl;
+ cout << "sample size: " << server.bits() << " bits" << endl;
+
+ if (server.fullDuplex())
+ cout << "duplex: full" << endl;
+ else
+ cout << "duplex: half" << endl;
+ cout << "device: " << server.audioDevice() << endl;
+ cout << "fragments: " << server.fragments() << endl;
+ cout << "fragment size: " << server.fragmentSize() << endl;
+}
+
+// terminate the sound server
+void terminate(Arts::SoundServer server)
+{
+ if(server.terminate())
+ {
+ cout << "sound server terminated" << endl;
+ return;
+ }
+ else
+ {
+ cout << "there were problems terminating the sound server" << endl;
+ return;
+ }
+}
+
+
+// set autosuspend time
+void autosuspend(Arts::SoundServerV2 server, int secs)
+{
+ server.autoSuspendSeconds(secs);
+}
+
+// set network buffers size
+void networkBuffers(Arts::SoundServerV2 server, int n)
+{
+ if (n > 0)
+ server.bufferSizeMultiplier(n);
+}
+
+// set the output volume
+void setVolume(Arts::SoundServerV2 server, float volume)
+{
+ server.outVolume().scaleFactor(volume);
+}
+
+// return the output volume
+float getVolume(Arts::SoundServerV2 server)
+{
+ return server.outVolume().scaleFactor();
+}
+
+// set the output volume in dB
+void setDecibelVolume( Arts::SoundServerV2 server, float volume )
+{
+ setVolume( server, pow( 10, volume/20 ) );
+}
+
+// get the output volume in dB
+float getDecibelVolume( Arts::SoundServerV2 server )
+{
+ return 20*log10( getVolume( server ) );
+}
+
+// stereoeffect command
+void stereoEffect(Arts::SoundServerV2 server, int argc, char **argv)
+{
+ // stereoeffect list
+ if (!strcmp(argv[0], "list"))
+ {
+ Arts::TraderQuery query = Arts::DynamicCast( server.createObject( "Arts::TraderQuery" ) );
+ if( query.isNull() )
+ {
+ cerr << "unable to create a query" << endl;
+ return;
+ }
+ query.supports("Interface", "Arts::StereoEffect");
+ vector<Arts::TraderOffer> *offers = query.query();
+ vector<Arts::TraderOffer>::iterator i;
+ for (i = offers->begin(); i != offers->end(); i++)
+ cout << i->interfaceName() << endl;
+ delete offers;
+ return;
+ }
+
+ // stereoeffect insert [top|bottom] <module name>
+ if (!strcmp(argv[0], "insert"))
+ {
+ if (argc < 2 || argc > 3)
+ {
+ cerr << "invalid arguments" << endl;
+ return;
+ }
+
+ bool bottom = true;
+ if (argc == 3)
+ {
+ if (!strcmp(argv[1], "bottom"))
+ bottom = true;
+ else if (!strcmp(argv[1], "top"))
+ bottom = false;
+ else
+ {
+ cerr << "invalid arguments" << endl;
+ return;
+ }
+ }
+
+ char *name;
+ if (argc == 2)
+ name = argv[1];
+ else
+ name = argv[2];
+
+ // first check if the interface exists using the Trader
+ Arts::TraderQuery query = Arts::DynamicCast( server.createObject( "Arts::TraderQuery" ) );
+ if( query.isNull() )
+ {
+ cerr << "unable to create a query" << endl;
+ return;
+ }
+ query.supports("Interface", name);
+ vector<Arts::TraderOffer> *offers = query.query();
+ if (offers->empty())
+ {
+ cerr << "no such interface: " << name << endl;
+ delete offers;
+ return;
+ }
+ delete offers;
+
+ Arts::Object obj = (server.createObject(name));
+ if (obj.isNull())
+ {
+ cerr << "unable to create: " << name << endl;
+ return;
+ }
+ Arts::StereoEffect effect = Arts::DynamicCast(obj);
+ if (effect.isNull())
+ {
+ cerr << "unable to load effect: " << name << endl;
+ return;
+ }
+ effect.start();
+ Arts::StereoEffectStack effectstack = server.outstack();
+ long id;
+ if (bottom)
+ id = effectstack.insertBottom(effect, name);
+ else
+ id = effectstack.insertTop(effect, name);
+ cout << id << endl;
+ return;
+ }
+
+ // stereoeffect remove <id>
+ if (!strcmp(argv[0], "remove"))
+ {
+ if (argc != 2)
+ {
+ cerr << "invalid arguments" << endl;
+ return;
+ }
+ Arts::StereoEffectStack effectstack = server.outstack();
+ long id = atoi(argv[1]);
+ effectstack.remove(id);
+ return;
+ }
+
+ cerr << "invalid arguments" << endl;
+}
+
+// traderquery command
+void traderQuery(Arts::SoundServerV2 server, int argc, char **argv)
+{
+ Arts::TraderQuery query = Arts::DynamicCast( server.createObject( "Arts::TraderQuery" ) );
+ if( query.isNull() )
+ {
+ cerr << "unable to create a query" << endl;
+ return;
+ }
+
+ for(int i=0;i<argc;i++)
+ {
+ char *buffer = strdup(argv[i]);
+ char *key = strtok(buffer,"=");
+ char *value = strtok(0,"\n");
+
+ query.supports(key, value);
+ }
+ vector<Arts::TraderOffer> *offers = query.query();
+ vector<Arts::TraderOffer>::iterator i;
+ for (i = offers->begin(); i != offers->end(); i++)
+ cout << i->interfaceName() << endl;
+ delete offers;
+}
+
+// tradercheck command
+void traderCheck()
+{
+ Arts::TraderCheck check;
+ check.run();
+}
+
+void version()
+{
+ cout << "aRts version " << ARTS_VERSION << endl;
+}
+
+int executeCommand(Arts::SoundServerV2 server, int argc, char **argv)
+{
+ if (!strcmp(argv[0], "help") || !strcmp(argv[0], "?")) {
+ help();
+ return 0;
+ }
+
+ if (!strcmp(argv[0], "version")) {
+ version();
+ return 0;
+ }
+
+ if (!strcmp(argv[0], "suspend")) {
+ suspend(server);
+ return 0;
+ }
+
+ if (!strcmp(argv[0], "status")) {
+ status(server);
+ return 0;
+ }
+
+ if(!strcmp(argv[0], "terminate")) {
+ terminate(server);
+ return 0;
+ }
+
+ if(!strcmp(argv[0], "volume") && (argc == 2)) {
+ setVolume(server,atof(argv[1]));
+ return 0;
+ }
+ if(!strcmp(argv[0], "volume") && (argc == 1)) {
+ cout << getVolume(server) << endl;;
+ return 0;
+ }
+
+ if ( !strcmp( argv[ 0 ],"volumedb" ) && ( argc==2 ) ) {
+ setDecibelVolume( server, atof( argv[ 1 ] ) );
+ return 0;
+ }
+ if ( !strcmp( argv[ 0 ],"volumedb" ) && ( argc==1 ) ) {
+ cout << getDecibelVolume( server ) << endl;
+ return 0;
+ }
+
+ if(!strcmp(argv[0], "cpuusage") && (argc == 1)) {
+ printf("%.1f\n",server.cpuUsage());
+ return 0;
+ }
+
+
+ if(!strcmp(argv[0], "autosuspend") && (argc == 2)) {
+ int secs = atoi(argv[1]);
+ autosuspend(server, secs);
+ return 0;
+ }
+
+ if(!strcmp(argv[0], "networkbuffers") && (argc == 2)) {
+ int n = atoi(argv[1]);
+ networkBuffers(server, n);
+ return 0;
+ }
+
+ if(!strcmp(argv[0], "stereoeffect") && (argc >= 2)) {
+ stereoEffect(server, argc-1, &argv[1]);
+ return 0;
+ }
+
+ if(!strcmp(argv[0], "traderquery")) {
+ traderQuery(server, argc-1, &argv[1]);
+ return 0;
+ }
+
+ if(!strcmp(argv[0], "tradercheck")) {
+ traderCheck();
+ return 0;
+ }
+
+ return -1;
+}
+
+int execute(Arts::SoundServerV2 server, const char *filename)
+{
+ char command[1024];
+ FILE *input = stdin;
+ bool prompt;
+
+ if(filename)
+ {
+ input = fopen(filename,"r");
+ if(!input)
+ {
+ printf("can't open file '%s'\n", filename);
+ return 1;
+ }
+ }
+
+ prompt = isatty(fileno(input));
+
+ if(prompt)
+ {
+ printf("> ");
+ fflush(stdout);
+ }
+
+ while(fgets(command, 1024, input) != 0)
+ {
+ char **argv = 0;
+ int argc = 0;
+ while(char *arg = strtok(argc?0:command, " \t\n"))
+ {
+ argv = (char **)realloc(argv, sizeof(char *)*(argc+1));
+ argv[argc++] = arg;
+ }
+ if (argv != 0)
+ {
+ if (executeCommand(server, argc, argv) == -1)
+ cerr << "Invalid command, type 'help' for a list of commands." << endl;
+ free(argv);
+ }
+
+ if(prompt)
+ {
+ printf("> ");
+ fflush(stdout);
+ }
+ }
+
+ if(prompt)
+ printf("\n");
+
+ if(input != stdin)
+ fclose(input);
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ Arts::Dispatcher dispatcher;
+ Arts::SoundServerV2 server(Arts::Reference("global:Arts_SoundServer"));
+
+ parseOptions(argc, argv);
+
+ if (server.isNull())
+ {
+ if (!quiet)
+ cerr << "unable to connect to sound server" << endl;
+ exit(1);
+ }
+
+ if (argc == optind)
+ return execute (server, filename);
+
+ if (executeCommand (server, argc-optind, &argv[optind]) == -1)
+ usage();
+
+ return 0;
+}
+
+// vim: sw=4 ts=4
diff --git a/soundserver/artsversion-new.h.in b/soundserver/artsversion-new.h.in
new file mode 100644
index 0000000..6c87684
--- /dev/null
+++ b/soundserver/artsversion-new.h.in
@@ -0,0 +1,31 @@
+ /*
+
+ Copyright (C) 2001 Stefan Westerfeld
+
+ 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 ARTS_VERSION
+
+/* version information for all aRts utilities and programs */
+#define ARTS_MAJOR_VERSION (@ARTS_MAJOR_VERSION@)
+#define ARTS_MINOR_VERSION (@ARTS_MINOR_VERSION@)
+#define ARTS_MICRO_VERSION (@ARTS_MICRO_VERSION@)
+#define ARTS_VERSION ("@ARTS_VERSION@")
+
+#endif
diff --git a/soundserver/artswrapper.c b/soundserver/artswrapper.c
new file mode 100644
index 0000000..53cc56a
--- /dev/null
+++ b/soundserver/artswrapper.c
@@ -0,0 +1,120 @@
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/resource.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdlib.h>
+
+/*
+ * adjust_priority
+ *
+ * sets realtime priority
+ */
+
+#include "config.h"
+
+#ifdef HAVE_REALTIME_SCHED
+#include <sched.h>
+
+void adjust_priority()
+{
+ int sched = sched_getscheduler(0);
+ if(sched == SCHED_FIFO || sched == SCHED_RR)
+ {
+ printf(">> since the scheduling policy is not standard, I assume\n");
+ printf(" it has been adjusted to fit the needs of realtime audio\n");
+ }
+ else
+ {
+ struct sched_param sp;
+ long priority = (sched_get_priority_max(SCHED_FIFO) +
+ sched_get_priority_min(SCHED_FIFO))/2;
+
+ sp.sched_priority = priority;
+
+ if(sched_setscheduler(0, SCHED_FIFO, &sp) != -1)
+ {
+ printf(">> running as realtime process now (priority %ld)\n",
+ priority);
+ putenv("STARTED_THROUGH_ARTSWRAPPER=1");
+ }
+ else
+ {
+ /* can't set realtime priority */
+ putenv("STARTED_THROUGH_ARTSWRAPPER=2");
+ }
+ }
+}
+#else
+void adjust_priority()
+{
+ int prio;
+
+ prio = getpriority(PRIO_PROCESS,getpid());
+ if(prio > -10)
+ {
+ setpriority(PRIO_PROCESS,getpid(),-17);
+ prio = getpriority(PRIO_PROCESS,getpid());
+ }
+
+ /* no system support for realtime priority */
+ putenv("STARTED_THROUGH_ARTSWRAPPER=3");
+
+ if(prio > -10) {
+ printf(">> synthesizer priority is %d (which is unacceptable,",prio);
+ printf(" try running as root)\n");
+ }
+ else {
+ printf(">> synthesizer priority is %d (which is the best\n",prio);
+ printf(" we can get out of a non realtime system)\n");
+ }
+}
+#endif
+
+int main(int argc, char **argv)
+{
+ if(argc == 2)
+ {
+ if(strcmp(argv[1],"check") == 0)
+ {
+ /* backward compatibility with old artswrapper */
+ printf("okay\n");
+ return 0;
+ }
+ }
+
+ adjust_priority();
+
+ /* drop root privileges if running setuid root
+ (due to realtime priority stuff) */
+ if (geteuid() != getuid())
+ {
+#if defined (HAVE_SETEUID) && !defined (HAVE_SETEUID_FAKE)
+ seteuid(getuid());
+#else
+ setreuid(-1, getuid());
+#endif
+ if (geteuid() != getuid()) {
+ perror("setuid()");
+ return 2;
+ }
+ }
+
+ if(argc == 0)
+ return 1;
+
+/*
+ * Real-time status is passed to artsd via the environment variable
+ * STARTED_THROUGH_ARTSWRAPPER. It has one of the following values:
+ *
+ * unset - not running as real-time
+ * 1 - running as real-time
+ * 2 - no privileges to set real-time scheduling
+ * 3 - no support for real-time scheduling
+ */
+ argv[0] = EXECUTE;
+ execv(EXECUTE,argv);
+ perror(EXECUTE);
+ return 1;
+}
diff --git a/soundserver/cpuusage.cc b/soundserver/cpuusage.cc
new file mode 100644
index 0000000..6c5dd47
--- /dev/null
+++ b/soundserver/cpuusage.cc
@@ -0,0 +1,137 @@
+ /*
+
+ Copyright (C) 2000-2002 Stefan Westerfeld
+
+ 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 "cpuusage.h"
+#include "dispatcher.h"
+#include "debug.h"
+
+#include <time.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <signal.h>
+
+#include <iostream>
+
+using namespace std;
+
+namespace Arts {
+
+class Benchmark
+{
+private:
+ struct timeval _start,_stop;
+public:
+ void start()
+ {
+ gettimeofday(&_start,NULL);
+ }
+ float stop()
+ {
+ gettimeofday(&_stop,NULL);
+
+ float diff = _stop.tv_sec-_start.tv_sec;
+ diff += (float)(_stop.tv_usec-_start.tv_usec)/1000000;
+ return diff;
+ }
+};
+
+class CPUUsagePrivate
+{
+public:
+ clock_t oldclock;
+ int stalled;
+ float usage;
+ Benchmark b;
+};
+
+/** signal handlers **/
+
+static CPUUsage *cpuUsage = 0;
+
+extern "C" void cpuUsageCheck(int)
+{
+ if(cpuUsage != 0)
+ cpuUsage->check();
+
+ signal(SIGALRM, cpuUsageCheck);
+}
+
+/** main stuff **/
+
+CPUUsage::CPUUsage() : d(new CPUUsagePrivate())
+{
+ d->oldclock = clock();
+ d->usage = 0;
+ d->stalled = 0;
+ d->b.start();
+ cpuUsage = this;
+
+ /* setup signal handler & timer */
+
+ struct itimerval oldvalue;
+ struct itimerval newvalue = {{ 1, 0 }, {1, 0}}; // 1 second
+
+ setitimer(ITIMER_REAL, &newvalue, &oldvalue);
+ signal(SIGALRM, cpuUsageCheck);
+}
+
+CPUUsage::~CPUUsage()
+{
+ delete d;
+ cpuUsage = 0;
+}
+
+float CPUUsage::usage()
+{
+ return d->usage;
+}
+
+void CPUUsage::check()
+{
+ float cpu_time = (clock()-d->oldclock)/(float)CLOCKS_PER_SEC;
+ float real_time = d->b.stop();
+
+ if(cpu_time > 0 && real_time > 0) // there may be wraparounds
+ {
+ d->usage = cpu_time / real_time;
+
+ if(d->usage > 0.95) // more than 95% -> not good! (probably freeze)
+ d->stalled++;
+ else
+ d->stalled=0;
+
+ // ok, cancel synthesis due to cpu overload! brutal method
+ if(d->stalled > 15)
+ arts_fatal("cpu overload, aborting");
+ }
+
+ // prepare for next checkpoint
+ d->oldclock = clock();
+ d->b.start();
+}
+
+CPUUsage *CPUUsage::the()
+{
+ return cpuUsage;
+}
+
+}
diff --git a/soundserver/cpuusage.h b/soundserver/cpuusage.h
new file mode 100644
index 0000000..764d30f
--- /dev/null
+++ b/soundserver/cpuusage.h
@@ -0,0 +1,53 @@
+ /*
+
+ Copyright (C) 2000-2002 Stefan Westerfeld
+
+ 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 _arts_cpuusage_h
+#define _arts_cpuusage_h
+
+#include "iomanager.h"
+
+namespace Arts {
+class CPUUsage
+{
+private:
+ class CPUUsagePrivate *d;
+
+public:
+ CPUUsage();
+ ~CPUUsage();
+
+ static CPUUsage *the();
+
+ /**
+ * get the current CPU usage
+ */
+ float usage();
+
+ /**
+ * check the current CPU usage
+ */
+ void check();
+};
+}
+
+#endif
diff --git a/soundserver/crashhandler.cc b/soundserver/crashhandler.cc
new file mode 100644
index 0000000..d3ba374
--- /dev/null
+++ b/soundserver/crashhandler.cc
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2003 Stefan Westerfeld
+ *
+ * This code is based on code from the KDE Libraries
+ * Copyright (C) 2000 Timo Hummel <[email protected]>
+ * Tom Braun <[email protected]>
+ *
+ * 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 file is used to catch signals which would normally
+ * crash the application (like segmentation fault, floating
+ * point exception and such).
+ */
+
+#include <string.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include "crashhandler.h"
+#include "audiosubsys.h"
+#include "debug.h"
+#include "config.h"
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/wait.h>
+
+#ifdef HAVE_REALTIME_SCHED
+#include <sched.h>
+#endif
+
+using namespace Arts;
+
+CrashHandler::HandlerType CrashHandler::_crashHandler = 0;
+const char *CrashHandler::appName = 0;
+const char *CrashHandler::appPath = 0;
+const char *CrashHandler::appVersion = 0;
+const char *CrashHandler::programName = 0;
+const char *CrashHandler::bugAddress = 0;
+const char *CrashHandler::crashApp = 0;
+
+// This function sets the function which should be responsible for
+// the application crash handling.
+void
+CrashHandler::setCrashHandler (HandlerType handler)
+{
+ if (!handler)
+ handler = SIG_DFL;
+
+ sigset_t mask;
+ sigemptyset(&mask);
+
+#ifdef SIGSEGV
+ signal (SIGSEGV, handler);
+ sigaddset(&mask, SIGSEGV);
+#endif
+#ifdef SIGFPE
+ signal (SIGFPE, handler);
+ sigaddset(&mask, SIGFPE);
+#endif
+#ifdef SIGILL
+ signal (SIGILL, handler);
+ sigaddset(&mask, SIGILL);
+#endif
+#ifdef SIGABRT
+ signal (SIGABRT, handler);
+ sigaddset(&mask, SIGABRT);
+#endif
+
+ sigprocmask(SIG_UNBLOCK, &mask, 0);
+
+ _crashHandler = handler;
+}
+
+void
+CrashHandler::defaultCrashHandler (int sig)
+{
+ // WABA: Do NOT use kdDebug() in this function because it is much too risky!
+ // Handle possible recursions
+ static int crashRecursionCounter = 0;
+ crashRecursionCounter++; // Nothing before this, please !
+
+ signal(SIGALRM, SIG_DFL);
+ alarm(3); // Kill me... (in case we deadlock in malloc)
+
+ if (crashRecursionCounter < 2) {
+ Arts::AudioSubSystem *a = Arts::AudioSubSystem::the();
+ if(a) a->emergencyCleanup();
+
+#ifdef HAVE_REALTIME_SCHED
+ struct sched_param sp;
+ sp.sched_priority = 0;
+ if (sched_setscheduler(0, SCHED_OTHER, &sp) != 0)
+ fprintf(stderr, "CrashHandler: can't stop running as realtime process (%s)\n", strerror(errno));
+#endif
+
+ crashRecursionCounter++; //
+ }
+
+ // Close all remaining file descriptors
+ struct rlimit rlp;
+ getrlimit(RLIMIT_NOFILE, &rlp);
+ for (int i = 0; i < (int)rlp.rlim_cur; i++)
+ close(i);
+
+ bool shuttingDown = false;
+
+ // don't load drkonqi during shutdown
+ if ( !shuttingDown )
+ {
+ if (crashRecursionCounter < 3)
+ {
+ if (appName)
+ {
+#ifndef NDEBUG
+ fprintf(stderr, "CrashHandler: crashing... crashRecursionCounter = %d\n", crashRecursionCounter);
+ fprintf(stderr, "CrashHandler: Application Name = %s path = %s pid = %d\n", appName ? appName : "<unknown>" , appPath ? appPath : "<unknown>", getpid());
+#else
+ fprintf(stderr, "CrashHandler: Application '%s' crashing...\n", appName ? appName : "<unknown>");
+#endif
+
+ pid_t pid = fork();
+
+ if (pid <= 0) {
+ // this code is leaking, but this should not hurt cause we will do a
+ // exec() afterwards. exec() is supposed to clean up.
+ char * argv[18]; // don't forget to update this
+ int i = 0;
+
+ // argument 0 has to be drkonqi
+ argv[i++] = strdup(crashApp);
+
+ char *display = getenv("DISPLAY");
+
+ if (display) {
+ // start up on the correct display
+ argv[i++] = strdup("-display");
+ argv[i++] = display;
+ }
+
+ // we have already tested this
+ argv[i++] = strdup("--appname");
+ argv[i++] = strdup(appName);
+
+ // only add apppath if it's not NULL
+ if (appPath) {
+ argv[i++] = strdup("--apppath");
+ argv[i++] = strdup(appPath);
+ }
+
+ // signal number -- will never be NULL
+ argv[i++] = strdup("--signal");
+ argv[i++] = arts_strdup_printf("%d", sig);
+
+ // pid number -- only include if this is the child
+ // the debug stuff will be disabled if we was not able to fork
+ if (pid == 0) {
+ argv[i++] = strdup("--pid");
+ argv[i++] = arts_strdup_printf("%d", getppid());
+ }
+
+ if (appVersion) {
+ argv[i++] = strdup("--appversion");
+ argv[i++] = strdup(appVersion);
+ }
+
+ if (programName) {
+ argv[i++] = strdup("--programname");
+ argv[i++] = strdup(programName);
+ }
+
+ if (bugAddress) {
+ argv[i++] = strdup("--bugaddress");
+ argv[i++] = strdup(bugAddress);
+ }
+
+ // NULL terminated list
+ argv[i++] = NULL;
+
+ setgid(getgid());
+ if (getuid() != geteuid())
+ setuid(getuid());
+ if (getuid() != geteuid()) {
+ perror("setuid()");
+ exit(255);
+ }
+
+ execvp(crashApp, argv);
+
+ // we could clean up here
+ // i = 0;
+ // while (argv[i])
+ // free(argv[i++]);
+ }
+ else
+ {
+
+ alarm(0); // Seems we made it....
+
+ fprintf(stderr, "ArtsCrash: ... waiting for child to exit\n");
+ // wait for child to exit
+ waitpid(pid, NULL, 0);
+ _exit(253);
+ }
+ }
+ else {
+ fprintf(stderr, "Unknown appname\n");
+ }
+ }
+
+ if (crashRecursionCounter < 4)
+ {
+ fprintf(stderr, "Unable to start %s\n", crashApp);
+ }
+ }
+
+ _exit(255);
+}
diff --git a/soundserver/crashhandler.h b/soundserver/crashhandler.h
new file mode 100644
index 0000000..a9264a1
--- /dev/null
+++ b/soundserver/crashhandler.h
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2003 Stefan Westerfeld
+ *
+ * This code is based on code from the KDE Libraries
+ * Copyright (C) 2000 Timo Hummel <[email protected]>
+ * Tom Braun <[email protected]>
+ * 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 __ARTS_CRASHHANDLER_H
+#define __ARTS_CRASHHANDLER_H
+
+#include <string>
+
+namespace Arts {
+
+/**
+ * This class handles segmentation-faults.
+ * By default it displays a message-box saying the application crashed.
+ * This default can be overridden by setting a custom crash handler with
+ * setCrashHandler().
+ */
+class CrashHandler
+{
+ private: // ;o)
+ static const char *appName;
+ static const char *appPath;
+ static const char *appVersion;
+ static const char *programName;
+ static const char *bugAddress;
+ static const char *crashApp;
+
+ public:
+ /**
+ * The default crash handler.
+ * @param signal the signal number
+ */
+ static void defaultCrashHandler (int signal);
+
+ /**
+ * This function type is a pointer to a crash handler function.
+ * The function's argument is the number of the signal.
+ */
+ typedef void (*HandlerType)(int);
+
+ /**
+ * Install a function to be called in case a SIGSEGV is caught.
+ * @param handler HandlerType handler can be one of
+ * @li null in which case signal-catching is disabled
+ * (by calling signal(SIGSEGV, SIG_DFL))
+ * @li if handler is omitted the default crash handler is installed.
+ * @li an user defined function in the form:
+ * static (if in a class) void myCrashHandler(int);
+ * @param handler the crash handler
+ */
+
+ static void setCrashHandler (HandlerType handler = defaultCrashHandler);
+
+ /**
+ * Returns the installed crash handler.
+ * @return the crash handler
+ */
+ static HandlerType crashHandler() { return _crashHandler; }
+
+ /**
+ * Sets the application @p path which should be passed to
+ * Dr. Konqi, our nice crash display application.
+ * @param path the application path.
+ */
+ static void setApplicationPath (const std::string& path) { appPath = strdup(path.c_str()); }
+
+ /**
+ * Sets the application name @p name which should be passed to
+ * Dr. Konqi, our nice crash display application.
+ * @param name the name of the application, as shown in Dr. Konqi
+ */
+ static void setApplicationName (const std::string& name) { appName = strdup(name.c_str()); }
+
+ /**
+ * Sets the application version @p name which should be passed to
+ * Dr. Konqi, our nice crash display application.
+ * @param name the name of the application, as shown in Dr. Konqi
+ */
+ static void setApplicationVersion (const std::string& name) { appVersion = strdup(name.c_str()); }
+
+ /**
+ * Sets the program name @p name which should be passed to
+ * Dr. Konqi, our nice crash display application.
+ * @param name the name of the application, as shown in Dr. Konqi
+ */
+ static void setProgramName (const std::string& name) { programName = strdup(name.c_str()); }
+
+ /**
+ * Sets the bug adress name @p name which should be passed to
+ * Dr. Konqi, our nice crash display application.
+ * @param name the name of the application, as shown in Dr. Konqi
+ */
+ static void setBugAddress (const std::string& name) { bugAddress = strdup(name.c_str()); }
+
+ /**
+ * Sets the crash application @p app which will be called to
+ * handle the crash. To use Dr. Konqi (KDE), pass "drkonqi" here.
+ * @param app the name of the application that handles the crash
+ */
+ static void setCrashApp (const std::string& app) { crashApp = strdup(app.c_str()); }
+
+ protected:
+ /**
+ * Pointer to the crash handler.
+ */
+ static HandlerType _crashHandler;
+};
+
+}
+
+#endif
+
diff --git a/soundserver/fileinputstream_impl.cc b/soundserver/fileinputstream_impl.cc
new file mode 100644
index 0000000..e44faa2
--- /dev/null
+++ b/soundserver/fileinputstream_impl.cc
@@ -0,0 +1,193 @@
+ /*
+
+ Copyright (C) 2001 Stefan Westerfeld
+
+ 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 "kmedia2.h"
+#include "stdsynthmodule.h"
+#include "debug.h"
+
+#include <unistd.h>
+#include <sys/mman.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <iostream>
+#include <cstring>
+
+using namespace std;
+using namespace Arts;
+
+namespace Arts {
+class FileInputStream_impl : virtual public FileInputStream_skel,
+ virtual public StdSynthModule
+{
+protected:
+ string _filename;
+ int age, fd;
+ unsigned int _size, position;
+ mcopbyte *data;
+ queue< DataPacket<mcopbyte>* > wqueue;
+
+public:
+ static const unsigned int PACKET_COUNT;
+ static const unsigned int PACKET_SIZE;
+
+ FileInputStream_impl()
+ {
+ fd = -1;
+ position = 0;
+ data = 0;
+ age = 0;
+ _size = 0;
+ }
+ ~FileInputStream_impl()
+ {
+ arts_assert(wqueue.size() == 0);
+
+ close();
+ }
+
+ void close()
+ {
+ if(data != 0)
+ {
+ munmap((char*)data, _size);
+ data = 0;
+ }
+
+ if(fd >= 0)
+ {
+ ::close(fd);
+ fd = -1;
+ }
+ }
+
+ bool open(const string& filename)
+ {
+ close();
+
+ fd = ::open(filename.c_str(), O_RDONLY);
+ if(fd < 0)
+ return false;
+
+ _size = lseek(fd, 0, SEEK_END);
+ lseek(fd, 0, SEEK_SET);
+
+ data = (mcopbyte *)mmap(0, _size, PROT_READ, MAP_SHARED, fd, 0);
+ if(data == 0)
+ {
+ close();
+ return false;
+ }
+
+ position = 0;
+ if(_filename != filename)
+ {
+ _filename = filename;
+ filename_changed(filename);
+ }
+ return true;
+ }
+
+ string filename() { return _filename; }
+ void filename(const string& newfilename) { open(newfilename); }
+
+ long size() { return _size; }
+ bool eof()
+ {
+ return (fd < 0 || position >= _size)
+ && (wqueue.size() == PACKET_COUNT);
+ }
+ bool seekOk() { return true; }
+
+ long seek(long newPosition)
+ {
+ arts_return_val_if_fail(fd < 0, -1);
+ arts_return_val_if_fail(newPosition < 0, -1);
+ arts_return_val_if_fail(newPosition > (long)_size, -1);
+
+ long ageBeforeSeek = age;
+ position = newPosition;
+
+ processQueue();
+ return ageBeforeSeek;
+ }
+
+ void processQueue()
+ {
+ unsigned int qsize = wqueue.size();
+
+ for(unsigned int i = 0; i < qsize; i++)
+ {
+ if(position < _size)
+ {
+ DataPacket<mcopbyte> *packet = wqueue.front();
+ wqueue.pop();
+
+ packet->size = min(PACKET_SIZE, _size - position);
+ memcpy(packet->contents, data+position, packet->size);
+ age += packet->size;
+ position += packet->size;
+ packet->send();
+ }
+ }
+ }
+
+ void request_outdata(DataPacket<mcopbyte> *packet)
+ {
+ wqueue.push(packet);
+ processQueue();
+ }
+
+ void streamStart()
+ {
+ /*
+ * start streaming
+ */
+ outdata.setPull(PACKET_COUNT, PACKET_SIZE);
+ }
+
+ void streamEnd()
+ {
+ /*
+ * end streaming
+ */
+ outdata.endPull();
+
+ while(!wqueue.empty())
+ {
+ DataPacket<mcopbyte> *packet = wqueue.front();
+ packet->size = 0;
+ packet->send();
+ wqueue.pop();
+ }
+ }
+};
+
+REGISTER_IMPLEMENTATION(FileInputStream_impl);
+
+const unsigned int
+FileInputStream_impl::PACKET_COUNT = 8;
+
+const unsigned int
+FileInputStream_impl::PACKET_SIZE = 8192;
+
+}
diff --git a/soundserver/gslplayobject_impl.cc b/soundserver/gslplayobject_impl.cc
new file mode 100644
index 0000000..7781fac
--- /dev/null
+++ b/soundserver/gslplayobject_impl.cc
@@ -0,0 +1,235 @@
+ /*
+
+ Copyright (C) 2002 Hans Meine
+
+ 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 "flowsystem.h"
+#include "soundserver.h"
+#include "artsflow.h"
+#include "stdsynthmodule.h"
+#include "debug.h"
+#include <math.h>
+
+using namespace std;
+using namespace Arts;
+
+class GSLPlayObject_impl : public GSLPlayObject_skel, public StdSynthModule {
+protected:
+ DataHandlePlay leftPlay_, rightPlay_;
+ poState state_;
+ WaveDataHandle dhandle_;
+ std::string filename_;
+
+ int sampleCount()
+ {
+ if(!dhandle_.isLoaded()) return 0;
+ return dhandle_.valueCount() / dhandle_.channelCount();
+ }
+
+public:
+ /*
+ * construction, destruction
+ */
+ GSLPlayObject_impl()
+ : leftPlay_(DataHandlePlay::null()),
+ rightPlay_(DataHandlePlay::null()),
+ state_(posIdle)
+ {
+ }
+
+ float speed() { return leftPlay_.speed(); }
+ void speed(float newSpeed)
+ {
+ if(newSpeed != speed())
+ {
+ leftPlay_.speed(newSpeed);
+ if(!rightPlay_.isNull())
+ rightPlay_.speed(newSpeed);
+ speed_changed(newSpeed);
+ }
+ }
+
+ void streamStart()
+ {
+ arts_return_if_fail(dhandle_.isLoaded());
+
+ if(dhandle_.channelCount()>=2)
+ {
+ rightPlay_ = leftPlay_.clone();
+ rightPlay_.channelIndex(1);
+ }
+
+ leftPlay_.start();
+ if(!rightPlay_.isNull())
+ rightPlay_.start();
+
+ arts_debug("GSLPlayObject: streamStart(%d channels) playing %d, %d",
+ dhandle_.channelCount(),
+ leftPlay_.channelIndex(),
+ rightPlay_.isNull() ? -1 : rightPlay_.channelIndex());
+
+ _node()->virtualize("left", leftPlay_._node(), "outvalue");
+ if(dhandle_.channelCount()>=2)
+ _node()->virtualize("right", rightPlay_._node(), "outvalue");
+ else
+ _node()->virtualize("right", leftPlay_._node(), "outvalue");
+ }
+
+ void streamEnd()
+ {
+ arts_return_if_fail(dhandle_.isLoaded());
+
+ _node()->devirtualize("left", leftPlay_._node(), "outvalue");
+ if(dhandle_.channelCount()>=2)
+ _node()->devirtualize("right", rightPlay_._node(), "outvalue");
+ else
+ _node()->devirtualize("right", leftPlay_._node(), "outvalue");
+
+ leftPlay_.stop();
+ if(!rightPlay_.isNull())
+ rightPlay_.stop();
+ }
+
+ /*
+ * KMedia2 private interface
+ */
+ bool loadMedia(const std::string &filename)
+ {
+ arts_return_val_if_fail(!dhandle_.isLoaded(), false);
+
+ arts_debug("GSLPlayObject: loadMedia('%s')", filename.c_str());
+ filename_ = filename;
+ dhandle_.load(filename);
+ if(dhandle_.isLoaded())
+ {
+ leftPlay_ = dhandle_.createPlayer();
+ if(dhandle_.channelCount()>2)
+ arts_debug("GSLPlayObject: can't play more than 2 channels, "
+ "playing first two!");
+
+ leftPlay_._node()->connect("finished_changed", _node(), "done");
+ }
+ return dhandle_.isLoaded();
+ }
+
+ /*
+ * KMedia2 interface
+ */
+ std::string description()
+ {
+ return "no description available (see "__FILE__")";
+ }
+
+ poTime currentTime()
+ {
+ if(!dhandle_.isLoaded())
+ return poTime(0, 0, 0, "samples");
+
+ float timesec = leftPlay_.pos()
+ / dhandle_.channelCount()
+ / dhandle_.mixerFrequency();
+ float timems = (timesec - floor(timesec)) * 1000.0;
+
+ return poTime((long)timesec, (long)timems,
+ (long)leftPlay_.pos(), "samples");
+ }
+
+ poTime overallTime()
+ {
+ float timesec = (float)sampleCount() / dhandle_.mixerFrequency();
+ float timems = (timesec - floor(timesec)) * 1000.0;
+
+ return poTime((long)timesec, (long)timems,
+ (long)sampleCount(), "samples");
+ }
+
+ poCapabilities capabilities()
+ {
+ return static_cast<poCapabilities>(capPause | capSeek);
+ }
+
+ std::string mediaName()
+ {
+ return filename_;
+ }
+
+ poState state()
+ {
+ return state_;
+ }
+
+ bool done() { return leftPlay_.finished(); }
+ void done(bool d)
+ {
+ if(d && (state_ == posPlaying))
+ halt();
+ }
+
+ void play()
+ {
+ leftPlay_.paused(false);
+ if(!rightPlay_.isNull())
+ rightPlay_.paused(false);
+ state_ = posPlaying;
+ }
+
+ void halt()
+ {
+ leftPlay_.paused(true);
+ if(!rightPlay_.isNull())
+ rightPlay_.paused(true);
+ state_ = posIdle;
+ seek(poTime(-1, -1, 0, "samples"));
+ }
+
+ void pause()
+ {
+ leftPlay_.paused(true);
+ if(!rightPlay_.isNull())
+ rightPlay_.paused(true);
+ state_ = posPaused;
+ }
+
+ void seek(const class poTime &newTime)
+ {
+ long newsamples = -1;
+ if(newTime.seconds != -1 && newTime.ms != -1)
+ {
+ float flnewtime = (float)newTime.seconds+((float)newTime.ms/1000.0);
+ newsamples = (long)(flnewtime * dhandle_.mixerFrequency());
+ }
+ else if(newTime.custom >= 0 && newTime.customUnit == "samples")
+ {
+ newsamples = (long)newTime.custom;
+ }
+
+ if(newsamples > sampleCount())
+ newsamples = sampleCount();
+
+ if(newsamples >= 0)
+ {
+ leftPlay_.pos(newsamples * dhandle_.channelCount());
+ if(!rightPlay_.isNull())
+ rightPlay_.pos(newsamples * dhandle_.channelCount());
+ }
+ }
+};
+
+REGISTER_IMPLEMENTATION(GSLPlayObject_impl);
diff --git a/soundserver/kmedia2.idl b/soundserver/kmedia2.idl
new file mode 100644
index 0000000..cdcb101
--- /dev/null
+++ b/soundserver/kmedia2.idl
@@ -0,0 +1,242 @@
+/*
+
+NEW KMedia2 layout:
+===================
+
+KMedia1 itself doesn't play anything. Instead it has a backend for every
+media type to play. It tells these backends: "start playing now", "stop
+playing now" and similar things (using libmediatool). So there is a backend
+for wave files, tacker files, midi files, etc., which all provide one
+common interface.
+
+The idea is to keep everything the old KMedia1 interface could do, but to
+move to a new IPC architecture (MCOP).
+
+That way, KMedia2 objects will be able to use the aRts streaming abilities.
+Of course, not every KMedia2 object will need these, but some of them. The
+result will be much nicer, if every of these objects can be treated in the
+standard "flow graph" way, like other aRts objects can.
+
+The ultimate media player, which KMedia2 aims to be, should play midi,
+video, audio, etc. It is about seeing a file, choosing a component which
+might be able to decode it, and play it. So it is not about starting
+the right application, but about loading the right component.
+
+This gives you the advantage that you can for instance reuse components
+even between quite different media types. It may for instance make sense
+to reuse a reverb effect to play midi, audio and video files.
+
+*/
+
+#include "artsflow.idl"
+
+module Arts {
+
+enum poState {
+ posIdle,
+ posPlaying,
+ posPaused
+};
+
+// use 2^n values here, since they can (should) be or'd together
+enum poCapabilities {
+ capSeek = 1,
+ capPause = 2
+};
+
+/**
+ * KMedia2 time information
+ *
+ * This is a time value which contains either milliseconds & seconds, or
+ * a custom unit or both. It is a flexible time base.
+ *
+ * If a value isn't there, it is set to -1.
+ */
+struct poTime {
+ /**
+ * time it takes in seconds; -1 if no clock time known
+ */
+ long seconds;
+
+ /**
+ * additional time in milliseconds (this doesn't contain all the time)
+ * -1 if no clock time known
+ */
+ long ms;
+
+ /**
+ * some custom time information
+ * -1 if no custom time known
+ */
+ float custom;
+
+ /**
+ * for instance for a tracker "pattern"
+ */
+ string customUnit;
+};
+
+/**
+ * private part of the PlayObject API (don't use)
+ */
+interface PlayObject_private {
+ /**
+ * loads a file
+ */
+ boolean loadMedia(string filename);
+};
+
+/**
+ * KMedia2 PlayObject - these can be used by Kaiman for instance
+ */
+interface PlayObject : PlayObject_private {
+ readonly attribute string description;
+ readonly attribute poTime currentTime;
+ readonly attribute poTime overallTime;
+ readonly attribute poCapabilities capabilities;
+ readonly attribute string mediaName;
+ readonly attribute poState state;
+
+ /**
+ * starts playing the media
+ */
+ void play();
+ /**
+ * seeks to a specific time
+ */
+ void seek(poTime newTime); // could be handled by setting currentTime
+ /**
+ * pauses playing the media
+ */
+ void pause();
+ /**
+ * stop playing the media. Normally this function would called stop,
+ * but the name is reserved for the start/stop mechanism of the
+ * aRts objects.
+ */
+ void halt();
+};
+
+/**
+ * use this to create new PlayObjects for media
+ */
+interface PlayObjectFactory {
+ /**
+ * creates a play object (or returns a null reference if this is not
+ * possible)
+ *
+ * @param filename the name of the file to create a play object for
+ */
+ PlayObject createPlayObject(string filename);
+};
+
+/**
+ * UNSTABLE/EXPERIMENTAL!
+ */
+interface InputStream : SynthModule {
+ /**
+ * whether the stream is at the end of the input
+ */
+ readonly attribute boolean eof;
+
+ /**
+ * total size of the stream in bytes
+ * -1: unknown
+ */
+ readonly attribute long size;
+
+ /**
+ * whether the stream can be seeked
+ */
+ readonly attribute boolean seekOk;
+
+ /**
+ * this returns the new AGE of the stream, where AGE is defined as the
+ * number of bytes that have been sent since the stream exists
+ */
+ long seek(long position);
+
+ /**
+ * the stream data
+ */
+ async out byte stream outdata;
+};
+
+/**
+ * UNSTABLE/EXPERIMENTAL! Example stream for files.
+ */
+interface FileInputStream : InputStream {
+ attribute string filename;
+
+ boolean open(string filename);
+};
+
+interface StdoutWriter : SynthModule {
+ async in byte stream indata;
+};
+
+interface StreamPlayObject : PlayObject {
+ /**
+ * prepares the playobject for the stream
+ */
+ boolean streamMedia(InputStream instream);
+
+ /**
+ * last used inputstream
+ * it should never change
+ */
+ InputStream inputStream();
+};
+
+/**
+ * Playobject with adjustable speed
+ */
+interface PitchablePlayObject {
+ /**
+ * speed relative to "original"/intended one
+ * (1.0=normal, 2.0=double ;-), 1.05=5% faster)
+ */
+ attribute float speed;
+};
+
+/**
+ * KMedia2 VideoPlayObject - new interface for video support
+ *
+ * Some of the communication is done using the X11 protocol.
+ */
+interface VideoPlayObject {
+ /**
+ * video output window (active)
+ */
+ attribute long x11WindowId;
+ /**
+ * take a snapshot and draw it onto a pixmap
+ * returns the X11 pixmap ID or -1 on error
+ */
+ long x11Snapshot();
+};
+
+/**
+ * Extended Version of PlayObjectFactory
+ */
+interface PlayObjectFactoryV2 : PlayObjectFactory {
+ /**
+ * creates a PlayObject
+ *
+ * @param url the url to create a play object for
+ * @param mimetype mimetype for the url
+ * @param createBUS specifies wheter to create the bus-uplink or not
+ */
+ PlayObject createPlayObjectForURL(string url, string mimetype, boolean createBUS);
+
+ /**
+ * creates a StreamPlayObject (used internally!)
+ *
+ * @param instream specifies the InputStream
+ * @param mimetype mimetype for the InputStream
+ * @param createBUS specifies wheter to create the bus-uplink or not
+ */
+ PlayObject createPlayObjectForStream(InputStream instream, string mimetype, boolean createBUS);
+};
+
+};
diff --git a/soundserver/samplestorage_impl.cc b/soundserver/samplestorage_impl.cc
new file mode 100644
index 0000000..8c14d23
--- /dev/null
+++ b/soundserver/samplestorage_impl.cc
@@ -0,0 +1,202 @@
+ /*
+
+ Copyright (C) 2001 Stefan Westerfeld
+
+ 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 <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <dirent.h>
+#include "soundserver.h"
+#include "debug.h"
+
+using namespace Arts;
+using namespace std;
+
+namespace Arts {
+
+class SampleStorageEntry_impl : public SampleStorageEntry_skel {
+protected:
+ string _name, _filename;
+ bool _completed;
+ FILE *file;
+
+public:
+ SampleStorageEntry_impl(const string& name, const string& filename)
+ {
+ _name = name;
+ _filename = filename;
+ _completed = false;
+ file = fopen(_filename.c_str(),"w");
+ }
+ ~SampleStorageEntry_impl()
+ {
+ if(file)
+ {
+ fclose(file);
+ file = 0;
+ }
+ unlink(_filename.c_str());
+ }
+
+ string name() { return _name; }
+ string filename() { return _filename; }
+ bool completed() { return _completed; }
+
+ void write(const vector<mcopbyte>& data)
+ {
+ arts_return_if_fail(file != 0);
+
+ fwrite(&data[0],1,data.size(),file);
+ }
+ void finish()
+ {
+ arts_return_if_fail(file != 0);
+
+ fclose(file);
+ file = 0;
+ _completed = true;
+ }
+};
+
+class SampleStorage_impl : public SampleStorage_skel {
+protected:
+ string directory;
+ long uniqueID;
+ vector<SampleStorageEntry> entries;
+
+public:
+ SampleStorage_impl()
+ {
+ uniqueID = 1;
+ }
+
+ void constructor(const string& newDirectory, bool clearOnInit)
+ {
+ arts_return_if_fail(directory.empty());
+
+ directory = newDirectory;
+ mkdir(directory.c_str(),0700);
+
+ if(clearOnInit) clear();
+ }
+
+ void clear()
+ {
+ DIR *dir = opendir(directory.c_str());
+ if(!dir) return;
+
+ struct dirent *de;
+ while((de = readdir(dir)) != 0)
+ {
+ string currentEntry = directory + "/" + de->d_name;
+
+ if(de->d_name[0] != '.')
+ {
+ unlink(currentEntry.c_str());
+ }
+ }
+ closedir(dir);
+ }
+
+ string encodeName(const string& name)
+ {
+ string::const_iterator i;
+ string result;
+
+ for(i = name.begin(); i != name.end(); i++)
+ {
+ unsigned char c = *i;
+
+ if ((c >= 'a' && c <= 'z') ||
+ (c >= 'A' && c <= 'Z') ||
+ (c >= '0' && c <= '9') ||
+ c == '_' || c == '-')
+ {
+ result += c;
+ }
+ else
+ {
+ char hex[17] = "0123456789ABCDEF";
+
+ result += '=';
+ result += hex[(c >> 4) & 0xf];
+ result += hex[c & 0xf];
+ }
+ }
+ return result;
+ }
+
+ SampleStorageEntry createEntry(const string& name)
+ {
+ char uniqueprefix[32];
+ sprintf(uniqueprefix,"s%ld-",uniqueID++);
+ string filename = directory + string("/")
+ + uniqueprefix + encodeName(name);
+
+ return SampleStorageEntry::_from_base(
+ new SampleStorageEntry_impl(name, filename)
+ );
+ }
+
+ SampleStorageEntry findEntry(const string& name)
+ {
+ vector<SampleStorageEntry>::iterator i;
+
+ for(i = entries.begin(); i != entries.end(); i++)
+ if(i->name() == name) return *i;
+
+ return SampleStorageEntry::null();
+ }
+
+ void addEntry(SampleStorageEntry entry)
+ {
+ arts_return_if_fail(!entry.isNull());
+
+ /* remove the old entry for that name, if any */
+ SampleStorageEntry oldEntry = findEntry(entry.name());
+ if(!oldEntry.isNull())
+ removeEntry(oldEntry);
+
+ /* add the new entry */
+ entries.push_back(entry);
+ }
+
+ void removeEntry(SampleStorageEntry entry)
+ {
+ arts_return_if_fail(!entry.isNull());
+
+ vector<SampleStorageEntry>::iterator i;
+ for(i = entries.begin(); i != entries.end(); i++)
+ {
+ if(i->name() == entry.name())
+ {
+ entries.erase(i);
+ return;
+ }
+ }
+ arts_warning("SampleStorage::removeEntry(%s) failed\n",
+ entry.name().c_str());
+ }
+
+};
+REGISTER_IMPLEMENTATION(SampleStorage_impl);
+
+}
diff --git a/soundserver/simplesoundserver_impl.cc b/soundserver/simplesoundserver_impl.cc
new file mode 100644
index 0000000..608f146
--- /dev/null
+++ b/soundserver/simplesoundserver_impl.cc
@@ -0,0 +1,453 @@
+ /*
+
+ Copyright (C) 1999-2001 Stefan Westerfeld
+ 2001 Matthias Kretz
+ 2003 Allan Sandfeld Jensen
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ Permission is also granted to link this program with the Qt
+ library, treating Qt like a library that normally accompanies the
+ operating system kernel, whether or not that is in fact the case.
+
+ */
+
+#include "simplesoundserver_impl.h"
+#include "artsflow.h"
+#include "flowsystem.h"
+#include "audiosubsys.h"
+#include "connect.h"
+#include "debug.h"
+#include "reference.h"
+
+#include <stdio.h>
+#include <iostream>
+
+using namespace std;
+using namespace Arts;
+
+void SoundServerJob::detach(const Object&)
+{
+ // nothing by default
+}
+
+SoundServerJob::~SoundServerJob()
+{
+ // virtual destructor, since we've got virtual functions
+}
+
+PlayWavJob::PlayWavJob(const string& filename) :terminated(false)
+{
+ arts_debug("play '%s'!",filename.c_str());
+
+ connect(wav,out);
+ wav.filename(filename);
+ wav.start();
+ out.start();
+}
+
+void PlayWavJob::terminate()
+{
+ terminated = true;
+}
+
+bool PlayWavJob::done()
+{
+ return terminated || wav.finished();
+}
+
+PlayObjectJob::PlayObjectJob(PlayObject newPlob)
+ : plob(newPlob), terminated(false)
+{
+ plob.play();
+}
+
+void PlayObjectJob::terminate()
+{
+ terminated = true;
+ plob.halt();
+}
+
+bool PlayObjectJob::done()
+{
+ return terminated || (plob.state() == posIdle);
+}
+
+PlayStreamJob::PlayStreamJob(ByteSoundProducer bsp) : sender(bsp)
+{
+ int samplingRate = bsp.samplingRate();
+ int channels = bsp.channels();
+ int bits = bsp.bits();
+
+ arts_debug("incoming stream, parameters: rate=%d, %d bit, %d channels",
+ samplingRate, bits, channels);
+
+ if((samplingRate < 500 || samplingRate > 2000000)
+ || (channels != 1 && channels != 2) || (bits != 8 && bits != 16))
+ {
+ arts_warning("invalid stream parameters: rate=%d, %d bit, %d channels",
+ samplingRate, bits, channels);
+ terminate();
+ return;
+ }
+
+ convert.samplingRate(samplingRate);
+ convert.channels(channels);
+ convert.bits(bits);
+ ByteSoundProducerV2 senderv2 = DynamicCast(sender);
+ if(!senderv2.isNull())
+ out.title(senderv2.title());
+
+ connect(sender,"outdata",convert,"indata");
+ connect(convert,out);
+
+ convert.start();
+ out.start();
+}
+
+void PlayStreamJob::detach(const Object& object)
+{
+ if(object._isEqual(sender))
+ terminate();
+}
+
+void PlayStreamJob::terminate()
+{
+ sender = ByteSoundProducer::null();
+}
+
+bool PlayStreamJob::done()
+{
+ // when the sender is not alive any longer, assign a null object
+ if(!sender.isNull() && sender.error())
+ sender = ByteSoundProducer::null();
+
+ return sender.isNull() && !convert.running();
+}
+
+RecordStreamJob::RecordStreamJob(ByteSoundReceiver bsr) : receiver(bsr)
+{
+ int samplingRate = bsr.samplingRate();
+ int channels = bsr.channels();
+ int bits = bsr.bits();
+
+ arts_debug("outgoing stream, parameters: rate=%d, %d bit, %d channels",
+ samplingRate, bits, channels);
+
+ if((samplingRate < 500 || samplingRate > 2000000)
+ || (channels != 1 && channels != 2) || (bits != 8 && bits != 16))
+ {
+ arts_warning("invalid stream parameters: rate=%d, %d bit, %d channels",
+ samplingRate, bits, channels);
+ terminate();
+ return;
+ }
+
+ convert.samplingRate(samplingRate);
+ convert.channels(channels);
+ convert.bits(bits);
+ in.title(receiver.title());
+
+ connect(in,convert);
+ connect(convert,"outdata",receiver,"indata");
+
+ in.start();
+ convert.start();
+}
+
+void RecordStreamJob::detach(const Object& object)
+{
+ if(object._isEqual(receiver))
+ terminate();
+}
+
+void RecordStreamJob::terminate()
+{
+ receiver = ByteSoundReceiver::null();
+ convert.stop();
+ in.stop();
+}
+
+bool RecordStreamJob::done()
+{
+ // when the sender is not alive any longer, assign a null object
+ if(!receiver.isNull() && receiver.error())
+ receiver = ByteSoundReceiver::null();
+
+ return receiver.isNull();
+}
+
+/*
+ * ( This place where other objects for playing wave files and such will
+ * be connected, to get their output mixed with the other clients ).
+ * _____________________
+ * | soundcardBus | (a Synth_BUS_DOWNLINK for "out_soundcard")
+ * ~~~~~~~~~~~~~~~~~~~~~
+ * | |
+ * ___V____________V____
+ * | outstack | (here the user can plugin various effects)
+ * ~~~~~~~~~~~~~~~~~~~~~
+ * | |
+ * ___V____________V____
+ * | outVolume | (global output volume for the soundserver)
+ * ~~~~~~~~~~~~~~~~~~~~~
+ * | |
+ * ___V____________V____
+ * | playSound | (output to soundcard)
+ * ~~~~~~~~~~~~~~~~~~~~~
+ */
+SimpleSoundServer_impl::SimpleSoundServer_impl() : autoSuspendTime(0), bufferMultiplier(1)
+{
+ soundcardBus.busname("out_soundcard");
+ connect(soundcardBus,_outstack);
+ connect(_outstack,_outVolume);
+ connect(_outVolume,playSound);
+
+ if(AudioSubSystem::the()->fullDuplex())
+ {
+ recordBus.busname("in_soundcard");
+ connect(recordSound,recordBus);
+
+ recordBus.start();
+ recordSound.start();
+ }
+
+ soundcardBus.start();
+ _outVolume.start();
+ playSound.start();
+
+ asCount = 0; // AutoSuspend
+ Dispatcher::the()->ioManager()->addTimer(200,this);
+
+ // load Arts::MidiManager when installed
+ TraderQuery query;
+ query.supports("InterfaceName","Arts::MidiManager");
+
+ vector<TraderOffer> *offers = query.query();
+ if(offers->size())
+ _addChild(Arts::SubClass("Arts::MidiManager"),
+ "Extension_Arts::MidiManager");
+ delete offers;
+}
+
+SimpleSoundServer_impl::~SimpleSoundServer_impl()
+{
+ /*
+ * we don't need to care about the flow system nodes we started, since
+ * we have put them into Object_var's, which means that they will be
+ * freed automatically here
+ */
+ Dispatcher::the()->ioManager()->removeTimer(this);
+}
+
+long SimpleSoundServer_impl::play(const string& filename)
+{
+ PlayObject tmp( createPlayObject( filename ) );
+ if ( tmp.isNull() )
+ return 0; // No success creating the PlayObject and playing the file...
+ jobs.push_back(new PlayObjectJob(tmp));
+ return 1;
+}
+
+float SimpleSoundServer_impl::serverBufferTime()
+{
+ float hardwareBuffer = AudioSubSystem::the()->fragmentSize()
+ * AudioSubSystem::the()->fragmentCount();
+
+ float playSpeed = AudioSubSystem::the()->channels()
+ * AudioSubSystem::the()->samplingRate()
+ * (AudioSubSystem::the()->bits() / 8);
+
+ return 1000.0 * hardwareBuffer / playSpeed;
+}
+
+float SimpleSoundServer_impl::minStreamBufferTime()
+{
+ /*
+ * It is sane to assume that client side stream buffers must be >= server
+ * side hardware buffers (or it can come to dropouts during hardware
+ * buffer refill). The buffer size can be increased using the multiplier.
+ */
+ return bufferMultiplier * serverBufferTime();
+}
+
+void SimpleSoundServer_impl::attach(ByteSoundProducer bsp)
+{
+ arts_return_if_fail(!bsp.isNull());
+
+ jobs.push_back(new PlayStreamJob(bsp));
+}
+
+void SimpleSoundServer_impl::detach(ByteSoundProducer bsp)
+{
+ arts_return_if_fail(!bsp.isNull());
+ arts_debug("detach incoming stream");
+
+ list<SoundServerJob *>::iterator j;
+
+ for(j = jobs.begin();j != jobs.end();j++)
+ (*j)->detach(bsp);
+}
+
+void SimpleSoundServer_impl::attachRecorder(ByteSoundReceiver bsr)
+{
+ arts_return_if_fail(!bsr.isNull());
+ jobs.push_back(new RecordStreamJob(bsr));
+}
+
+void SimpleSoundServer_impl::detachRecorder(ByteSoundReceiver bsr)
+{
+ arts_return_if_fail(!bsr.isNull());
+ arts_debug("detach outgoing stream");
+
+ list<SoundServerJob *>::iterator j;
+
+ for(j = jobs.begin();j != jobs.end();j++)
+ (*j)->detach(bsr);
+}
+
+StereoEffectStack SimpleSoundServer_impl::outstack()
+{
+ return _outstack;
+}
+
+Object SimpleSoundServer_impl::createObject(const string& name)
+{
+ // don't use SubClass(name) as this will abort if the object is not found
+ return Object::_from_base(ObjectManager::the()->create(name));
+}
+
+void SimpleSoundServer_impl::notifyTime()
+{
+ static long lock = 0;
+ assert(!lock); // paranoid reentrancy check (remove me later)
+ lock++;
+ /*
+ * Three times the same game: look if a certain object is still
+ * active - if yes, keep, if no, remove
+ */
+
+ /* look for jobs which may have terminated by now */
+ list<SoundServerJob *>::iterator i;
+
+ i = jobs.begin();
+ while(i != jobs.end())
+ {
+ SoundServerJob *job = *i;
+
+ if(job->done())
+ {
+ delete job;
+ jobs.erase(i);
+ arts_debug("job finished");
+ i = jobs.begin();
+ }
+ else i++;
+ }
+
+/*
+ * AutoSuspend
+ */
+ if(Dispatcher::the()->flowSystem()->suspendable() &&
+ !Dispatcher::the()->flowSystem()->suspended() &&
+ autoSuspendTime != 0)
+ {
+ asCount++;
+ if(asCount > autoSuspendTime*5)
+ {
+ Dispatcher::the()->flowSystem()->suspend();
+ arts_info("sound server suspended");
+ }
+ }
+ else
+ asCount = 0;
+ lock--;
+}
+
+PlayObject SimpleSoundServer_impl::createPlayObject(const string& filename)
+{
+ string objectType = "";
+
+ /*
+ * figure out extension (as lowercased letters)
+ */
+ string extension = "";
+ bool extensionok = false;
+ string::const_reverse_iterator i;
+ for(i = filename.rbegin(); i != filename.rend() && !extensionok; i++)
+ {
+ if(*i == '.')
+ extensionok = true;
+ else
+ extension.insert(extension.begin(), (char)tolower(*i));
+ }
+
+ /*
+ * query trader for PlayObjects which support this
+ */
+ if(extensionok)
+ {
+ arts_debug("search playobject, extension = %s",extension.c_str());
+
+ TraderQuery query;
+ query.supports("Interface","Arts::PlayObject");
+ query.supports("Extension",extension);
+
+ vector<TraderOffer> *offers = query.query();
+ if(!offers->empty())
+ objectType = offers->front().interfaceName(); // first offer
+
+ delete offers;
+ }
+
+ /*
+ * create a PlayObject and connect it
+ */
+ if(!objectType.empty())
+ {
+ arts_debug("creating %s to play file %s", objectType.c_str(), filename.c_str() );
+
+ PlayObject result = SubClass(objectType);
+ if(result.loadMedia(filename))
+ {
+ // TODO: check for existence of left & right streams
+ Synth_BUS_UPLINK uplink;
+ uplink.busname("out_soundcard");
+ connect(result,"left",uplink,"left");
+ connect(result,"right",uplink,"right");
+ uplink.start();
+ result._node()->start();
+ result._addChild(uplink,"uplink");
+ return result;
+ }
+ else arts_warning("couldn't load file %s", filename.c_str());
+ }
+ else arts_warning("file format extension %s unsupported",extension.c_str());
+
+ return PlayObject::null();
+}
+
+#ifndef __SUNPRO_CC
+/* SunPRO CC has problems finding the SimpleSoundServer_impl constructor
+ implementation from a template instantiation file, if this is here,
+ although I verified that the requested and provided symbols do indeed match,
+ and the latter is global. Wonderfully this problem goes away, if we don't
+ register the implementation here, but in another file. I bet this is
+ because of the static var that is created by the macro. */
+REGISTER_IMPLEMENTATION(SimpleSoundServer_impl);
+#endif
diff --git a/soundserver/simplesoundserver_impl.h b/soundserver/simplesoundserver_impl.h
new file mode 100644
index 0000000..88a530a
--- /dev/null
+++ b/soundserver/simplesoundserver_impl.h
@@ -0,0 +1,148 @@
+ /*
+
+ Copyright (C) 1999-2000 Stefan Westerfeld
+ 2001 Matthias Kretz
+ 2003 Allan Sandfeld Jensen
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ Permission is also granted to link this program with the Qt
+ library, treating Qt like a library that normally accompanies the
+ operating system kernel, whether or not that is in fact the case.
+
+ */
+
+#ifndef SIMPLESOUNDSERVER_IMPL_H
+#define SIMPLESOUNDSERVER_IMPL_H
+
+#include "soundserver.h"
+#include "artsflow.h"
+#include <list>
+#include "arts_export.h"
+
+namespace Arts {
+
+class ARTS_EXPORT SoundServerJob
+{
+public:
+ long ID;
+ virtual ~SoundServerJob();
+
+ virtual void detach(const Object& object);
+ virtual void terminate() = 0;
+ virtual bool done() = 0;
+};
+
+class ARTS_EXPORT PlayWavJob : public SoundServerJob
+{
+protected:
+ Synth_PLAY_WAV wav;
+ Synth_AMAN_PLAY out;
+ bool terminated;
+
+public:
+ PlayWavJob(const std::string& filename);
+
+ void terminate();
+ bool done();
+};
+
+class ARTS_EXPORT PlayObjectJob : public SoundServerJob
+{
+protected:
+ PlayObject plob;
+ bool terminated;
+
+public:
+ PlayObjectJob(PlayObject newPlob);
+
+ void terminate();
+ bool done();
+};
+
+class ARTS_EXPORT PlayStreamJob : public SoundServerJob
+{
+protected:
+ ByteSoundProducer sender;
+ ByteStreamToAudio convert;
+ Synth_AMAN_PLAY out;
+
+public:
+ PlayStreamJob(ByteSoundProducer bsp);
+
+ void detach(const Object& object);
+ void terminate();
+ bool done();
+};
+
+class ARTS_EXPORT RecordStreamJob : public SoundServerJob
+{
+protected:
+ ByteSoundReceiver receiver;
+ AudioToByteStream convert;
+ Synth_AMAN_RECORD in;
+
+public:
+ RecordStreamJob(ByteSoundReceiver bsr);
+
+ void detach(const Object& object);
+ void terminate();
+ bool done();
+};
+
+class ARTS_EXPORT SimpleSoundServer_impl : virtual public SimpleSoundServer_skel,
+ public TimeNotify
+{
+protected:
+ Synth_PLAY playSound;
+ Synth_RECORD recordSound;
+ Synth_BUS_DOWNLINK soundcardBus;
+ Synth_BUS_UPLINK recordBus;
+ std::list<SoundServerJob *> jobs;
+ StereoEffectStack _outstack;
+ StereoVolumeControl _outVolume;
+ long asCount;
+ long autoSuspendTime;
+ long bufferMultiplier;
+
+public:
+ SimpleSoundServer_impl();
+ ~SimpleSoundServer_impl();
+
+ void notifyTime();
+
+ // streaming audio
+ float minStreamBufferTime();
+ float serverBufferTime();
+ void attach(ByteSoundProducer bsp);
+ void detach(ByteSoundProducer bsp);
+ void attachRecorder(ByteSoundReceiver bsr);
+ void detachRecorder(ByteSoundReceiver bsr);
+
+ // simple soundserver interface
+ long play(const std::string& s);
+
+ // kmedia2
+ PlayObject createPlayObject(const std::string& filename);
+ StereoEffectStack outstack();
+ Object createObject(const std::string& name);
+};
+
+}
+
+#endif /* SIMPLESOUNDSERVER_IMPL_H */
diff --git a/soundserver/soundserver.idl b/soundserver/soundserver.idl
new file mode 100644
index 0000000..7c2cc41
--- /dev/null
+++ b/soundserver/soundserver.idl
@@ -0,0 +1,294 @@
+#include "artsflow.idl"
+#include "kmedia2.idl"
+#include "core.idl"
+
+module Arts {
+
+/**
+ * One entry of the sample storage - initially, you'll need to fill the entry.
+ *
+ * To do so, call write repeatedly to fill it with data, and finish() when
+ * you are done. After that you can use the filename attribute to get the
+ * name of the file on the server that has stored the data. You can use
+ * this filename for other things (i.e. SimpleSoundServer::play).
+ */
+interface SampleStorageEntry {
+ readonly attribute string name;
+ readonly attribute string filename;
+ readonly attribute boolean completed;
+
+ void write(sequence<byte> data);
+ void finish();
+};
+
+/**
+ * Interface for storing files on the sound server
+ */
+interface SampleStorage {
+ void constructor(string directory, boolean clearOnInit);
+
+ /**
+ * creates a new entry which you can use to store a sample - if you just
+ * create an entry, it will be private to you, i.e. you can use it, and
+ * as soon as you don't need it any longer, it will be freed
+ *
+ * if you want that the entry stays in the storage, you need to add it,
+ * and it will stay then until you remove it
+ */
+ SampleStorageEntry createEntry(string name);
+
+ /**
+ * add an entry (which will make it accessible via findEntry) - remember
+ * to eventually call removeEntry, or the entry will stay there forever
+ */
+ void addEntry(SampleStorageEntry entry);
+
+ /**
+ * removes an entry, that is, the entry will only stay there until
+ * nobody needs it any more and then get freed
+ */
+ void removeEntry(SampleStorageEntry entry);
+
+ /**
+ * finds an entry by name
+ */
+ SampleStorageEntry findEntry(string name);
+};
+
+/**
+ * Producer of byte sound
+ *
+ * This is used inside the sound server interface
+ */
+interface ByteSoundProducer : SynthModule
+{
+ readonly attribute long samplingRate;
+ readonly attribute long channels;
+ readonly attribute long bits;
+
+ async out byte stream outdata;
+};
+
+/**
+ * V2 version of the ByteSoundProducer interface that implements the title
+ * attribute
+ */
+interface ByteSoundProducerV2 : ByteSoundProducer
+{
+ readonly attribute string title;
+};
+
+/**
+ * Receiver of byte sound
+ */
+interface ByteSoundReceiver : SynthModule
+{
+ readonly attribute long samplingRate;
+ readonly attribute long channels;
+ readonly attribute long bits;
+ readonly attribute string title;
+
+ async in byte stream indata;
+};
+
+/**
+ * This is a very simple sound server interface
+ *
+ * WARNING: This currently inherits a KMedia2 PlayObjectFactory for test
+ * purposes, but don't rely on that
+ */
+
+interface SimpleSoundServer : PlayObjectFactory
+{
+ readonly attribute StereoEffectStack outstack;
+
+ /**
+ * tries to play the sound in "filename"
+ *
+ * returns an ID when success 0 when it fails
+ */
+ long play(string filename);
+
+ /**
+ * returns true if the sound in ID is still playing
+ */
+ //boolean isPlaying(long ID);
+
+ /**
+ * stops a playing sound by ID
+ */
+ //void stop(long ID);
+
+ /**
+ * specifies the minimum amount of milliseconds that have to be buffered
+ * to allow safe streaming (without interruptions) from/to external apps
+ *
+ * this depends on the realtime parameters the sound server itself uses
+ * to talk to the hardware
+ */
+ readonly attribute float minStreamBufferTime;
+
+ /**
+ * specifies the amount of milliseconds the server itself spends with
+ * the hardware (buffering latency) - so if you stream into the server,
+ * you should have a yourStreamBufferTime >= minStreamBufferTime, and
+ * the total latency is
+ *
+ * totalLatency = yourStreamBufferTime + serverBufferTime
+ */
+ readonly attribute float serverBufferTime;
+
+ /**
+ * attaches a byte sound producer (read: a client which produces/mixes
+ * an audio stream itself and just wants playback via the soundserver)
+ */
+ void attach(ByteSoundProducer producer);
+
+ /**
+ * detaches a previous attached byte sound producer
+ */
+ void detach(ByteSoundProducer producer);
+
+ /**
+ * attaches a byte sound receiver (a client that records an
+ * audio stream from the soundserver)
+ */
+ void attachRecorder(ByteSoundReceiver receiver);
+
+ /**
+ * detaches a previous attached byte sound receiver
+ */
+ void detachRecorder(ByteSoundReceiver receiver);
+
+ object createObject(string name);
+};
+
+enum RealtimeStatus { rtRealtime, rtNoSupport, rtNoWrapper, rtNoRealtime };
+
+/**
+ * This is an enhanced sound server interface which can be used to
+ * query status information or suspend the soundserver right away
+ */
+interface SoundServer : SimpleSoundServer
+{
+ readonly attribute RealtimeStatus realtimeStatus;
+
+ /**
+ * Returns how many seconds you have to wait _now_ for the soundserver
+ * to suspend. A value of -1 signals that the sound server is busy and
+ * will not suspend automatically at the moment.
+ */
+ readonly attribute long secondsUntilSuspend;
+
+ /**
+ * Makes the soundserver suspend now _if_ it is not busy playing, that
+ * is, if it is "suspendable". Returns true if successful.
+ */
+ boolean suspend();
+
+ /**
+ * Asks the soundserver if it is suspended. Returns true if so.
+ */
+ boolean suspended();
+
+ /**
+ * Permanently terminates the sound server - this is not intended to be
+ * widely used. However, it provides a way to "kill" the sound server,
+ * even if you don't reside on the same host with it, and even if you
+ * don't know the process id, and so on. In the future it also offers
+ * the possibility for interested apps to be informed before the server
+ * goes away, and for important apps to block termination.
+ *
+ * Returns true if successful.
+ */
+ boolean terminate();
+};
+
+/**
+ * This is an even more enhanced sound server interface that supports changing
+ * the autosuspend time, and returning more information about the server
+ * settings.
+ */
+interface SoundServerV2 : SoundServer, PlayObjectFactoryV2
+{
+ /**
+ * Time in seconds after which server will suspend if idle.
+ */
+ attribute long autoSuspendSeconds;
+
+ /**
+ * Multiplier for size of network buffers. Default is 1,
+ * which is fragment size * fragment count. (expressed
+ * as milliseconds).
+ */
+ attribute long bufferSizeMultiplier;
+
+ /**
+ * Current CPU usage in percent
+ */
+ readonly attribute float cpuUsage;
+
+ /**
+ * AudioSubSystem parameters
+ */
+ readonly attribute string audioMethod;
+ readonly attribute long samplingRate;
+ readonly attribute long channels;
+ readonly attribute long bits;
+ readonly attribute boolean fullDuplex;
+ readonly attribute string audioDevice;
+ readonly attribute long fragments;
+ readonly attribute long fragmentSize;
+
+ /**
+ * version
+ */
+ readonly attribute string version;
+
+ /**
+ * global output volume for the sound server
+ */
+ readonly attribute StereoVolumeControl outVolume;
+
+ /**
+ * for storing samples on the sound server
+ */
+ readonly attribute SampleStorage sampleStorage;
+
+ /**
+ * this method checks for new object implementations (you can call this
+ * if you have implemented and installed new components in C++ or with
+ * artsbuilder, to make the soundserver use them without restart)
+ */
+ void checkNewObjects();
+};
+
+/**
+ * A KMedia2 Wave PlayObject
+ */
+interface WavPlayObject : PlayObject, SynthModule
+{
+ out audio stream left,right;
+};
+
+/**
+ * An advanced KMedia2 PlayObject based on GSL datahandles
+ */
+interface GSLPlayObject : PlayObject, PitchablePlayObject, SynthModule
+{
+ attribute boolean done;
+
+ out audio stream left,right;
+};
+
+/**
+ * Helper interface to ensure that artsd gets initialized properly when
+ * multiple artsd processes are started at the same time.
+ */
+interface SoundServerStartup
+{
+ void lock();
+ void unlock();
+};
+
+};
diff --git a/soundserver/soundserver_impl.cc b/soundserver/soundserver_impl.cc
new file mode 100644
index 0000000..26a9b1f
--- /dev/null
+++ b/soundserver/soundserver_impl.cc
@@ -0,0 +1,90 @@
+ /*
+
+ Copyright (C) 2000 Hans Meine
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ Permission is also granted to link this program with the Qt
+ library, treating Qt like a library that normally accompanies the
+ operating system kernel, whether or not that is in fact the case.
+
+ */
+
+#include "soundserver_impl.h"
+#include "artsflow.h"
+#include "flowsystem.h"
+#include "audiosubsys.h"
+#include "connect.h"
+#include "debug.h"
+#include <stdlib.h>
+#include <iostream>
+#include <algorithm>
+
+using namespace Arts;
+
+#include "config.h"
+
+#ifdef HAVE_REALTIME_SCHED
+#include <sched.h>
+RealtimeStatus SoundServer_impl::realtimeStatus() {
+ int sched = sched_getscheduler(0);
+ if (sched == SCHED_FIFO || sched == SCHED_RR) return rtRealtime;
+ if (!getenv("STARTED_THROUGH_ARTSWRAPPER")) return rtNoWrapper;
+ return rtNoRealtime;
+}
+#else
+RealtimeStatus SoundServer_impl::realtimeStatus() {
+ return rtNoSupport;
+}
+#endif
+
+long SoundServer_impl::secondsUntilSuspend() {
+ if (Dispatcher::the()->flowSystem()->suspended())
+ return 0;
+ if (Dispatcher::the()->flowSystem()->suspendable() && autoSuspendTime == 0)
+ return -2;
+ if (Dispatcher::the()->flowSystem()->suspendable())
+ return (autoSuspendTime*5 - asCount)/5;
+ return -1;
+}
+
+bool SoundServer_impl::suspend() {
+ if (Dispatcher::the()->flowSystem()->suspended())
+ {
+ return true;
+ }
+ if(Dispatcher::the()->flowSystem()->suspendable())
+ {
+ Dispatcher::the()->flowSystem()->suspend();
+ arts_info("sound server suspended by client");
+ return true;
+ }
+ return false;
+}
+
+bool SoundServer_impl::suspended() {
+ return Dispatcher::the()->flowSystem()->suspended();
+}
+
+bool SoundServer_impl::terminate() {
+ Dispatcher::the()->terminate();
+ return true;
+}
+
+#ifndef __SUNPRO_CC
+/* See bottom of simplesoundserver_impl.cc for the reason this is here. */
+REGISTER_IMPLEMENTATION(SoundServer_impl);
+#endif
diff --git a/soundserver/soundserver_impl.h b/soundserver/soundserver_impl.h
new file mode 100644
index 0000000..de2aff6
--- /dev/null
+++ b/soundserver/soundserver_impl.h
@@ -0,0 +1,46 @@
+ /*
+
+ Copyright (C) 2000 Hans Meine
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ Permission is also granted to link this program with the Qt
+ library, treating Qt like a library that normally accompanies the
+ operating system kernel, whether or not that is in fact the case.
+
+ */
+
+#ifndef SOUNDSERVER_IMPL_H
+#define SOUNDSERVER_IMPL_H
+
+#include "soundserver.h"
+#include "simplesoundserver_impl.h"
+#include "soundserver.h"
+
+namespace Arts {
+
+ class SoundServer_impl : virtual public SoundServer_skel,
+ public SimpleSoundServer_impl
+ {
+ RealtimeStatus realtimeStatus();
+ long secondsUntilSuspend();
+ bool suspend();
+ bool suspended();
+ bool terminate();
+ };
+}
+
+#endif /* VERBOSESOUNDSERVER_IMPL_H */
diff --git a/soundserver/soundserverstartup_impl.cc b/soundserver/soundserverstartup_impl.cc
new file mode 100644
index 0000000..ba68dd4
--- /dev/null
+++ b/soundserver/soundserverstartup_impl.cc
@@ -0,0 +1,99 @@
+ /*
+
+ Copyright (C) 2003 Stefan Westerfeld
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+#include "config.h"
+#include "debug.h"
+#include "soundserverstartup_impl.h"
+#include <sys/time.h>
+#include <time.h>
+#include <cstdlib>
+
+using namespace Arts;
+
+SoundServerStartup_impl::SoundServerStartup_impl()
+{
+ locked = false;
+ /*
+ * create a sane random seed, pseudo random is not enough for
+ * our purposes
+ */
+ timeval tv;
+ gettimeofday(&tv, NULL);
+
+ int pid = getpid();
+ srand(tv.tv_sec);
+ srand(rand() + tv.tv_usec);
+ srand(rand() + pid);
+}
+
+SoundServerStartup_impl::~SoundServerStartup_impl()
+{
+ if (locked)
+ unlock();
+}
+
+void SoundServerStartup_impl::cleanReference()
+{
+ SoundServerStartup test = Reference("global:Arts_SoundServerStartup");
+ if(test.isNull())
+ Dispatcher::the()->globalComm().erase("Arts_SoundServerStartup");
+}
+
+void SoundServerStartup_impl::lock()
+{
+ arts_return_if_fail (!locked);
+
+ while (!ObjectManager::the()->addGlobalReference(self(), "Arts_SoundServerStartup"))
+ {
+ arts_debug ("[artsd: %5d] parallel startup detected: sleeping",getpid());
+
+ /*
+ * if several instances of artsd are started at the same time,
+ * we don't want all of them to retry getting a reference at
+ * exactly the same moment ; thus we sleep for a random amount
+ * of time, rather than for exactly one second, if possible
+ */
+#ifdef HAVE_USLEEP
+ usleep((rand() % 700000) + 200000);
+#else
+ sleep(1);
+#endif
+ cleanReference();
+ }
+ locked = true;
+ arts_debug ("[artsd: %5d] SoundServerStartup --> got lock",getpid());
+}
+
+void SoundServerStartup_impl::unlock()
+{
+ arts_return_if_fail (locked);
+
+ arts_debug ("[artsd: %5d] SoundServerStartup <-- released lock",getpid());
+ Dispatcher::the()->globalComm().erase("Arts_SoundServerStartup");
+ locked = false;
+}
+
+namespace Arts {
+#ifndef __SUNPRO_CC
+ /* See bottom of simplesoundserver_impl.cc for the reason this is here. */
+REGISTER_IMPLEMENTATION(SoundServerStartup_impl);
+#endif
+}
diff --git a/soundserver/soundserverstartup_impl.h b/soundserver/soundserverstartup_impl.h
new file mode 100644
index 0000000..d84731a
--- /dev/null
+++ b/soundserver/soundserverstartup_impl.h
@@ -0,0 +1,49 @@
+ /*
+
+ Copyright (C) 2003 Stefan Westerfeld
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ */
+
+#ifndef SOUNDSERVERSTARTUP_IMPL_H
+#define SOUNDSERVERSTARTUP_IMPL_H
+
+#include "soundserver.h"
+
+namespace Arts {
+
+class SoundServerStartup_impl : public SoundServerStartup_skel {
+protected:
+ bool locked;
+
+ void cleanReference();
+public:
+ SoundServerStartup self() {
+ return SoundServerStartup::_from_base(_copy());
+ }
+
+ SoundServerStartup_impl();
+ ~SoundServerStartup_impl();
+
+ void lock();
+ void unlock();
+};
+
+}
+
+#endif // SOUNDSERVERSTARTUP_IMPL_H
+
diff --git a/soundserver/soundserverv2_impl.cc b/soundserver/soundserverv2_impl.cc
new file mode 100644
index 0000000..0abe5c7
--- /dev/null
+++ b/soundserver/soundserverv2_impl.cc
@@ -0,0 +1,386 @@
+ /*
+
+ Copyright (C) 2001 Jeff Tranter
+ 2001 Stefan Westerfeld
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ Permission is also granted to link this program with the Qt
+ library, treating Qt like a library that normally accompanies the
+ operating system kernel, whether or not that is in fact the case.
+
+ */
+
+#include "artsflow.h"
+#include "flowsystem.h"
+#include "audiosubsys.h"
+#include "connect.h"
+#include "debug.h"
+#include "soundserverv2_impl.h"
+#include "artsversion.h"
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fstream>
+#include <set>
+#include <cstring>
+#include <cstdlib>
+
+#include "config.h"
+
+using namespace Arts;
+using namespace std;
+
+SoundServerV2_impl::SoundServerV2_impl()
+ : _sampleStorage(SampleStorage(
+ MCOPUtils::createFilePath("artsd-samples"),true))
+{
+ checkNewObjects();
+}
+
+std::string SoundServerV2_impl:: audioMethod() {
+ return AudioSubSystem::the()->audioIO();
+}
+
+long SoundServerV2_impl:: samplingRate() {
+ return AudioSubSystem::the()->samplingRate();
+}
+
+long SoundServerV2_impl:: channels() {
+ return AudioSubSystem::the()->channels();
+}
+
+long SoundServerV2_impl:: bits() {
+ return AudioSubSystem::the()->bits();
+}
+
+bool SoundServerV2_impl:: fullDuplex() {
+ return AudioSubSystem::the()->fullDuplex();
+}
+
+std::string SoundServerV2_impl:: audioDevice() {
+ return AudioSubSystem::the()->deviceName();
+}
+
+long SoundServerV2_impl::fragments() {
+ return AudioSubSystem::the()->fragmentCount();
+}
+
+long SoundServerV2_impl::fragmentSize() {
+ return AudioSubSystem::the()->fragmentSize();
+}
+
+long SoundServerV2_impl::autoSuspendSeconds() {
+ return autoSuspendTime;
+}
+
+void SoundServerV2_impl::autoSuspendSeconds(long int newValue) {
+ autoSuspendTime = newValue;
+}
+
+std::string SoundServerV2_impl::version() {
+ return ARTS_VERSION;
+}
+
+long SoundServerV2_impl::bufferSizeMultiplier() {
+ return bufferMultiplier;
+}
+
+void SoundServerV2_impl::bufferSizeMultiplier(long newValue) {
+ bufferMultiplier = newValue;
+}
+
+StereoVolumeControl SoundServerV2_impl::outVolume() {
+ return _outVolume;
+}
+
+SampleStorage SoundServerV2_impl::sampleStorage() {
+ return _sampleStorage;
+}
+
+PlayObject SoundServerV2_impl::createPlayObjectForURL(const std::string& url, const std::string& mimetype, bool createBUS)
+{
+ arts_debug("search playobject, mimetype = %s", mimetype.c_str());
+
+ TraderQuery query;
+ query.supports("Interface","Arts::PlayObject");
+ query.supports("MimeType", mimetype);
+
+ string objectType;
+
+ vector<TraderOffer> *offers = query.query();
+ if(!offers->empty())
+ objectType = offers->front().interfaceName(); // first offer
+
+ delete offers;
+
+ /*
+ * create a PlayObject and connect it
+ */
+ if(!objectType.empty())
+ {
+ arts_debug("creating %s to play file", objectType.c_str());
+
+ PlayObject result = SubClass(objectType);
+ if(result.loadMedia(url))
+ {
+ if(createBUS)
+ {
+ // TODO: check for existence of left & right streams
+ Synth_BUS_UPLINK uplink;
+ uplink.busname("out_soundcard");
+ connect(result,"left",uplink,"left");
+ connect(result,"right",uplink,"right");
+ uplink.start();
+ result._node()->start();
+ result._addChild(uplink,"uplink");
+ return result;
+ }
+ else
+ return result;
+ }
+ else arts_warning("couldn't load file %s", url.c_str());
+ }
+ else arts_warning("mimetype %s unsupported", mimetype.c_str());
+
+ return PlayObject::null();
+}
+
+PlayObject SoundServerV2_impl::createPlayObjectForStream(InputStream instream, const std::string& mimetype, bool createBUS)
+{
+ arts_debug("search streamplayobject, mimetype = %s", mimetype.c_str());
+
+ TraderQuery query;
+ query.supports("Interface","Arts::StreamPlayObject");
+ query.supports("MimeType", mimetype);
+
+ string objectType;
+
+ vector<TraderOffer> *offers = query.query();
+ if(!offers->empty())
+ objectType = offers->front().interfaceName(); // first offer
+
+ delete offers;
+
+ /*
+ * create a PlayObject and connect it
+ */
+ if(!objectType.empty())
+ {
+ arts_debug("creating %s to play file", objectType.c_str());
+
+ StreamPlayObject result = SubClass(objectType);
+ result.streamMedia(instream);
+
+ if(createBUS)
+ {
+ // TODO: check for existence of left & right streams
+ Synth_BUS_UPLINK uplink;
+ uplink.busname("out_soundcard");
+ connect(result,"left",uplink,"left");
+ connect(result,"right",uplink,"right");
+ uplink.start();
+ result._node()->start();
+ result._addChild(uplink,"uplink");
+ return result;
+ }
+ else
+ return result;
+ }
+ else arts_warning("mimetype %s unsupported for streaming", mimetype.c_str());
+
+ return PlayObject::null();
+}
+
+static void clearDirectory(const string& directory)
+{
+ DIR *dir = opendir(directory.c_str());
+ if(!dir) return;
+
+ struct dirent *de;
+ while((de = readdir(dir)) != 0)
+ {
+ string currentEntry = directory + "/" + de->d_name;
+
+ if(de->d_name[0] != '.')
+ {
+ unlink(currentEntry.c_str());
+ }
+ }
+ closedir(dir);
+}
+
+/* copied from mcopidl */
+static void doTypeIndex(string dir, string prefix, ModuleDef& module)
+{
+ FILE *typeIndex = fopen((dir+"/"+prefix+".mcopclass").c_str(),"w");
+
+ vector<string> supportedTypes;
+
+ vector<InterfaceDef>::iterator ii;
+ for(ii = module.interfaces.begin(); ii != module.interfaces.end(); ii++)
+ supportedTypes.push_back(ii->name);
+
+ vector<TypeDef>::iterator ti;
+ for(ti = module.types.begin(); ti != module.types.end(); ti++)
+ supportedTypes.push_back(ti->name);
+
+ string supportedTypesList;
+ vector<string>::iterator si;
+ bool first = true;
+ for(si = supportedTypes.begin(); si != supportedTypes.end(); si++)
+ {
+ if(!first) supportedTypesList += ",";
+
+ supportedTypesList += (*si);
+ first = false;
+ }
+ fprintf(typeIndex, "# this file was generated by artsd - do not edit\n");
+ fprintf(typeIndex,"Type=%s\n",supportedTypesList.c_str());
+ fprintf(typeIndex,"TypeFile=%s.mcoptype\n",prefix.c_str());
+ fclose(typeIndex);
+}
+
+void SoundServerV2_impl::checkNewObjects()
+{
+ const char *home = getenv("HOME");
+ arts_return_if_fail(home != 0);
+
+ string dir = home + string("/.mcop/trader-cache");
+ string dataVersionFileName = dir + "/cache-data-version";
+
+ mkdir(home,0755);
+ mkdir((home+string("/.mcop")).c_str(),0755);
+ if(mkdir(dir.c_str(),0755) != 0)
+ {
+ string why = strerror(errno);
+
+ struct stat st;
+ stat(dir.c_str(),&st);
+ if(!S_ISDIR(st.st_mode))
+ {
+ arts_warning("can't create directory %s to fill it with"
+ " trader data (%s)", dir.c_str(), why.c_str());
+ return;
+ }
+ }
+
+ TraderQuery query;
+ query.supports("Interface", "Arts::Loader");
+ vector<TraderOffer> *offers = query.query();
+ vector<TraderOffer>::iterator i;
+
+ set<string> newDataVersion, cacheDataVersion;
+ for(i = offers->begin(); i != offers->end(); i++)
+ {
+ // TODO: error checking?
+ Arts::Loader loader = SubClass(i->interfaceName());
+ newDataVersion.insert(loader.dataVersion());
+ }
+
+ /* change this line if you change the cache update code */
+ newDataVersion.insert("Cache-Update-Code-Version:1.0");
+
+ /* load cache-data-version file */
+ {
+ ifstream infile(dataVersionFileName.c_str());
+
+ string line;
+ while(infile >> line)
+ cacheDataVersion.insert(line);
+ }
+
+ /* if it differs, rebuild trader cache */
+ if(cacheDataVersion != newDataVersion)
+ {
+ clearDirectory(dir);
+
+ /* save new cache-data-version file */
+ {
+ ofstream out(dataVersionFileName.c_str());
+
+ set<string>::iterator i;
+ for(i = newDataVersion.begin(); i != newDataVersion.end(); i++)
+ out << *i << endl;
+ }
+ rebuildTraderCache(dir, offers);
+ }
+ delete offers;
+}
+
+void SoundServerV2_impl::rebuildTraderCache(const string& directory,
+ vector<TraderOffer> *offers)
+{
+ vector<TraderOffer>::iterator i;
+
+ for(i = offers->begin(); i != offers->end(); i++)
+ {
+ // TODO: error checking?
+ Arts::Loader loader = SubClass(i->interfaceName());
+
+ /* put trader-information in ~/.mcop/trader-cache */
+ vector<TraderEntry> *entries = loader.traderEntries();
+ vector<TraderEntry>::iterator ei;
+ for(ei = entries->begin(); ei != entries->end(); ei++)
+ {
+ const TraderEntry& entry = *ei;
+
+ FILE *traderFile = fopen((directory+"/"+entry.interfaceName+".mcopclass").c_str(),"w");
+ fprintf(traderFile, "# this file was generated by artsd - do not edit\n");
+ vector<string>::const_iterator li;
+ for(li = entry.lines.begin(); li != entry.lines.end(); li++)
+ fprintf(traderFile,"%s\n", li->c_str());
+
+ fclose(traderFile);
+ }
+ delete entries;
+
+ /* put type-information in ~/.mcop/trader-cache */
+ vector<ModuleDef> *modules = loader.modules();
+ vector<ModuleDef>::iterator mi;
+ for(mi = modules->begin(); mi != modules->end(); mi++)
+ {
+ Arts::ModuleDef& module = *mi;
+
+ Buffer b;
+ module.writeType(b);
+
+ FILE *typeFile = fopen((directory + "/" + module.moduleName+".arts.mcoptype").c_str(),"w");
+ unsigned long towrite = b.size();
+ fwrite(b.read(towrite),1,towrite,typeFile);
+ fclose(typeFile);
+
+ doTypeIndex(directory,module.moduleName+".arts",module);
+ }
+ delete modules;
+ }
+ Dispatcher::the()->reloadTraderData();
+}
+
+float SoundServerV2_impl::cpuUsage()
+{
+ return CPUUsage::the()->usage() * 100.0;
+}
+
+
+#ifndef __SUNPRO_CC
+/* See bottom of simplesoundserver_impl.cc for the reason this is here. */
+REGISTER_IMPLEMENTATION(SoundServerV2_impl);
+#endif
diff --git a/soundserver/soundserverv2_impl.h b/soundserver/soundserverv2_impl.h
new file mode 100644
index 0000000..22bd1cf
--- /dev/null
+++ b/soundserver/soundserverv2_impl.h
@@ -0,0 +1,71 @@
+ /*
+
+ Copyright (C) 2001 Jeff Tranter
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ Permission is also granted to link this program with the Qt
+ library, treating Qt like a library that normally accompanies the
+ operating system kernel, whether or not that is in fact the case.
+
+ */
+
+#ifndef SOUNDSERVERV2_IMPL_H
+#define SOUNDSERVERV2_IMPL_H
+
+#include "soundserver.h"
+#include "simplesoundserver_impl.h"
+#include "soundserver_impl.h"
+#include "soundserver.h"
+#include "cpuusage.h"
+
+namespace Arts {
+ class SoundServerV2_impl : virtual public SoundServerV2_skel,
+ public SoundServer_impl
+ {
+ protected:
+ SampleStorage _sampleStorage;
+
+ void rebuildTraderCache(const std::string& directory,
+ std::vector<TraderOffer> *offers);
+
+ public:
+ SoundServerV2_impl();
+
+ long autoSuspendSeconds();
+ void autoSuspendSeconds(long newValue);
+ std::string audioMethod();
+ long samplingRate();
+ long channels();
+ long bits();
+ bool fullDuplex();
+ std::string audioDevice();
+ long fragments();
+ long fragmentSize();
+ std::string version();
+ long bufferSizeMultiplier();
+ void bufferSizeMultiplier(long newValue);
+ StereoVolumeControl outVolume();
+ SampleStorage sampleStorage();
+ void checkNewObjects();
+ float cpuUsage();
+
+ Arts::PlayObject createPlayObjectForURL(const std::string& url, const std::string& mimetype, bool createBUS);
+ Arts::PlayObject createPlayObjectForStream(Arts::InputStream instream, const std::string& mimetype, bool createBUS);
+ };
+}
+
+#endif /* SOUNDSERVERV2_IMPL_H */
diff --git a/soundserver/stdoutwriter_impl.cc b/soundserver/stdoutwriter_impl.cc
new file mode 100644
index 0000000..d93ca2b
--- /dev/null
+++ b/soundserver/stdoutwriter_impl.cc
@@ -0,0 +1,52 @@
+ /*
+
+ Copyright (C) 2001 Stefan Westerfeld
+
+ 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 "kmedia2.h"
+#include "stdsynthmodule.h"
+#include <errno.h>
+
+using namespace std;
+using namespace Arts;
+
+namespace Arts {
+
+class StdoutWriter_impl : virtual public StdoutWriter_skel,
+ virtual public StdSynthModule
+{
+public:
+ StdoutWriter_impl()
+ {
+ }
+ void process_indata(DataPacket<mcopbyte> *data)
+ {
+ int result;
+ errno = 0;
+ do {
+ result = write(1, data->contents, data->size);
+ } while(errno == EINTR && result <= 0);
+ data->processed();
+ }
+};
+
+REGISTER_IMPLEMENTATION(StdoutWriter_impl);
+
+}
diff --git a/soundserver/tradercheck.cc b/soundserver/tradercheck.cc
new file mode 100644
index 0000000..f14cd7e
--- /dev/null
+++ b/soundserver/tradercheck.cc
@@ -0,0 +1,213 @@
+ /*
+
+ Copyright (C) 2000-2003 Stefan Westerfeld
+
+ 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 "common.h"
+#include "debug.h"
+#include "tradercheck.h"
+#include <stdio.h>
+#include <stdarg.h>
+#include <map>
+
+using namespace Arts;
+using namespace std;
+
+bool TraderCheck::haveProperty(TraderOffer& offer, const string& property)
+{
+ vector<string>* plist = offer.getProperty(property);
+ bool result = !plist->empty();
+ delete plist;
+
+ return result;
+}
+
+string TraderCheck::getSingleProperty(TraderOffer& offer, const string& property)
+{
+ string result="";
+
+ vector<string>* plist = offer.getProperty(property);
+ if(!plist->empty())
+ result = plist->front();
+ delete plist;
+
+ return result;
+}
+
+bool TraderCheck::findFile(const vector<string> *path, const string& filename)
+{
+ vector<string>::const_iterator pi;
+ for(pi = path->begin(); pi != path->end(); pi++)
+ {
+ string pfilename = *pi + "/" + filename;
+
+ if(access(pfilename.c_str(),F_OK) == 0)
+ return true;
+ }
+ return false;
+}
+
+void TraderCheck::collectInterfaces(const string& interfaceName, map<string, int>& i)
+{
+ InterfaceDef idef = interfaceRepo.queryInterface(interfaceName);
+
+ if(!idef.name.empty())
+ {
+ if(i[idef.name] == 1) return;
+ i[idef.name]++;
+ }
+ vector<string>::const_iterator ii;
+ for(ii = idef.inheritedInterfaces.begin(); ii != idef.inheritedInterfaces.end(); ii++)
+ collectInterfaces(*ii, i);
+ collectInterfaces("Arts::Object", i);
+}
+
+
+void
+#ifdef __GNUC__
+__attribute__ ( ( format ( printf, 3, 4 ) ) )
+#endif
+TraderCheck::check(bool cond, const char *fmt, ...)
+{
+ if(cond)
+ return;
+
+ if(!wroteHeader)
+ {
+ wroteHeader = true;
+ printf("Trader inconsistency in \'%s\':\n", interfaceName.c_str());
+
+ }
+ printf(" * ");
+ va_list ap;
+ va_start(ap, fmt);
+ vfprintf(stdout, fmt, ap);
+ va_end(ap);
+ printf("\n");
+}
+
+void TraderCheck::run()
+{
+ interfaceRepo = DynamicCast(Dispatcher::the()->interfaceRepo());
+
+ /* prevent the screen from being filled with aRts warnings */
+ Debug::init("", Debug::lFatal);
+
+ TraderQuery everything; /* a query without any restriction */
+ vector<TraderOffer> *allObjects = everything.query();
+ vector<TraderOffer>::iterator i;
+
+ for(i = allObjects->begin(); i != allObjects->end(); i++)
+ {
+ TraderOffer& offer = *i;
+
+ wroteHeader = false;
+ interfaceName = offer.interfaceName();
+
+
+ if(haveProperty(offer,"Type"))
+ {
+ // check type file consistency
+ check(haveProperty(offer,"TypeFile"),
+ "trader entries with a Type entry MUST have a TypeFile entry");
+
+ check(!haveProperty(offer,"Language"),
+ "trader entries with a Type entry MUST NOT have a Language entry");
+ }
+ else if(haveProperty(offer,"Language") || haveProperty(offer,"Library"))
+ {
+ // check class file consistency
+ InterfaceDef idef = interfaceRepo.queryInterface(offer.interfaceName());
+ if(idef.name.empty())
+ {
+ check(false, "interface type not found");
+ }
+ else
+ {
+ // verify correctness of the Interface= line
+ map<string,int> ifaces;
+ collectInterfaces(offer.interfaceName(), ifaces);
+
+ vector<string>* plist = offer.getProperty("Interface");
+ vector<string>::iterator pi;
+ for(pi = plist->begin(); pi != plist->end(); pi++)
+ {
+ ifaces[*pi]+=2;
+ }
+ delete plist;
+
+ map<string,int>::iterator ii;
+
+ for(ii = ifaces.begin(); ii != ifaces.end(); ii++)
+ {
+ switch(ii->second)
+ {
+ case 0:
+ check(false, "INTERNAL verification error");
+ break;
+
+ case 1:
+ check(false, "missing interface %s in Interface entry",
+ ii->first.c_str());
+ break;
+
+ case 2:
+ check(false, "given unimplemented(?) interface %s in Interface entry",
+ ii->first.c_str());
+ break;
+
+ case 3:
+ /* the way things should be */
+ break;
+
+ default:
+ check(false, "given interface %s in Interface entry more than once?",
+ ii->first.c_str());
+ break;
+ }
+ }
+ }
+
+ if(haveProperty(offer,"Library"))
+ {
+ check(getSingleProperty(offer,"Language") == "C++",
+ "trader entries with a Library entry SHOULD have a Language=C++ entry");
+
+ }
+
+ if (getSingleProperty(offer,"Language") == "C++")
+ {
+ string library = getSingleProperty(offer,"Library");
+ check(!library.empty(),
+ "entries with a Language entry must have a Library entry");
+ check(findFile(MCOPUtils::extensionPath(), library),
+ "Library entry MUST be loadable via extension path");
+ }
+
+ check(haveProperty(offer, "Interface"),
+ "entries with Language/Library MUST have an Interface entry");
+ }
+ else
+ {
+ check(false,"entry MUST have either Language or Type entry");
+ }
+ }
+ delete allObjects;
+}
diff --git a/soundserver/tradercheck.h b/soundserver/tradercheck.h
new file mode 100644
index 0000000..aa0b894
--- /dev/null
+++ b/soundserver/tradercheck.h
@@ -0,0 +1,50 @@
+/*
+ Copyright (C) 2003 Stefan Westerfeld
+
+ 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 TRADERCHECK_H
+#define TRADERCHECK_H
+
+#include "core.h"
+
+namespace Arts {
+
+/**
+ * This is an internal class used by artscontrol to check the consistency of
+ * the entries in the trader made through mcopclass/mcoptype files
+ */
+class TraderCheck {
+protected:
+ bool wroteHeader;
+ std::string interfaceName;
+ Arts::InterfaceRepoV2 interfaceRepo;
+
+ void check(bool cond, const char *fmt, ...);
+ bool haveProperty(Arts::TraderOffer& offer, const std::string& property);
+ std::string getSingleProperty(Arts::TraderOffer& offer, const std::string& property);
+ bool findFile(const std::vector<std::string> *path, const std::string& filename);
+ void collectInterfaces(const std::string& interfaceName, std::map<std::string, int>& i);
+
+public:
+ void run();
+};
+
+}
+
+#endif
+
diff --git a/soundserver/wavplayobject_impl.cc b/soundserver/wavplayobject_impl.cc
new file mode 100644
index 0000000..1a5fb82
--- /dev/null
+++ b/soundserver/wavplayobject_impl.cc
@@ -0,0 +1,172 @@
+ /*
+
+ Copyright (C) 2000 Stefan Westerfeld
+
+ 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 "config.h"
+
+#ifdef HAVE_LIBAUDIOFILE
+
+#include "soundserver.h"
+#include "cachedwav.h"
+#include "stdsynthmodule.h"
+#include "convert.h"
+#include "debug.h"
+#include <math.h>
+
+using namespace std;
+using namespace Arts;
+
+class WavPlayObject_impl :public WavPlayObject_skel, public StdSynthModule {
+protected:
+ CachedWav *wav;
+ double flpos;
+ poState _state;
+
+ int sampleCount()
+ {
+ if(!wav) return 0;
+ return wav->bufferSize / wav->channelCount / (wav->sampleWidth/8);
+ }
+public:
+ /*
+ * construction, destruction
+ */
+ WavPlayObject_impl() :wav(0), flpos(0.0), _state(posIdle)
+ {
+ arts_debug("WavPlayObject_impl");
+ }
+
+ virtual ~WavPlayObject_impl()
+ {
+ arts_debug("~WavPlayObject_impl");
+ if(wav) wav->decRef();
+ }
+
+ /*
+ * KMedia2 private interface
+ */
+
+ bool loadMedia(const string &filename) {
+ arts_debug("Wav: loadMedia %s", filename.c_str());
+ wav = CachedWav::load(Cache::the(), filename);
+ return (wav != 0);
+ }
+
+ /*
+ * KMedia2 interface
+ */
+ string description() {
+ return "no description (see "__FILE__")";
+ }
+
+ poTime currentTime() {
+ if(!wav) return poTime(0,0,0,"samples");
+
+ float timesec = flpos / (float)wav->samplingRate;
+ float timems = (timesec - floor(timesec)) * 1000.0;
+
+ return poTime( int( timesec ), int( timems ), (int)flpos, "samples" );
+ }
+
+ poTime overallTime() {
+ float timesec = (float)sampleCount() / (float)wav->samplingRate;
+ float timems = (timesec - floor(timesec)) * 1000.0;
+
+ return poTime( int( timesec ), int( timems ), sampleCount(), "samples" );
+ }
+
+ poCapabilities capabilities() {
+ return static_cast<poCapabilities>(capPause+capSeek);
+ }
+
+ string mediaName() {
+ return wav ? wav->mediaName() : "";
+ }
+
+ poState state() {
+ return _state;
+ }
+
+ void play() {
+ _state = posPlaying;
+ }
+
+ void halt() {
+ _state = posIdle;
+ flpos = 0.0;
+ }
+
+ void seek(const class poTime &newTime) {
+ if(!wav) return;
+
+ float fnewsamples = -1;
+ if(newTime.seconds != -1 && newTime.ms != -1)
+ {
+ float flnewtime = (float)newTime.seconds+((float)newTime.ms/1000.0);
+ fnewsamples = flnewtime * (float)wav->samplingRate;
+ }
+ else if(newTime.custom >= 0 && newTime.customUnit == "samples")
+ {
+ fnewsamples = newTime.custom;
+ }
+
+ if(fnewsamples > (float)sampleCount())
+ fnewsamples = (float)sampleCount();
+
+ if(fnewsamples >= 0) flpos = fnewsamples;
+ }
+
+ void pause() {
+ arts_debug("pause");
+ _state = posPaused;
+ }
+
+ void calculateBlock(unsigned long samples) {
+ unsigned long haveSamples = 0;
+
+ if(wav && _state == posPlaying)
+ {
+ double speed = wav->samplingRate / samplingRateFloat;
+
+ haveSamples = uni_convert_stereo_2float(samples, wav->buffer,
+ wav->bufferSize,wav->channelCount,wav->sampleWidth,
+ left,right,speed,flpos);
+
+ flpos += (double)haveSamples * speed;
+ }
+
+ if(haveSamples != samples)
+ {
+ unsigned long i;
+
+ for(i=haveSamples;i<samples;i++)
+ left[i] = right[i] = 0.0;
+
+ if(_state == posPlaying) {
+ _state = posIdle;
+ flpos = 0.0;
+ }
+ }
+ }
+};
+
+REGISTER_IMPLEMENTATION(WavPlayObject_impl);
+#endif