Added NoFaceDetection quirk
[harmattan/cameraplus] / declarative / sounds.cpp
1 /*!
2  * This file is part of CameraPlus.
3  *
4  * Copyright (C) 2012-2013 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 #if defined(QT4)
29 #include <QDeclarativeInfo>
30 #elif defined(QT5)
31 #include <QQmlInfo>
32 #endif
33
34 #define CAMERA_IMAGE_START_SOUND_ID  "camera-image-start"
35 #define CAMERA_IMAGE_END_SOUND_ID    "camera-image-end"
36 #define CAMERA_VIDEO_START_SOUND_ID  "camera-video-start"
37 #define CAMERA_VIDEO_STOP_SOUND_ID   "camera-video-stop"
38 #define CAMERA_FOCUS_SOUND_ID        "camera-focus"
39
40 // Odd, volume has to be a char *
41 #define CANBERRA_FULL_VOLUME         "0.0"
42 #define CANBERRA_HEADSET_VOLUME      "-24.0"
43
44 Sounds::Sounds(QObject *parent) :
45   QObject(parent),
46   m_muted(false),
47   m_ctx(0),
48   m_volume(Sounds::VolumeHigh),
49   m_watcher(new QDBusServiceWatcher("org.pulseaudio.Server",
50                                     QDBusConnection::systemBus(),
51                                     QDBusServiceWatcher::WatchForOwnerChange)) {
52
53   QObject::connect(m_watcher,
54                    SIGNAL(serviceOwnerChanged(const QString&, const QString&, const QString&)),
55                    this,
56                    SLOT(serviceOwnerChanged(const QString&, const QString&, const QString&)));
57
58   // No idea why but canberra will not cache without that!!!
59   setenv("CANBERRA_EVENT_LOOKUP", "1", 1);
60 }
61
62 Sounds::~Sounds() {
63   if (m_ctx) {
64     ca_context_destroy(m_ctx);
65     m_ctx = 0;
66   }
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::playImageCaptureStartedSound() {
87   if (isMuted() || !m_ctx) {
88     return;
89   }
90
91   play(CAMERA_IMAGE_START_SOUND_ID);
92 }
93
94 void Sounds::playImageCaptureEndedSound() {
95   if (isMuted() || !m_ctx) {
96     return;
97   }
98
99   play(CAMERA_IMAGE_END_SOUND_ID);
100 }
101
102 void Sounds::playVideoRecordingStartedSound() {
103   if (isMuted() || !m_ctx) {
104     return;
105   }
106
107   playAndBlock(CAMERA_VIDEO_START_SOUND_ID);
108 }
109
110 void Sounds::playVideoRecordingEndedSound() {
111   if (isMuted() || !m_ctx) {
112     return;
113   }
114
115   play(CAMERA_VIDEO_STOP_SOUND_ID);
116 }
117
118 void Sounds::playAutoFocusAcquiredSound() {
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_imageCaptureStart, CAMERA_IMAGE_START_SOUND_ID);
172   cache(m_imageCaptureEnd, CAMERA_IMAGE_END_SOUND_ID);
173   cache(m_videoRecordingStart, CAMERA_VIDEO_START_SOUND_ID);
174   cache(m_videoRecordingEnd, CAMERA_VIDEO_STOP_SOUND_ID);
175   cache(m_autoFocusAcquired, 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   const char *volume = m_volume == Sounds::VolumeLow ?
200     CANBERRA_HEADSET_VOLUME : CANBERRA_FULL_VOLUME;
201
202   int code = ca_context_play(m_ctx, 0,
203                              CA_PROP_CANBERRA_VOLUME, volume,
204                              CA_PROP_EVENT_ID, id,
205                              CA_PROP_MEDIA_ROLE, "camera-sound-effect",
206                              NULL);
207   if (code != CA_SUCCESS) {
208     qmlInfo(this) << "Failed to play sound" << ca_strerror(code) << code;
209   }
210 }
211
212 void ca_finish_callback(ca_context *c, uint32_t id, int error_code, void *userdata) {
213   Q_UNUSED(c);
214   Q_UNUSED(id);
215   Q_UNUSED(error_code);
216
217   QPair<QMutex *, QWaitCondition *> *data =
218     static_cast<QPair<QMutex *, QWaitCondition *> *>(userdata);
219
220   data->second->wakeAll();
221 }
222
223 void Sounds::playAndBlock(const char *id) {
224   QMutex mutex;
225   QWaitCondition cond;
226   ca_proplist *p = 0;
227   if (ca_proplist_create(&p) != CA_SUCCESS) {
228     qDebug() << "Failed to create proplist";
229     return;
230   }
231
232   ca_proplist_sets(p, CA_PROP_CANBERRA_VOLUME, CANBERRA_FULL_VOLUME);
233   ca_proplist_sets(p, CA_PROP_EVENT_ID, id);
234   ca_proplist_sets(p, CA_PROP_MEDIA_ROLE, "camera-sound-effect");
235
236   QPair<QMutex *, QWaitCondition *> data = qMakePair<QMutex *, QWaitCondition *>(&mutex, &cond);
237
238   mutex.lock();
239
240   int code = ca_context_play_full(m_ctx, 0, p, ca_finish_callback, &data);
241
242   if (code != CA_SUCCESS) {
243     qDebug() << "Failed to play sound" << ca_strerror(code) << code;
244     mutex.unlock();
245     ca_proplist_destroy(p);
246
247     return;
248   }
249
250   cond.wait(&mutex);
251   ca_proplist_destroy(p);
252   mutex.unlock();
253 }
254
255 QString Sounds::imageCaptureStart() const {
256   return m_imageCaptureStart;
257 }
258
259 void Sounds::setImageCaptureStart(const QString& path) {
260   if (path != m_imageCaptureStart) {
261     m_imageCaptureStart = path;
262     cache(m_imageCaptureStart, CAMERA_IMAGE_START_SOUND_ID);
263     emit imageCaptureStartChanged();
264   }
265 }
266
267 QString Sounds::imageCaptureEnd() const {
268   return m_imageCaptureEnd;
269 }
270
271 void Sounds::setImageCaptureEnd(const QString& path) {
272   if (path != m_imageCaptureEnd) {
273     m_imageCaptureEnd = path;
274     cache(m_imageCaptureEnd, CAMERA_IMAGE_END_SOUND_ID);
275     emit imageCaptureEndChanged();
276   }
277 }
278
279 QString Sounds::videoRecordingStart() const {
280   return m_videoRecordingStart;
281 }
282
283 void Sounds::setVideoRecordingStart(const QString& path) {
284   if (path != m_videoRecordingStart) {
285     m_videoRecordingStart = path;
286     cache(m_videoRecordingStart, CAMERA_VIDEO_START_SOUND_ID);
287     emit videoRecordingStartChanged();
288   }
289 }
290
291 QString Sounds::videoRecordingEnd() const {
292   return m_videoRecordingEnd;
293 }
294
295 void Sounds::setVideoRecordingEnd(const QString& path) {
296   if (path != m_videoRecordingEnd) {
297     m_videoRecordingEnd = path;
298     cache(m_videoRecordingEnd, CAMERA_VIDEO_STOP_SOUND_ID);
299     emit videoRecordingEndChanged();
300   }
301 }
302
303 QString Sounds::autoFocusAcquired() const {
304   return m_autoFocusAcquired;
305 }
306
307 void Sounds::setAutoFocusAcquired(const QString& path) {
308   if (path != m_autoFocusAcquired) {
309     m_autoFocusAcquired = path;
310     cache(m_autoFocusAcquired, CAMERA_FOCUS_SOUND_ID);
311     emit autoFocusAcquiredChanged();
312   }
313 }
314
315 Sounds::Volume Sounds::volume() const {
316   return m_volume;
317 }
318
319 void Sounds::setVolume(const Sounds::Volume& volume) {
320   if (m_volume != volume) {
321     m_volume = volume;
322     emit volumeChanged();
323   }
324 }