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>
29 #include <QDeclarativeInfo>
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"
40 // Odd, volume has to be a char *
41 #define CANBERRA_FULL_VOLUME "0.0"
42 #define CANBERRA_HEADSET_VOLUME "-24.0"
44 Sounds::Sounds(QObject *parent) :
48 m_volume(Sounds::VolumeHigh),
49 m_watcher(new QDBusServiceWatcher("org.pulseaudio.Server",
50 QDBusConnection::systemBus(),
51 QDBusServiceWatcher::WatchForOwnerChange)) {
53 QObject::connect(m_watcher,
54 SIGNAL(serviceOwnerChanged(const QString&, const QString&, const QString&)),
56 SLOT(serviceOwnerChanged(const QString&, const QString&, const QString&)));
58 // No idea why but canberra will not cache without that!!!
59 setenv("CANBERRA_EVENT_LOOKUP", "1", 1);
64 ca_context_destroy(m_ctx);
69 void Sounds::serviceOwnerChanged(const QString& serviceName, const QString& oldOwner,
70 const QString& newOwner) {
71 Q_UNUSED(serviceName);
74 if (newOwner.isEmpty()) {
77 ca_context_destroy(m_ctx);
81 else if (!newOwner.isEmpty()) {
86 void Sounds::playImageCaptureStartedSound() {
87 if (isMuted() || !m_ctx) {
91 play(CAMERA_IMAGE_START_SOUND_ID);
94 void Sounds::playImageCaptureEndedSound() {
95 if (isMuted() || !m_ctx) {
99 play(CAMERA_IMAGE_END_SOUND_ID);
102 void Sounds::playVideoRecordingStartedSound() {
103 if (isMuted() || !m_ctx) {
107 playAndBlock(CAMERA_VIDEO_START_SOUND_ID);
110 void Sounds::playVideoRecordingEndedSound() {
111 if (isMuted() || !m_ctx) {
115 play(CAMERA_VIDEO_STOP_SOUND_ID);
118 void Sounds::playAutoFocusAcquiredSound() {
119 if (isMuted() || !m_ctx) {
123 play(CAMERA_FOCUS_SOUND_ID);
126 bool Sounds::isMuted() const {
130 void Sounds::setMuted(bool mute) {
131 if (mute != m_muted) {
137 void Sounds::reload() {
139 ca_context_destroy(m_ctx);
143 int code = CA_SUCCESS;
145 code = ca_context_create(&m_ctx);
146 if (code != CA_SUCCESS) {
147 qWarning() << "Failed to create canberra context" << ca_strerror(code) << code;
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;
156 code = ca_context_change_props(m_ctx,
157 CA_PROP_MEDIA_ROLE, "camera-sound-effect",
159 if (code != CA_SUCCESS) {
160 qWarning() << "Failed to set context properties" << ca_strerror(code) << code;
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);
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);
178 void Sounds::cache(const QString& path, const char *id) {
179 if (path.isEmpty()) {
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",
188 if (code != CA_SUCCESS) {
189 qWarning() << "Failed to cache" << path << ca_strerror(code) << code;
193 void Sounds::play(const char *id) {
195 qWarning() << "Not connected to pulse audio";
199 const char *volume = m_volume == Sounds::VolumeLow ?
200 CANBERRA_HEADSET_VOLUME : CANBERRA_FULL_VOLUME;
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",
207 if (code != CA_SUCCESS) {
208 qmlInfo(this) << "Failed to play sound" << ca_strerror(code) << code;
212 void ca_finish_callback(ca_context *c, uint32_t id, int error_code, void *userdata) {
215 Q_UNUSED(error_code);
217 QPair<QMutex *, QWaitCondition *> *data =
218 static_cast<QPair<QMutex *, QWaitCondition *> *>(userdata);
220 data->second->wakeAll();
223 void Sounds::playAndBlock(const char *id) {
227 if (ca_proplist_create(&p) != CA_SUCCESS) {
228 qDebug() << "Failed to create proplist";
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");
236 QPair<QMutex *, QWaitCondition *> data = qMakePair<QMutex *, QWaitCondition *>(&mutex, &cond);
240 int code = ca_context_play_full(m_ctx, 0, p, ca_finish_callback, &data);
242 if (code != CA_SUCCESS) {
243 qDebug() << "Failed to play sound" << ca_strerror(code) << code;
245 ca_proplist_destroy(p);
251 ca_proplist_destroy(p);
255 QString Sounds::imageCaptureStart() const {
256 return m_imageCaptureStart;
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();
267 QString Sounds::imageCaptureEnd() const {
268 return m_imageCaptureEnd;
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();
279 QString Sounds::videoRecordingStart() const {
280 return m_videoRecordingStart;
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();
291 QString Sounds::videoRecordingEnd() const {
292 return m_videoRecordingEnd;
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();
303 QString Sounds::autoFocusAcquired() const {
304 return m_autoFocusAcquired;
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();
315 Sounds::Volume Sounds::volume() const {
319 void Sounds::setVolume(const Sounds::Volume& volume) {
320 if (m_volume != volume) {
322 emit volumeChanged();