• Skip to content
  • Skip to link menu
KDE API Reference
  • KDE API Reference
  • kde-runtime API Reference
  • KDE Home
  • Contact Us
 

KNotify

  • sources
  • kde-4.14
  • kde-runtime
  • knotify
notifybysound.cpp
Go to the documentation of this file.
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>
10 
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.
15 
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.
20 
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.
24 */
25 
26 
27 #include "notifybysound.h"
28 
29 // QT headers
30 #include <QHash>
31 #include <QtCore/QBasicTimer>
32 #include <QtCore/QQueue>
33 #include <QtCore/QTimer>
34 #include <QtCore/QTimerEvent>
35 #include <QtCore/QStack>
36 #include <QSignalMapper>
37 
38 // KDE headers
39 #include <kdebug.h>
40 #include <klocale.h>
41 #include <kprocess.h>
42 #include <kstandarddirs.h>
43 #include <kconfiggroup.h>
44 #include <kurl.h>
45 #include <config-runtime.h>
46 #include <kcomponentdata.h>
47 #include <knotifyconfig.h>
48 
49 // Phonon headers
50 #include <phonon/mediaobject.h>
51 #include <phonon/path.h>
52 #include <phonon/audiooutput.h>
53 
54 struct Player
55 {
56  Player()
57  : media(new Phonon::MediaObject),
58  output(new Phonon::AudioOutput(Phonon::NotificationCategory))
59  {
60  Phonon::createPath(media, output);
61  }
62 
63  inline void play(const QString &file) { media->setCurrentSource(file); media->enqueue(Phonon::MediaSource()); media->play(); }
64  inline void stop() { media->stop(); }
65  inline void setVolume(float volume) { output->setVolume(volume); }
66 
67  ~Player()
68  {
69  output->deleteLater();
70  media->deleteLater();
71  }
72 
73  Phonon::MediaObject *const media;
74  Phonon::AudioOutput *const output;
75 };
76 
77 class PlayerPool
78 {
79  public:
80  PlayerPool() : m_idlePlayer(0), m_volume(1.0) {}
81 
82  Player *getPlayer();
83  void returnPlayer(Player *);
84  void clear();
85 
86  void setVolume(float volume);
87 
88  private:
89  Player *m_idlePlayer;
90  QList<Player *> m_playersInUse;
91  float m_volume;
92 };
93 
94 Player *PlayerPool::getPlayer()
95 {
96  Player *p = 0;
97  if (!m_idlePlayer) {
98  p = new Player;
99  } else {
100  p = m_idlePlayer;
101  m_idlePlayer = 0;
102  }
103  p->setVolume(m_volume);
104  m_playersInUse << p;
105  return p;
106 }
107 
108 void PlayerPool::returnPlayer(Player *p)
109 {
110  m_playersInUse.removeAll(p);
111  if (m_idlePlayer) {
112  delete p;
113  } else {
114  m_idlePlayer = p;
115  }
116 }
117 
118 void PlayerPool::clear()
119 {
120  delete m_idlePlayer;
121  m_idlePlayer = 0;
122 }
123 
124 void PlayerPool::setVolume(float v)
125 {
126  m_volume = v;
127  foreach (Player *p, m_playersInUse) {
128  p->setVolume(v);
129  }
130 }
131 
132 class NotifyBySound::Private
133 {
134  public:
135  enum { NoSound, UsePhonon, ExternalPlayer } playerMode;
136  QString externalPlayer;
137 
138  QHash<int, KProcess *> processes;
139  QHash<int, Player*> playerObjects;
140  QSignalMapper *signalmapper;
141  PlayerPool playerPool;
142  QBasicTimer poolTimer;
143  QQueue<int> closeQueue;
144 
145  int volume;
146 
147 };
148 
149 NotifyBySound::NotifyBySound(QObject *parent) : KNotifyPlugin(parent),d(new Private)
150 {
151  d->signalmapper = new QSignalMapper(this);
152  connect(d->signalmapper, SIGNAL(mapped(int)), this, SLOT(slotSoundFinished(int)));
153 
154  loadConfig();
155 }
156 
157 
158 NotifyBySound::~NotifyBySound()
159 {
160  delete d;
161 }
162 
163 
164 void NotifyBySound::loadConfig()
165 {
166  // load external player settings
167  KSharedConfig::Ptr kc = KGlobal::config();
168  KConfigGroup cg(kc, "Sounds");
169 
170  d->playerMode = Private::UsePhonon;
171  if(cg.readEntry( "Use external player", false ))
172  {
173  d->playerMode = Private::ExternalPlayer;
174  d->externalPlayer = cg.readPathEntry("External player", QString());
175  // try to locate a suitable player if none is configured
176  if ( d->externalPlayer.isEmpty() ) {
177  QStringList players;
178  players << "wavplay" << "aplay" << "auplay" << "artsplay" << "akodeplay";
179  QStringList::const_iterator it = players.constBegin();
180  while ( d->externalPlayer.isEmpty() && it != players.constEnd() ) {
181  d->externalPlayer = KStandardDirs::findExe( *it );
182  ++it;
183  }
184  }
185  }
186  else if(cg.readEntry( "No sound" , false ))
187  {
188  d->playerMode = Private::NoSound;
189  }
190  // load default volume
191  setVolume( cg.readEntry( "Volume", 100 ) );
192 }
193 
194 
195 
196 
197 void NotifyBySound::notify( int eventId, KNotifyConfig * config )
198 {
199  if(d->playerMode == Private::NoSound)
200  {
201  finish( eventId );
202  return;
203  }
204 
205  if(d->playerObjects.contains(eventId) || d->processes.contains(eventId) )
206  {
207  //a sound is already playing for this notification, we don't support playing two sounds.
208  finish( eventId );
209  return;
210  }
211 
212  KUrl soundFileURL = config->readEntry( "Sound" , true );
213  QString soundFile = soundFileURL.toLocalFile();
214 
215  if (soundFile.isEmpty())
216  {
217  finish( eventId );
218  return;
219  }
220 
221  // get file name
222  if ( KUrl::isRelativeUrl(soundFile) )
223  {
224  QString search = QString("%1/sounds/%2").arg(config->appname).arg(soundFile);
225  search = KGlobal::mainComponent().dirs()->findResource("data", search);
226  if ( search.isEmpty() )
227  soundFile = KStandardDirs::locate( "sound", soundFile );
228  else
229  soundFile = search;
230  }
231  if ( soundFile.isEmpty() )
232  {
233  finish( eventId );
234  return;
235  }
236 
237  kDebug() << " going to play " << soundFile;
238  d->poolTimer.stop();
239 
240  if(d->playerMode == Private::UsePhonon)
241  {
242  Player *player = d->playerPool.getPlayer();
243  connect(player->media, SIGNAL(finished()), d->signalmapper, SLOT(map()));
244  d->signalmapper->setMapping(player->media, eventId);
245  player->play(soundFile);
246  d->playerObjects.insert(eventId, player);
247  }
248  else if (d->playerMode == Private::ExternalPlayer && !d->externalPlayer.isEmpty())
249  {
250  // use an external player to play the sound
251  KProcess *proc = new KProcess( this );
252  connect( proc, SIGNAL(finished(int, QProcess::ExitStatus)),
253  d->signalmapper, SLOT(map()) );
254  d->signalmapper->setMapping( proc , eventId );
255 
256  (*proc) << d->externalPlayer << soundFile;
257  proc->start();
258  }
259 }
260 
261 
262 void NotifyBySound::setVolume( int volume )
263 {
264  if ( volume<0 ) volume=0;
265  if ( volume>=100 ) volume=100;
266  d->volume = volume;
267  d->playerPool.setVolume(d->volume / 100.0);
268 }
269 
270 
271 void NotifyBySound::timerEvent(QTimerEvent *e)
272 {
273  if (e->timerId() == d->poolTimer.timerId()) {
274  d->poolTimer.stop();
275  d->playerPool.clear();
276  return;
277  }
278  KNotifyPlugin::timerEvent(e);
279 }
280 
281 void NotifyBySound::slotSoundFinished(int id)
282 {
283  kDebug() << id;
284  if(d->playerObjects.contains(id))
285  {
286  Player *player=d->playerObjects.take(id);
287  disconnect(player->media, SIGNAL(finished()), d->signalmapper, SLOT(map()));
288  d->playerPool.returnPlayer(player);
289  //d->poolTimer.start(1000, this);
290  }
291  if(d->processes.contains(id))
292  {
293  d->processes[id]->deleteLater();
294  d->processes.remove(id);
295  }
296  finish(id);
297 }
298 
299 void NotifyBySound::close(int id)
300 {
301  // close in 1 min - ugly workaround for sounds getting cut off because the close call in kdelibs
302  // is hardcoded to 6 seconds
303  d->closeQueue.enqueue(id);
304  QTimer::singleShot(60000, this, SLOT(closeNow()));
305 }
306 
307 void NotifyBySound::closeNow()
308 {
309  const int id = d->closeQueue.dequeue();
310  if(d->playerObjects.contains(id))
311  {
312  Player *p = d->playerObjects.take(id);
313  p->stop();
314  d->playerPool.returnPlayer(p);
315  //d->poolTimer.start(1000, this);
316  }
317  if(d->processes.contains(id))
318  {
319  d->processes[id]->kill();
320  d->processes[id]->deleteLater();
321  d->processes.remove(id);
322  }
323 }
324 
325 #include "notifybysound.moc"
326 // vim: ts=4 noet
NotifyBySound::notify
virtual void notify(int id, KNotifyConfig *config)
This function is called when the notification is sent.
Definition: notifybysound.cpp:197
QQueue< int >
QBasicTimer
KNotifyConfig::readEntry
QString readEntry(const QString &entry, bool path=false)
Definition: knotifyconfig.cpp:80
notifybysound.h
KNotifyPlugin
abstract class for KNotify actions
Definition: knotifyplugin.h:44
knotifyconfig.h
NotifyBySound::~NotifyBySound
virtual ~NotifyBySound()
Definition: notifybysound.cpp:158
QObject::timerEvent
virtual void timerEvent(QTimerEvent *event)
QObject::disconnect
bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method)
QList::const_iterator
QTimerEvent
QHash< int, KProcess * >
QObject
Phonon::MediaObject
QString::isEmpty
bool isEmpty() const
Phonon::MediaSource
QString
QList< Player * >
NotifyBySound::close
virtual void close(int id)
This function is called when the notification has been closed.
Definition: notifybysound.cpp:299
QStringList
Phonon::createPath
Path createPath(MediaNode *source, MediaNode *sink)
KNotifyPlugin::finished
void finished(int id)
the presentation is finished.
KNotifyPlugin::finish
void finish(int id)
emit the finished signal you MUST call this function for each call to notify(), even if you do nothin...
Definition: knotifyplugin.cpp:49
NotifyBySound::NotifyBySound
NotifyBySound(QObject *parent=0l)
Definition: notifybysound.cpp:149
QTimerEvent::timerId
int timerId() const
NotifyBySound::timerEvent
void timerEvent(QTimerEvent *)
Definition: notifybysound.cpp:271
Phonon::AudioOutput
QList::constEnd
const_iterator constEnd() const
QList::constBegin
const_iterator constBegin() const
QObject::connect
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
KNotifyConfig::appname
QString appname
the name of the application that triggered the notification
Definition: knotifyconfig.h:109
KNotifyConfig
Represent the configuration for an event.
Definition: knotifyconfig.h:60
QString::arg
QString arg(qlonglong a, int fieldWidth, int base, const QChar &fillChar) const
QSignalMapper
NotifyBySound::setVolume
void setVolume(int v)
Definition: notifybysound.cpp:262
QTimer::singleShot
singleShot
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Mon Jun 22 2020 13:08:23 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

KNotify

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

kde-runtime API Reference

Skip menu "kde-runtime API Reference"
  • KCMShell
  • KNotify
  • Plasma Runtime
  •     PlasmaCore
  •     DragAndDrop
  •     PlasmaComponents
  •     PlasmaExtraComponents
  •     QtExtraComponents

Search



Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal