/* Copyright (C) 2000-2001 Stefan Westerfeld stefan@space.twc.de 2003 Arnold Krille #include #include #include #include #include #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 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 increase network buffers by factor of \n"); fprintf(stderr,"\n"); fprintf(stderr,"audio options:\n"); fprintf(stderr,"-a select audio i/o method (oss, alsa, ...)\n"); fprintf(stderr,"-r set samplingrate to use\n"); fprintf(stderr,"-b set number of bits (8 or 16)\n"); fprintf(stderr,"-d enable full duplex operation\n"); fprintf(stderr,"-V [dB] set output volume\n"); fprintf(stderr,"-D audio device (usually /dev/dsp)\n"); fprintf(stderr,"-F number of fragments\n"); fprintf(stderr,"-S fragment size in bytes\n"); fprintf(stderr,"-s 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 information level\n"); fprintf(stderr," 3: quiet, 2: warnings, 1: info, 0: debug\n"); fprintf(stderr,"-m application to display messages\n"); fprintf(stderr,"-c 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( 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( 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 " <(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(1)); arts_info("%s", warnNullDevice.c_str()); Debug::init("[artsd]", static_cast(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