diff options
Diffstat (limited to 'soundserver/artsd.cpp')
-rw-r--r-- | soundserver/artsd.cpp | 377 |
1 files changed, 377 insertions, 0 deletions
diff --git a/soundserver/artsd.cpp b/soundserver/artsd.cpp new file mode 100644 index 0000000..50cfb6e --- /dev/null +++ b/soundserver/artsd.cpp @@ -0,0 +1,377 @@ + /* + + Copyright (C) 2000-2001 Stefan Westerfeld + stefan@space.twc.de + 2003 Arnold Krille <arnold@arnoldarts.de + + 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 << +"[artsd] 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 << "[artsd] 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 << "[artsd] ... 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("submit@bugs.kde.org"); + } + + 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()) + { + //Don't show an error (this looks bad and may confuse users without sound cards), kmix makes it obvious if sound isn't working + //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( + "[artsd] 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( + "[artsd] 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.cpp 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 |