2 * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 * @brief This file include implementation of protected API
20 * for the sound streams(OpenAL sources) as part of SoundPool.
23 #include "internal/stream.h"
24 #include "internal/priority.h"
26 #include "internal/stream_cb_manager.h"
28 #define DEFAULT_STREAM_PRIORITY 255U
29 #define DEFAULT_STREAM_LOOP 1U
30 #define DEFAULT_STREAM_VOLUME 1.0f
32 static const char *__stringify_stream_state(sound_pool_stream_state_e state);
33 static void __al_source_state_cb(ALuint source, ALenum state, ALvoid *data);
34 static sound_pool_error_e __sound_pool_add_stream(sound_pool_t *pool, sound_stream_t *stream);
35 static sound_pool_error_e __sound_pool_remove_stream(sound_pool_t *pool, sound_stream_t *stream);
37 static const char *__stringify_stream_state(sound_pool_stream_state_e state)
40 case SOUND_POOL_STREAM_STATE_NONE:
41 return "SOUND_POOL_STREAM_STATE_NONE";
42 case SOUND_POOL_STREAM_STATE_PLAYING:
43 return "SOUND_POOL_STREAM_STATE_PLAYING";
44 case SOUND_POOL_STREAM_STATE_PAUSED:
45 return "SOUND_POOL_STREAM_STATE_PAUSED";
46 case SOUND_POOL_STREAM_STATE_SUSPENDED:
47 return "SOUND_POOL_STREAM_STATE_SUSPENDED";
48 case SOUND_POOL_STREAM_STATE_STOPPED:
49 return "SOUND_POOL_STREAM_STATE_STOPPED";
50 case SOUND_POOL_STREAM_STATE_FINISHED:
51 return "SOUND_POOL_STREAM_STATE_FINISHED";
57 static void __al_source_state_cb(ALuint source, ALenum state, ALvoid *data)
61 sound_stream_t *stream = (sound_stream_t *)data;
62 SP_RETM_IF(!stream, "Can't handle stream in OpenAL callback, it is NULL");
63 sound_pool_t *pool = stream->parent_source->parent_pool;
64 SP_RETM_IF(!pool, "Can't handle stream in OpenAL callback, stream's sound "
67 stream->state_previous = stream->state;
71 stream->state = SOUND_POOL_STREAM_STATE_NONE;
74 stream->state = SOUND_POOL_STREAM_STATE_PLAYING;
77 if (pool->state == SOUND_POOL_STATE_INACTIVE)
78 stream->state = SOUND_POOL_STREAM_STATE_SUSPENDED;
79 else if (pool->state == SOUND_POOL_STATE_ACTIVE)
80 stream->state = SOUND_POOL_STREAM_STATE_PAUSED;
84 stream->state = SOUND_POOL_STREAM_STATE_STOPPED;
86 stream->state = SOUND_POOL_STREAM_STATE_FINISHED;
89 SP_ERROR("Invalid OpenAl state value [%#04x]", state);
93 SP_INFO("Stream [%s:%d] state changed from [%s] to [%s]",
94 stream->parent_source->tag_name, stream->id,
95 __stringify_stream_state(stream->state_previous),
96 __stringify_stream_state(stream->state));
98 SP_RETM_IF(_stream_cb_manager_register_event(pool->cbmgr, stream) !=
99 SOUND_POOL_ERROR_NONE, "State changing event wasn't registered."
100 "Callbacks will be not called");
105 static sound_pool_error_e __sound_pool_add_stream(sound_pool_t *pool,
106 sound_stream_t *stream)
109 SP_INST_CHECK(pool, SOUND_POOL_ERROR_INVALID_PARAMETER);
110 SP_INST_CHECK(stream, SOUND_POOL_ERROR_INVALID_PARAMETER);
111 SP_RETVM_IF(!pool->streams, SOUND_POOL_ERROR_INVALID_OPERATION, "Sound "
112 "pool to add the stream is corrupted: NULL streams hash table");
113 stream->id = ++(pool->max_stream_index);
114 SP_DEBUG("Id assigned to the stream is [%u]", stream->id);
115 SP_RETVM_IF(g_hash_table_contains(pool->streams, (gpointer)&stream->id),
116 SOUND_POOL_ERROR_INVALID_PARAMETER,
117 "Stream ID already exists in streams hash table.");
118 SP_RETVM_IF(stream->parent_source->parent_pool != pool,
119 SOUND_POOL_ERROR_INVALID_OPERATION, "Sound stream can't be added "
120 "to the pool different to the pool for which sound stream was created");
122 SP_RETVM_IF(!g_hash_table_insert(pool->streams, (gpointer)&stream->id, stream),
123 SOUND_POOL_ERROR_INVALID_OPERATION, "Error occurred when adding "
124 "the stream [%u] to the sound pool", stream->id);
126 sound_pool_error_e ret =
127 _sound_stream_priority_add_stream(pool->mgr_priority, stream);
128 SP_RETVM_IF(SOUND_POOL_ERROR_NONE != ret, ret, "Error occurred when adding "
129 "the stream [%u] to the priority queue.", stream->id);
132 return SOUND_POOL_ERROR_NONE;
135 static sound_pool_error_e __sound_pool_remove_stream(sound_pool_t *pool, sound_stream_t *stream)
138 SP_INST_CHECK(pool, SOUND_POOL_ERROR_INVALID_PARAMETER);
139 SP_INST_CHECK(pool->mgr_priority, SOUND_POOL_ERROR_INVALID_PARAMETER);
140 SP_INST_CHECK(stream, SOUND_POOL_ERROR_INVALID_PARAMETER);
141 SP_INST_CHECK(stream->parent_source, SOUND_POOL_ERROR_INVALID_PARAMETER);
143 if (stream->parent_source->parent_pool == pool) {
144 if (!g_hash_table_remove(pool->streams, &stream->id)
145 || SOUND_POOL_ERROR_NONE
146 != _sound_stream_priority_remove_stream(pool->mgr_priority,
148 SP_DEBUG("Id [%u] doesn't exist in streams hash table", stream->id);
150 SP_INFO("[%u] sound stream was removed from pool", stream->id);
152 SP_DEBUG("Stream wasn't removed from sound pool as it isn't known by "
153 "the pool for which operation is performed");
157 return SOUND_POOL_ERROR_NONE;
160 sound_pool_error_e _sound_stream_create(sound_source_t *src,
161 sound_stream_t **stream)
164 SP_RETVM_IF(!src, SOUND_POOL_ERROR_INVALID_PARAMETER,
165 "Can't create sound stream in NULL sound source");
166 SP_RETVM_IF(!stream, SOUND_POOL_ERROR_INVALID_PARAMETER,
167 "Can't create sound stream. Stream pointer is NULL");
168 SP_RETVM_IF(!src->parent_pool, SOUND_POOL_ERROR_INVALID_PARAMETER,
169 "Can't create sound stream. Source's pool is NULL");
171 SP_RETVM_IF(!alcMakeContextCurrent(src->parent_pool->al_context),
172 SOUND_POOL_ERROR_INVALID_OPERATION, "Can't set current context.");
174 sound_stream_t *_stream = NULL;
175 SP_RETVM_IF(!(_stream = g_try_malloc0(sizeof(*_stream))),
176 SOUND_POOL_ERROR_OUT_OF_MEMORY,
177 "Memory alloc failure. Can't create sound stream");
179 _stream->parent_source = src;
180 _stream->priority = DEFAULT_STREAM_PRIORITY;
181 _stream->loop = DEFAULT_STREAM_LOOP;
182 _stream->volume = DEFAULT_STREAM_VOLUME;
183 _stream->state_previous = SOUND_POOL_STREAM_STATE_NONE;
184 _stream->state = SOUND_POOL_STREAM_STATE_NONE;
185 _stream->stopped = FALSE;
186 _stream->state_cb_info.callback = NULL;
187 _stream->state_cb_info.user_data = NULL;
189 sound_pool_error_e ret = SOUND_POOL_ERROR_NONE;
190 alGenSources((ALsizei)1, &_stream->al_source);
191 if (alGetError() != AL_NO_ERROR) {
192 ret = SOUND_POOL_ERROR_OUT_OF_MEMORY;
193 GOTO_FAIL("OpenAL error occurred when trying to generate OpenAL Source",
197 alSourcei(_stream->al_source, AL_BUFFER, src->al_buffer);
198 if (alGetError() != AL_NO_ERROR) {
199 ret = SOUND_POOL_ERROR_INVALID_OPERATION;
200 GOTO_FAIL("OpenAL error occurred when trying to assign OpenAL Buffer "
201 "to OpenAL Source", cfail);
204 alSourcep(_stream->al_source, AL_SOURCE_STATE_CALLBACK,
205 (ALvoid *)__al_source_state_cb);
206 if (alGetError() != AL_NO_ERROR) {
207 ret = SOUND_POOL_ERROR_INVALID_OPERATION;
208 GOTO_FAIL("OpenAL error occurred when trying to assign OpenAL Callback "
209 "to OpenAL Source", cfail);
212 alSourcep(_stream->al_source, AL_SOURCE_STATE_CALLBACK_DATA, (ALvoid *)_stream);
213 if (alGetError() != AL_NO_ERROR) {
214 ret = SOUND_POOL_ERROR_INVALID_OPERATION;
215 GOTO_FAIL("OpenAL error occurred when trying to assign OpenAL Callback "
216 "Data to OpenAL Source", cfail);
219 ret = __sound_pool_add_stream(src->parent_pool, _stream);
220 if (ret != SOUND_POOL_ERROR_NONE)
221 GOTO_FAIL("Error occurred when trying to add source to pool", cfail);
228 _sound_stream_destroy(_stream);
233 sound_pool_error_e _sound_stream_destroy(sound_stream_t *stream)
236 SP_RETVM_IF(!stream, SOUND_POOL_ERROR_INVALID_PARAMETER,
237 "Can't destroy NULL sound stream");
238 SP_RETVM_IF(!stream->parent_source, SOUND_POOL_ERROR_INVALID_PARAMETER,
239 "Invalid parent source.");
240 SP_RETVM_IF(!stream->parent_source->parent_pool, SOUND_POOL_ERROR_INVALID_PARAMETER,
241 "Empty parent pool pointer.");
243 if (stream->parent_source) {
244 if (__sound_pool_remove_stream(stream->parent_source->parent_pool,
245 stream) != SOUND_POOL_ERROR_NONE)
246 SP_DEBUG("Stream[%s] wasn't removed from sound pool. Continue destroying",
247 stream->parent_source->tag_name);
248 if (alcMakeContextCurrent(stream->parent_source->parent_pool->al_context)) {
249 alSourcei(stream->al_source, AL_BUFFER, 0);
250 alDeleteSources(1, &stream->al_source);
251 SP_DEBUG("Deleting OpenAL source with id [%u]", stream->al_source);
252 if (alGetError() != AL_NO_ERROR)
253 SP_ERROR("OpenAL error while stream[%d] destroying.", stream->id);
255 SP_DEBUG("Can't set current context for deleting OpenAL source with id [%u]",
260 SP_SAFE_GFREE(stream);
263 return SOUND_POOL_ERROR_NONE;
266 sound_pool_error_e _sound_stream_play(sound_stream_t *stream)
269 SP_INST_CHECK(stream, SOUND_POOL_ERROR_INVALID_PARAMETER);
270 SP_INST_CHECK(stream->parent_source, SOUND_POOL_ERROR_INVALID_PARAMETER);
271 sound_pool_t *pool = stream->parent_source->parent_pool;
272 sound_pool_error_e ret = SOUND_POOL_ERROR_NONE;
273 SP_INST_CHECK(pool, SOUND_POOL_ERROR_INVALID_PARAMETER);
274 SP_RETVM_IF(stream->state != SOUND_POOL_STREAM_STATE_SUSPENDED &&
275 stream->state != SOUND_POOL_STREAM_STATE_PAUSED &&
276 stream->state != SOUND_POOL_STREAM_STATE_NONE,
277 SOUND_POOL_ERROR_INVALID_OPERATION, "Can't play stream[%s] in [%s] "
278 "state", stream->parent_source->tag_name, __stringify_stream_state(stream->state));
279 SP_RETVM_IF(stream->state == SOUND_POOL_STREAM_STATE_SUSPENDED &&
280 pool->state == SOUND_POOL_STATE_INACTIVE,
281 SOUND_POOL_ERROR_INVALID_OPERATION, "Can't play suspended stream "
282 "in inactive sound pool");
284 SP_RETVM_IF(!alcMakeContextCurrent(pool->al_context),
285 SOUND_POOL_ERROR_INVALID_OPERATION, "Can't set current context.");
287 if (pool->state == SOUND_POOL_STATE_INACTIVE &&
288 stream->state == SOUND_POOL_STREAM_STATE_NONE) {
289 stream->state_previous = stream->state;
290 stream->state = SOUND_POOL_STREAM_STATE_SUSPENDED;
291 ret = _stream_cb_manager_register_event(pool->cbmgr, stream);
292 SP_RETVM_IF(SOUND_POOL_ERROR_NONE != ret, ret, "State changing event "
293 "wasn't registered. Callbacks will be not called");
294 SP_INFO("Don't play due to SoundPool is in inactive state.");
296 alSourcePlay(stream->al_source);
303 sound_pool_error_e _sound_stream_pause(sound_stream_t *stream)
306 SP_INST_CHECK(stream, SOUND_POOL_ERROR_INVALID_PARAMETER);
307 SP_INST_CHECK(stream->parent_source, SOUND_POOL_ERROR_INVALID_PARAMETER);
308 sound_pool_t *pool = stream->parent_source->parent_pool;
309 sound_pool_error_e ret = SOUND_POOL_ERROR_NONE;
310 SP_INST_CHECK(pool, SOUND_POOL_ERROR_INVALID_PARAMETER);
311 SP_RETVM_IF(stream->state != SOUND_POOL_STREAM_STATE_SUSPENDED &&
312 stream->state != SOUND_POOL_STREAM_STATE_PLAYING,
313 SOUND_POOL_ERROR_INVALID_OPERATION, "Can't pause stream in [%s] "
314 "state", __stringify_stream_state(stream->state));
316 SP_RETVM_IF(!alcMakeContextCurrent(pool->al_context),
317 SOUND_POOL_ERROR_INVALID_OPERATION, "Can't set current context.");
319 if (pool->state == SOUND_POOL_STATE_INACTIVE &&
320 stream->state == SOUND_POOL_STREAM_STATE_SUSPENDED) {
321 stream->state_previous = stream->state;
322 stream->state = SOUND_POOL_STREAM_STATE_PAUSED;
323 ret = _stream_cb_manager_register_event(pool->cbmgr, stream);
324 SP_RETVM_IF(SOUND_POOL_ERROR_NONE != ret, ret, "State changing event "
325 "wasn't registered. Callbacks will be not called");
326 SP_INFO("Don't paused due to SoundPool is in inactive state.");
328 alSourcePause(stream->al_source);
335 sound_pool_error_e _sound_stream_resume(sound_stream_t *stream)
338 SP_INST_CHECK(stream, SOUND_POOL_ERROR_INVALID_PARAMETER);
339 SP_INST_CHECK(stream->parent_source, SOUND_POOL_ERROR_INVALID_PARAMETER);
340 sound_pool_t *pool = stream->parent_source->parent_pool;
341 sound_pool_error_e ret = SOUND_POOL_ERROR_NONE;
342 SP_INST_CHECK(pool, SOUND_POOL_ERROR_INVALID_PARAMETER);
343 SP_RETVM_IF(stream->state != SOUND_POOL_STREAM_STATE_PAUSED,
344 SOUND_POOL_ERROR_INVALID_OPERATION, "Can't resume stream in [%s] "
345 "state", __stringify_stream_state(stream->state));
347 SP_RETVM_IF(!alcMakeContextCurrent(pool->al_context),
348 SOUND_POOL_ERROR_INVALID_OPERATION, "Can't set current context.");
350 if (pool->state == SOUND_POOL_STATE_INACTIVE &&
351 stream->state == SOUND_POOL_STREAM_STATE_PAUSED) {
352 stream->state_previous = stream->state;
353 stream->state = SOUND_POOL_STREAM_STATE_SUSPENDED;
354 ret = _stream_cb_manager_register_event(pool->cbmgr, stream);
355 SP_RETVM_IF(SOUND_POOL_ERROR_NONE != ret, ret, "State changing event "
356 "wasn't registered. Callbacks will be not called");
357 SP_INFO("Don't resumed due to SoundPool is in inactive state.");
359 alSourcePlay(stream->al_source);
366 sound_pool_error_e _sound_stream_stop(sound_stream_t *stream)
369 SP_INST_CHECK(stream, SOUND_POOL_ERROR_INVALID_PARAMETER);
370 SP_INST_CHECK(stream->parent_source, SOUND_POOL_ERROR_INVALID_PARAMETER);
371 sound_pool_t *pool = stream->parent_source->parent_pool;
372 SP_INST_CHECK(pool, SOUND_POOL_ERROR_INVALID_PARAMETER);
373 SP_RETVM_IF(stream->state != SOUND_POOL_STREAM_STATE_PLAYING &&
374 stream->state != SOUND_POOL_STREAM_STATE_SUSPENDED &&
375 stream->state != SOUND_POOL_STREAM_STATE_PAUSED,
376 SOUND_POOL_ERROR_INVALID_OPERATION, "Can't stop stream in [%s] "
377 "state", __stringify_stream_state(stream->state));
379 SP_RETVM_IF(!alcMakeContextCurrent(pool->al_context),
380 SOUND_POOL_ERROR_INVALID_OPERATION, "Can't set current context.");
382 stream->stopped = TRUE;
383 alSourceStop(stream->al_source);
386 return SOUND_POOL_ERROR_NONE;
389 sound_pool_error_e _sound_stream_set_loop(sound_stream_t *stream, unsigned loop)
392 SP_INST_CHECK(stream, SOUND_POOL_ERROR_INVALID_PARAMETER);
393 sound_pool_t *pool = stream->parent_source->parent_pool;
394 SP_INST_CHECK(pool, SOUND_POOL_ERROR_INVALID_PARAMETER);
395 SP_RETVM_IF(!alcMakeContextCurrent(pool->al_context),
396 SOUND_POOL_ERROR_INVALID_OPERATION, "Can't set current context.");
398 alSourcei(stream->al_source, AL_LOOP_COUNT, (ALint)loop);
402 return SOUND_POOL_ERROR_NONE;
405 sound_pool_error_e _sound_stream_get_loop(sound_stream_t *stream, unsigned *loop)
408 SP_INST_CHECK(stream, SOUND_POOL_ERROR_INVALID_PARAMETER);
409 SP_INST_CHECK(loop, SOUND_POOL_ERROR_INVALID_PARAMETER);
411 *loop = stream->loop;
414 return SOUND_POOL_ERROR_NONE;
417 sound_pool_error_e _sound_stream_set_volume(sound_stream_t *stream, float volume)
420 SP_INST_CHECK(stream, SOUND_POOL_ERROR_INVALID_PARAMETER);
421 SP_RETVM_IF((volume < 0.0f || volume > 1.0f),
422 SOUND_POOL_ERROR_INVALID_PARAMETER,
423 "Volume for sound stream should be in range 0.0..1.0.");
424 sound_pool_t *pool = stream->parent_source->parent_pool;
425 SP_INST_CHECK(pool, SOUND_POOL_ERROR_INVALID_PARAMETER);
426 SP_RETVM_IF(!alcMakeContextCurrent(pool->al_context),
427 SOUND_POOL_ERROR_INVALID_OPERATION, "Can't set current context.");
429 alSourcef(stream->al_source, AL_GAIN, volume);
431 stream->volume = volume;
434 return SOUND_POOL_ERROR_NONE;
437 sound_pool_error_e _sound_stream_get_volume(sound_stream_t *stream, float *volume)
440 SP_INST_CHECK(stream, SOUND_POOL_ERROR_INVALID_PARAMETER);
441 SP_INST_CHECK(volume, SOUND_POOL_ERROR_INVALID_PARAMETER);
443 *volume = stream->volume;
446 return SOUND_POOL_ERROR_NONE;
449 sound_pool_error_e _sound_stream_get_state(sound_stream_t *stream,
450 sound_pool_stream_state_e *state)
453 SP_INST_CHECK(stream, SOUND_POOL_ERROR_INVALID_PARAMETER);
454 SP_INST_CHECK(state, SOUND_POOL_ERROR_INVALID_PARAMETER);
456 *state = stream->state;
459 return SOUND_POOL_ERROR_NONE;
462 sound_pool_error_e _sound_stream_set_priority(sound_stream_t *stream,
466 SP_INST_CHECK(stream, SOUND_POOL_ERROR_INVALID_PARAMETER);
467 SP_INST_CHECK(stream->parent_source->parent_pool, SOUND_POOL_ERROR_INVALID_PARAMETER);
468 SP_INST_CHECK(stream->parent_source->parent_pool->mgr_priority,
469 SOUND_POOL_ERROR_INVALID_PARAMETER);
470 sound_pool_error_e ret = SOUND_POOL_ERROR_NONE;
472 stream->priority = rank;
473 if (stream->parent_source->parent_pool->state == SOUND_POOL_STATE_INACTIVE) {
474 if (stream->state == SOUND_POOL_STREAM_STATE_NONE) {
475 stream->state_previous = stream->state;
476 stream->state = SOUND_POOL_STREAM_STATE_SUSPENDED;
477 ret = _stream_cb_manager_register_event(stream->parent_source->parent_pool->cbmgr,
479 SP_RETVM_IF(SOUND_POOL_ERROR_NONE != ret, ret, "State changing event "
480 "wasn't registered. Callbacks will be not called");
482 SP_DEBUG("No need to update priority, Sound pool is INACTIVE.");
483 return SOUND_POOL_ERROR_NONE;
486 _sound_stream_priority_update_playback(stream->parent_source->parent_pool->mgr_priority);
489 return SOUND_POOL_ERROR_NONE;
492 sound_pool_error_e _sound_stream_get_priority(sound_stream_t *stream,
496 SP_INST_CHECK(stream, SOUND_POOL_ERROR_INVALID_PARAMETER);
497 SP_INST_CHECK(rank, SOUND_POOL_ERROR_INVALID_PARAMETER);
499 *rank = stream->priority;
502 return SOUND_POOL_ERROR_NONE;
505 sound_pool_error_e _sound_stream_set_callback(sound_stream_t *stream,
506 sound_pool_stream_state_change_cb callback, void *data)
509 SP_INST_CHECK(stream, SOUND_POOL_ERROR_INVALID_PARAMETER);
510 SP_INST_CHECK(callback, SOUND_POOL_ERROR_INVALID_PARAMETER);
512 stream->state_cb_info.callback = callback;
513 stream->state_cb_info.user_data = data;
516 return SOUND_POOL_ERROR_NONE;
519 sound_pool_error_e _sound_stream_unset_callback(sound_stream_t *stream)
522 SP_INST_CHECK(stream, SOUND_POOL_ERROR_INVALID_PARAMETER);
524 stream->state_cb_info.callback = NULL;
525 stream->state_cb_info.user_data = NULL;
528 return SOUND_POOL_ERROR_NONE;
531 sound_pool_error_e _sound_pool_get_stream_by_id(sound_pool_t *pool, unsigned id,
532 sound_stream_t **stream)
535 SP_INST_CHECK(pool, SOUND_POOL_ERROR_INVALID_PARAMETER);
536 SP_INST_CHECK(stream, SOUND_POOL_ERROR_INVALID_PARAMETER);
538 SP_RETVM_IF(!pool->streams, SOUND_POOL_ERROR_INVALID_OPERATION, "Corrupted "
539 "sound pool. Sources hash table is NULL");
541 *stream = (sound_stream_t *)g_hash_table_lookup(pool->streams, &id);
542 SP_RETVM_IF(!(*stream), SOUND_POOL_ERROR_KEY_NOT_AVAILABLE, "Tag doesn't "
543 "exist in sources hash table");
546 return SOUND_POOL_ERROR_NONE;