• Skip to content
  • Skip to link menu
KDE 4.1 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/QTimerEvent>
00035 #include <QtCore/QStack>
00036 #include <QSignalMapper>
00037 
00038 // KDE headers
00039 #include <kdebug.h>
00040 #include <klocale.h>
00041 #include <kprocess.h>
00042 #include <kstandarddirs.h>
00043 #include <kconfiggroup.h>
00044 #include <kurl.h>
00045 #include <config-runtime.h>
00046 #include <kcomponentdata.h>
00047 
00048 // Phonon headers
00049 #include <phonon/mediaobject.h>
00050 #include <phonon/path.h>
00051 #include <phonon/audiooutput.h>
00052 
00053 struct Player
00054 {
00055     Player()
00056         : media(new Phonon::MediaObject),
00057         output(new Phonon::AudioOutput(Phonon::NotificationCategory))
00058     {
00059         Phonon::createPath(media, output);
00060     }
00061 
00062     inline void play(const QString &file) { media->setCurrentSource(file); media->play(); }
00063     inline void stop() { media->stop(); }
00064     inline void setVolume(float volume) { output->setVolume(volume); }
00065 
00066     ~Player()
00067     {
00068         output->deleteLater();
00069         media->deleteLater();
00070     }
00071 
00072     Phonon::MediaObject *const media;
00073     Phonon::AudioOutput *const output;
00074 };
00075 
00076 class PlayerPool
00077 {
00078     public:
00079         PlayerPool() : m_volume(1.0) {}
00080 
00081         Player *getPlayer();
00082         void returnPlayer(Player *);
00083         void clear();
00084 
00085         void setVolume(float volume);
00086 
00087     private:
00088         QStack<Player *> m_playerPool;
00089         QList<Player *> m_playersInUse;
00090         float m_volume;
00091 };
00092 
00093 Player *PlayerPool::getPlayer()
00094 {
00095     Player *p = 0;
00096     if (m_playerPool.isEmpty()) {
00097         p = new Player;
00098     } else {
00099         p = m_playerPool.pop();
00100     }
00101     p->setVolume(m_volume);
00102     m_playersInUse << p;
00103     return p;
00104 }
00105 
00106 void PlayerPool::returnPlayer(Player *p)
00107 {
00108     m_playersInUse.removeAll(p);
00109     if (m_playerPool.size() > 2) {
00110         delete p;
00111     } else {
00112         m_playerPool.push(p);
00113     }
00114 }
00115 
00116 void PlayerPool::clear()
00117 {
00118     qDeleteAll(m_playerPool);
00119     m_playerPool.clear();
00120 }
00121 
00122 void PlayerPool::setVolume(float v)
00123 {
00124     m_volume = v;
00125     foreach (Player *p, m_playersInUse) {
00126         p->setVolume(v);
00127     }
00128 }
00129 
00130 class NotifyBySound::Private
00131 {
00132     public:
00133         enum { NoSound, UsePhonon, ExternalPlayer } playerMode;
00134         QString externalPlayer;
00135 
00136         QHash<int, KProcess *> processes;
00137         QHash<int, Player*> playerObjects;
00138         QSignalMapper *signalmapper;
00139         PlayerPool playerPool;
00140         QBasicTimer poolTimer;
00141 
00142         int volume;
00143 
00144 };
00145 
00146 NotifyBySound::NotifyBySound(QObject *parent) : KNotifyPlugin(parent),d(new Private)
00147 {
00148     d->signalmapper = new QSignalMapper(this);
00149     connect(d->signalmapper, SIGNAL(mapped(int)), this, SLOT(slotSoundFinished(int)));
00150 
00151     loadConfig();
00152 }
00153 
00154 
00155 NotifyBySound::~NotifyBySound()
00156 {
00157     delete d;
00158 }
00159 
00160 
00161 void NotifyBySound::loadConfig()
00162 {
00163     // load external player settings
00164     KSharedConfig::Ptr kc = KGlobal::config();
00165     KConfigGroup cg(kc, "Sounds");
00166 
00167     d->playerMode = Private::UsePhonon;
00168     if(cg.readEntry( "Use external player", false ))
00169     {
00170         d->playerMode = Private::ExternalPlayer;
00171         d->externalPlayer = cg.readPathEntry("External player", QString());
00172         // try to locate a suitable player if none is configured
00173         if ( d->externalPlayer.isEmpty() ) {
00174             QStringList players;
00175             players << "wavplay" << "aplay" << "auplay" << "artsplay" << "akodeplay";
00176             QStringList::Iterator it = players.begin();
00177             while ( d->externalPlayer.isEmpty() && it != players.end() ) {
00178                 d->externalPlayer = KStandardDirs::findExe( *it );
00179                 ++it;
00180             }
00181         }
00182     }
00183     else if(cg.readEntry( "No sound" , false ))
00184     {
00185         d->playerMode = Private::NoSound;
00186     }
00187     // load default volume
00188     setVolume( cg.readEntry( "Volume", 100 ) );
00189 }
00190 
00191 
00192 
00193 
00194 void NotifyBySound::notify( int eventId, KNotifyConfig * config )
00195 {
00196     if(d->playerObjects.contains(eventId)  || d->processes.contains(eventId) )
00197     {
00198         //a sound is already playing for this notification,  we don't support playing two sounds.
00199         finish( eventId );
00200         return;
00201     }
00202 
00203     KUrl soundFileURL = config->readEntry( "Sound" , true );
00204     QString soundFile = soundFileURL.toLocalFile();
00205 
00206     if (soundFile.isEmpty())
00207     {
00208         finish( eventId );
00209         return;
00210     }
00211     
00212     // get file name
00213     if ( KUrl::isRelativeUrl(soundFile) )
00214     {
00215         QString search = QString("%1/sounds/%2").arg(config->appname).arg(soundFile);
00216         search = KGlobal::mainComponent().dirs()->findResource("data", search);
00217         if ( search.isEmpty() )
00218             soundFile = KStandardDirs::locate( "sound", soundFile );
00219         else
00220             soundFile = search;
00221     }
00222     if ( soundFile.isEmpty() )
00223     {
00224         finish( eventId );
00225         return;
00226     }
00227 
00228     kDebug(300) << " going to play " << soundFile;
00229     d->poolTimer.stop();
00230 
00231     if(d->playerMode == Private::UsePhonon)
00232     {
00233         Player *player = d->playerPool.getPlayer();
00234         connect(player->media, SIGNAL(finished()), d->signalmapper, SLOT(map()));
00235         d->signalmapper->setMapping(player->media, eventId);
00236         player->play(soundFile);
00237         d->playerObjects.insert(eventId, player);
00238     }
00239     else if (d->playerMode == Private::ExternalPlayer && !d->externalPlayer.isEmpty())
00240     {
00241         // use an external player to play the sound
00242         KProcess *proc = new KProcess( this );
00243         connect( proc, SIGNAL(finished(int, QProcess::ExitStatus)),
00244                  d->signalmapper,  SLOT(map()) );
00245         d->signalmapper->setMapping( proc , eventId );
00246 
00247         (*proc) << d->externalPlayer << soundFile;
00248         proc->start();
00249     }
00250 }
00251 
00252 
00253 void NotifyBySound::setVolume( int volume )
00254 {
00255     if ( volume<0 ) volume=0;
00256     if ( volume>=100 ) volume=100;
00257     d->volume = volume;
00258     d->playerPool.setVolume(d->volume / 100.0);
00259 }
00260 
00261 
00262 void NotifyBySound::timerEvent(QTimerEvent *e)
00263 {
00264     if (e->timerId() == d->poolTimer.timerId()) {
00265         d->poolTimer.stop();
00266         d->playerPool.clear();
00267         return;
00268     }
00269     KNotifyPlugin::timerEvent(e);
00270 }
00271 
00272 void NotifyBySound::slotSoundFinished(int id)
00273 {
00274     kDebug(300) << id;
00275     if(d->playerObjects.contains(id))
00276     {
00277         Player *player=d->playerObjects.take(id);
00278         disconnect(player->media, SIGNAL(finished()), d->signalmapper, SLOT(map()));
00279         d->playerPool.returnPlayer(player);
00280         d->poolTimer.start(1000, this);
00281     }
00282     if(d->processes.contains(id))
00283     {
00284         d->processes[id]->deleteLater();
00285         d->processes.remove(id);
00286     }
00287     finish(id);
00288 }
00289 
00290 
00291 void NotifyBySound::close(int id)
00292 {
00293     if(d->playerObjects.contains(id))
00294     {
00295         Player *p = d->playerObjects.take(id);
00296         p->stop();
00297         d->playerPool.returnPlayer(p);
00298         d->poolTimer.start(1000, this);
00299     }
00300     if(d->processes.contains(id))
00301     {
00302         d->processes[id]->kill();
00303         d->processes[id]->deleteLater();
00304         d->processes.remove(id);
00305     }
00306 }
00307 
00308 #include "notifybysound.moc"
00309 // 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.4
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