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