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) :
47 m_watcher(new QDBusServiceWatcher("org.pulseaudio.Server",
48 QDBusConnection::systemBus(),
49 QDBusServiceWatcher::WatchForOwnerChange)) {
51 QObject::connect(m_watcher,
52 SIGNAL(serviceOwnerChanged(const QString&, const QString&, const QString&)),
54 SLOT(serviceOwnerChanged(const QString&, const QString&, const QString&)));
56 // No idea why but canberra will not cache without that!!!
57 setenv("CANBERRA_EVENT_LOOKUP", "1", 1);
59 m_audioRoute = new ContextProperty(AUDIO_ROUTE_PROPERTY, this);
60 QObject::connect(m_audioRoute, SIGNAL(valueChanged()), this, SLOT(audioConnectionChanged()));
61 m_audioRoute->waitForSubscription(true);
62 audioConnectionChanged();
67 ca_context_destroy(m_ctx);
72 void Sounds::setConfig(QtCamConfig *conf) {
76 void Sounds::serviceOwnerChanged(const QString& serviceName, const QString& oldOwner,
77 const QString& newOwner) {
78 Q_UNUSED(serviceName);
81 if (newOwner.isEmpty()) {
84 ca_context_destroy(m_ctx);
88 else if (!newOwner.isEmpty()) {
93 void Sounds::imageCaptureStarted() {
94 if (isMuted() || !m_ctx) {
98 play(CAMERA_IMAGE_START_SOUND_ID);
101 void Sounds::imageCaptureEnded() {
102 if (isMuted() || !m_ctx) {
106 play(CAMERA_IMAGE_END_SOUND_ID);
109 void Sounds::videoRecordingStarted() {
110 if (isMuted() || !m_ctx) {
114 playAndBlock(CAMERA_VIDEO_START_SOUND_ID);
117 void Sounds::videoRecordingEnded() {
118 if (isMuted() || !m_ctx) {
122 play(CAMERA_VIDEO_STOP_SOUND_ID);
125 void Sounds::autoFocusAcquired() {
126 if (isMuted() || !m_ctx) {
130 play(CAMERA_FOCUS_SOUND_ID);
133 bool Sounds::isMuted() const {
137 void Sounds::setMuted(bool mute) {
138 if (mute != m_muted) {
144 void Sounds::reload() {
146 ca_context_destroy(m_ctx);
150 int code = CA_SUCCESS;
152 code = ca_context_create(&m_ctx);
153 if (code != CA_SUCCESS) {
154 qWarning() << "Failed to create canberra context" << ca_strerror(code) << code;
158 code = ca_context_set_driver(m_ctx, "pulse");
159 if (code != CA_SUCCESS) {
160 qWarning() << "Failed to set canberra driver to pulse audio" << ca_strerror(code) << code;
163 code = ca_context_change_props(m_ctx,
164 CA_PROP_MEDIA_ROLE, "camera-sound-effect",
166 if (code != CA_SUCCESS) {
167 qWarning() << "Failed to set context properties" << ca_strerror(code) << code;
170 code = ca_context_open(m_ctx);
171 if (code != CA_SUCCESS) {
172 qWarning() << "Failed to open canberra context" << ca_strerror(code) << code;
173 ca_context_destroy(m_ctx);
178 cache(m_conf->imageCaptureStartedSound(), CAMERA_IMAGE_START_SOUND_ID);
179 cache(m_conf->imageCaptureEndedSound(), CAMERA_IMAGE_END_SOUND_ID);
180 cache(m_conf->videoRecordingStartedSound(), CAMERA_VIDEO_START_SOUND_ID);
181 cache(m_conf->videoRecordingEndedSound(), CAMERA_VIDEO_STOP_SOUND_ID);
182 cache(m_conf->autoFocusAcquiredSound(), CAMERA_FOCUS_SOUND_ID);
185 void Sounds::cache(const QString& path, const char *id) {
186 if (path.isEmpty()) {
190 int code = ca_context_cache(m_ctx,
191 CA_PROP_EVENT_ID, id,
192 CA_PROP_MEDIA_FILENAME, path.toLocal8Bit().data(),
193 CA_PROP_CANBERRA_CACHE_CONTROL, "permanent",
195 if (code != CA_SUCCESS) {
196 qWarning() << "Failed to cache" << path << ca_strerror(code) << code;
200 void Sounds::play(const char *id) {
202 qWarning() << "Not connected to pulse audio";
206 int code = ca_context_play(m_ctx, 0,
207 CA_PROP_CANBERRA_VOLUME, m_volume.toAscii().constData(),
208 CA_PROP_EVENT_ID, id,
209 CA_PROP_MEDIA_ROLE, "camera-sound-effect",
211 if (code != CA_SUCCESS) {
212 qDebug() << "Failed to play sound" << ca_strerror(code) << code;
216 void ca_finish_callback(ca_context *c, uint32_t id, int error_code, void *userdata) {
219 Q_UNUSED(error_code);
221 QPair<QMutex *, QWaitCondition *> *data =
222 static_cast<QPair<QMutex *, QWaitCondition *> *>(userdata);
224 data->second->wakeAll();
227 void Sounds::playAndBlock(const char *id) {
231 if (ca_proplist_create(&p) != CA_SUCCESS) {
232 qDebug() << "Failed to create proplist";
236 ca_proplist_sets(p, CA_PROP_CANBERRA_VOLUME, CANBERRA_FULL_VOLUME);
237 ca_proplist_sets(p, CA_PROP_EVENT_ID, id);
238 ca_proplist_sets(p, CA_PROP_MEDIA_ROLE, "camera-sound-effect");
240 QPair<QMutex *, QWaitCondition *> data = qMakePair<QMutex *, QWaitCondition *>(&mutex, &cond);
244 int code = ca_context_play_full(m_ctx, 0, p, ca_finish_callback, &data);
246 if (code != CA_SUCCESS) {
247 qDebug() << "Failed to play sound" << ca_strerror(code) << code;
249 ca_proplist_destroy(p);
255 ca_proplist_destroy(p);
259 void Sounds::audioConnectionChanged() {
260 if (m_audioRoute->value().toString() != AUDIO_ROUTE_SPEAKERS) {
261 m_volume = CANBERRA_HEADSET_VOLUME;
263 m_volume = CANBERRA_FULL_VOLUME;