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.
590 lines
13 KiB
590 lines
13 KiB
// $Id$
|
|
|
|
#include <noatun/engine.h>
|
|
#include <noatun/equalizer.h>
|
|
#include <noatun/player.h>
|
|
#include <noatun/plugin.h>
|
|
#include <noatun/effects.h>
|
|
#include "titleproxy.h"
|
|
|
|
#include <string.h>
|
|
|
|
#include <kmessagebox.h>
|
|
#include <klocale.h>
|
|
#include <kstandarddirs.h>
|
|
#include <kconfig.h>
|
|
#include <kdebug.h>
|
|
#include <tqfile.h>
|
|
#include <tqdir.h>
|
|
#include <sys/wait.h>
|
|
#include <kplayobject.h>
|
|
#include <kplayobjectfactory.h>
|
|
|
|
#include <dynamicrequest.h>
|
|
#include <soundserver.h>
|
|
#include <kmedia2.h>
|
|
#include <flowsystem.h>
|
|
#include <noatunarts.h>
|
|
#include <connect.h>
|
|
#include <cpuinfo.h>
|
|
|
|
#include <sys/ioctl.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <math.h>
|
|
|
|
|
|
static Arts::PlayObject nullPO() { return Arts::PlayObject::null(); }
|
|
|
|
#define HARDWARE_VOLUME
|
|
|
|
#if defined(__osf__)
|
|
#undef HARDWARE_VOLUME
|
|
#elif defined(__linux__)
|
|
#include <sys/soundcard.h>
|
|
#elif defined(__FreeBSD__)
|
|
#include <sys/soundcard.h>
|
|
#elif defined(__NetBSD__)
|
|
#include <soundcard.h>
|
|
#elif defined(___SOMETHING_UNKNOWN__)
|
|
#include <sys/soundcard.h>
|
|
#elif defined(_UNIXWARE)
|
|
#include <sys/soundcard.h>
|
|
#else
|
|
#undef HARDWARE_VOLUME
|
|
#endif
|
|
|
|
using namespace std;
|
|
|
|
namespace VolumeControls
|
|
{
|
|
struct VC
|
|
{
|
|
virtual ~VC() {}
|
|
virtual void setVolume(int percent)=0;
|
|
virtual int volume() const =0;
|
|
};
|
|
|
|
#ifdef HARDWARE_VOLUME
|
|
|
|
struct Hardware : public VC
|
|
{
|
|
Hardware(Engine *)
|
|
{
|
|
if ((fd=::open( "/dev/mixer", O_RDWR)) < 0)
|
|
{
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
#define ERROR { fd=-1; return; }
|
|
int devmask, recmask, i_recsrc, stereodevs;
|
|
// Mixer is open. Now define properties
|
|
if (ioctl(fd, SOUND_MIXER_READ_DEVMASK, &devmask) == -1) ERROR
|
|
if (ioctl(fd, SOUND_MIXER_READ_RECMASK, &recmask) == -1) ERROR
|
|
if (ioctl(fd, SOUND_MIXER_READ_RECSRC, &i_recsrc) == -1) ERROR
|
|
if (ioctl(fd, SOUND_MIXER_READ_STEREODEVS, &stereodevs) == -1) ERROR
|
|
if (!devmask) ERROR
|
|
#undef ERROR
|
|
}
|
|
|
|
}
|
|
|
|
virtual ~Hardware() { ::close(fd); }
|
|
|
|
virtual void setVolume(int percent)
|
|
{
|
|
percent=percent+(percent<<8);
|
|
ioctl(fd, MIXER_WRITE(4), &percent);
|
|
}
|
|
|
|
virtual int volume() const
|
|
{
|
|
int volume, left, right;
|
|
left=100;
|
|
if (ioctl(fd, MIXER_READ(4), &volume) != -1)
|
|
{
|
|
left=volume & 0x7f;
|
|
right=(volume>>8) & 0x7f;
|
|
left=(left+right)/2;
|
|
}
|
|
return left;
|
|
}
|
|
private:
|
|
int fd;
|
|
};
|
|
#endif
|
|
|
|
struct SoftwareSSE : public VC
|
|
{
|
|
SoftwareSSE(Engine *e) : mVolume(100)
|
|
{
|
|
volumeControl = Arts::DynamicCast(e->server()->createObject("Noatun::StereoVolumeControlSSE"));
|
|
|
|
if(volumeControl.isNull())
|
|
volumeControl = Arts::DynamicCast(e->server()->createObject("Noatun::StereoVolumeControl"));
|
|
|
|
volumeControl.start();
|
|
|
|
id=e->globalEffectStack()->insertBottom(volumeControl,"Volume Control");
|
|
}
|
|
|
|
virtual void setVolume(int percent)
|
|
{
|
|
mVolume=percent;
|
|
float vol = pow(2.0, ((400 - (100-percent)*12)/200.0))/4.0;
|
|
if (percent == 0) vol = 0.0;
|
|
if (!volumeControl.isNull())
|
|
volumeControl.percent(vol);
|
|
}
|
|
|
|
virtual int volume() const
|
|
{
|
|
return mVolume;
|
|
}
|
|
|
|
private:
|
|
Noatun::StereoVolumeControlSSE volumeControl;
|
|
long id;
|
|
int mVolume;
|
|
};
|
|
|
|
struct Software : public VC
|
|
{
|
|
Software(Engine *e) : mVolume(100)
|
|
{
|
|
volumeControl = Arts::DynamicCast(e->server()->createObject("Noatun::StereoVolumeControl"));
|
|
volumeControl.start();
|
|
|
|
id=e->globalEffectStack()->insertBottom(volumeControl,"Volume Control");
|
|
}
|
|
|
|
virtual void setVolume(int percent)
|
|
{
|
|
mVolume=percent;
|
|
if (!volumeControl.isNull())
|
|
volumeControl.percent((float)percent/100.0);
|
|
}
|
|
|
|
virtual int volume() const
|
|
{
|
|
return mVolume;
|
|
}
|
|
|
|
private:
|
|
Noatun::StereoVolumeControl volumeControl;
|
|
long id;
|
|
int mVolume;
|
|
};
|
|
|
|
static VC *volumeControl(Engine *e)
|
|
{
|
|
#ifdef HARDWARE_VOLUME
|
|
if (napp->fastMixer())
|
|
return new Hardware(e);
|
|
else
|
|
{
|
|
#endif
|
|
if (!getenv("NO_SSE") && Arts::CpuInfo::flags()&Arts::CpuInfo::CpuSSE)
|
|
return new SoftwareSSE(e);
|
|
else
|
|
return new Software(e);
|
|
|
|
#ifdef HARDWARE_VOLUME
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
|
|
class Engine::EnginePrivate
|
|
{
|
|
public:
|
|
EnginePrivate()
|
|
: playobj(0),
|
|
server(Arts::SoundServerV2::null()),
|
|
globalEffectStack(Noatun::StereoEffectStack::null()),
|
|
effectsStack(Noatun::StereoEffectStack::null()),
|
|
visStack(Noatun::StereoEffectStack::null()),
|
|
volumeControl(0), session(Noatun::Session::null()),
|
|
pProxy(0)
|
|
{
|
|
}
|
|
|
|
~EnginePrivate()
|
|
{
|
|
visStack=Noatun::StereoEffectStack::null();
|
|
}
|
|
|
|
KDE::PlayObject *playobj;
|
|
Arts::SoundServerV2 server;
|
|
Arts::Synth_AMAN_PLAY amanPlay;
|
|
|
|
// globalEffectStack
|
|
// |- effectsStack
|
|
// |- Effects...
|
|
// |- visStack
|
|
// |- Visualizations
|
|
// |- Volume Control
|
|
//
|
|
Noatun::StereoEffectStack globalEffectStack;
|
|
Noatun::StereoEffectStack effectsStack;
|
|
Noatun::StereoEffectStack visStack;
|
|
Noatun::Equalizer equalizer;
|
|
|
|
int volumeID;
|
|
VolumeControls::VC *volumeControl;
|
|
Noatun::Session session;
|
|
TitleProxy::Proxy *pProxy;
|
|
};
|
|
|
|
Arts::SoundServerV2 *Engine::server() const { return &d->server;}
|
|
Arts::PlayObject Engine::playObject() const { return d->playobj ? d->playobj->object() : nullPO(); }
|
|
Arts::SoundServerV2 *Engine::simpleSoundServer() const { return &d->server; }
|
|
Noatun::StereoEffectStack *Engine::effectStack() const { return &d->effectsStack; }
|
|
Noatun::StereoEffectStack *Engine::visualizationStack() const { return &d->visStack; }
|
|
Noatun::StereoEffectStack *Engine::globalEffectStack() const { return &d->globalEffectStack; }
|
|
Noatun::Equalizer *Engine::equalizer() const { return &d->equalizer; }
|
|
Noatun::Session *Engine::session() const { return &d->session; }
|
|
|
|
Engine::Engine(TQObject *parent) : TQObject(parent, "Engine"), mPlay(false)
|
|
{
|
|
d=new EnginePrivate;
|
|
// Connect to aRts
|
|
if (!initArts())
|
|
{
|
|
KMessageBox::error(0, i18n("There was an error communicating to the aRts daemon."), i18n("aRts error"));
|
|
exit(0);
|
|
}
|
|
|
|
}
|
|
|
|
Engine::~Engine()
|
|
{
|
|
stop();
|
|
delete d->volumeControl;
|
|
d->server=Arts::SoundServerV2::null();
|
|
delete d;
|
|
}
|
|
|
|
void Engine::setInitialized()
|
|
{
|
|
mPlay=true;
|
|
}
|
|
|
|
bool Engine::initialized() const
|
|
{
|
|
return mPlay;
|
|
}
|
|
|
|
bool Engine::open(const PlaylistItem &file)
|
|
{
|
|
if(!initArts())
|
|
return false;
|
|
|
|
d->playobj = 0;
|
|
|
|
KDE::PlayObjectFactory factory(d->server);
|
|
|
|
if (file.isProperty("stream_") && file.url().protocol() == "http")
|
|
{
|
|
deleteProxy();
|
|
d->pProxy = new TitleProxy::Proxy(KURL(file.property("stream_")));
|
|
d->playobj = factory.createPlayObject(d->pProxy->proxyUrl(), false);
|
|
|
|
connect(d->playobj, TQT_SIGNAL(destroyed()), this, TQT_SLOT(deleteProxy()));
|
|
connect(
|
|
d->pProxy, TQT_SIGNAL(
|
|
metaData(
|
|
const TQString &, const TQString &,
|
|
const TQString &, const TQString &,
|
|
const TQString &, const TQString &)),
|
|
this, TQT_SIGNAL(
|
|
receivedStreamMeta(const TQString &, const TQString &,
|
|
const TQString &, const TQString &,
|
|
const TQString &, const TQString &))
|
|
);
|
|
connect(d->pProxy, TQT_SIGNAL(proxyError()), this, TQT_SLOT(slotProxyError()));
|
|
}
|
|
else
|
|
{
|
|
d->playobj = factory.createPlayObject(file.url(), false);
|
|
}
|
|
|
|
if (!d->playobj || d->playobj->isNull())
|
|
{
|
|
kdDebug(66666) << k_funcinfo <<
|
|
"No playobject for '" << file.url().prettyURL() << "'" << endl;
|
|
delete d->playobj;
|
|
d->playobj=0;
|
|
emit playingFailed();
|
|
return false;
|
|
}
|
|
|
|
if ( !d->playobj->object().isNull() )
|
|
{
|
|
connectPlayObject();
|
|
}
|
|
else
|
|
{
|
|
connect( d->playobj, TQT_SIGNAL( playObjectCreated() ), this, TQT_SLOT( connectPlayObject() ) );
|
|
}
|
|
|
|
if (mPlay)
|
|
d->playobj->play();
|
|
|
|
return true;
|
|
}
|
|
|
|
void Engine::slotProxyError()
|
|
{
|
|
kdDebug(66666) << k_funcinfo << endl;
|
|
emit playingFailed();
|
|
deleteProxy();
|
|
}
|
|
|
|
void Engine::deleteProxy()
|
|
{
|
|
delete d->pProxy;
|
|
d->pProxy = 0;
|
|
}
|
|
|
|
void Engine::connectPlayObject()
|
|
{
|
|
if (d->playobj->object().isNull())
|
|
{
|
|
emit playingFailed();
|
|
return;
|
|
}
|
|
d->playobj->object()._node()->start();
|
|
|
|
// TODO: check for existence of left & right streams
|
|
Arts::connect(d->playobj->object(),"left",d->globalEffectStack,"inleft");
|
|
Arts::connect(d->playobj->object(),"right",d->globalEffectStack,"inright");
|
|
|
|
emit aboutToPlay();
|
|
}
|
|
|
|
bool Engine::play()
|
|
{
|
|
if (!mPlay) return true;
|
|
if(!d->playobj)
|
|
return false;
|
|
d->playobj->play();
|
|
return true;
|
|
}
|
|
|
|
void Engine::pause()
|
|
{
|
|
d->playobj->pause();
|
|
}
|
|
|
|
void Engine::stop()
|
|
{
|
|
if(!d->playobj) return;
|
|
|
|
d->playobj->halt();
|
|
delete d->playobj;
|
|
d->playobj=0;
|
|
}
|
|
|
|
void Engine::seek(int msec) // pass time in msecs
|
|
{
|
|
if(!d->playobj) return;
|
|
|
|
Arts::poTime t;
|
|
|
|
t.custom = 0.0;
|
|
t.ms = (long) msec % 1000;
|
|
t.seconds = (long) (msec - t.ms) / 1000;
|
|
|
|
if(d->playobj)
|
|
d->playobj->seek(t);
|
|
}
|
|
|
|
int Engine::position()
|
|
{
|
|
if(!d->playobj) return -1;
|
|
|
|
Arts::poTime time(d->playobj->currentTime());
|
|
return (int)(time.ms + (time.seconds*1000)); // return position in milliseconds
|
|
}
|
|
|
|
int Engine::length()
|
|
{
|
|
if(!d->playobj) return -1;
|
|
if (!(d->playobj->capabilities() & Arts::capSeek))
|
|
return -1;
|
|
|
|
Arts::poTime time(d->playobj->overallTime());
|
|
return (int)(time.ms + (time.seconds*1000)); // return track-length in milliseconds
|
|
}
|
|
|
|
int Engine::state()
|
|
{
|
|
if(d->playobj)
|
|
return d->playobj->state();
|
|
else
|
|
return Arts::posIdle;
|
|
}
|
|
|
|
void Engine::setVolume(int percent)
|
|
{
|
|
if (percent>100)
|
|
percent=100;
|
|
if (percent<0)
|
|
percent=0;
|
|
d->volumeControl->setVolume(percent);
|
|
}
|
|
|
|
int Engine::volume() const
|
|
{
|
|
return d->volumeControl->volume();
|
|
}
|
|
|
|
void Engine::useHardwareMixer(bool)
|
|
{
|
|
delete d->volumeControl;
|
|
d->volumeControl=VolumeControls::volumeControl(this);
|
|
}
|
|
|
|
bool Engine::initArts()
|
|
{
|
|
if ( d->server.isNull() || d->server.error() )
|
|
{
|
|
d->server = Arts::Reference("global:Arts_SoundServerV2");
|
|
int volume = d->volumeControl ? d->volumeControl->volume() : -1;
|
|
delete d->volumeControl;
|
|
d->volumeControl=0;
|
|
|
|
if( d->server.isNull() || d->server.error() )
|
|
{
|
|
// aRts seems not to be running, let's try to run it
|
|
// First, let's read the configuration as in kcmarts
|
|
KConfig config("kcmartsrc");
|
|
TQCString cmdline;
|
|
|
|
config.setGroup("Arts");
|
|
|
|
bool rt = config.readBoolEntry("StartRealtime",false);
|
|
bool x11Comm = config.readBoolEntry("X11GlobalComm",false);
|
|
|
|
// put the value of x11Comm into .mcoprc
|
|
{
|
|
KConfig X11CommConfig(TQDir::homeDirPath()+"/.mcoprc");
|
|
|
|
if(x11Comm)
|
|
X11CommConfig.writeEntry("GlobalComm", "Arts::X11GlobalComm");
|
|
else
|
|
X11CommConfig.writeEntry("GlobalComm", "Arts::TmpGlobalComm");
|
|
|
|
X11CommConfig.sync();
|
|
}
|
|
|
|
cmdline = TQFile::encodeName(KStandardDirs::findExe(TQString::tqfromLatin1("kdeinit_wrapper")));
|
|
cmdline += " ";
|
|
|
|
if (rt)
|
|
cmdline += TQFile::encodeName(KStandardDirs::findExe(
|
|
TQString::tqfromLatin1("artswrapper")));
|
|
else
|
|
cmdline += TQFile::encodeName(KStandardDirs::findExe(
|
|
TQString::tqfromLatin1("artsd")));
|
|
|
|
cmdline += " ";
|
|
cmdline += config.readEntry("Arguments","-F 10 -S 4096 -s 60 -m artsmessage -l 3 -f").utf8();
|
|
|
|
int status=::system(cmdline);
|
|
|
|
if ( status!=-1 && WIFEXITED(status) )
|
|
{
|
|
// We could have a race-condition here.
|
|
// The correct way to do it is to make artsd fork-and-exit
|
|
// after starting to listen to connections (and running artsd
|
|
// directly instead of using kdeinit), but this is better
|
|
// than nothing.
|
|
int time = 0;
|
|
do
|
|
{
|
|
// every time it fails, we should wait a little longer
|
|
// between tries
|
|
::sleep(1+time/2);
|
|
d->server = Arts::Reference("global:Arts_SoundServerV2");
|
|
} while(++time < 5 && (d->server.isNull()));
|
|
}
|
|
}
|
|
|
|
if ( !d->server.isNull() )
|
|
{
|
|
d->amanPlay = Arts::DynamicCast(
|
|
d->server.createObject("Arts::Synth_AMAN_PLAY")
|
|
);
|
|
|
|
if (d->amanPlay.isNull())
|
|
goto crapOut;
|
|
|
|
d->session = Arts::DynamicCast(
|
|
d->server.createObject("Noatun::Session"));
|
|
if (d->session.isNull())
|
|
{
|
|
kdWarning() << "Couldn't instanciate artsobject Noatun::Session. "
|
|
<< "(This is normally caused by a broken package or "
|
|
<< "compiling kdemultimedia in a --prefix different "
|
|
<< "from arts. It may also be from two conflicting "
|
|
<< "packages, so uninstall every arts/artsd package "
|
|
<< "you have installed and try again." << endl;
|
|
goto crapOut;
|
|
}
|
|
|
|
d->amanPlay.title("noatun");
|
|
d->amanPlay.autoRestoreID("noatun");
|
|
d->amanPlay.start();
|
|
|
|
d->globalEffectStack=Arts::DynamicCast(
|
|
d->server.createObject("Noatun::StereoEffectStack"));;
|
|
d->globalEffectStack.start();
|
|
Arts::connect(d->globalEffectStack,d->amanPlay);
|
|
|
|
d->effectsStack=Arts::DynamicCast(
|
|
d->server.createObject("Noatun::StereoEffectStack"));
|
|
d->effectsStack.start();
|
|
d->globalEffectStack.insertBottom(d->effectsStack, "Effects Stack");
|
|
|
|
d->equalizer=Arts::DynamicCast(d->server.createObject("Noatun::Equalizer"));
|
|
d->equalizer.start();
|
|
d->globalEffectStack.insertBottom(d->equalizer, "Equalizer");
|
|
|
|
if (napp->equalizer())
|
|
{
|
|
napp->equalizer()->update(true);
|
|
napp->equalizer()->setPreamp(napp->equalizer()->preamp());
|
|
napp->equalizer()->setEnabled(napp->equalizer()->isEnabled());
|
|
}
|
|
|
|
d->visStack=Arts::DynamicCast(
|
|
d->server.createObject("Noatun::StereoEffectStack"));
|
|
d->visStack.start();
|
|
|
|
d->globalEffectStack.insertBottom(d->visStack, "Visualization Stack");
|
|
d->volumeControl=VolumeControls::volumeControl(this);
|
|
if (volume != -1)
|
|
d->volumeControl->setVolume(volume);
|
|
}
|
|
else
|
|
{
|
|
crapOut:
|
|
KMessageBox::error( 0, i18n("Connecting/starting aRts soundserver failed. Make sure that artsd is configured properly."));
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
d->playobj=0;
|
|
emit artsError();
|
|
return true;
|
|
}
|
|
|
|
|
|
#include "engine.moc"
|
|
|