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 <QDeclarativeInfo>
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"
40 Sounds::Sounds(QObject *parent) :
44 m_volume(Sounds::VolumeHigh),
45 m_watcher(new QDBusServiceWatcher("org.pulseaudio.Server",
46 QDBusConnection::systemBus(),
47 QDBusServiceWatcher::WatchForOwnerChange)) {
49 QObject::connect(m_watcher,
50 SIGNAL(serviceOwnerChanged(const QString&, const QString&, const QString&)),
52 SLOT(serviceOwnerChanged(const QString&, const QString&, const QString&)));
54 // No idea why but canberra will not cache without that!!!
55 setenv("CANBERRA_EVENT_LOOKUP", "1", 1);
60 ca_context_destroy(m_ctx);
65 void Sounds::serviceOwnerChanged(const QString& serviceName, const QString& oldOwner,
66 const QString& newOwner) {
67 Q_UNUSED(serviceName);
70 if (newOwner.isEmpty()) {
73 ca_context_destroy(m_ctx);
77 else if (!newOwner.isEmpty()) {
82 void Sounds::playImageCaptureStartedSound() {
83 if (isMuted() || !m_ctx) {
87 play(CAMERA_IMAGE_START_SOUND_ID);
90 void Sounds::playImageCaptureEndedSound() {
91 if (isMuted() || !m_ctx) {
95 play(CAMERA_IMAGE_END_SOUND_ID);
98 void Sounds::playVideoRecordingStartedSound() {
99 if (isMuted() || !m_ctx) {
103 playAndBlock(CAMERA_VIDEO_START_SOUND_ID);
106 void Sounds::playVideoRecordingEndedSound() {
107 if (isMuted() || !m_ctx) {
111 play(CAMERA_VIDEO_STOP_SOUND_ID);
114 void Sounds::playAutoFocusAcquiredSound() {
115 if (isMuted() || !m_ctx) {
119 play(CAMERA_FOCUS_SOUND_ID);
122 bool Sounds::isMuted() const {
126 void Sounds::setMuted(bool mute) {
127 if (mute != m_muted) {
133 void Sounds::reload() {
135 ca_context_destroy(m_ctx);
139 int code = CA_SUCCESS;
141 code = ca_context_create(&m_ctx);
142 if (code != CA_SUCCESS) {
143 qWarning() << "Failed to create canberra context" << ca_strerror(code) << code;
147 code = ca_context_set_driver(m_ctx, "pulse");
148 if (code != CA_SUCCESS) {
149 qWarning() << "Failed to set canberra driver to pulse audio" << ca_strerror(code) << code;
152 code = ca_context_change_props(m_ctx,
153 CA_PROP_MEDIA_ROLE, "camera-sound-effect",
155 if (code != CA_SUCCESS) {
156 qWarning() << "Failed to set context properties" << ca_strerror(code) << code;
159 code = ca_context_open(m_ctx);
160 if (code != CA_SUCCESS) {
161 qWarning() << "Failed to open canberra context" << ca_strerror(code) << code;
162 ca_context_destroy(m_ctx);
167 cache(m_imageCaptureStart, CAMERA_IMAGE_START_SOUND_ID);
168 cache(m_imageCaptureEnd, CAMERA_IMAGE_END_SOUND_ID);
169 cache(m_videoRecordingStart, CAMERA_VIDEO_START_SOUND_ID);
170 cache(m_videoRecordingEnd, CAMERA_VIDEO_STOP_SOUND_ID);
171 cache(m_autoFocusAcquired, CAMERA_FOCUS_SOUND_ID);
174 void Sounds::cache(const QString& path, const char *id) {
175 if (path.isEmpty()) {
179 int code = ca_context_cache(m_ctx,
180 CA_PROP_EVENT_ID, id,
181 CA_PROP_MEDIA_FILENAME, path.toLocal8Bit().data(),
182 CA_PROP_CANBERRA_CACHE_CONTROL, "permanent",
184 if (code != CA_SUCCESS) {
185 qWarning() << "Failed to cache" << path << ca_strerror(code) << code;
189 void Sounds::play(const char *id) {
191 qWarning() << "Not connected to pulse audio";
195 const char *volume = m_volume == Sounds::VolumeLow ?
196 CANBERRA_HEADSET_VOLUME : CANBERRA_FULL_VOLUME;
198 int code = ca_context_play(m_ctx, 0,
199 CA_PROP_CANBERRA_VOLUME, volume,
200 CA_PROP_EVENT_ID, id,
201 CA_PROP_MEDIA_ROLE, "camera-sound-effect",
203 if (code != CA_SUCCESS) {
204 qmlInfo(this) << "Failed to play sound" << ca_strerror(code) << code;
208 void ca_finish_callback(ca_context *c, uint32_t id, int error_code, void *userdata) {
211 Q_UNUSED(error_code);
213 QPair<QMutex *, QWaitCondition *> *data =
214 static_cast<QPair<QMutex *, QWaitCondition *> *>(userdata);
216 data->second->wakeAll();
219 void Sounds::playAndBlock(const char *id) {
223 if (ca_proplist_create(&p) != CA_SUCCESS) {
224 qDebug() << "Failed to create proplist";
228 ca_proplist_sets(p, CA_PROP_CANBERRA_VOLUME, CANBERRA_FULL_VOLUME);
229 ca_proplist_sets(p, CA_PROP_EVENT_ID, id);
230 ca_proplist_sets(p, CA_PROP_MEDIA_ROLE, "camera-sound-effect");
232 QPair<QMutex *, QWaitCondition *> data = qMakePair<QMutex *, QWaitCondition *>(&mutex, &cond);
236 int code = ca_context_play_full(m_ctx, 0, p, ca_finish_callback, &data);
238 if (code != CA_SUCCESS) {
239 qDebug() << "Failed to play sound" << ca_strerror(code) << code;
241 ca_proplist_destroy(p);
247 ca_proplist_destroy(p);
251 QString Sounds::imageCaptureStart() const {
252 return m_imageCaptureStart;
255 void Sounds::setImageCaptureStart(const QString& path) {
256 if (path != m_imageCaptureStart) {
257 m_imageCaptureStart = path;
258 cache(m_imageCaptureStart, CAMERA_IMAGE_START_SOUND_ID);
259 emit imageCaptureStartChanged();
263 QString Sounds::imageCaptureEnd() const {
264 return m_imageCaptureEnd;
267 void Sounds::setImageCaptureEnd(const QString& path) {
268 if (path != m_imageCaptureEnd) {
269 m_imageCaptureEnd = path;
270 cache(m_imageCaptureEnd, CAMERA_IMAGE_END_SOUND_ID);
271 emit imageCaptureEndChanged();
275 QString Sounds::videoRecordingStart() const {
276 return m_videoRecordingStart;
279 void Sounds::setVideoRecordingStart(const QString& path) {
280 if (path != m_videoRecordingStart) {
281 m_videoRecordingStart = path;
282 cache(m_videoRecordingStart, CAMERA_VIDEO_START_SOUND_ID);
283 emit videoRecordingStartChanged();
287 QString Sounds::videoRecordingEnd() const {
288 return m_videoRecordingEnd;
291 void Sounds::setVideoRecordingEnd(const QString& path) {
292 if (path != m_videoRecordingEnd) {
293 m_videoRecordingEnd = path;
294 cache(m_videoRecordingEnd, CAMERA_VIDEO_STOP_SOUND_ID);
295 emit videoRecordingEndChanged();
299 QString Sounds::autoFocusAcquired() const {
300 return m_autoFocusAcquired;
303 void Sounds::setAutoFocusAcquired(const QString& path) {
304 if (path != m_autoFocusAcquired) {
305 m_autoFocusAcquired = path;
306 cache(m_autoFocusAcquired, CAMERA_FOCUS_SOUND_ID);
307 emit autoFocusAcquiredChanged();
311 Sounds::Volume Sounds::volume() const {
315 void Sounds::setVolume(const Sounds::Volume& volume) {
316 if (m_volume != volume) {
318 emit volumeChanged();