Logo Search packages:      
Sourcecode: kdemultimedia version File versions

engine.cpp

// $Id: engine.cpp,v 1.96 2004/03/02 00:19:22 jriddell Exp $

#include <noatun/engine.h>
#include <noatun/equalizer.h>
#include <noatun/player.h>
#include <noatun/plugin.h>
#include <noatun/effects.h>

#include <string.h>

#include <kmessagebox.h>
#include <klocale.h>
#include <kstandarddirs.h>
#include <kconfig.h>
#include <kdebug.h>
#include <qfile.h>
#include <qdir.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())
            {
            }

      ~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;
};

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(QObject *parent) : QObject(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;
}

00278 bool Engine::open(const PlaylistItem &file)
{
      if(!initArts()) return false;
      d->playobj=0;

      string objectType=(const char*)file.playObject();
      if (!objectType.length())
      {
            d->playobj=0;
            return false;
      }

      KDE::PlayObjectFactory factory(d->server);

      factory.setAllowStreaming(file.isProperty("stream_"));

      if (file.isProperty("stream_"))
      {
            // let the factory decide
            // d->playobj=factory.createPlayObject(file.property("stream_"), file.mimetype(), false);
            d->playobj=factory.createPlayObject(file.property("stream_"), false);
      }
      else
      {
            KURL u;
            u.setPath(file.file());
            d->playobj=factory.createPlayObject(u, file.mimetype(), false);
      }

      if (!d->playobj || d->playobj->isNull())
      {
            delete d->playobj;
            d->playobj=0;
            return false;
      }

      if ( !d->playobj->object().isNull() )
      {
            connectPlayObject();
      }
      else
      {
            connect( d->playobj, SIGNAL( playObjectCreated() ), this, SLOT( connectPlayObject() ) );
      }

      if (mPlay)
            d->playobj->play();

      return true;
}

void Engine::connectPlayObject()
{
      if (d->playobj->object().isNull()) 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();
}

00341 bool Engine::play()
{
      if (!mPlay) return true;
      if(!d->playobj)
            return false;
      d->playobj->play();
      return true;
}

00350 void Engine::pause()
{
      d->playobj->pause();
}

00355 void Engine::stop()
{
      if(!d->playobj) return;

      d->playobj->halt();
      delete d->playobj;
      d->playobj=0;
}

00364 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");
                  QCString 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(QDir::homeDirPath()+"/.mcoprc");

                        if(x11Comm)
                              X11CommConfig.writeEntry("GlobalComm", "Arts::X11GlobalComm");
                        else
                              X11CommConfig.writeEntry("GlobalComm", "Arts::TmpGlobalComm");

                        X11CommConfig.sync();
                  }

                  cmdline = QFile::encodeName(KStandardDirs::findExe(QString::fromLatin1("kdeinit_wrapper")));
                  cmdline += " ";

                  if (rt)
                        cmdline += QFile::encodeName(KStandardDirs::findExe(
                        QString::fromLatin1("artswrapper")));
                  else
                        cmdline += QFile::encodeName(KStandardDirs::findExe(
                        QString::fromLatin1("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;

                  }

                  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 "noatun/engine.moc"


Generated by  Doxygen 1.6.0   Back to index