diff options
author | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-01-05 00:01:18 +0000 |
---|---|---|
committer | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-01-05 00:01:18 +0000 |
commit | 42995d7bf396933ee60c5f89c354ea89cf13df0d (patch) | |
tree | cfdcea0ac57420e7baf570bfe435e107bb842541 /soundserver | |
download | arts-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')
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 |