2eaaa5793909aa90b8909265d11e901d507138ac
[platform/core/api/sound-pool.git] / src / soundpool.c
1 /*
2  * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  * Licensed under the Apache License, Version 2.0 (the License);
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an AS IS BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 /**
18  * @file soundpool.c
19  * @brief This file include implementation of protected API for the SoundPool.
20  */
21 #include <unistd.h>
22 #include <mm_session.h>
23 #include <mm_sound.h>
24 #include <mm_session_private.h>
25
26 #include "internal/soundpool.h"
27
28 #include "internal/priority.h"
29 #include "internal/stream_cb_manager.h"
30
31 #include "internal/source.h"
32 #include "internal/stream.h"
33
34 #define DEFAULT_VOLUME_VALUE 1.0f
35
36 static void __stream_activate_iter(gpointer key, gpointer value,
37                 gpointer user_data);
38 static void __stream_deactivate_iter(gpointer key, gpointer value,
39                 gpointer user_data);
40
41 static void __stream_activate_iter(gpointer key, gpointer value,
42                 gpointer user_data)
43 {
44         SP_DEBUG_FENTER();
45         SP_RETM_IF(!user_data, "Empty user data.");
46         SP_RETM_IF(!value, "Empty sound stream data.");
47         guint *len = (guint*)user_data;
48         sound_stream_t *stream = (sound_stream_t *)value;
49
50         gboolean can_run = TRUE;
51         if (SOUND_POOL_ERROR_NONE
52                         == _sound_stream_priority_check(stream->parent_source->parent_pool->mgr_priority,
53                                         stream, &can_run))
54                 if (stream->state == SOUND_POOL_STREAM_STATE_SUSPENDED && TRUE == can_run) {
55                         alSourcePlay(stream->al_source);
56                         (*len)++;
57                 }
58
59         SP_DEBUG_FLEAVE();
60 }
61
62 static void __stream_deactivate_iter(gpointer key, gpointer value,
63                 gpointer user_data)
64 {
65         SP_DEBUG_FENTER();
66         SP_RETM_IF(!user_data, "Empty user data.");
67         SP_RETM_IF(!value, "Empty sound stream data.");
68         guint *len = (guint*)user_data;
69         sound_stream_t *stream = (sound_stream_t *)value;
70
71         if (stream->state == SOUND_POOL_STREAM_STATE_PLAYING) {
72                 alSourcePause(stream->al_source);
73                 (*len)++;
74         }
75
76         SP_DEBUG_FLEAVE();
77 }
78 static void __stream_stop_iter(gpointer key, gpointer value,
79                 gpointer user_data)
80 {
81         SP_DEBUG_FENTER();
82         SP_RETM_IF(!user_data, "Empty user data.");
83         SP_RETM_IF(!value, "Empty sound stream data.");
84         guint *len = (guint*)user_data;
85         sound_stream_t *stream = (sound_stream_t *)value;
86
87         if ((stream->state == SOUND_POOL_STREAM_STATE_PLAYING)
88                 || (stream->state == SOUND_POOL_STREAM_STATE_SUSPENDED)
89                 || (stream->state == SOUND_POOL_STREAM_STATE_PAUSED)) {
90                 alSourceStop(stream->al_source);
91                 (*len)++;
92         }
93
94         SP_DEBUG_FLEAVE();
95 }
96 sound_pool_error_e _sound_pool_create(sound_pool_t **pool)
97 {
98         SP_DEBUG_FENTER();
99         SP_INST_CHECK(pool, SOUND_POOL_ERROR_INVALID_PARAMETER);
100
101         sound_pool_t *_pool = NULL;
102         SP_RETVM_IF(!(_pool = g_try_malloc0(sizeof(*_pool))),
103                         SOUND_POOL_ERROR_OUT_OF_MEMORY,
104                         "Memory alloc failure. Can't create sound pool");
105         SP_RETVM_IF(!pool, SOUND_POOL_ERROR_INVALID_PARAMETER, "Can't create "
106                         "sound pool. Pool pointer is NULL");
107
108         sound_pool_error_e ret = SOUND_POOL_ERROR_NONE;
109         _pool->al_context = NULL;
110         _pool->al_context = alcCreateContext(alcOpenDevice(NULL), NULL);
111         if (!_pool->al_context) {
112                 ret = SOUND_POOL_ERROR_OUT_OF_MEMORY;
113                 GOTO_FAIL("Memory alloc failure. Can't create OpenAL Context", cfail);
114         }
115
116         _pool->sources = NULL;
117         _pool->streams = NULL;
118
119         _pool->sources = g_hash_table_new(g_str_hash, g_str_equal);
120         if (!_pool->sources) {
121                 ret = SOUND_POOL_ERROR_OUT_OF_MEMORY;
122                 GOTO_FAIL("Memory alloc failure. Can't create internal sources hash "
123                                 "table in sound pool", cfail);
124         }
125
126         _pool->streams = g_hash_table_new(g_int_hash, g_int_equal);
127         if (!_pool->streams) {
128                 ret = SOUND_POOL_ERROR_OUT_OF_MEMORY;
129                 GOTO_FAIL("Memory alloc failure. Can't create internal streams hash "
130                                 "table in sound pool", cfail);
131         }
132
133         _pool->volume = DEFAULT_VOLUME_VALUE;
134         _pool->max_stream_index = 0;
135         _pool->state = SOUND_POOL_STATE_INACTIVE;
136         _pool->state_cb_info.callback = NULL;
137         _pool->state_cb_info.user_data = NULL;
138
139         _pool->cbmgr = NULL;
140         ret = _stream_cb_manager_create(_pool, &_pool->cbmgr);
141         if (ret != SOUND_POOL_ERROR_NONE)
142                 GOTO_FAIL("Error occurred when trying to create stream state changing "
143                 "callback manager instance", cfail);
144
145         _pool->mgr_priority = NULL;
146         ret = _sound_stream_priority_create(_pool, &_pool->mgr_priority);
147         if (ret != SOUND_POOL_ERROR_NONE)
148                 GOTO_FAIL("Error occurred initialising of priorities.", cfail);
149
150         *pool = _pool;
151         SP_DEBUG_FLEAVE();
152         return ret;
153
154 cfail:
155         _sound_pool_destroy(_pool);
156         SP_DEBUG_FLEAVE();
157         return ret;
158 }
159
160 sound_pool_error_e _sound_pool_destroy(sound_pool_t *pool)
161 {
162         SP_DEBUG_FENTER();
163         SP_INST_CHECK(pool, SOUND_POOL_ERROR_INVALID_PARAMETER);
164         _sound_pool_stop_streams(pool);
165
166         if (pool->sources) {
167                 GHashTableIter iter;
168                 gpointer key, value;
169                 g_hash_table_iter_init(&iter, pool->sources);
170                 while (g_hash_table_iter_next(&iter, &key, &value)) {
171                         guint size_before = g_hash_table_size(pool->sources);
172                         _sound_source_destroy((sound_source_t*)value);
173                         guint size_after = g_hash_table_size(pool->sources);
174                         if (size_before != size_after)
175                                 g_hash_table_iter_init(&iter, pool->sources);
176                 }
177                 g_hash_table_unref(pool->sources);
178                 pool->sources = NULL;
179         }
180
181         if (pool->cbmgr) {
182                 sound_pool_error_e ret = _stream_cb_manager_destroy(pool->cbmgr);
183                 SP_RETVM_IF(ret != SOUND_POOL_ERROR_NONE, ret,
184                                 "Error occurred when " "trying to destroy sound pool callback manager.");
185                 pool->cbmgr = NULL;
186         }
187
188         if (pool->streams) {
189                 g_hash_table_unref(pool->streams);
190                 pool->streams = NULL;
191         }
192
193         if (pool->mgr_priority) {
194                 sound_pool_error_e ret = _sound_stream_priority_destroy(pool->mgr_priority);
195                 SP_RETVM_IF(ret != SOUND_POOL_ERROR_NONE, ret,
196                                 "Error occurred when trying to destroy priority manager.");
197                 pool->mgr_priority = NULL;
198         }
199
200         if (pool->al_context) {
201                 ALCdevice *device = alcGetContextsDevice(pool->al_context);
202                 alcMakeContextCurrent(NULL);
203                 alcDestroyContext(pool->al_context);
204                 if (device)
205                         alcCloseDevice(device);
206         }
207
208         SP_SAFE_GFREE(pool);
209
210         SP_DEBUG_FLEAVE();
211         return SOUND_POOL_ERROR_NONE;
212 }
213
214 sound_pool_error_e _sound_pool_activate(sound_pool_t *pool)
215 {
216         SP_DEBUG_FENTER();
217         SP_INST_CHECK(pool, SOUND_POOL_ERROR_INVALID_PARAMETER);
218
219         SP_RETVM_IF(pool->state == SOUND_POOL_STATE_ACTIVE,
220                         SOUND_POOL_ERROR_INVALID_OPERATION,
221                         "Already activated sound pool can't be activated again.");
222
223         sound_pool_state_e old_state = pool->state;
224         pool->state = SOUND_POOL_STATE_ACTIVE;
225
226         _sound_stream_priority_update_playback(pool->mgr_priority);
227
228         if (g_hash_table_size(pool->streams) > 0) {
229                 SP_RETVM_IF(!alcMakeContextCurrent(pool->al_context),
230                                 SOUND_POOL_ERROR_INVALID_OPERATION, "Can't set current context.");
231                 guint len = 0;
232                 g_hash_table_foreach(pool->streams, __stream_activate_iter, &len);
233                 SP_INFO("Resuming [%d] number of streams.", len);
234         }
235
236         SP_INFO("Sound pool has been activated");
237
238         if (pool->state_cb_info.callback)
239                 pool->state_cb_info.callback(pool, old_state, pool->state,
240                                 pool->state_cb_info.user_data);
241
242         SP_DEBUG_FLEAVE();
243         return SOUND_POOL_ERROR_NONE;
244 }
245
246 sound_pool_error_e _sound_pool_deactivate(sound_pool_t *pool)
247 {
248         SP_DEBUG_FENTER();
249         SP_INST_CHECK(pool, SOUND_POOL_ERROR_INVALID_PARAMETER);
250         SP_RETVM_IF(pool->state == SOUND_POOL_STATE_INACTIVE,
251                         SOUND_POOL_ERROR_INVALID_OPERATION,
252                         "Already deactivated sound pool can't be deactivated again.");
253
254         sound_pool_state_e old_state = pool->state;
255
256         /* Wait for completing the isoloator callback thread events */
257         _stream_cb_manager_process_pending_events(pool->cbmgr);
258
259         pool->state = SOUND_POOL_STATE_INACTIVE;
260
261         if (g_hash_table_size(pool->streams) > 0) {
262                 SP_RETVM_IF(!alcMakeContextCurrent(pool->al_context),
263                                 SOUND_POOL_ERROR_INVALID_OPERATION, "Can't set current context.");
264                 guint len = 0;
265                 g_hash_table_foreach(pool->streams, __stream_deactivate_iter, &len);
266                 SP_INFO("Suspending [%d] number of streams.", len);
267         }
268
269         SP_INFO("Sound pool has been deactivated");
270
271         if (pool->state_cb_info.callback)
272                 pool->state_cb_info.callback(pool, old_state, pool->state,
273                                 pool->state_cb_info.user_data);
274
275         SP_DEBUG_FLEAVE();
276         return SOUND_POOL_ERROR_NONE;
277 }
278
279 sound_pool_error_e _sound_pool_stop_streams(sound_pool_t *pool)
280 {
281         SP_DEBUG_FENTER();
282         SP_INST_CHECK(pool, SOUND_POOL_ERROR_INVALID_PARAMETER);
283
284         pool->state = SOUND_POOL_STATE_INACTIVE;
285
286         if (g_hash_table_size(pool->streams) > 0) {
287                 SP_RETVM_IF(!alcMakeContextCurrent(pool->al_context),
288                                 SOUND_POOL_ERROR_INVALID_OPERATION, "Can't set current context.");
289                 guint len = 0;
290                 g_hash_table_foreach(pool->streams, __stream_stop_iter, &len);
291                 SP_INFO("Stopping [%d] number of streams.", len);
292         }
293
294         SP_INFO("Sound pool has been stopped");
295
296         SP_DEBUG_FLEAVE();
297         return SOUND_POOL_ERROR_NONE;
298 }
299 sound_pool_error_e _sound_pool_get_state(sound_pool_t *pool,
300                 sound_pool_state_e *state)
301 {
302         SP_DEBUG_FENTER();
303         SP_INST_CHECK(pool, SOUND_POOL_ERROR_INVALID_PARAMETER);
304         SP_INST_CHECK(state, SOUND_POOL_ERROR_INVALID_PARAMETER);
305         *state = pool->state;
306         SP_DEBUG_FLEAVE();
307         return SOUND_POOL_ERROR_NONE;
308 }
309
310 sound_pool_error_e _sound_pool_set_volume(sound_pool_t *pool, float volume)
311 {
312         SP_DEBUG_FENTER();
313         SP_INST_CHECK(pool, SOUND_POOL_ERROR_INVALID_PARAMETER);
314         SP_RETVM_IF((volume < 0.0f || volume > 1.0f),
315                         SOUND_POOL_ERROR_INVALID_PARAMETER,
316                         "Sound pool volume is not in [0.0..1.0] range.");
317
318         SP_RETVM_IF(!alcMakeContextCurrent(pool->al_context),
319                         SOUND_POOL_ERROR_INVALID_OPERATION, "Can't set current context.");
320         alListenerf(AL_GAIN, volume);
321         pool->volume = volume;
322
323         SP_DEBUG_FLEAVE();
324         return SOUND_POOL_ERROR_NONE;
325 }
326
327 sound_pool_error_e _sound_pool_get_volume(sound_pool_t *pool, float *volume)
328 {
329         SP_DEBUG_FENTER();
330         SP_INST_CHECK(pool, SOUND_POOL_ERROR_INVALID_PARAMETER);
331         SP_INST_CHECK(volume, SOUND_POOL_ERROR_INVALID_PARAMETER);
332         *volume = pool->volume;
333         SP_DEBUG_FLEAVE();
334         return SOUND_POOL_ERROR_NONE;
335 }
336
337 sound_pool_error_e _sound_pool_set_callback(sound_pool_t *pool,
338                 sound_pool_state_changed_cb callback, void *data)
339 {
340         SP_DEBUG_FENTER();
341         SP_INST_CHECK(pool, SOUND_POOL_ERROR_INVALID_PARAMETER);
342         SP_INST_CHECK(callback, SOUND_POOL_ERROR_INVALID_PARAMETER);
343
344         pool->state_cb_info.callback = callback;
345         pool->state_cb_info.user_data = data;
346
347         SP_DEBUG_FLEAVE();
348         return SOUND_POOL_ERROR_NONE;
349 }
350
351 sound_pool_error_e _sound_pool_unset_callback(sound_pool_t *pool)
352 {
353         SP_DEBUG_FENTER();
354         SP_INST_CHECK(pool, SOUND_POOL_ERROR_INVALID_PARAMETER);
355
356         pool->state_cb_info.callback = NULL;
357         pool->state_cb_info.user_data = NULL;
358
359         SP_DEBUG_FLEAVE();
360         return SOUND_POOL_ERROR_NONE;
361 }
362
363 void _sound_pool_disable_session_backward_compatibility(void)
364 {
365         int mm_ret = MM_ERROR_NONE;
366         int session_type = 0;
367         int session_options = 0;
368
369         /* read session information */
370         mm_ret = _mm_session_util_read_information(-1, &session_type, &session_options);
371         if (mm_ret == MM_ERROR_NONE) {
372                 /* Session exists */
373                 SP_INFO(" Session exists:session_type %d, session_option %d", session_type, session_options);
374         }
375
376         if ((_mm_session_util_write_information((int)getpid(), MM_SESSION_TYPE_REPLACED_BY_STREAM, 0)))
377                 SP_INFO("failed to _mm_session_util_write_information for MM_SESSION_TYPE_REPLACED_BY_STREAM");
378 }