|
|
|
/*
|
|
|
|
|
|
|
|
Copyright (C) 2001 Jeff Tranter
|
|
|
|
tranter@kde.org
|
|
|
|
2001 Stefan Westerfeld
|
|
|
|
stefan@space.twc.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 "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
|