2 * This file is part of CameraPlus.
4 * Copyright (C) 2012 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 #define CAMERA_IMAGE_START_SOUND_ID "camera-image-start"
30 #define CAMERA_IMAGE_END_SOUND_ID "camera-image-end"
31 #define CAMERA_VIDEO_START_SOUND_ID "camera-video-start"
32 #define CAMERA_VIDEO_STOP_SOUND_ID "camera-video-stop"
33 #define CAMERA_FOCUS_SOUND_ID "camera-focus"
35 // Odd, volume has to be a char *
36 #define CANBERRA_FULL_VOLUME "0.0"
38 // TODO: if we are using headphones then sound volume might be loud. Detect and lower it.
40 Sounds::Sounds(QObject *parent) :
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::setConfig(QtCamConfig *conf) {
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::imageCaptureStarted() {
87 if (isMuted() || !m_ctx) {
91 play(CAMERA_IMAGE_START_SOUND_ID);
94 void Sounds::imageCaptureEnded() {
95 if (isMuted() || !m_ctx) {
99 play(CAMERA_IMAGE_END_SOUND_ID);
102 void Sounds::videoRecordingStarted() {
103 if (isMuted() || !m_ctx) {
107 playAndBlock(CAMERA_VIDEO_START_SOUND_ID);
110 void Sounds::videoRecordingEnded() {
111 if (isMuted() || !m_ctx) {
115 play(CAMERA_VIDEO_STOP_SOUND_ID);
118 void Sounds::autoFocusAcquired() {
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_conf->imageCaptureStartedSound(), CAMERA_IMAGE_START_SOUND_ID);
172 cache(m_conf->imageCaptureEndedSound(), CAMERA_IMAGE_END_SOUND_ID);
173 cache(m_conf->videoRecordingStartedSound(), CAMERA_VIDEO_START_SOUND_ID);
174 cache(m_conf->videoRecordingEndedSound(), CAMERA_VIDEO_STOP_SOUND_ID);
175 cache(m_conf->autoFocusAcquiredSound(), 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 int code = ca_context_play(m_ctx, 0,
200 CA_PROP_CANBERRA_VOLUME, CANBERRA_FULL_VOLUME,
201 CA_PROP_EVENT_ID, id,
202 CA_PROP_MEDIA_ROLE, "camera-sound-effect",
204 if (code != CA_SUCCESS) {
205 qDebug() << "Failed to play sound" << ca_strerror(code) << code;
209 void ca_finish_callback(ca_context *c, uint32_t id, int error_code, void *userdata) {
212 Q_UNUSED(error_code);
214 QPair<QMutex *, QWaitCondition *> *data =
215 static_cast<QPair<QMutex *, QWaitCondition *> *>(userdata);
217 data->second->wakeAll();
220 void Sounds::playAndBlock(const char *id) {
224 if (ca_proplist_create(&p) != CA_SUCCESS) {
225 qDebug() << "Failed to create proplist";
229 ca_proplist_sets(p, CA_PROP_CANBERRA_VOLUME, CANBERRA_FULL_VOLUME);
230 ca_proplist_sets(p, CA_PROP_EVENT_ID, id);
231 ca_proplist_sets(p, CA_PROP_MEDIA_ROLE, "camera-sound-effect");
233 QPair<QMutex *, QWaitCondition *> data = qMakePair<QMutex *, QWaitCondition *>(&mutex, &cond);
237 int code = ca_context_play_full(m_ctx, 0, p, ca_finish_callback, &data);
239 if (code != CA_SUCCESS) {
240 qDebug() << "Failed to play sound" << ca_strerror(code) << code;
242 ca_proplist_destroy(p);
248 ca_proplist_destroy(p);