Updated priority concept has been implemented.
[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
22 #include "internal/soundpool.h"
23
24 #include "internal/priority.h"
25 #include "internal/stream_cb_manager.h"
26
27 #include "internal/source.h"
28 #include "internal/stream.h"
29
30 #define DEFAULT_VOLUME_VALUE 1.0f
31
32 static void __stream_activate_iter(gpointer key, gpointer value,
33                 gpointer user_data);
34 static void __stream_deactivate_iter(gpointer key, gpointer value,
35                 gpointer user_data);
36
37 static void __stream_activate_iter(gpointer key, gpointer value,
38                 gpointer user_data)
39 {
40         SP_DEBUG_FENTER();
41         SP_RETM_IF(!user_data, "Empty user data.");
42         SP_RETM_IF(!value, "Empty sound stream data.");
43         guint *len = (guint*)user_data;
44         sound_stream_t *stream = (sound_stream_t *)value;
45
46         gboolean can_run = TRUE;
47         if (SOUND_POOL_ERROR_NONE
48                         == _sound_stream_priority_check(stream->parent_source->parent_pool->mgr_priority,
49                                         stream, &can_run))
50                 if (stream->state == SOUND_POOL_STREAM_STATE_SUSPENDED && TRUE == can_run) {
51                         alSourcePlay(stream->al_source);
52                         (*len)++;
53                 }
54
55         SP_DEBUG_FLEAVE();
56 }
57
58 static void __stream_deactivate_iter(gpointer key, gpointer value,
59                 gpointer user_data)
60 {
61         SP_DEBUG_FENTER();
62         SP_RETM_IF(!user_data, "Empty user data.");
63         SP_RETM_IF(!value, "Empty sound stream data.");
64         guint *len = (guint*)user_data;
65         sound_stream_t *stream = (sound_stream_t *)value;
66
67         if (stream->state == SOUND_POOL_STREAM_STATE_PLAYING) {
68                 alSourcePause(stream->al_source);
69                 (*len)++;
70         }
71
72         SP_DEBUG_FLEAVE();
73 }
74
75 sound_pool_error_e _sound_pool_create(sound_pool_t **pool)
76 {
77         SP_DEBUG_FENTER();
78         SP_INST_CHECK(pool, SOUND_POOL_ERROR_INVALID_PARAMETER);
79
80         sound_pool_t *_pool = NULL;
81         SP_RETVM_IF(!(_pool = g_try_malloc0(sizeof(*_pool))),
82                         SOUND_POOL_ERROR_OUT_OF_MEMORY,
83                         "Memory alloc failure. Can't create sound pool");
84         SP_RETVM_IF(!pool, SOUND_POOL_ERROR_INVALID_PARAMETER, "Can't create "
85                         "sound pool. Pool pointer is NULL");
86
87         sound_pool_error_e ret = SOUND_POOL_ERROR_NONE;
88         _pool->al_context = NULL;
89         _pool->al_context = alcCreateContext(alcOpenDevice(NULL), NULL);
90         if (!_pool->al_context) {
91                 ret = SOUND_POOL_ERROR_OUT_OF_MEMORY;
92                 GOTO_FAIL("Memory alloc failure. Can't create OpenAL Context", cfail);
93         }
94
95         _pool->sources = NULL;
96         _pool->streams = NULL;
97
98         _pool->sources = g_hash_table_new(g_str_hash, g_str_equal);
99         if (!_pool->sources) {
100                 ret = SOUND_POOL_ERROR_OUT_OF_MEMORY;
101                 GOTO_FAIL("Memory alloc failure. Can't create internal sources hash "
102                                 "table in sound pool", cfail);
103         }
104
105         _pool->streams = g_hash_table_new(g_int_hash, g_int_equal);
106         if (!_pool->streams) {
107                 ret = SOUND_POOL_ERROR_OUT_OF_MEMORY;
108                 GOTO_FAIL("Memory alloc failure. Can't create internal streams hash "
109                                 "table in sound pool", cfail);
110         }
111
112         _pool->volume = DEFAULT_VOLUME_VALUE;
113         _pool->max_stream_index = 0;
114         _pool->state = SOUND_POOL_STATE_INACTIVE;
115         _pool->state_cb_info.callback = NULL;
116         _pool->state_cb_info.user_data = NULL;
117
118         _pool->cbmgr = NULL;
119         ret = _stream_cb_manager_create(_pool, &_pool->cbmgr);
120         if (ret != SOUND_POOL_ERROR_NONE)
121                 GOTO_FAIL("Error occurred when trying to create stream state changing "
122                 "callback manager instance", cfail);
123
124         _pool->mgr_priority = NULL;
125         ret = _sound_stream_priority_create(_pool, &_pool->mgr_priority);
126         if (ret != SOUND_POOL_ERROR_NONE)
127                 GOTO_FAIL("Error occurred initialising of priorities.", cfail);
128
129         *pool = _pool;
130         SP_DEBUG_FLEAVE();
131         return ret;
132
133 cfail:
134         _sound_pool_destroy(_pool);
135         SP_DEBUG_FLEAVE();
136         return ret;
137 }
138
139 sound_pool_error_e _sound_pool_destroy(sound_pool_t *pool)
140 {
141         SP_DEBUG_FENTER();
142         SP_INST_CHECK(pool, SOUND_POOL_ERROR_INVALID_PARAMETER);
143         _sound_pool_deactivate(pool);
144
145         if (pool->cbmgr) {
146                 sound_pool_error_e ret = _stream_cb_manager_destroy(pool->cbmgr);
147                 SP_RETVM_IF(ret != SOUND_POOL_ERROR_NONE, ret,
148                                 "Error occurred when " "trying to destroy sound pool callback manager.");
149                 pool->cbmgr = NULL;
150         }
151
152         if (pool->sources) {
153                 GHashTableIter iter;
154                 gpointer key, value;
155                 g_hash_table_iter_init(&iter, pool->sources);
156                 while (g_hash_table_iter_next(&iter, &key, &value)) {
157                         guint size_before = g_hash_table_size(pool->sources);
158                         _sound_source_destroy((sound_source_t*)value);
159                         guint size_after = g_hash_table_size(pool->sources);
160                         if (size_before != size_after)
161                                 g_hash_table_iter_init(&iter, pool->sources);
162                 }
163                 g_hash_table_unref(pool->sources);
164                 pool->sources = NULL;
165         }
166
167         if (pool->streams) {
168                 g_hash_table_unref(pool->streams);
169                 pool->streams = NULL;
170         }
171
172         if (pool->mgr_priority) {
173                 sound_pool_error_e ret = _sound_stream_priority_destroy(pool->mgr_priority);
174                 SP_RETVM_IF(ret != SOUND_POOL_ERROR_NONE, ret,
175                                 "Error occurred when trying to destroy priority manager.");
176                 pool->mgr_priority = NULL;
177         }
178
179         if (pool->al_context) {
180                 ALCdevice *device = alcGetContextsDevice(pool->al_context);
181                 alcMakeContextCurrent(NULL);
182                 alcDestroyContext(pool->al_context);
183                 alcCloseDevice(device);
184         }
185
186         SP_SAFE_GFREE(pool);
187
188         SP_DEBUG_FLEAVE();
189         return SOUND_POOL_ERROR_NONE;
190 }
191
192 sound_pool_error_e _sound_pool_activate(sound_pool_t *pool)
193 {
194         SP_DEBUG_FENTER();
195         SP_INST_CHECK(pool, SOUND_POOL_ERROR_INVALID_PARAMETER);
196
197         SP_RETVM_IF(pool->state == SOUND_POOL_STATE_ACTIVE,
198                         SOUND_POOL_ERROR_INVALID_OPERATION,
199                         "Already activated sound pool can't be activated again.");
200
201         sound_pool_state_e old_state = pool->state;
202         pool->state = SOUND_POOL_STATE_ACTIVE;
203
204         _sound_stream_priority_update_playback(pool->mgr_priority);
205
206         /* Generate array of all AlSources in pool */
207         GPtrArray *streams = NULL;
208         if (g_hash_table_size(pool->streams) > 0) {
209                 SP_RETVM_IF(!alcMakeContextCurrent(pool->al_context),
210                                 SOUND_POOL_ERROR_INVALID_OPERATION, "Can't set current context.");
211                 guint len = 0;
212                 g_hash_table_foreach(pool->streams, __stream_activate_iter, &len);
213                 SP_INFO("Resuming [%d] number of streams.", len);
214         }
215
216         if (streams)
217                 g_ptr_array_free(streams, TRUE);
218
219         SP_INFO("Sound pool has been activated");
220
221         if (pool->state_cb_info.callback)
222                 pool->state_cb_info.callback(pool, old_state, pool->state,
223                                 pool->state_cb_info.user_data);
224
225         SP_DEBUG_FLEAVE();
226         return SOUND_POOL_ERROR_NONE;
227 }
228
229 sound_pool_error_e _sound_pool_deactivate(sound_pool_t *pool)
230 {
231         SP_DEBUG_FENTER();
232         SP_INST_CHECK(pool, SOUND_POOL_ERROR_INVALID_PARAMETER);
233         SP_RETVM_IF(pool->state == SOUND_POOL_STATE_INACTIVE,
234                         SOUND_POOL_ERROR_INVALID_OPERATION,
235                         "Already deactivated sound pool can't be deactivated again.");
236
237         sound_pool_state_e old_state = pool->state;
238         pool->state = SOUND_POOL_STATE_INACTIVE;
239
240         /* Generate array of all AlSources in pool */
241         GPtrArray *streams = NULL;
242         if (g_hash_table_size(pool->streams) > 0) {
243                 SP_RETVM_IF(!alcMakeContextCurrent(pool->al_context),
244                                 SOUND_POOL_ERROR_INVALID_OPERATION, "Can't set current context.");
245                 guint len = 0;
246                 g_hash_table_foreach(pool->streams, __stream_deactivate_iter, &len);
247                 SP_INFO("Suspending [%d] number of streams.", len);
248         }
249
250         if (streams)
251                 g_ptr_array_free(streams, TRUE);
252
253         SP_INFO("Sound pool has been deactivated");
254
255         if (pool->state_cb_info.callback)
256                 pool->state_cb_info.callback(pool, old_state, pool->state,
257                                 pool->state_cb_info.user_data);
258
259         SP_DEBUG_FLEAVE();
260         return SOUND_POOL_ERROR_NONE;
261 }
262
263 sound_pool_error_e _sound_pool_get_state(sound_pool_t *pool,
264                 sound_pool_state_e *state)
265 {
266         SP_DEBUG_FENTER();
267         SP_INST_CHECK(pool, SOUND_POOL_ERROR_INVALID_PARAMETER);
268         SP_INST_CHECK(state, SOUND_POOL_ERROR_INVALID_PARAMETER);
269         *state = pool->state;
270         SP_DEBUG_FLEAVE();
271         return SOUND_POOL_ERROR_NONE;
272 }
273
274 sound_pool_error_e _sound_pool_set_volume(sound_pool_t *pool, float volume)
275 {
276         SP_DEBUG_FENTER();
277         SP_INST_CHECK(pool, SOUND_POOL_ERROR_INVALID_PARAMETER);
278         SP_RETVM_IF((volume < 0.0f || volume > 1.0f),
279                         SOUND_POOL_ERROR_INVALID_PARAMETER,
280                         "Sound pool volume is not in [0.0..1.0] range.");
281
282         SP_RETVM_IF(!alcMakeContextCurrent(pool->al_context),
283                         SOUND_POOL_ERROR_INVALID_OPERATION, "Can't set current context.");
284         alListenerf(AL_GAIN, volume);
285         pool->volume = volume;
286
287         SP_DEBUG_FLEAVE();
288         return SOUND_POOL_ERROR_NONE;
289 }
290
291 sound_pool_error_e _sound_pool_get_volume(sound_pool_t *pool, float *volume)
292 {
293         SP_DEBUG_FENTER();
294         SP_INST_CHECK(pool, SOUND_POOL_ERROR_INVALID_PARAMETER);
295         SP_INST_CHECK(volume, SOUND_POOL_ERROR_INVALID_PARAMETER);
296         *volume = pool->volume;
297         SP_DEBUG_FLEAVE();
298         return SOUND_POOL_ERROR_NONE;
299 }
300
301 sound_pool_error_e _sound_pool_set_callback(sound_pool_t *pool,
302                 sound_pool_state_change_cb callback, void *data)
303 {
304         SP_DEBUG_FENTER();
305         SP_INST_CHECK(pool, SOUND_POOL_ERROR_INVALID_PARAMETER);
306         SP_INST_CHECK(callback, SOUND_POOL_ERROR_INVALID_PARAMETER);
307
308         pool->state_cb_info.callback = callback;
309         pool->state_cb_info.user_data = data;
310
311         SP_DEBUG_FLEAVE();
312         return SOUND_POOL_ERROR_NONE;
313 }
314
315 sound_pool_error_e _sound_pool_unset_callback(sound_pool_t *pool)
316 {
317         SP_DEBUG_FENTER();
318         SP_INST_CHECK(pool, SOUND_POOL_ERROR_INVALID_PARAMETER);
319
320         pool->state_cb_info.callback = NULL;
321         pool->state_cb_info.user_data = NULL;
322
323         SP_DEBUG_FLEAVE();
324         return SOUND_POOL_ERROR_NONE;
325 }