KNotify
notifybysound.cpp
Go to the documentation of this file.00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027 #include "notifybysound.h"
00028 #include "knotifyconfig.h"
00029
00030
00031
00032 #include <QHash>
00033 #include <QtCore/QBasicTimer>
00034 #include <QtCore/QTimerEvent>
00035 #include <QtCore/QStack>
00036 #include <QSignalMapper>
00037
00038
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
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
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
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
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
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
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
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