You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
arts/soundserver/artsd.cc

378 lines
12 KiB

/*
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.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