Autohide video player toolbar after 3 seconds of inactivity
[harmattan/cameraplus] / declarative / sounds.cpp
1 /*!
2  * This file is part of CameraPlus.
3  *
4  * Copyright (C) 2012 Mohammed Sameer <msameer@foolab.org>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
19  */
20
21 #include "sounds.h"
22 #include <QDebug>
23 #include "qtcamconfig.h"
24 #include <QMutex>
25 #include <QWaitCondition>
26 #include <QDBusServiceWatcher>
27 #include <QDBusConnection>
28
29 #define CAMERA_IMAGE_START_SOUND_ID  "camera-image-start"
30 #define CAMERA_IMAGE_END_SOUND_ID    "camera-image-end"
31 #define CAMERA_VIDEO_START_SOUND_ID  "camera-video-start"
32 #define CAMERA_VIDEO_STOP_SOUND_ID   "camera-video-stop"
33 #define CAMERA_FOCUS_SOUND_ID        "camera-focus"
34
35 // Odd, volume has to be a char *
36 #define CANBERRA_FULL_VOLUME "0.0"
37
38 // TODO: if we are using headphones then sound volume might be loud. Detect and lower it.
39
40 Sounds::Sounds(QObject *parent) :
41   QObject(parent),
42   m_muted(false),
43   m_ctx(0),
44   m_conf(0),
45   m_watcher(new QDBusServiceWatcher("org.pulseaudio.Server",
46                                     QDBusConnection::systemBus(),
47                                     QDBusServiceWatcher::WatchForOwnerChange)) {
48
49   QObject::connect(m_watcher,
50                    SIGNAL(serviceOwnerChanged(const QString&, const QString&, const QString&)),
51                    this,
52                    SLOT(serviceOwnerChanged(const QString&, const QString&, const QString&)));
53
54   // No idea why but canberra will not cache without that!!!
55   setenv("CANBERRA_EVENT_LOOKUP", "1", 1);
56 }
57
58 Sounds::~Sounds() {
59   if (m_ctx) {
60     ca_context_destroy(m_ctx);
61     m_ctx = 0;
62   }
63 }
64
65 void Sounds::setConfig(QtCamConfig *conf) {
66   m_conf = conf;
67 }
68
69 void Sounds::serviceOwnerChanged(const QString& serviceName, const QString& oldOwner,
70                                  const QString& newOwner) {
71   Q_UNUSED(serviceName);
72   Q_UNUSED(oldOwner);
73
74   if (newOwner.isEmpty()) {
75     // pulse died:
76     if (m_ctx) {
77       ca_context_destroy(m_ctx);
78       m_ctx = 0;
79     }
80   }
81   else if (!newOwner.isEmpty()) {
82     reload();
83   }
84 }
85
86 void Sounds::imageCaptureStarted() {
87   if (isMuted() || !m_ctx) {
88     return;
89   }
90
91   play(CAMERA_IMAGE_START_SOUND_ID);
92 }
93
94 void Sounds::imageCaptureEnded() {
95   if (isMuted() || !m_ctx) {
96     return;
97   }
98
99   play(CAMERA_IMAGE_END_SOUND_ID);
100 }
101
102 void Sounds::videoRecordingStarted() {
103   if (isMuted() || !m_ctx) {
104     return;
105   }
106
107   playAndBlock(CAMERA_VIDEO_START_SOUND_ID);
108 }
109
110 void Sounds::videoRecordingEnded() {
111   if (isMuted() || !m_ctx) {
112     return;
113   }
114
115   play(CAMERA_VIDEO_STOP_SOUND_ID);
116 }
117
118 void Sounds::autoFocusAcquired() {
119   if (isMuted() || !m_ctx) {
120     return;
121   }
122
123   play(CAMERA_FOCUS_SOUND_ID);
124 }
125
126 bool Sounds::isMuted() const {
127   return m_muted;
128 }
129
130 void Sounds::setMuted(bool mute) {
131   if (mute != m_muted) {
132     m_muted = mute;
133     emit muteChanged();
134   }
135 }
136
137 void Sounds::reload() {
138   if (m_ctx) {
139     ca_context_destroy(m_ctx);
140     m_ctx = 0;
141   }
142
143   int code = CA_SUCCESS;
144
145   code = ca_context_create(&m_ctx);
146   if (code != CA_SUCCESS) {
147     qWarning() << "Failed to create canberra context" << ca_strerror(code) << code;
148     return;
149   }
150
151   code = ca_context_set_driver(m_ctx, "pulse");
152   if (code != CA_SUCCESS) {
153     qWarning() << "Failed to set canberra driver to pulse audio" << ca_strerror(code) << code;
154   }
155
156   code = ca_context_change_props(m_ctx,
157                                  CA_PROP_MEDIA_ROLE, "camera-sound-effect",
158                                  NULL);
159   if (code != CA_SUCCESS) {
160     qWarning() << "Failed to set context properties" << ca_strerror(code) << code;
161   }
162
163   code = ca_context_open(m_ctx);
164   if (code != CA_SUCCESS) {
165     qWarning() << "Failed to open canberra context" << ca_strerror(code) << code;
166     ca_context_destroy(m_ctx);
167     m_ctx = 0;
168     return;
169   }
170
171   cache(m_conf->imageCaptureStartedSound(), CAMERA_IMAGE_START_SOUND_ID);
172   cache(m_conf->imageCaptureEndedSound(), CAMERA_IMAGE_END_SOUND_ID);
173   cache(m_conf->videoRecordingStartedSound(), CAMERA_VIDEO_START_SOUND_ID);
174   cache(m_conf->videoRecordingEndedSound(), CAMERA_VIDEO_STOP_SOUND_ID);
175   cache(m_conf->autoFocusAcquiredSound(), CAMERA_FOCUS_SOUND_ID);
176 }
177
178 void Sounds::cache(const QString& path, const char *id) {
179   if (path.isEmpty()) {
180     return;
181   }
182
183   int code = ca_context_cache(m_ctx,
184                               CA_PROP_EVENT_ID, id,
185                               CA_PROP_MEDIA_FILENAME, path.toLocal8Bit().data(),
186                               CA_PROP_CANBERRA_CACHE_CONTROL, "permanent",
187                               NULL);
188   if (code != CA_SUCCESS) {
189     qWarning() << "Failed to cache" << path << ca_strerror(code) << code;
190   }
191 }
192
193 void Sounds::play(const char *id) {
194   if (!m_ctx) {
195     qWarning() << "Not connected to pulse audio";
196     return;
197   }
198
199   int code = ca_context_play(m_ctx, 0,
200                              CA_PROP_CANBERRA_VOLUME, CANBERRA_FULL_VOLUME,
201                              CA_PROP_EVENT_ID, id,
202                              CA_PROP_MEDIA_ROLE, "camera-sound-effect",
203                              NULL);
204   if (code != CA_SUCCESS) {
205     qDebug() << "Failed to play sound" << ca_strerror(code) << code;
206   }
207 }
208
209 void ca_finish_callback(ca_context *c, uint32_t id, int error_code, void *userdata) {
210   Q_UNUSED(c);
211   Q_UNUSED(id);
212   Q_UNUSED(error_code);
213
214   QPair<QMutex *, QWaitCondition *> *data =
215     static_cast<QPair<QMutex *, QWaitCondition *> *>(userdata);
216
217   data->second->wakeAll();
218 }
219
220 void Sounds::playAndBlock(const char *id) {
221   QMutex mutex;
222   QWaitCondition cond;
223   ca_proplist *p = 0;
224   if (ca_proplist_create(&p) != CA_SUCCESS) {
225     qDebug() << "Failed to create proplist";
226     return;
227   }
228
229   ca_proplist_sets(p, CA_PROP_CANBERRA_VOLUME, CANBERRA_FULL_VOLUME);
230   ca_proplist_sets(p, CA_PROP_EVENT_ID, id);
231   ca_proplist_sets(p, CA_PROP_MEDIA_ROLE, "camera-sound-effect");
232
233   QPair<QMutex *, QWaitCondition *> data = qMakePair<QMutex *, QWaitCondition *>(&mutex, &cond);
234
235   mutex.lock();
236
237   int code = ca_context_play_full(m_ctx, 0, p, ca_finish_callback, &data);
238
239   if (code != CA_SUCCESS) {
240     qDebug() << "Failed to play sound" << ca_strerror(code) << code;
241     mutex.unlock();
242     ca_proplist_destroy(p);
243
244     return;
245   }
246
247   cond.wait(&mutex);
248   ca_proplist_destroy(p);
249   mutex.unlock();
250 }