Added notification when focus gets acquired
[harmattan/cameraplus] / imports / sounds.cpp
1 /*!
2  * This file is part of CameraPlus.
3  *
4  * Copyright (C) 2012 Mohammed Sameer <msameer@foolab.org>
5  *
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.
10  *
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.
15  *
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
19  */
20
21 #include "sounds.h"
22 #include <QDebug>
23 #include "qtcamconfig.h"
24 #include <QMutex>
25 #include <QWaitCondition>
26
27 #define CAMERA_IMAGE_START_SOUND_ID  "camera-image-start"
28 #define CAMERA_IMAGE_END_SOUND_ID    "camera-image-end"
29 #define CAMERA_VIDEO_START_SOUND_ID  "camera-video-start"
30 #define CAMERA_VIDEO_STOP_SOUND_ID   "camera-video-stop"
31 #define CAMERA_FOCUS_SOUND_ID        "camera-focus"
32
33 // Odd, volume has to be a char *
34 #define CANBERRA_FULL_VOLUME "0.0"
35
36 // TODO: monitor pulse audio death and re-upload our samples
37 // TODO: if we are using headphones then sound volume might be loud. Detect and lower it.
38
39 Sounds::Sounds(QObject *parent) :
40   QObject(parent),
41   m_muted(false),
42   m_ctx(0),
43   m_conf(0) {
44
45   // No idea why but canberra will not cache without that!!!
46   setenv("CANBERRA_EVENT_LOOKUP", "1", 1);
47 }
48
49 Sounds::~Sounds() {
50   if (m_ctx) {
51     ca_context_destroy(m_ctx);
52     m_ctx = 0;
53   }
54 }
55
56 void Sounds::setConfig(QtCamConfig *conf) {
57   m_conf = conf;
58 }
59
60 void Sounds::imageCaptureStarted() {
61   if (isMuted() || !m_ctx) {
62     return;
63   }
64
65   play(CAMERA_IMAGE_START_SOUND_ID);
66 }
67
68 void Sounds::imageCaptureEnded() {
69   if (isMuted() || !m_ctx) {
70     return;
71   }
72
73   play(CAMERA_IMAGE_END_SOUND_ID);
74 }
75
76 void Sounds::videoRecordingStarted() {
77   if (isMuted() || !m_ctx) {
78     return;
79   }
80
81   playAndBlock(CAMERA_VIDEO_START_SOUND_ID);
82 }
83
84 void Sounds::videoRecordingEnded() {
85   if (isMuted() || !m_ctx) {
86     return;
87   }
88
89   play(CAMERA_VIDEO_STOP_SOUND_ID);
90 }
91
92 void Sounds::autoFocusAcquired() {
93   if (isMuted() || !m_ctx) {
94     return;
95   }
96
97   play(CAMERA_FOCUS_SOUND_ID);
98 }
99
100 bool Sounds::isMuted() const {
101   return m_muted;
102 }
103
104 void Sounds::setMuted(bool mute) {
105   if (mute != m_muted) {
106     m_muted = mute;
107     emit muteChanged();
108   }
109 }
110
111 void Sounds::reload() {
112   if (m_ctx) {
113     ca_context_destroy(m_ctx);
114     m_ctx = 0;
115   }
116
117   int code = CA_SUCCESS;
118
119   code = ca_context_create(&m_ctx);
120   if (code != CA_SUCCESS) {
121     qWarning() << "Failed to create canberra context" << ca_strerror(code) << code;
122     return;
123   }
124
125   code = ca_context_set_driver(m_ctx, "pulse");
126   if (code != CA_SUCCESS) {
127     qWarning() << "Failed to set canberra driver to pulse audio" << ca_strerror(code) << code;
128   }
129
130   code = ca_context_change_props(m_ctx,
131                                  CA_PROP_MEDIA_ROLE, "camera-sound-effect",
132                                  NULL);
133   if (code != CA_SUCCESS) {
134     qWarning() << "Failed to set context properties" << ca_strerror(code) << code;
135   }
136
137   code = ca_context_open(m_ctx);
138   if (code != CA_SUCCESS) {
139     qWarning() << "Failed to open canberra context" << ca_strerror(code) << code;
140     ca_context_destroy(m_ctx);
141     m_ctx = 0;
142     return;
143   }
144
145   cache(m_conf->imageCaptureStartedSound(), CAMERA_IMAGE_START_SOUND_ID);
146   cache(m_conf->imageCaptureEndedSound(), CAMERA_IMAGE_END_SOUND_ID);
147   cache(m_conf->videoRecordingStartedSound(), CAMERA_VIDEO_START_SOUND_ID);
148   cache(m_conf->videoRecordingEndedSound(), CAMERA_VIDEO_STOP_SOUND_ID);
149   cache(m_conf->autoFocusAcquiredSound(), CAMERA_FOCUS_SOUND_ID);
150 }
151
152 void Sounds::cache(const QString& path, const char *id) {
153   if (path.isEmpty()) {
154     return;
155   }
156
157   int code = ca_context_cache(m_ctx,
158                               CA_PROP_EVENT_ID, id,
159                               CA_PROP_MEDIA_FILENAME, path.toLocal8Bit().data(),
160                               CA_PROP_CANBERRA_CACHE_CONTROL, "permanent",
161                               NULL);
162   if (code != CA_SUCCESS) {
163     qWarning() << "Failed to cache" << path << ca_strerror(code) << code;
164   }
165 }
166
167 void Sounds::play(const char *id) {
168   int code = ca_context_play(m_ctx, 0,
169                              CA_PROP_CANBERRA_VOLUME, CANBERRA_FULL_VOLUME,
170                              CA_PROP_EVENT_ID, id,
171                              CA_PROP_MEDIA_ROLE, "camera-sound-effect",
172                              NULL);
173   if (code != CA_SUCCESS) {
174     qDebug() << "Failed to play sound" << ca_strerror(code) << code;
175   }
176 }
177
178 void ca_finish_callback(ca_context *c, uint32_t id, int error_code, void *userdata) {
179   Q_UNUSED(c);
180   Q_UNUSED(id);
181   Q_UNUSED(error_code);
182
183   QPair<QMutex *, QWaitCondition *> *data =
184     static_cast<QPair<QMutex *, QWaitCondition *> *>(userdata);
185
186   data->second->wakeAll();
187 }
188
189 void Sounds::playAndBlock(const char *id) {
190   QMutex mutex;
191   QWaitCondition cond;
192   ca_proplist *p = 0;
193   if (ca_proplist_create(&p) != CA_SUCCESS) {
194     qDebug() << "Failed to create proplist";
195     return;
196   }
197
198   ca_proplist_sets(p, CA_PROP_CANBERRA_VOLUME, CANBERRA_FULL_VOLUME);
199   ca_proplist_sets(p, CA_PROP_EVENT_ID, id);
200   ca_proplist_sets(p, CA_PROP_MEDIA_ROLE, "camera-sound-effect");
201
202   QPair<QMutex *, QWaitCondition *> data = qMakePair<QMutex *, QWaitCondition *>(&mutex, &cond);
203
204   mutex.lock();
205
206   int code = ca_context_play_full(m_ctx, 0, p, ca_finish_callback, &data);
207
208   if (code != CA_SUCCESS) {
209     qDebug() << "Failed to play sound" << ca_strerror(code) << code;
210     mutex.unlock();
211     ca_proplist_destroy(p);
212
213     return;
214   }
215
216   cond.wait(&mutex);
217   ca_proplist_destroy(p);
218   mutex.unlock();
219 }