2 * This file is part of CameraPlus.
4 * Copyright (C) 2012-2013 Mohammed Sameer <msameer@foolab.org>
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.
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.
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
23 #include "qtcamconfig.h"
25 #include <QWaitCondition>
26 #include <QDBusServiceWatcher>
27 #include <QDBusConnection>
28 #include <contextsubscriber/contextproperty.h>
30 #define CAMERA_IMAGE_START_SOUND_ID "camera-image-start"
31 #define CAMERA_IMAGE_END_SOUND_ID "camera-image-end"
32 #define CAMERA_VIDEO_START_SOUND_ID "camera-video-start"
33 #define CAMERA_VIDEO_STOP_SOUND_ID "camera-video-stop"
34 #define CAMERA_FOCUS_SOUND_ID "camera-focus"
36 // Odd, volume has to be a char *
37 #define CANBERRA_FULL_VOLUME "0.0"
38 #define CANBERRA_HEADSET_VOLUME "-24.0"
39 #define AUDIO_ROUTE_PROPERTY "/com/nokia/policy/audio_route"
40 #define AUDIO_ROUTE_SPEAKERS "ihf"
42 Sounds::Sounds(QObject *parent) :
46 m_watcher(new QDBusServiceWatcher("org.pulseaudio.Server",
47 QDBusConnection::systemBus(),
48 QDBusServiceWatcher::WatchForOwnerChange)) {
50 QObject::connect(m_watcher,
51 SIGNAL(serviceOwnerChanged(const QString&, const QString&, const QString&)),
53 SLOT(serviceOwnerChanged(const QString&, const QString&, const QString&)));
55 // No idea why but canberra will not cache without that!!!
56 setenv("CANBERRA_EVENT_LOOKUP", "1", 1);
58 m_audioRoute = new ContextProperty(AUDIO_ROUTE_PROPERTY, this);
59 QObject::connect(m_audioRoute, SIGNAL(valueChanged()), this, SLOT(audioConnectionChanged()));
60 m_audioRoute->waitForSubscription(true);
61 audioConnectionChanged();
66 ca_context_destroy(m_ctx);
71 void Sounds::serviceOwnerChanged(const QString& serviceName, const QString& oldOwner,
72 const QString& newOwner) {
73 Q_UNUSED(serviceName);
76 if (newOwner.isEmpty()) {
79 ca_context_destroy(m_ctx);
83 else if (!newOwner.isEmpty()) {
88 void Sounds::playImageCaptureStartedSound() {
89 if (isMuted() || !m_ctx) {
93 play(CAMERA_IMAGE_START_SOUND_ID);
96 void Sounds::playImageCaptureEndedSound() {
97 if (isMuted() || !m_ctx) {
101 play(CAMERA_IMAGE_END_SOUND_ID);
104 void Sounds::playVideoRecordingStartedSound() {
105 if (isMuted() || !m_ctx) {
109 playAndBlock(CAMERA_VIDEO_START_SOUND_ID);
112 void Sounds::playVideoRecordingEndedSound() {
113 if (isMuted() || !m_ctx) {
117 play(CAMERA_VIDEO_STOP_SOUND_ID);
120 void Sounds::playAutoFocusAcquiredSound() {
121 if (isMuted() || !m_ctx) {
125 play(CAMERA_FOCUS_SOUND_ID);
128 bool Sounds::isMuted() const {
132 void Sounds::setMuted(bool mute) {
133 if (mute != m_muted) {
139 void Sounds::reload() {
141 ca_context_destroy(m_ctx);
145 int code = CA_SUCCESS;
147 code = ca_context_create(&m_ctx);
148 if (code != CA_SUCCESS) {
149 qWarning() << "Failed to create canberra context" << ca_strerror(code) << code;
153 code = ca_context_set_driver(m_ctx, "pulse");
154 if (code != CA_SUCCESS) {
155 qWarning() << "Failed to set canberra driver to pulse audio" << ca_strerror(code) << code;
158 code = ca_context_change_props(m_ctx,
159 CA_PROP_MEDIA_ROLE, "camera-sound-effect",
161 if (code != CA_SUCCESS) {
162 qWarning() << "Failed to set context properties" << ca_strerror(code) << code;
165 code = ca_context_open(m_ctx);
166 if (code != CA_SUCCESS) {
167 qWarning() << "Failed to open canberra context" << ca_strerror(code) << code;
168 ca_context_destroy(m_ctx);
173 cache(m_imageCaptureStart, CAMERA_IMAGE_START_SOUND_ID);
174 cache(m_imageCaptureEnd, CAMERA_IMAGE_END_SOUND_ID);
175 cache(m_videoRecordingStart, CAMERA_VIDEO_START_SOUND_ID);
176 cache(m_videoRecordingEnd, CAMERA_VIDEO_STOP_SOUND_ID);
177 cache(m_autoFocusAcquired, CAMERA_FOCUS_SOUND_ID);
180 void Sounds::cache(const QString& path, const char *id) {
181 if (path.isEmpty()) {
185 int code = ca_context_cache(m_ctx,
186 CA_PROP_EVENT_ID, id,
187 CA_PROP_MEDIA_FILENAME, path.toLocal8Bit().data(),
188 CA_PROP_CANBERRA_CACHE_CONTROL, "permanent",
190 if (code != CA_SUCCESS) {
191 qWarning() << "Failed to cache" << path << ca_strerror(code) << code;
195 void Sounds::play(const char *id) {
197 qWarning() << "Not connected to pulse audio";
201 int code = ca_context_play(m_ctx, 0,
202 CA_PROP_CANBERRA_VOLUME, m_volume.toAscii().constData(),
203 CA_PROP_EVENT_ID, id,
204 CA_PROP_MEDIA_ROLE, "camera-sound-effect",
206 if (code != CA_SUCCESS) {
207 qDebug() << "Failed to play sound" << ca_strerror(code) << code;
211 void ca_finish_callback(ca_context *c, uint32_t id, int error_code, void *userdata) {
214 Q_UNUSED(error_code);
216 QPair<QMutex *, QWaitCondition *> *data =
217 static_cast<QPair<QMutex *, QWaitCondition *> *>(userdata);
219 data->second->wakeAll();
222 void Sounds::playAndBlock(const char *id) {
226 if (ca_proplist_create(&p) != CA_SUCCESS) {
227 qDebug() << "Failed to create proplist";
231 ca_proplist_sets(p, CA_PROP_CANBERRA_VOLUME, CANBERRA_FULL_VOLUME);
232 ca_proplist_sets(p, CA_PROP_EVENT_ID, id);
233 ca_proplist_sets(p, CA_PROP_MEDIA_ROLE, "camera-sound-effect");
235 QPair<QMutex *, QWaitCondition *> data = qMakePair<QMutex *, QWaitCondition *>(&mutex, &cond);
239 int code = ca_context_play_full(m_ctx, 0, p, ca_finish_callback, &data);
241 if (code != CA_SUCCESS) {
242 qDebug() << "Failed to play sound" << ca_strerror(code) << code;
244 ca_proplist_destroy(p);
250 ca_proplist_destroy(p);
254 void Sounds::audioConnectionChanged() {
255 if (m_audioRoute->value().toString() != AUDIO_ROUTE_SPEAKERS) {
256 m_volume = CANBERRA_HEADSET_VOLUME;
258 m_volume = CANBERRA_FULL_VOLUME;
262 QString Sounds::imageCaptureStart() const {
263 return m_imageCaptureStart;
266 void Sounds::setImageCaptureStart(const QString& path) {
267 if (path != m_imageCaptureStart) {
268 m_imageCaptureStart = path;
269 cache(m_imageCaptureStart, CAMERA_IMAGE_START_SOUND_ID);
270 emit imageCaptureStartChanged();
274 QString Sounds::imageCaptureEnd() const {
275 return m_imageCaptureEnd;
278 void Sounds::setImageCaptureEnd(const QString& path) {
279 if (path != m_imageCaptureEnd) {
280 m_imageCaptureEnd = path;
281 cache(m_imageCaptureEnd, CAMERA_IMAGE_END_SOUND_ID);
282 emit imageCaptureEndChanged();
286 QString Sounds::videoRecordingStart() const {
287 return m_videoRecordingStart;
290 void Sounds::setVideoRecordingStart(const QString& path) {
291 if (path != m_videoRecordingStart) {
292 m_videoRecordingStart = path;
293 cache(m_videoRecordingStart, CAMERA_VIDEO_START_SOUND_ID);
294 emit videoRecordingStartChanged();
298 QString Sounds::videoRecordingEnd() const {
299 return m_videoRecordingEnd;
302 void Sounds::setVideoRecordingEnd(const QString& path) {
303 if (path != m_videoRecordingEnd) {
304 m_videoRecordingEnd = path;
305 cache(m_videoRecordingEnd, CAMERA_VIDEO_STOP_SOUND_ID);
306 emit videoRecordingEndChanged();
310 QString Sounds::autoFocusAcquired() const {
311 return m_autoFocusAcquired;
314 void Sounds::setAutoFocusAcquired(const QString& path) {
315 if (path != m_autoFocusAcquired) {
316 m_autoFocusAcquired = path;
317 cache(m_autoFocusAcquired, CAMERA_FOCUS_SOUND_ID);
318 emit autoFocusAcquiredChanged();