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)
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"
33 #include <QtCore/QBasicTimer>
34 #include <QtCore/QQueue>
35 #include <QtCore/QTimer>
36 #include <QtCore/QTimerEvent>
37 #include <QtCore/QStack>
38 #include <QSignalMapper>
44 #include <kstandarddirs.h>
45 #include <kconfiggroup.h>
47 #include <config-runtime.h>
48 #include <kcomponentdata.h>
51 #include <phonon/mediaobject.h>
52 #include <phonon/path.h>
53 #include <phonon/audiooutput.h>
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
); }
70 output
->deleteLater();
74 Phonon::MediaObject
*const media
;
75 Phonon::AudioOutput
*const output
;
81 PlayerPool() : m_idlePlayer(0), m_volume(1.0) {}
84 void returnPlayer(Player
*);
87 void setVolume(float volume
);
91 QList
<Player
*> m_playersInUse
;
95 Player
*PlayerPool::getPlayer()
104 p
->setVolume(m_volume
);
109 void PlayerPool::returnPlayer(Player
*p
)
111 m_playersInUse
.removeAll(p
);
119 void PlayerPool::clear()
125 void PlayerPool::setVolume(float v
)
128 foreach (Player
*p
, m_playersInUse
) {
133 class NotifyBySound::Private
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
;
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)));
159 NotifyBySound::~NotifyBySound()
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() ) {
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
);
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.
207 KUrl soundFileURL
= config
->readEntry( "Sound" , true );
208 QString soundFile
= soundFileURL
.toLocalFile();
210 if (soundFile
.isEmpty())
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
);
226 if ( soundFile
.isEmpty() )
232 kDebug(300) << " going to play " << soundFile
;
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
;
257 void NotifyBySound::setVolume( int volume
)
259 if ( volume
<0 ) volume
=0;
260 if ( volume
>=100 ) volume
=100;
262 d
->playerPool
.setVolume(d
->volume
/ 100.0);
266 void NotifyBySound::timerEvent(QTimerEvent
*e
)
268 if (e
->timerId() == d
->poolTimer
.timerId()) {
270 d
->playerPool
.clear();
273 KNotifyPlugin::timerEvent(e
);
276 void NotifyBySound::slotSoundFinished(int 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
);
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
);
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"