• Skip to content
  • Skip to link menu
KDE 4.2 API Reference
  • KDE API Reference
  • API Reference
  • Sitemap
  • Contact Us
 

KNotify

notifybysound.cpp

Go to the documentation of this file.
00001 /*
00002    Copyright (c) 1997 Christian Esken (esken@kde.org)
00003                  2000 Charles Samuels (charles@kde.org)
00004                  2000 Stefan Schimanski (1Stein@gmx.de)
00005                  2000 Matthias Ettrich (ettrich@kde.org)
00006                  2000 Waldo Bastian <bastian@kde.org>
00007                  2000-2003 Carsten Pfeiffer <pfeiffer@kde.org>
00008                  2005 Allan Sandfeld Jensen <kde@carewolf.com>
00009                  2005-2006 by Olivier Goffart <ogoffart at kde.org>
00010 
00011    This program is free software; you can redistribute it and/or modify
00012    it under the terms of the GNU General Public License as published by
00013    the Free Software Foundation; either version 2, or (at your option)
00014    any later version.
00015 
00016    This program is distributed in the hope that it will be useful,
00017    but WITHOUT ANY WARRANTY; without even the implied warranty of
00018    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019    GNU General Public License for more details.
00020 
00021    You should have received a copy of the GNU General Public License
00022    along with this program; if not, write to the Free Software
00023    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
00024 */
00025 
00026 
00027 #include "notifybysound.h"
00028 #include "knotifyconfig.h"
00029 
00030 
00031 // QT headers
00032 #include <QHash>
00033 #include <QtCore/QBasicTimer>
00034 #include <QtCore/QQueue>
00035 #include <QtCore/QTimer>
00036 #include <QtCore/QTimerEvent>
00037 #include <QtCore/QStack>
00038 #include <QSignalMapper>
00039 
00040 // KDE headers
00041 #include <kdebug.h>
00042 #include <klocale.h>
00043 #include <kprocess.h>
00044 #include <kstandarddirs.h>
00045 #include <kconfiggroup.h>
00046 #include <kurl.h>
00047 #include <config-runtime.h>
00048 #include <kcomponentdata.h>
00049 
00050 // Phonon headers
00051 #include <phonon/mediaobject.h>
00052 #include <phonon/path.h>
00053 #include <phonon/audiooutput.h>
00054 
00055 struct Player
00056 {
00057     Player()
00058         : media(new Phonon::MediaObject),
00059         output(new Phonon::AudioOutput(Phonon::NotificationCategory))
00060     {
00061         Phonon::createPath(media, output);
00062     }
00063 
00064     inline void play(const QString &file) { media->setCurrentSource(file); media->enqueue(Phonon::MediaSource()); media->play(); }
00065     inline void stop() { media->stop(); }
00066     inline void setVolume(float volume) { output->setVolume(volume); }
00067 
00068     ~Player()
00069     {
00070         output->deleteLater();
00071         media->deleteLater();
00072     }
00073 
00074     Phonon::MediaObject *const media;
00075     Phonon::AudioOutput *const output;
00076 };
00077 
00078 class PlayerPool
00079 {
00080     public:
00081         PlayerPool() : m_idlePlayer(0), m_volume(1.0) {}
00082 
00083         Player *getPlayer();
00084         void returnPlayer(Player *);
00085         void clear();
00086 
00087         void setVolume(float volume);
00088 
00089     private:
00090         Player *m_idlePlayer;
00091         QList<Player *> m_playersInUse;
00092         float m_volume;
00093 };
00094 
00095 Player *PlayerPool::getPlayer()
00096 {
00097     Player *p = 0;
00098     if (!m_idlePlayer) {
00099         p = new Player;
00100     } else {
00101         p = m_idlePlayer;
00102         m_idlePlayer = 0;
00103     }
00104     p->setVolume(m_volume);
00105     m_playersInUse << p;
00106     return p;
00107 }
00108 
00109 void PlayerPool::returnPlayer(Player *p)
00110 {
00111     m_playersInUse.removeAll(p);
00112     if (m_idlePlayer) {
00113         delete p;
00114     } else {
00115         m_idlePlayer = p;
00116     }
00117 }
00118 
00119 void PlayerPool::clear()
00120 {
00121     delete m_idlePlayer;
00122     m_idlePlayer = 0;
00123 }
00124 
00125 void PlayerPool::setVolume(float v)
00126 {
00127     m_volume = v;
00128     foreach (Player *p, m_playersInUse) {
00129         p->setVolume(v);
00130     }
00131 }
00132 
00133 class NotifyBySound::Private
00134 {
00135     public:
00136         enum { NoSound, UsePhonon, ExternalPlayer } playerMode;
00137         QString externalPlayer;
00138 
00139         QHash<int, KProcess *> processes;
00140         QHash<int, Player*> playerObjects;
00141         QSignalMapper *signalmapper;
00142         PlayerPool playerPool;
00143         QBasicTimer poolTimer;
00144         QQueue<int> closeQueue;
00145 
00146         int volume;
00147 
00148 };
00149 
00150 NotifyBySound::NotifyBySound(QObject *parent) : KNotifyPlugin(parent),d(new Private)
00151 {
00152     d->signalmapper = new QSignalMapper(this);
00153     connect(d->signalmapper, SIGNAL(mapped(int)), this, SLOT(slotSoundFinished(int)));
00154 
00155     loadConfig();
00156 }
00157 
00158 
00159 NotifyBySound::~NotifyBySound()
00160 {
00161     delete d;
00162 }
00163 
00164 
00165 void NotifyBySound::loadConfig()
00166 {
00167     // load external player settings
00168     KSharedConfig::Ptr kc = KGlobal::config();
00169     KConfigGroup cg(kc, "Sounds");
00170 
00171     d->playerMode = Private::UsePhonon;
00172     if(cg.readEntry( "Use external player", false ))
00173     {
00174         d->playerMode = Private::ExternalPlayer;
00175         d->externalPlayer = cg.readPathEntry("External player", QString());
00176         // try to locate a suitable player if none is configured
00177         if ( d->externalPlayer.isEmpty() ) {
00178             QStringList players;
00179             players << "wavplay" << "aplay" << "auplay" << "artsplay" << "akodeplay";
00180             QStringList::const_iterator it = players.constBegin();
00181             while ( d->externalPlayer.isEmpty() && it != players.constEnd() ) {
00182                 d->externalPlayer = KStandardDirs::findExe( *it );
00183                 ++it;
00184             }
00185         }
00186     }
00187     else if(cg.readEntry( "No sound" , false ))
00188     {
00189         d->playerMode = Private::NoSound;
00190     }
00191     // load default volume
00192     setVolume( cg.readEntry( "Volume", 100 ) );
00193 }
00194 
00195 
00196 
00197 
00198 void NotifyBySound::notify( int eventId, KNotifyConfig * config )
00199 {
00200     if(d->playerObjects.contains(eventId)  || d->processes.contains(eventId) )
00201     {
00202         //a sound is already playing for this notification,  we don't support playing two sounds.
00203         finish( eventId );
00204         return;
00205     }
00206 
00207     KUrl soundFileURL = config->readEntry( "Sound" , true );
00208     QString soundFile = soundFileURL.toLocalFile();
00209 
00210     if (soundFile.isEmpty())
00211     {
00212         finish( eventId );
00213         return;
00214     }
00215     
00216     // get file name
00217     if ( KUrl::isRelativeUrl(soundFile) )
00218     {
00219         QString search = QString("%1/sounds/%2").arg(config->appname).arg(soundFile);
00220         search = KGlobal::mainComponent().dirs()->findResource("data", search);
00221         if ( search.isEmpty() )
00222             soundFile = KStandardDirs::locate( "sound", soundFile );
00223         else
00224             soundFile = search;
00225     }
00226     if ( soundFile.isEmpty() )
00227     {
00228         finish( eventId );
00229         return;
00230     }
00231 
00232     kDebug(300) << " going to play " << soundFile;
00233     d->poolTimer.stop();
00234 
00235     if(d->playerMode == Private::UsePhonon)
00236     {
00237         Player *player = d->playerPool.getPlayer();
00238         connect(player->media, SIGNAL(finished()), d->signalmapper, SLOT(map()));
00239         d->signalmapper->setMapping(player->media, eventId);
00240         player->play(soundFile);
00241         d->playerObjects.insert(eventId, player);
00242     }
00243     else if (d->playerMode == Private::ExternalPlayer && !d->externalPlayer.isEmpty())
00244     {
00245         // use an external player to play the sound
00246         KProcess *proc = new KProcess( this );
00247         connect( proc, SIGNAL(finished(int, QProcess::ExitStatus)),
00248                  d->signalmapper,  SLOT(map()) );
00249         d->signalmapper->setMapping( proc , eventId );
00250 
00251         (*proc) << d->externalPlayer << soundFile;
00252         proc->start();
00253     }
00254 }
00255 
00256 
00257 void NotifyBySound::setVolume( int volume )
00258 {
00259     if ( volume<0 ) volume=0;
00260     if ( volume>=100 ) volume=100;
00261     d->volume = volume;
00262     d->playerPool.setVolume(d->volume / 100.0);
00263 }
00264 
00265 
00266 void NotifyBySound::timerEvent(QTimerEvent *e)
00267 {
00268     if (e->timerId() == d->poolTimer.timerId()) {
00269         d->poolTimer.stop();
00270         d->playerPool.clear();
00271         return;
00272     }
00273     KNotifyPlugin::timerEvent(e);
00274 }
00275 
00276 void NotifyBySound::slotSoundFinished(int id)
00277 {
00278     kDebug(300) << id;
00279     if(d->playerObjects.contains(id))
00280     {
00281         Player *player=d->playerObjects.take(id);
00282         disconnect(player->media, SIGNAL(finished()), d->signalmapper, SLOT(map()));
00283         d->playerPool.returnPlayer(player);
00284         //d->poolTimer.start(1000, this);
00285     }
00286     if(d->processes.contains(id))
00287     {
00288         d->processes[id]->deleteLater();
00289         d->processes.remove(id);
00290     }
00291     finish(id);
00292 }
00293 
00294 void NotifyBySound::close(int id)
00295 {
00296     // close in 1 min - ugly workaround for sounds getting cut off because the close call in kdelibs
00297     // is hardcoded to 6 seconds
00298     d->closeQueue.enqueue(id);
00299     QTimer::singleShot(60000, this, SLOT(closeNow()));
00300 }
00301 
00302 void NotifyBySound::closeNow()
00303 {
00304     const int id = d->closeQueue.dequeue();
00305     if(d->playerObjects.contains(id))
00306     {
00307         Player *p = d->playerObjects.take(id);
00308         p->stop();
00309         d->playerPool.returnPlayer(p);
00310         //d->poolTimer.start(1000, this);
00311     }
00312     if(d->processes.contains(id))
00313     {
00314         d->processes[id]->kill();
00315         d->processes[id]->deleteLater();
00316         d->processes.remove(id);
00317     }
00318 }
00319 
00320 #include "notifybysound.moc"
00321 // vim: ts=4 noet

KNotify

Skip menu "KNotify"
  • Main Page
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Class Members
  • Related Pages

API Reference

Skip menu "API Reference"
  • KCMShell
  • KNotify
  • KStyles
  • Nepomuk Daemons
Generated for API Reference by doxygen 1.5.7
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal