add more spacing
[personal-kdebase.git] / runtime / knotify / notifybysound.cpp
blobf9a98122cc4fb1ed75ef18ad2c003ff9a2c82aa3
1 /*
2 Copyright (c) 1997 Christian Esken (esken@kde.org)
3 2000 Charles Samuels (charles@kde.org)
4 2000 Stefan Schimanski (1Stein@gmx.de)
5 2000 Matthias Ettrich (ettrich@kde.org)
6 2000 Waldo Bastian <bastian@kde.org>
7 2000-2003 Carsten Pfeiffer <pfeiffer@kde.org>
8 2005 Allan Sandfeld Jensen <kde@carewolf.com>
9 2005-2006 by Olivier Goffart <ogoffart at kde.org>
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 2, or (at your option)
14 any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
27 #include "notifybysound.h"
28 #include "knotifyconfig.h"
31 // QT headers
32 #include <QHash>
33 #include <QtCore/QBasicTimer>
34 #include <QtCore/QQueue>
35 #include <QtCore/QTimer>
36 #include <QtCore/QTimerEvent>
37 #include <QtCore/QStack>
38 #include <QSignalMapper>
40 // KDE headers
41 #include <kdebug.h>
42 #include <klocale.h>
43 #include <kprocess.h>
44 #include <kstandarddirs.h>
45 #include <kconfiggroup.h>
46 #include <kurl.h>
47 #include <config-runtime.h>
48 #include <kcomponentdata.h>
50 // Phonon headers
51 #include <phonon/mediaobject.h>
52 #include <phonon/path.h>
53 #include <phonon/audiooutput.h>
55 struct Player
57 Player()
58 : media(new Phonon::MediaObject),
59 output(new Phonon::AudioOutput(Phonon::NotificationCategory))
61 Phonon::createPath(media, output);
64 inline void play(const QString &file) { media->setCurrentSource(file); media->enqueue(Phonon::MediaSource()); media->play(); }
65 inline void stop() { media->stop(); }
66 inline void setVolume(float volume) { output->setVolume(volume); }
68 ~Player()
70 output->deleteLater();
71 media->deleteLater();
74 Phonon::MediaObject *const media;
75 Phonon::AudioOutput *const output;
78 class PlayerPool
80 public:
81 PlayerPool() : m_idlePlayer(0), m_volume(1.0) {}
83 Player *getPlayer();
84 void returnPlayer(Player *);
85 void clear();
87 void setVolume(float volume);
89 private:
90 Player *m_idlePlayer;
91 QList<Player *> m_playersInUse;
92 float m_volume;
95 Player *PlayerPool::getPlayer()
97 Player *p = 0;
98 if (!m_idlePlayer) {
99 p = new Player;
100 } else {
101 p = m_idlePlayer;
102 m_idlePlayer = 0;
104 p->setVolume(m_volume);
105 m_playersInUse << p;
106 return p;
109 void PlayerPool::returnPlayer(Player *p)
111 m_playersInUse.removeAll(p);
112 if (m_idlePlayer) {
113 delete p;
114 } else {
115 m_idlePlayer = p;
119 void PlayerPool::clear()
121 delete m_idlePlayer;
122 m_idlePlayer = 0;
125 void PlayerPool::setVolume(float v)
127 m_volume = v;
128 foreach (Player *p, m_playersInUse) {
129 p->setVolume(v);
133 class NotifyBySound::Private
135 public:
136 enum { NoSound, UsePhonon, ExternalPlayer } playerMode;
137 QString externalPlayer;
139 QHash<int, KProcess *> processes;
140 QHash<int, Player*> playerObjects;
141 QSignalMapper *signalmapper;
142 PlayerPool playerPool;
143 QBasicTimer poolTimer;
144 QQueue<int> closeQueue;
146 int volume;
150 NotifyBySound::NotifyBySound(QObject *parent) : KNotifyPlugin(parent),d(new Private)
152 d->signalmapper = new QSignalMapper(this);
153 connect(d->signalmapper, SIGNAL(mapped(int)), this, SLOT(slotSoundFinished(int)));
155 loadConfig();
159 NotifyBySound::~NotifyBySound()
161 delete d;
165 void NotifyBySound::loadConfig()
167 // load external player settings
168 KSharedConfig::Ptr kc = KGlobal::config();
169 KConfigGroup cg(kc, "Sounds");
171 d->playerMode = Private::UsePhonon;
172 if(cg.readEntry( "Use external player", false ))
174 d->playerMode = Private::ExternalPlayer;
175 d->externalPlayer = cg.readPathEntry("External player", QString());
176 // try to locate a suitable player if none is configured
177 if ( d->externalPlayer.isEmpty() ) {
178 QStringList players;
179 players << "wavplay" << "aplay" << "auplay" << "artsplay" << "akodeplay";
180 QStringList::const_iterator it = players.constBegin();
181 while ( d->externalPlayer.isEmpty() && it != players.constEnd() ) {
182 d->externalPlayer = KStandardDirs::findExe( *it );
183 ++it;
187 else if(cg.readEntry( "No sound" , false ))
189 d->playerMode = Private::NoSound;
191 // load default volume
192 setVolume( cg.readEntry( "Volume", 100 ) );
198 void NotifyBySound::notify( int eventId, KNotifyConfig * config )
200 if(d->playerObjects.contains(eventId) || d->processes.contains(eventId) )
202 //a sound is already playing for this notification, we don't support playing two sounds.
203 finish( eventId );
204 return;
207 KUrl soundFileURL = config->readEntry( "Sound" , true );
208 QString soundFile = soundFileURL.toLocalFile();
210 if (soundFile.isEmpty())
212 finish( eventId );
213 return;
216 // get file name
217 if ( KUrl::isRelativeUrl(soundFile) )
219 QString search = QString("%1/sounds/%2").arg(config->appname).arg(soundFile);
220 search = KGlobal::mainComponent().dirs()->findResource("data", search);
221 if ( search.isEmpty() )
222 soundFile = KStandardDirs::locate( "sound", soundFile );
223 else
224 soundFile = search;
226 if ( soundFile.isEmpty() )
228 finish( eventId );
229 return;
232 kDebug(300) << " going to play " << soundFile;
233 d->poolTimer.stop();
235 if(d->playerMode == Private::UsePhonon)
237 Player *player = d->playerPool.getPlayer();
238 connect(player->media, SIGNAL(finished()), d->signalmapper, SLOT(map()));
239 d->signalmapper->setMapping(player->media, eventId);
240 player->play(soundFile);
241 d->playerObjects.insert(eventId, player);
243 else if (d->playerMode == Private::ExternalPlayer && !d->externalPlayer.isEmpty())
245 // use an external player to play the sound
246 KProcess *proc = new KProcess( this );
247 connect( proc, SIGNAL(finished(int, QProcess::ExitStatus)),
248 d->signalmapper, SLOT(map()) );
249 d->signalmapper->setMapping( proc , eventId );
251 (*proc) << d->externalPlayer << soundFile;
252 proc->start();
257 void NotifyBySound::setVolume( int volume )
259 if ( volume<0 ) volume=0;
260 if ( volume>=100 ) volume=100;
261 d->volume = volume;
262 d->playerPool.setVolume(d->volume / 100.0);
266 void NotifyBySound::timerEvent(QTimerEvent *e)
268 if (e->timerId() == d->poolTimer.timerId()) {
269 d->poolTimer.stop();
270 d->playerPool.clear();
271 return;
273 KNotifyPlugin::timerEvent(e);
276 void NotifyBySound::slotSoundFinished(int id)
278 kDebug(300) << id;
279 if(d->playerObjects.contains(id))
281 Player *player=d->playerObjects.take(id);
282 disconnect(player->media, SIGNAL(finished()), d->signalmapper, SLOT(map()));
283 d->playerPool.returnPlayer(player);
284 //d->poolTimer.start(1000, this);
286 if(d->processes.contains(id))
288 d->processes[id]->deleteLater();
289 d->processes.remove(id);
291 finish(id);
294 void NotifyBySound::close(int id)
296 // close in 1 min - ugly workaround for sounds getting cut off because the close call in kdelibs
297 // is hardcoded to 6 seconds
298 d->closeQueue.enqueue(id);
299 QTimer::singleShot(60000, this, SLOT(closeNow()));
302 void NotifyBySound::closeNow()
304 const int id = d->closeQueue.dequeue();
305 if(d->playerObjects.contains(id))
307 Player *p = d->playerObjects.take(id);
308 p->stop();
309 d->playerPool.returnPlayer(p);
310 //d->poolTimer.start(1000, this);
312 if(d->processes.contains(id))
314 d->processes[id]->kill();
315 d->processes[id]->deleteLater();
316 d->processes.remove(id);
320 #include "notifybysound.moc"
321 // vim: ts=4 noet