From: o.danchenko Date: Tue, 14 Jun 2016 20:02:40 +0000 (+0300) Subject: SoundPool implementation has been added. X-Git-Tag: submit/tizen/20160824.052737~6 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=015e69523ec3fba82b4984ca1ec5b6cf76a30243;p=platform%2Fcore%2Fapi%2Fsound-pool.git SoundPool implementation has been added. Change-Id: I232a8cc5af30a4b14135d5b75c8b1debd7b71f3e Signed-off-by: o.danchenko --- diff --git a/CMakeLists.txt b/CMakeLists.txt index 12706f9..d34c3c1 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -51,7 +51,7 @@ INSTALL(TARGETS ${fw_name} DESTINATION ${LIB_INSTALL_DIR}) INSTALL( DIRECTORY ${INC_DIR}/ DESTINATION ${INC_DIR}/media FILES_MATCHING - PATTERN "mm_soundpool_private.h" EXCLUDE + PATTERN "sound_pool_private.h" EXCLUDE PATTERN "${INC_DIR}/*.h" ) diff --git a/include/internal/priority.h b/include/internal/priority.h new file mode 100644 index 0000000..caf767b --- /dev/null +++ b/include/internal/priority.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __TIZEN_SOUNDPOOL_INTERNAL_PRIORITY_H_ +#define __TIZEN_SOUNDPOOL_INTERNAL_PRIORITY_H_ + +#include "sound_pool_private.h" +#include "internal/stream.h" + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Pool Priority Manager type. + * + * @since_tizen 3.0 + */ +struct stream_priority_manager_s { + sound_pool_t *pool; + GList *priority_queue; +}; + +sound_pool_error_e _sound_stream_priority_create(sound_pool_t *pool, + stream_priority_manager_t **mgr); + +sound_pool_error_e _sound_stream_priority_destroy( + stream_priority_manager_t *mgr); + +sound_pool_error_e _sound_stream_priority_add_stream( + stream_priority_manager_t *mgr, sound_stream_t *stream); + +sound_pool_error_e _sound_stream_priority_remove_stream( + stream_priority_manager_t *mgr, sound_stream_t *stream); + +sound_pool_error_e _sound_stream_priority_check(stream_priority_manager_t *mgr, + sound_stream_t *stream, gboolean *out); + +void _sound_stream_priority_update_playback(stream_priority_manager_t *mgr); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __TIZEN_SOUNDPOOL_INTERNAL_PRIORITY_H_ */ diff --git a/include/internal/soundpool.h b/include/internal/soundpool.h new file mode 100644 index 0000000..9b015d3 --- /dev/null +++ b/include/internal/soundpool.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __TIZEN_SOUNDPOOL_INTERNAL_SOUNDPOOL_H__ +#define __TIZEN_SOUNDPOOL_INTERNAL_SOUNDPOOL_H__ + +#include "sound_pool_type.h" +#include "sound_pool_private.h" + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct stream_cb_manager_s stream_cb_manager_t; +typedef struct stream_priority_manager_s stream_priority_manager_t; + +/** + * @brief Sound Pool pool type. + * + * @since_tizen 3.0 + */ +typedef struct sound_pool_s { + ALCcontext *al_context; + float volume; + + GHashTable *sources; + GHashTable *streams; + + guint max_streams; + guint max_stream_index; + sound_pool_state_e state; + + struct { + sound_pool_state_change_cb callback; + void *user_data; + } state_cb_info; + + stream_cb_manager_t *cbmgr; + stream_priority_manager_t *mgr_priority; +} sound_pool_t; + +/* Max amount of sound pools which could be created. */ +#define SOUND_POOL_MAX_POOLS_AMOUNT 32U + +sound_pool_error_e _sound_pool_create(size_t max_streams, sound_pool_t **pool); + +sound_pool_error_e _sound_pool_destroy(sound_pool_t *pool); + +sound_pool_error_e _sound_pool_activate(sound_pool_t *pool); + +sound_pool_error_e _sound_pool_deactivate(sound_pool_t *pool); + +sound_pool_error_e _sound_pool_get_state(sound_pool_t *pool, sound_pool_state_e *state); + +sound_pool_error_e _sound_pool_set_volume(sound_pool_t *pool, float volume); + +sound_pool_error_e _sound_pool_get_volume(sound_pool_t *pool, float *volume); + +sound_pool_error_e _sound_pool_set_callback(sound_pool_t *pool, + sound_pool_state_change_cb callback, void *data); + +sound_pool_error_e _sound_pool_unset_callback(sound_pool_t *pool); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __TIZEN_SOUNDPOOL_INTERNAL_SOUNDPOOL_H__ */ diff --git a/include/internal/source.h b/include/internal/source.h new file mode 100644 index 0000000..58117f9 --- /dev/null +++ b/include/internal/source.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __TIZEN_SOUNDPOOL_INTERNAL_SOURCE_H__ +#define __TIZEN_SOUNDPOOL_INTERNAL_SOURCE_H__ + +#include "sound_pool_private.h" +#include "internal/soundpool.h" + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Sound Pool source type. + * + * @since_tizen 3.0 + */ +typedef struct sound_source_s { + sound_pool_t *parent_pool; + gchar *tag_name; + ALuint al_buffer; +} sound_source_t; + +/* Max length of the tag can be used for source identification. */ +#define MAX_TAG_LEN 255U + +sound_pool_error_e _sound_source_create(sound_pool_t *pool, const char *tag, + sound_source_t **src); + +sound_pool_error_e _sound_source_destroy(sound_source_t *src); + +sound_pool_error_e _sound_source_load_from_file(sound_source_t *src, const char *fname); + +sound_pool_error_e _sound_pool_get_source_by_tag(sound_pool_t *pool, const char *tag, + sound_source_t **src); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __TIZEN_SOUNDPOOL_INTERNAL_SOURCE_H__ */ diff --git a/include/internal/stream.h b/include/internal/stream.h new file mode 100644 index 0000000..9d67b34 --- /dev/null +++ b/include/internal/stream.h @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __TIZEN_SOUNDPOOL_INTERNAL_STREAM_H__ +#define __TIZEN_SOUNDPOOL_INTERNAL_STREAM_H__ + + +#include "sound_pool_private.h" +#include "internal/source.h" + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Sound Pool stream type. + * + * @since_tizen 3.0 + */ +typedef struct sound_stream_s { + unsigned id; + sound_source_t *parent_source; + + unsigned priority; + unsigned loop; + float volume; + + sound_pool_stream_state_e state; + sound_pool_stream_state_e state_previous; + gboolean stopped; + + struct { + sound_pool_stream_state_change_cb callback; + void *user_data; + } state_cb_info; + + ALuint al_source; +} sound_stream_t; + +sound_pool_error_e _sound_stream_create(sound_source_t *src, sound_stream_t **stream); + +sound_pool_error_e _sound_stream_destroy(sound_stream_t *src); + +sound_pool_error_e _sound_stream_play(sound_stream_t *stream); + +sound_pool_error_e _sound_stream_pause(sound_stream_t *stream); + +sound_pool_error_e _sound_stream_resume(sound_stream_t *stream); + +sound_pool_error_e _sound_stream_stop(sound_stream_t *stream); + +sound_pool_error_e _sound_stream_set_loop(sound_stream_t *stream, unsigned loop); +sound_pool_error_e _sound_stream_get_loop(sound_stream_t *stream, unsigned *loop); + +sound_pool_error_e _sound_stream_set_volume(sound_stream_t *stream, float volume); +sound_pool_error_e _sound_stream_get_volume(sound_stream_t *stream, float *volume); + +sound_pool_error_e _sound_stream_set_priority(sound_stream_t *stream, unsigned rank); +sound_pool_error_e _sound_stream_get_priority(sound_stream_t *stream, unsigned *rank); + +sound_pool_error_e _sound_stream_get_state(sound_stream_t *stream, + sound_pool_stream_state_e *state); + +sound_pool_error_e _sound_stream_set_callback(sound_stream_t *stream, + sound_pool_stream_state_change_cb callback, void *data); + +sound_pool_error_e _sound_stream_unset_callback(sound_stream_t *stream); + +sound_pool_error_e _sound_pool_get_stream_by_id(sound_pool_t *pool, unsigned id, + sound_stream_t **stream); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __TIZEN_SOUNDPOOL_INTERNAL_STREAM_H__ */ diff --git a/include/internal/stream_cb_manager.h b/include/internal/stream_cb_manager.h new file mode 100644 index 0000000..60ef83a --- /dev/null +++ b/include/internal/stream_cb_manager.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __TIZEN_SOUNDPOOL_INTERNAL_STREAMCBMANAGER_H__ +#define __TIZEN_SOUNDPOOL_INTERNAL_STREAMCBMANAGER_H__ + +#include "internal/stream.h" + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Stream Callback Manager type. + * + * @since_tizen 3.0 + */ +struct stream_cb_manager_s { + sound_pool_t *pool; + GAsyncQueue *isolator_callback_queue; + + pthread_t isolator_thread; + pthread_mutex_t isolator_data_mutex; + pthread_cond_t isolator_data_cond; + gboolean isolator_state_changed; + gboolean isolator_loop_run; +}; + +typedef struct stream_cb_manager_event_data_s { + sound_stream_t *stream; + sound_pool_stream_state_e state; + sound_pool_stream_state_e state_previous; +} stream_cb_manager_event_data_t; + +sound_pool_error_e _stream_cb_manager_create(sound_pool_t *pool, stream_cb_manager_t **cbmgr); + +sound_pool_error_e _stream_cb_manager_destroy(stream_cb_manager_t *cbmgr); + +sound_pool_error_e _stream_cb_manager_register_event(stream_cb_manager_t *cbmgr, + sound_stream_t *stream); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __TIZEN_SOUNDPOOL_INTERNAL_STREAMCBMANAGER_H__ */ diff --git a/include/sound_pool.h b/include/sound_pool.h index 67e9d41..b862f0d 100644 --- a/include/sound_pool.h +++ b/include/sound_pool.h @@ -99,7 +99,7 @@ typedef void (*sound_pool_stream_state_change_cb) (sound_pool_h pool, * @param [out] pool The handle to the pool that will be created * @return @c 0 on success, otherwise a negative error value * @retval #SOUND_POOL_ERROR_INVALID_PARAMETER - * Invalid parameter (@a pool is NULL or @ max_streams is 0) + * Invalid parameter (@a pool is NULL or @a max_streams is 0) * @retval #SOUND_POOL_ERROR_OUT_OF_MEMORY * Not enough memory to create sound pool * @retval #SOUND_POOL_ERROR_INVALID_OPERATION @@ -107,7 +107,7 @@ typedef void (*sound_pool_stream_state_change_cb) (sound_pool_h pool, * * @see sound_pool_destroy() */ -int sound_pool_create(unsigned max_streams, sound_pool_h *pool); +sound_pool_error_e sound_pool_create(unsigned max_streams, sound_pool_h *pool); /** * @brief Destroys sound pool and cleans allocated memory. @@ -123,7 +123,7 @@ int sound_pool_create(unsigned max_streams, sound_pool_h *pool); * * @see sound_pool_create() */ -int sound_pool_destroy(sound_pool_h pool); +sound_pool_error_e sound_pool_destroy(sound_pool_h pool); /** * @brief Loads sound source data from file to pool. @@ -151,7 +151,7 @@ int sound_pool_destroy(sound_pool_h pool); * @see sound_pool_destroy() * @see sound_pool_unload_source() */ -int sound_pool_load_source_from_file(sound_pool_h pool, const char *file_name, +sound_pool_error_e sound_pool_load_source_from_file(sound_pool_h pool, const char *file_name, const char *tag); /** @@ -175,7 +175,7 @@ int sound_pool_load_source_from_file(sound_pool_h pool, const char *file_name, * @see sound_pool_create() * @see sound_pool_load_source_from_file() */ -int sound_pool_unload_source(sound_pool_h pool, const char *tag); +sound_pool_error_e sound_pool_unload_source(sound_pool_h pool, const char *tag); /** * @brief Gets current @a state of @a pool. @@ -194,7 +194,7 @@ int sound_pool_unload_source(sound_pool_h pool, const char *tag); * @see sound_pool_create() * @see sound_pool_state_e */ -int sound_pool_get_state(sound_pool_h pool, +sound_pool_error_e sound_pool_get_state(sound_pool_h pool, sound_pool_state_e *state); /** @@ -215,7 +215,7 @@ int sound_pool_get_state(sound_pool_h pool, * @see sound_pool_deactivate() * @see sound_pool_state_e */ -int sound_pool_activate(sound_pool_h pool); +sound_pool_error_e sound_pool_activate(sound_pool_h pool); /** * @brief Changes current @a state of @a pool to SOUND_POOL_STATE_INACTIVE. @@ -236,7 +236,7 @@ int sound_pool_activate(sound_pool_h pool); * @see sound_pool_activate() * @see sound_pool_state_e */ -int sound_pool_deactivate(sound_pool_h pool); +sound_pool_error_e sound_pool_deactivate(sound_pool_h pool); /** * @brief Sets callback for handling sound @a pool state change. @@ -258,7 +258,7 @@ int sound_pool_deactivate(sound_pool_h pool); * @see sound_pool_create() * @see sound_pool_pool_state_change_cb */ -int sound_pool_set_state_change_callback(sound_pool_h pool, +sound_pool_error_e sound_pool_set_state_change_callback(sound_pool_h pool, sound_pool_state_change_cb callback, void *user_data); /** @@ -276,7 +276,7 @@ int sound_pool_set_state_change_callback(sound_pool_h pool, * @see sound_pool_create() * @see sound_pool_set_state_change_callback() */ -int sound_pool_unset_state_change_callback(sound_pool_h pool); +sound_pool_error_e sound_pool_unset_state_change_callback(sound_pool_h pool); /** * @brief Plays source by @a tag. @@ -324,7 +324,7 @@ int sound_pool_unset_state_change_callback(sound_pool_h pool); * @see sound_pool_set_volume * @see sound_pool_get_volume */ -int sound_pool_stream_play(sound_pool_h pool, const char *tag, unsigned loop, +sound_pool_error_e sound_pool_stream_play(sound_pool_h pool, const char *tag, unsigned loop, float volume, unsigned priority, sound_pool_stream_state_change_cb callback, void *user_data, unsigned *id); @@ -353,7 +353,7 @@ int sound_pool_stream_play(sound_pool_h pool, const char *tag, unsigned loop, * @see sound_pool_load_source_from_file() * @see sound_pool_stream_play() */ -int sound_pool_stream_pause(sound_pool_h pool, unsigned id); +sound_pool_error_e sound_pool_stream_pause(sound_pool_h pool, unsigned id); /** * @brief Resumes stream by @a id. @@ -381,7 +381,7 @@ int sound_pool_stream_pause(sound_pool_h pool, unsigned id); * @see sound_pool_stream_play() * @see sound_pool_stream_pause() */ -int sound_pool_stream_resume(sound_pool_h pool, unsigned id); +sound_pool_error_e sound_pool_stream_resume(sound_pool_h pool, unsigned id); /** * @brief Stops stream by @a id. @@ -410,7 +410,7 @@ int sound_pool_stream_resume(sound_pool_h pool, unsigned id); * @see sound_pool_load_source_from_file() * @see sound_pool_stream_play() */ -int sound_pool_stream_stop(sound_pool_h pool, unsigned id); +sound_pool_error_e sound_pool_stream_stop(sound_pool_h pool, unsigned id); /** * @brief Sets pool global volume parameter. @@ -431,7 +431,7 @@ int sound_pool_stream_stop(sound_pool_h pool, unsigned id); * * @see sound_pool_create() */ -int sound_pool_set_volume(sound_pool_h pool, float volume); +sound_pool_error_e sound_pool_set_volume(sound_pool_h pool, float volume); /** * @brief Gets pool global volume parameter. @@ -448,7 +448,7 @@ int sound_pool_set_volume(sound_pool_h pool, float volume); * * @see sound_pool_create() */ -int sound_pool_get_volume(sound_pool_h pool, float *volume); +sound_pool_error_e sound_pool_get_volume(sound_pool_h pool, float *volume); /** * @brief Gets current @a state of stream by @a id. @@ -473,7 +473,7 @@ int sound_pool_get_volume(sound_pool_h pool, float *volume); * @see sound_pool_stream_play() * @see sound_pool_stream_state_e */ -int sound_pool_stream_get_state(sound_pool_h pool, unsigned id, +sound_pool_error_e sound_pool_stream_get_state(sound_pool_h pool, unsigned id, sound_pool_stream_state_e *state); /** @@ -499,7 +499,7 @@ int sound_pool_stream_get_state(sound_pool_h pool, unsigned id, * @see sound_pool_load_source_from_file() * @see sound_pool_stream_play() */ -int sound_pool_stream_set_loop(sound_pool_h pool, unsigned id, unsigned loop); +sound_pool_error_e sound_pool_stream_set_loop(sound_pool_h pool, unsigned id, unsigned loop); /** * @brief Gets stream @a loop parameter by stream @a id. @@ -525,7 +525,7 @@ int sound_pool_stream_set_loop(sound_pool_h pool, unsigned id, unsigned loop); * @see sound_pool_load_source_from_file() * @see sound_pool_stream_play() */ -int sound_pool_stream_get_loop(sound_pool_h pool, unsigned id, unsigned *loop); +sound_pool_error_e sound_pool_stream_get_loop(sound_pool_h pool, unsigned id, unsigned *loop); /** * @brief Sets stream volume parameters by stream @a id. @@ -552,7 +552,7 @@ int sound_pool_stream_get_loop(sound_pool_h pool, unsigned id, unsigned *loop); * @see sound_pool_set_volume * @see sound_pool_get_volume */ -int sound_pool_stream_set_volume(sound_pool_h pool, unsigned id, +sound_pool_error_e sound_pool_stream_set_volume(sound_pool_h pool, unsigned id, float volume); /** @@ -577,7 +577,7 @@ int sound_pool_stream_set_volume(sound_pool_h pool, unsigned id, * @see sound_pool_load_source_from_file() * @see sound_pool_stream_play() */ -int sound_pool_stream_get_volume(sound_pool_h pool, unsigned id, +sound_pool_error_e sound_pool_stream_get_volume(sound_pool_h pool, unsigned id, float *volume); /** @@ -602,7 +602,7 @@ int sound_pool_stream_get_volume(sound_pool_h pool, unsigned id, * @see sound_pool_load_source_from_file() * @see sound_pool_stream_play() */ -int sound_pool_stream_set_priority(sound_pool_h pool, unsigned id, +sound_pool_error_e sound_pool_stream_set_priority(sound_pool_h pool, unsigned id, unsigned priority); /** @@ -627,7 +627,7 @@ int sound_pool_stream_set_priority(sound_pool_h pool, unsigned id, * @see sound_pool_load_source_from_file() * @see sound_pool_stream_play() */ -int sound_pool_stream_get_priority(sound_pool_h pool, unsigned id, +sound_pool_error_e sound_pool_stream_get_priority(sound_pool_h pool, unsigned id, unsigned *priority); /** @@ -658,7 +658,7 @@ int sound_pool_stream_get_priority(sound_pool_h pool, unsigned id, * @see sound_pool_stream_play() * @see sound_pool_stream_state_change_cb */ -int sound_pool_stream_set_state_change_callback(sound_pool_h pool, unsigned id, +sound_pool_error_e sound_pool_stream_set_state_change_callback(sound_pool_h pool, unsigned id, sound_pool_stream_state_change_cb callback, void *user_data); /** @@ -683,7 +683,7 @@ int sound_pool_stream_set_state_change_callback(sound_pool_h pool, unsigned id, * @see sound_pool_stream_play() * @see sound_pool_stream_state_change_cb */ -int sound_pool_stream_unset_state_change_callback(sound_pool_h pool, +sound_pool_error_e sound_pool_stream_unset_state_change_callback(sound_pool_h pool, unsigned id); /** diff --git a/include/sound_pool_private.h b/include/sound_pool_private.h new file mode 100644 index 0000000..2b3af54 --- /dev/null +++ b/include/sound_pool_private.h @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __TIZEN_SOUNDPOOL_PRIVATE_H__ +#define __TIZEN_SOUNDPOOL_PRIVATE_H__ + +#include +#include +#include +#include + +#include "sound_pool.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef LOG_TAG +#undef LOG_TAG +#endif + +#define LOG_TAG "TIZEN_SOUND_POOL" + +#ifdef COLORED_LOG +#define FONT_COLOR_RESET "\033[0m" +#define FONT_COLOR_RED "\033[31m" +#define FONT_COLOR_GREEN "\033[32m" +#define FONT_COLOR_YELLOW "\033[33m" +#define FONT_COLOR_BLUE "\033[34m" +#define FONT_COLOR_PURPLE "\033[35m" +#define FONT_COLOR_CYAN "\033[36m" +#define FONT_COLOR_GRAY "\033[37m" +#else +#define FONT_COLOR_RESET +#define FONT_COLOR_RED +#define FONT_COLOR_GREEN +#define FONT_COLOR_YELLOW +#define FONT_COLOR_BLUE +#define FONT_COLOR_PURPLE +#define FONT_COLOR_CYAN +#define FONT_COLOR_GRAY +#endif + +/** + * @brief Max amount of sound pools which could be created. + * + * @since_tizen 3.0 + */ +#define SOUND_POOL_MAX_POOLS_AMOUNT 32U + +#define SP_DEBUG(fmt, arg...) \ + do { \ + LOGD(FONT_COLOR_RESET""fmt"", ##arg); \ + } while (0) + +#define SP_INFO(fmt, arg...) \ + do { \ + LOGI(FONT_COLOR_GREEN""fmt""FONT_COLOR_RESET, \ + ##arg); \ + } while (0) + +#define SP_ERROR(fmt, arg...) \ + do { \ + LOGE(FONT_COLOR_RED""fmt""FONT_COLOR_RESET, \ + ##arg); \ + } while (0) + +#define SP_DEBUF_FENTER() \ + do { \ + LOGD(FONT_COLOR_YELLOW""FONT_COLOR_RESET); \ + } while (0) + +#define SP_DEBUG_FLEAVE() \ + do { \ + LOGD(FONT_COLOR_PURPLE""FONT_COLOR_RESET); \ + } while (0) + +#define SP_INST_CHECK(expr, ret_code) \ + do { \ + SP_RETVM_IF(NULL == expr, ret_code, #ret_code); \ + } while (0) + +#define SP_RETM_IF(expr, fmt, arg...) \ + do { \ + if (expr) { \ + LOGE(FONT_COLOR_RED""fmt""FONT_COLOR_RESET, \ + ##arg); \ + SP_DEBUG_FLEAVE(); \ + return; \ + } \ + } while (0) + +#define SP_RETVM_IF(expr, val, fmt, arg...) \ + do { \ + if (expr) { \ + LOGE(FONT_COLOR_RED""fmt""FONT_COLOR_RESET, \ + ##arg); \ + SP_DEBUG_FLEAVE(); \ + return(val); \ + } \ + } while (0) + +#define GOTO_FAIL(msg, label) \ + do { \ + SP_ERROR(msg); \ + goto label; \ + } while (0) + +#define SP_SAFE_GFREE(src) { if (src) { g_free(src); src = NULL; } } + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __TIZEN_SOUNDPOOL_PRIVATE_H__ */ diff --git a/packaging/sound-pool.spec b/packaging/sound-pool.spec index 5d3ff1f..0f74e31 100644 --- a/packaging/sound-pool.spec +++ b/packaging/sound-pool.spec @@ -17,7 +17,7 @@ BuildRequires: pkgconfig(freealut) Tizen Sound Pool Module allowing sounds playing for audio resources. %package devel -Summary: Tizen Sound Pool Module allowing sounds playing for audio resources (Development). +Summary: Tizen Module allowing sounds playing for audio resources (Development) Group: Multimedia/Framework Requires: %{name} = %{version}-%{release} @@ -31,7 +31,7 @@ cp %{SOURCE1001} . %build export CFLAGS="$CFLAGS -fprofile-arcs -ftest-coverage" export CXXFLAGS="$CXXFLAGS -fprofile-arcs -ftest-coverage" -export LDLAGS="$LDLAGS -lgcov" +export LDFLAGS="$LDFLAGS -lgcov" MAJORVER=`echo %{version} | awk 'BEGIN {FS="."}{print $1}'` %cmake . -DCMAKE_INSTALL_PREFIX=%{_prefix} -DFULLVER=%{version} -DMAJORVER=${MAJORVER} diff --git a/src/priority.c b/src/priority.c new file mode 100644 index 0000000..9399567 --- /dev/null +++ b/src/priority.c @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file priority.c + * @brief This file include implementation of prioritization of streams in the + * SoundPool. + */ + +#include "internal/priority.h" + +static gint __stream_stream_proirity_cmp(gconstpointer a, gconstpointer b); + +static gint __stream_stream_proirity_cmp(gconstpointer a, gconstpointer b) +{ + SP_DEBUF_FENTER(); + SP_RETVM_IF(!a, 0, "Firs pointer is NULL."); + SP_RETVM_IF(!b, 0, "Second pointer is NULL."); + sound_stream_t *stream_a = (sound_stream_t *)a; + sound_stream_t *stream_b = (sound_stream_t *)b; + + SP_DEBUG_FLEAVE(); + if (stream_a->priority != stream_b->priority) + return (stream_b->priority - stream_a->priority); + return (stream_a->id - stream_b->id); +} + +sound_pool_error_e _sound_stream_priority_create(sound_pool_t *pool, + stream_priority_manager_t **mgr) +{ + SP_DEBUF_FENTER(); + SP_RETVM_IF(!pool, SOUND_POOL_ERROR_INVALID_PARAMETER, "Stream priority " + "manager can't be created for NULL sound pool"); + SP_RETVM_IF(!mgr, SOUND_POOL_ERROR_INVALID_PARAMETER, "Can't create " + "stream priority manager. Manager pointer is NULL"); + + stream_priority_manager_t *_mgr = NULL; + SP_RETVM_IF(!(_mgr = g_try_malloc0(sizeof(*_mgr))), + SOUND_POOL_ERROR_OUT_OF_MEMORY, + "Memory alloc failure. Can't create stream priority manager."); + + int ret = SOUND_POOL_ERROR_NONE; + + _mgr->priority_queue = NULL; + _mgr->pool = pool; + pool->mgr_priority = _mgr; + + *mgr = _mgr; + + SP_DEBUG_FLEAVE(); + return ret; +} + +sound_pool_error_e _sound_stream_priority_destroy(stream_priority_manager_t *mgr) +{ + SP_DEBUF_FENTER(); + SP_RETVM_IF(!mgr, SOUND_POOL_ERROR_INVALID_PARAMETER, "Can't handle priority " + "manager, it is NULL."); + + int ret = SOUND_POOL_ERROR_NONE; + + if (mgr->priority_queue) { + g_list_free(mgr->priority_queue); + mgr->priority_queue = NULL; + } + + mgr->pool = NULL; + + SP_SAFE_GFREE(mgr); + + SP_DEBUG_FLEAVE(); + return ret; +} + +sound_pool_error_e _sound_stream_priority_add_stream( + stream_priority_manager_t *mgr, sound_stream_t *stream) +{ + SP_DEBUF_FENTER(); + int ret = SOUND_POOL_ERROR_NONE; + SP_INST_CHECK(mgr, SOUND_POOL_ERROR_INVALID_PARAMETER); + SP_INST_CHECK(stream, SOUND_POOL_ERROR_INVALID_PARAMETER); + + mgr->priority_queue = g_list_append(mgr->priority_queue,(gpointer)stream); + _sound_stream_priority_update_playback(mgr); + + SP_DEBUG_FLEAVE(); + return ret; +} + +sound_pool_error_e _sound_stream_priority_remove_stream( + stream_priority_manager_t *mgr, sound_stream_t *stream) +{ + SP_DEBUF_FENTER(); + SP_INST_CHECK(mgr, SOUND_POOL_ERROR_INVALID_PARAMETER); + SP_INST_CHECK(stream, SOUND_POOL_ERROR_INVALID_PARAMETER); + int ret = SOUND_POOL_ERROR_NONE; + + GList* to_delete; + to_delete = g_list_find(mgr->priority_queue, (gpointer)stream); + SP_RETVM_IF(NULL == to_delete, SOUND_POOL_ERROR_INVALID_PARAMETER, + "Can't find to deleting link to stream [id:%d] from priority queue.", + stream->id); + + mgr->priority_queue = g_list_delete_link(mgr->priority_queue, to_delete); + _sound_stream_priority_update_playback(mgr); + + SP_DEBUG_FLEAVE(); + return ret; +} + +sound_pool_error_e _sound_stream_priority_check(stream_priority_manager_t *mgr, + sound_stream_t *stream, gboolean *out) +{ + SP_DEBUF_FENTER(); + SP_INST_CHECK(mgr, SOUND_POOL_ERROR_INVALID_PARAMETER); + SP_INST_CHECK(stream, SOUND_POOL_ERROR_INVALID_PARAMETER); + SP_INST_CHECK(out, SOUND_POOL_ERROR_INVALID_PARAMETER); + SP_INST_CHECK(stream->parent_source, SOUND_POOL_ERROR_INVALID_PARAMETER); + SP_INST_CHECK(mgr->pool, SOUND_POOL_ERROR_INVALID_PARAMETER); + SP_INST_CHECK(mgr->pool->sources, SOUND_POOL_ERROR_INVALID_PARAMETER); + SP_INST_CHECK(g_hash_table_lookup(mgr->pool->sources, + stream->parent_source->tag_name), SOUND_POOL_ERROR_KEY_NOT_AVAILABLE); + *out = FALSE; + int ret = SOUND_POOL_ERROR_NONE; + + int position = g_list_index(mgr->priority_queue, (gconstpointer)stream); + SP_RETVM_IF(-1 == position, SOUND_POOL_ERROR_INVALID_PARAMETER, + "Can't find stream in priority queue[%s]", stream->parent_source->tag_name); + + if (position < mgr->pool->max_streams) + *out = TRUE; + + SP_DEBUG_FLEAVE(); + return ret; +} + +void _sound_stream_priority_update_playback(stream_priority_manager_t *mgr) +{ + SP_DEBUF_FENTER(); + SP_RETM_IF(!mgr, "Priority manager pointer is NULL."); + SP_RETM_IF(!mgr->pool, "Pool pointer is NULL."); + SP_RETM_IF(!mgr->pool->cbmgr, "Callback manager pointer is NULL."); + gboolean can_run = FALSE; + + if (mgr->pool->state == SOUND_POOL_STATE_INACTIVE) { + SP_INFO("No need to update priority, Sound pool is INACTIVE."); + return; + } + + mgr->priority_queue = g_list_sort(mgr->priority_queue, + __stream_stream_proirity_cmp); + + GList *iter = mgr->priority_queue; + for (; iter != NULL; iter = iter->next) { + sound_stream_t *stream = (sound_stream_t *)iter->data; + + if (SOUND_POOL_ERROR_NONE != _sound_stream_priority_check(mgr, stream, + &can_run)) + continue; + if (TRUE == can_run) { + if (SOUND_POOL_STREAM_STATE_PLAYING != stream->state) + _sound_stream_play(stream); + } else { + if (SOUND_POOL_STREAM_STATE_PLAYING == stream->state) + _sound_stream_pause(stream); + stream->state_previous = stream->state; + stream->state = SOUND_POOL_STREAM_STATE_SUSPENDED; + if (_stream_cb_manager_register_event(mgr->pool->cbmgr, stream) != + SOUND_POOL_ERROR_NONE) + SP_DEBUG("State changing event wasn't registered." + "Callbacks will be not called"); + } + } + + SP_DEBUG_FLEAVE(); +} diff --git a/src/sound_pool.c b/src/sound_pool.c index f720f45..6236e6e 100644 --- a/src/sound_pool.c +++ b/src/sound_pool.c @@ -14,8 +14,466 @@ * limitations under the License. */ +/** + * @file sound_pool.c + * @brief This file include implementation of public API for the SoundPool. + */ + #include "sound_pool.h" +#include "sound_pool_private.h" + +#include "internal/soundpool.h" +#include "internal/source.h" +#include "internal/stream.h" /* * Public Implementation */ + +static unsigned int sound_pool_amount = 0; + +sound_pool_error_e sound_pool_create(unsigned max_streams, sound_pool_h *pool) +{ + SP_DEBUF_FENTER(); + SP_RETVM_IF(max_streams == 0, SOUND_POOL_ERROR_INVALID_PARAMETER, + "Max streams count should be greater then 0."); + SP_RETVM_IF(SOUND_POOL_MAX_POOLS_AMOUNT < (sound_pool_amount+1), + SOUND_POOL_ERROR_INVALID_OPERATION, + "Max pools count should be no more than %d.", + SOUND_POOL_MAX_POOLS_AMOUNT); + SP_INST_CHECK(pool, SOUND_POOL_ERROR_INVALID_PARAMETER); + + sound_pool_t *_pool = NULL; + const int ret = _sound_pool_create(max_streams, &_pool); + SP_RETVM_IF(ret != SOUND_POOL_ERROR_NONE, ret, + "Error while creating sound pool instance."); + SP_INST_CHECK(pool, SOUND_POOL_ERROR_INVALID_OPERATION); + + *pool = _pool; + ++sound_pool_amount; + SP_INFO("Number of created pools is [%u]", sound_pool_amount); + + SP_DEBUG_FLEAVE(); + return ret; +} + +sound_pool_error_e sound_pool_destroy(sound_pool_h pool) +{ + SP_DEBUF_FENTER(); + + const int ret = _sound_pool_destroy((sound_pool_t *)pool); + SP_RETVM_IF(ret != SOUND_POOL_ERROR_NONE, ret, + "Error occurred when destroying the sound pool"); + --sound_pool_amount; + SP_INFO("Number of pools is [%u]", sound_pool_amount); + + SP_DEBUG_FLEAVE(); + return ret; +} + +sound_pool_error_e sound_pool_load_source_from_file(sound_pool_h pool, + const char *file_name, const char *tag) +{ + SP_DEBUF_FENTER(); + + sound_pool_t *_pool = (sound_pool_t *)pool; + sound_source_t *_src = NULL; + int ret = _sound_source_create(_pool, tag, &_src); + SP_RETVM_IF(ret != SOUND_POOL_ERROR_NONE, ret, + "Error while creating sound source instance."); + + ret = _sound_source_load_from_file(_src, file_name); + if (ret != SOUND_POOL_ERROR_NONE) { + SP_ERROR("Error occurred when loading sound source from file"); + if (_sound_source_destroy(_src) != SOUND_POOL_ERROR_NONE) + SP_ERROR("Error occurred during removal of sound source[%s].", tag); + } + + SP_DEBUG_FLEAVE(); + return ret; +} + +sound_pool_error_e sound_pool_unload_source(sound_pool_h pool, const char *tag) +{ + SP_DEBUF_FENTER(); + + sound_pool_t *_pool = (sound_pool_t *)pool; + sound_source_t *_source = NULL; + int ret = _sound_pool_get_source_by_tag(_pool, tag, &_source); + SP_RETVM_IF(ret != SOUND_POOL_ERROR_NONE, ret, "Error occurred when " + "getting sound source [%s] from the sound pool", tag); + SP_INST_CHECK(_source, SOUND_POOL_ERROR_INVALID_OPERATION); + + ret = _sound_source_destroy(_source); + SP_RETVM_IF(ret != SOUND_POOL_ERROR_NONE, ret, "Error occurred when " + "destroying sound source [%s]", tag); + + SP_DEBUG_FLEAVE(); + return ret; +} + +sound_pool_error_e sound_pool_stream_play(sound_pool_h pool, const char *tag, + unsigned loop, float volume, unsigned priority, + sound_pool_stream_state_change_cb callback, void *data, unsigned *id) +{ + SP_DEBUF_FENTER(); + SP_INST_CHECK(id, SOUND_POOL_ERROR_INVALID_PARAMETER); + + sound_pool_t *_pool = (sound_pool_t *)pool; + sound_source_t *_source = NULL; + int ret = _sound_pool_get_source_by_tag(_pool, tag, &_source); + SP_RETVM_IF(ret != SOUND_POOL_ERROR_NONE, ret, "Error occurred when " + "getting sound source [%s] from the sound pool", tag); + SP_INST_CHECK(_source, SOUND_POOL_ERROR_INVALID_OPERATION); + + sound_stream_t *_stream = NULL; + ret = _sound_stream_create(_source, &_stream); + SP_RETVM_IF(ret != SOUND_POOL_ERROR_NONE, ret, + "Error while creating sound source instance."); + + ret = _sound_stream_set_loop(_stream, loop); + SP_RETVM_IF(ret != SOUND_POOL_ERROR_NONE, ret, "Error occurred when " + "setting sound stream loop parameter", tag); + + ret = _sound_stream_set_volume(_stream, volume); + SP_RETVM_IF(ret != SOUND_POOL_ERROR_NONE, ret, "Error occurred when " + "setting sound stream volume parameter", tag); + + if (callback) { + ret = _sound_stream_set_callback(_stream, callback, data); + SP_RETVM_IF(ret != SOUND_POOL_ERROR_NONE, ret, "Error occurred when " + "setting sound stream callback", tag); + } + + ret = _sound_stream_set_priority(_stream, priority); + SP_RETVM_IF(ret != SOUND_POOL_ERROR_NONE, ret, "Error occurred when " + "setting sound stream priority parameter", tag); + + *id = _stream->id; + + SP_DEBUG_FLEAVE(); + return ret; +} + +sound_pool_error_e sound_pool_stream_pause(sound_pool_h pool, unsigned id) +{ + SP_DEBUF_FENTER(); + + sound_pool_t *_pool = (sound_pool_t *)pool; + sound_stream_t *_stream = NULL; + int ret = _sound_pool_get_stream_by_id(_pool, id, &_stream); + SP_RETVM_IF(ret != SOUND_POOL_ERROR_NONE, ret, "Error occurred when " + "getting sound stream [%u] from the sound pool", id); + SP_INST_CHECK(_stream, SOUND_POOL_ERROR_INVALID_OPERATION); + + ret = _sound_stream_pause(_stream); + SP_RETVM_IF(ret != SOUND_POOL_ERROR_NONE, ret, "Error occurred when " + "pausing sound stream [%u]", id); + + SP_DEBUG_FLEAVE(); + return ret; +} + +sound_pool_error_e sound_pool_stream_resume(sound_pool_h pool, unsigned id) +{ + SP_DEBUF_FENTER(); + sound_pool_t *_pool = (sound_pool_t *)pool; + sound_stream_t *_stream = NULL; + int ret = _sound_pool_get_stream_by_id(_pool, id, &_stream); + SP_RETVM_IF(ret != SOUND_POOL_ERROR_NONE, ret, "Error occurred when " + "getting sound stream [%u] from the sound pool", id); + SP_INST_CHECK(_stream, SOUND_POOL_ERROR_INVALID_OPERATION); + + ret = _sound_stream_resume(_stream); + SP_RETVM_IF(ret != SOUND_POOL_ERROR_NONE, ret, "Error occurred when " + "resuming sound stream [%u]", id); + + SP_DEBUG_FLEAVE(); + return ret; +} + +sound_pool_error_e sound_pool_stream_stop(sound_pool_h pool, unsigned id) +{ + SP_DEBUF_FENTER(); + + sound_pool_t *_pool = (sound_pool_t *)pool; + sound_stream_t *_stream = NULL; + int ret = _sound_pool_get_stream_by_id(_pool, id, &_stream); + SP_RETVM_IF(ret != SOUND_POOL_ERROR_NONE, ret, "Error occurred when " + "getting sound stream [%u] from the sound pool", id); + SP_INST_CHECK(_stream, SOUND_POOL_ERROR_INVALID_OPERATION); + + ret = _sound_stream_stop(_stream); + SP_RETVM_IF(ret != SOUND_POOL_ERROR_NONE, ret, "Error occurred when " + "stopping sound stream [%u]", id); + + SP_DEBUG_FLEAVE(); + return ret; +} + +sound_pool_error_e sound_pool_get_state(sound_pool_h pool, sound_pool_state_e *state) +{ + SP_DEBUF_FENTER(); + + const int ret = _sound_pool_get_state(pool, state); + SP_RETVM_IF(ret != SOUND_POOL_ERROR_NONE, ret, "Error occurred when " + "getting state of the sound pool"); + + SP_DEBUG_FLEAVE(); + return ret; +} + +sound_pool_error_e sound_pool_activate(sound_pool_h pool) +{ + SP_DEBUF_FENTER(); + + const int ret = _sound_pool_activate((sound_pool_t *)pool); + SP_RETVM_IF(ret != SOUND_POOL_ERROR_NONE, ret, "Error occurred when " + "activating sound pool"); + + SP_DEBUG_FLEAVE(); + return ret; +} + +sound_pool_error_e sound_pool_deactivate(sound_pool_h pool) +{ + SP_DEBUF_FENTER(); + + const int ret = _sound_pool_deactivate((sound_pool_t *)pool); + SP_RETVM_IF(ret != SOUND_POOL_ERROR_NONE, ret, "Error occurred when " + "deactivating sound pool"); + + SP_DEBUG_FLEAVE(); + return ret; +} + +sound_pool_error_e sound_pool_set_volume(sound_pool_h pool, float volume) +{ + SP_DEBUF_FENTER(); + + const int ret = _sound_pool_set_volume((sound_pool_t *)pool, volume); + SP_RETVM_IF(ret != SOUND_POOL_ERROR_NONE, ret, "Error occurred when " + "setting volume for sound pool"); + + SP_DEBUG_FLEAVE(); + return ret; +} + +sound_pool_error_e sound_pool_get_volume(sound_pool_h pool, float *volume) +{ + SP_DEBUF_FENTER(); + + const int ret = _sound_pool_get_volume((sound_pool_t *)pool, volume); + SP_RETVM_IF(ret != SOUND_POOL_ERROR_NONE, ret, "Error occurred when " + "getting volume for sound pool"); + + SP_DEBUG_FLEAVE(); + return ret; +} + +sound_pool_error_e sound_pool_stream_get_state(sound_pool_h pool, unsigned id, + sound_pool_stream_state_e *state) +{ + SP_DEBUF_FENTER(); + + sound_pool_t *_pool = (sound_pool_t *)pool; + sound_stream_t *_stream = NULL; + int ret = _sound_pool_get_stream_by_id(_pool, id, &_stream); + SP_RETVM_IF(ret != SOUND_POOL_ERROR_NONE, ret, "Error occurred when " + "getting sound stream [%u] from the sound pool", id); + SP_INST_CHECK(_stream, SOUND_POOL_ERROR_INVALID_OPERATION); + + ret = _sound_stream_get_state(_stream, state); + SP_RETVM_IF(ret != SOUND_POOL_ERROR_NONE, ret, "Error occurred when " + "getting sound stream [%u] state", id); + + SP_DEBUG_FLEAVE(); + return ret; +} + +sound_pool_error_e sound_pool_stream_set_loop(sound_pool_h pool, unsigned id, + unsigned loop) +{ + SP_DEBUF_FENTER(); + + sound_pool_t *_pool = (sound_pool_t *)pool; + sound_stream_t *_stream = NULL; + int ret = _sound_pool_get_stream_by_id(_pool, id, &_stream); + SP_RETVM_IF(ret != SOUND_POOL_ERROR_NONE, ret, "Error occurred when " + "getting sound stream [%u] from the sound pool", id); + SP_INST_CHECK(_stream, SOUND_POOL_ERROR_INVALID_OPERATION); + + ret = _sound_stream_set_loop(_stream, loop); + SP_RETVM_IF(ret != SOUND_POOL_ERROR_NONE, ret, "Error occurred when " + "setting sound stream [%u] loop parameter", id); + + SP_DEBUG_FLEAVE(); + return ret; +} + +sound_pool_error_e sound_pool_stream_get_loop(sound_pool_h pool, unsigned id, + unsigned *loop) +{ + SP_DEBUF_FENTER(); + + sound_pool_t *_pool = (sound_pool_t *)pool; + sound_stream_t *_stream = NULL; + int ret = _sound_pool_get_stream_by_id(_pool, id, &_stream); + SP_RETVM_IF(ret != SOUND_POOL_ERROR_NONE, ret, "Error occurred when " + "getting sound stream [%u] from the sound pool", id); + SP_INST_CHECK(_stream, SOUND_POOL_ERROR_INVALID_OPERATION); + + ret = _sound_stream_get_loop(_stream, loop); + SP_RETVM_IF(ret != SOUND_POOL_ERROR_NONE, ret, "Error occurred when " + "getting sound stream [%u] loop parameter", id); + + SP_DEBUG_FLEAVE(); + return ret; +} + +sound_pool_error_e sound_pool_stream_set_volume(sound_pool_h pool, unsigned id, + float volume) +{ + SP_DEBUF_FENTER(); + + sound_pool_t *_pool = (sound_pool_t *)pool; + sound_stream_t *_stream = NULL; + int ret = _sound_pool_get_stream_by_id(_pool, id, &_stream); + SP_RETVM_IF(ret != SOUND_POOL_ERROR_NONE, ret, "Error occurred when " + "getting sound stream [%u] from the sound pool", id); + SP_INST_CHECK(_stream, SOUND_POOL_ERROR_INVALID_OPERATION); + + ret = _sound_stream_set_volume(_stream, volume); + SP_RETVM_IF(ret != SOUND_POOL_ERROR_NONE, ret, "Error occurred when " + "setting sound stream [%u] volume parameter", id); + + SP_DEBUG_FLEAVE(); + return ret; +} + +sound_pool_error_e sound_pool_stream_get_volume(sound_pool_h pool, unsigned id, + float *volume) +{ + SP_DEBUF_FENTER(); + + sound_pool_t *_pool = (sound_pool_t *)pool; + sound_stream_t *_stream = NULL; + int ret = _sound_pool_get_stream_by_id(_pool, id, &_stream); + SP_RETVM_IF(ret != SOUND_POOL_ERROR_NONE, ret, "Error occurred when " + "getting sound stream [%u] from the sound pool", id); + SP_INST_CHECK(_stream, SOUND_POOL_ERROR_INVALID_OPERATION); + + ret = _sound_stream_get_volume(_stream, volume); + SP_RETVM_IF(ret != SOUND_POOL_ERROR_NONE, ret, "Error occurred when " + "getting sound stream [%u] volume parameter", id); + + SP_DEBUG_FLEAVE(); + return ret; +} + +sound_pool_error_e sound_pool_stream_set_priority(sound_pool_h pool, + unsigned id, unsigned priority) +{ + SP_DEBUF_FENTER(); + + sound_pool_t *_pool = (sound_pool_t *)pool; + sound_stream_t *_stream = NULL; + int ret = _sound_pool_get_stream_by_id(_pool, id, &_stream); + SP_RETVM_IF(ret != SOUND_POOL_ERROR_NONE, ret, "Error occurred when " + "getting sound stream [%u] from the sound pool", id); + SP_INST_CHECK(_stream, SOUND_POOL_ERROR_INVALID_OPERATION); + + ret = _sound_stream_set_priority(_stream, priority); + SP_RETVM_IF(ret != SOUND_POOL_ERROR_NONE, ret, "Error occurred when " + "setting sound stream [%u] priority parameter", id); + + SP_DEBUG_FLEAVE(); + return ret; +} + +sound_pool_error_e sound_pool_stream_get_priority(sound_pool_h pool, + unsigned id, unsigned *priority) +{ + SP_DEBUF_FENTER(); + + sound_pool_t *_pool = (sound_pool_t *)pool; + sound_stream_t *_stream = NULL; + int ret = _sound_pool_get_stream_by_id(_pool, id, &_stream); + SP_RETVM_IF(ret != SOUND_POOL_ERROR_NONE, ret, "Error occurred when " + "getting sound stream [%u] from the sound pool", id); + SP_INST_CHECK(_stream, SOUND_POOL_ERROR_INVALID_OPERATION); + + ret = _sound_stream_get_priority(_stream, priority); + SP_RETVM_IF(ret != SOUND_POOL_ERROR_NONE, ret, "Error occurred when " + "getting sound stream [%u] priority parameter", id); + + SP_DEBUG_FLEAVE(); + return ret; +} + +sound_pool_error_e sound_pool_set_state_change_callback(sound_pool_h pool, + sound_pool_state_change_cb callback, void *data) +{ + SP_DEBUF_FENTER(); + + const int ret = + _sound_pool_set_callback((sound_pool_t *)pool, callback, data); + SP_RETVM_IF(ret != SOUND_POOL_ERROR_NONE, ret, "Error occurred when " + "setting state changing callback for sound pool"); + + SP_DEBUG_FLEAVE(); + return ret; +} + +sound_pool_error_e sound_pool_unset_state_change_callback(sound_pool_h pool) +{ + SP_DEBUF_FENTER(); + + const int ret = _sound_pool_unset_callback((sound_pool_t *)pool); + SP_RETVM_IF(ret != SOUND_POOL_ERROR_NONE, ret, "Error occurred when " + "unsetting state changing callback for sound pool"); + + SP_DEBUG_FLEAVE(); + return ret; +} + +sound_pool_error_e sound_pool_stream_set_state_change_callback( + sound_pool_h pool, unsigned id, + sound_pool_stream_state_change_cb callback, void *data) +{ + SP_DEBUF_FENTER(); + + sound_pool_t *_pool = (sound_pool_t *)pool; + sound_stream_t *_stream = NULL; + int ret = _sound_pool_get_stream_by_id(_pool, id, &_stream); + SP_RETVM_IF(ret != SOUND_POOL_ERROR_NONE, ret, "Error occurred when " + "getting sound stream [%u] from the sound pool", id); + SP_INST_CHECK(_stream, SOUND_POOL_ERROR_INVALID_OPERATION); + + ret = _sound_stream_set_callback(_stream, callback, data); + SP_RETVM_IF(ret != SOUND_POOL_ERROR_NONE, ret, "Error occurred when " + "setting sound stream [%u] state changing callback", id); + + SP_DEBUG_FLEAVE(); + return ret; +} + +sound_pool_error_e sound_pool_stream_unset_state_change_callback( + sound_pool_h pool, unsigned id) +{ + SP_DEBUF_FENTER(); + + sound_pool_t *_pool = (sound_pool_t *)pool; + sound_stream_t *_stream = NULL; + int ret = _sound_pool_get_stream_by_id(_pool, id, &_stream); + SP_RETVM_IF(ret != SOUND_POOL_ERROR_NONE, ret, "Error occurred when " + "getting sound stream [%u] from the sound pool", id); + SP_INST_CHECK(_stream, SOUND_POOL_ERROR_INVALID_OPERATION); + + ret = _sound_stream_unset_callback(_stream); + SP_RETVM_IF(ret != SOUND_POOL_ERROR_NONE, ret, "Error occurred when " + "unsetting sound stream [%u] state changing callback", id); + + SP_DEBUG_FLEAVE(); + return ret; +} diff --git a/src/soundpool.c b/src/soundpool.c new file mode 100644 index 0000000..631a3aa --- /dev/null +++ b/src/soundpool.c @@ -0,0 +1,326 @@ +/* + * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file soundpool.c + * @brief This file include implementation of protected API for the SoundPool. + */ + +#include "internal/soundpool.h" + +#include "internal/priority.h" +#include "internal/stream_cb_manager.h" + +#include "internal/source.h" +#include "internal/stream.h" + +#define DEFAULT_VOLUME_VALUE 1.0f + +static ALCdevice *device = NULL; +static void __stream_activate_iter(gpointer key, gpointer value, + gpointer user_data); +static void __stream_deactivate_iter(gpointer key, gpointer value, + gpointer user_data); + +static void __stream_activate_iter(gpointer key, gpointer value, + gpointer user_data) +{ + SP_DEBUF_FENTER(); + SP_RETM_IF(!user_data, "Empty user data."); + SP_RETM_IF(!value, "Empty sound stream data."); + guint *len = (guint*)user_data; + sound_stream_t *stream = (sound_stream_t *)value; + + gboolean can_run = TRUE; + if (SOUND_POOL_ERROR_NONE + == _sound_stream_priority_check(stream->parent_source->parent_pool->mgr_priority, + stream, &can_run)) + if (stream->state == SOUND_POOL_STREAM_STATE_SUSPENDED && TRUE == can_run) { + alSourcePlay(stream->al_source); + (*len)++; + } + + SP_DEBUG_FLEAVE(); +} + +static void __stream_deactivate_iter(gpointer key, gpointer value, + gpointer user_data) +{ + SP_DEBUF_FENTER(); + SP_RETM_IF(!user_data, "Empty user data."); + SP_RETM_IF(!value, "Empty sound stream data."); + guint *len = (guint*)user_data; + sound_stream_t *stream = (sound_stream_t *)value; + + if (stream->state == SOUND_POOL_STREAM_STATE_PLAYING) { + alSourcePause(stream->al_source); + (*len)++; + } + + SP_DEBUG_FLEAVE(); +} + +sound_pool_error_e _sound_pool_create(size_t max_streams, sound_pool_t **pool) +{ + SP_DEBUF_FENTER(); + SP_RETVM_IF(max_streams == 0, SOUND_POOL_ERROR_INVALID_PARAMETER, + "Max streams count should be greater than 0."); + + sound_pool_t *_pool = NULL; + SP_RETVM_IF(!(_pool = g_try_malloc0(sizeof(*_pool))), + SOUND_POOL_ERROR_OUT_OF_MEMORY, + "Memory alloc failure. Can't create sound pool"); + SP_RETVM_IF(!pool, SOUND_POOL_ERROR_INVALID_PARAMETER, "Can't create " + "sound pool. Pool pointer is NULL"); + + int ret = SOUND_POOL_ERROR_NONE; + _pool->al_context = NULL; + _pool->al_context = alcCreateContext(alcOpenDevice(NULL), NULL); + if (!_pool->al_context) { + ret = SOUND_POOL_ERROR_OUT_OF_MEMORY; + GOTO_FAIL("Memory alloc failure. Can't create OpenAL Context", cfail); + } + + _pool->sources = NULL; + _pool->streams = NULL; + + _pool->sources = g_hash_table_new(g_str_hash, g_str_equal); + if (!_pool->sources) { + ret = SOUND_POOL_ERROR_OUT_OF_MEMORY; + GOTO_FAIL("Memory alloc failure. Can't create internal sources hash " + "table in sound pool", cfail); + } + + _pool->streams = g_hash_table_new(g_int_hash, g_int_equal); + if (!_pool->streams) { + ret = SOUND_POOL_ERROR_OUT_OF_MEMORY; + GOTO_FAIL("Memory alloc failure. Can't create internal streams hash " + "table in sound pool", cfail); + } + + _pool->max_streams = max_streams; + _pool->volume = DEFAULT_VOLUME_VALUE; + _pool->max_stream_index = 0; + _pool->state = SOUND_POOL_STATE_INACTIVE; + _pool->state_cb_info.callback = NULL; + _pool->state_cb_info.user_data = NULL; + + _pool->cbmgr = NULL; + ret = _stream_cb_manager_create(_pool, &_pool->cbmgr); + if (ret != SOUND_POOL_ERROR_NONE) + GOTO_FAIL("Error occurred when trying to create stream state changing " + "callback manager instance", cfail); + + _pool->mgr_priority = NULL; + ret = _sound_stream_priority_create(_pool, &_pool->mgr_priority); + if (ret != SOUND_POOL_ERROR_NONE) + GOTO_FAIL("Error occurred initialising of priorities.", cfail); + + *pool = _pool; + SP_DEBUG_FLEAVE(); + return ret; + +cfail: + _sound_pool_destroy(_pool); + SP_DEBUG_FLEAVE(); + return ret; +} + +sound_pool_error_e _sound_pool_destroy(sound_pool_t *pool) +{ + SP_DEBUF_FENTER(); + SP_INST_CHECK(pool, SOUND_POOL_ERROR_INVALID_PARAMETER); + _sound_pool_deactivate(pool); + + if (pool->cbmgr) { + const int ret = _stream_cb_manager_destroy(pool->cbmgr); + SP_RETVM_IF(ret != SOUND_POOL_ERROR_NONE, ret, + "Error occurred when " "trying to destroy sound pool callback manager."); + pool->cbmgr = NULL; + } + + if (pool->sources) { + GHashTableIter iter; + gpointer key, value; + g_hash_table_iter_init(&iter, pool->sources); + while (g_hash_table_iter_next(&iter, &key, &value)) { + guint size_before = g_hash_table_size(pool->sources); + _sound_source_destroy((sound_source_t*)value); + guint size_after = g_hash_table_size(pool->sources); + if (size_before != size_after) + g_hash_table_iter_init(&iter, pool->sources); + } + g_hash_table_unref(pool->sources); + pool->sources = NULL; + } + + if (pool->streams) { + g_hash_table_unref(pool->streams); + pool->streams = NULL; + } + + if (pool->mgr_priority) { + const int ret = _sound_stream_priority_destroy(pool->mgr_priority); + SP_RETVM_IF(ret != SOUND_POOL_ERROR_NONE, ret, + "Error occurred when trying to destroy priority manager."); + pool->mgr_priority = NULL; + } + + if (pool->al_context) { + ALCdevice *device = alcGetContextsDevice(pool->al_context); + alcMakeContextCurrent(NULL); + alcDestroyContext(pool->al_context); + alcCloseDevice(device); + } + + SP_SAFE_GFREE(pool); + + SP_DEBUG_FLEAVE(); + return SOUND_POOL_ERROR_NONE; +} + +sound_pool_error_e _sound_pool_activate(sound_pool_t *pool) +{ + SP_DEBUF_FENTER(); + SP_INST_CHECK(pool, SOUND_POOL_ERROR_INVALID_PARAMETER); + + SP_RETVM_IF(pool->state == SOUND_POOL_STATE_ACTIVE, + SOUND_POOL_ERROR_INVALID_OPERATION, + "Already activated sound pool can't be activated again."); + + sound_pool_state_e old_state = pool->state; + pool->state = SOUND_POOL_STATE_ACTIVE; + + /* Generate array of all AlSources in pool */ + GPtrArray *streams = NULL; + if (g_hash_table_size(pool->streams) > 0) { + SP_RETVM_IF(!alcMakeContextCurrent(pool->al_context), + SOUND_POOL_ERROR_INVALID_OPERATION, "Can't set current context."); + guint len = 0; + g_hash_table_foreach(pool->streams, __stream_activate_iter, &len); + SP_INFO("Resuming [%d] number of streams.", len); + } + + if (streams) + g_ptr_array_free(streams, TRUE); + + SP_INFO("Sound pool has been activated"); + + if (pool->state_cb_info.callback) + pool->state_cb_info.callback(pool, old_state, pool->state, + pool->state_cb_info.user_data); + + SP_DEBUG_FLEAVE(); + return SOUND_POOL_ERROR_NONE; +} + +sound_pool_error_e _sound_pool_deactivate(sound_pool_t *pool) +{ + SP_DEBUF_FENTER(); + SP_INST_CHECK(pool, SOUND_POOL_ERROR_INVALID_PARAMETER); + SP_RETVM_IF(pool->state == SOUND_POOL_STATE_INACTIVE, + SOUND_POOL_ERROR_INVALID_OPERATION, + "Already deactivated sound pool can't be deactivated again."); + + sound_pool_state_e old_state = pool->state; + pool->state = SOUND_POOL_STATE_INACTIVE; + + /* Generate array of all AlSources in pool */ + GPtrArray *streams = NULL; + if (g_hash_table_size(pool->streams) > 0) { + SP_RETVM_IF(!alcMakeContextCurrent(pool->al_context), + SOUND_POOL_ERROR_INVALID_OPERATION, "Can't set current context."); + guint len = 0; + g_hash_table_foreach(pool->streams, __stream_deactivate_iter, &len); + SP_INFO("Suspending [%d] number of streams.", len); + } + + if (streams) + g_ptr_array_free(streams, TRUE); + + SP_INFO("Sound pool has been deactivated"); + + if (pool->state_cb_info.callback) + pool->state_cb_info.callback(pool, old_state, pool->state, + pool->state_cb_info.user_data); + + SP_DEBUG_FLEAVE(); + return SOUND_POOL_ERROR_NONE; +} + +sound_pool_error_e _sound_pool_get_state(sound_pool_t *pool, + sound_pool_state_e *state) +{ + SP_DEBUF_FENTER(); + SP_INST_CHECK(pool, SOUND_POOL_ERROR_INVALID_PARAMETER); + SP_INST_CHECK(state, SOUND_POOL_ERROR_INVALID_PARAMETER); + *state = pool->state; + SP_DEBUG_FLEAVE(); + return SOUND_POOL_ERROR_NONE; +} + +sound_pool_error_e _sound_pool_set_volume(sound_pool_t *pool, float volume) +{ + SP_DEBUF_FENTER(); + SP_INST_CHECK(pool, SOUND_POOL_ERROR_INVALID_PARAMETER); + SP_RETVM_IF((volume < 0.0f || volume > 1.0f), + SOUND_POOL_ERROR_INVALID_PARAMETER, + "Sound pool volume is not in [0.0..1.0] range."); + + SP_RETVM_IF(!alcMakeContextCurrent(pool->al_context), + SOUND_POOL_ERROR_INVALID_OPERATION, "Can't set current context."); + alListenerf(AL_GAIN, volume); + pool->volume = volume; + + SP_DEBUG_FLEAVE(); + return SOUND_POOL_ERROR_NONE; +} + +sound_pool_error_e _sound_pool_get_volume(sound_pool_t *pool, float *volume) +{ + SP_DEBUF_FENTER(); + SP_INST_CHECK(pool, SOUND_POOL_ERROR_INVALID_PARAMETER); + SP_INST_CHECK(volume, SOUND_POOL_ERROR_INVALID_PARAMETER); + *volume = pool->volume; + SP_DEBUG_FLEAVE(); + return SOUND_POOL_ERROR_NONE; +} + +sound_pool_error_e _sound_pool_set_callback(sound_pool_t *pool, + sound_pool_state_change_cb callback, void *data) +{ + SP_DEBUF_FENTER(); + SP_INST_CHECK(pool, SOUND_POOL_ERROR_INVALID_PARAMETER); + SP_INST_CHECK(callback, SOUND_POOL_ERROR_INVALID_PARAMETER); + + pool->state_cb_info.callback = callback; + pool->state_cb_info.user_data = data; + + SP_DEBUG_FLEAVE(); + return SOUND_POOL_ERROR_NONE; +} + +sound_pool_error_e _sound_pool_unset_callback(sound_pool_t *pool) +{ + SP_DEBUF_FENTER(); + SP_INST_CHECK(pool, SOUND_POOL_ERROR_INVALID_PARAMETER); + + pool->state_cb_info.callback = NULL; + pool->state_cb_info.user_data = NULL; + + SP_DEBUG_FLEAVE(); + return SOUND_POOL_ERROR_NONE; +} diff --git a/src/source.c b/src/source.c new file mode 100644 index 0000000..d9584dd --- /dev/null +++ b/src/source.c @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file source.c + * @brief This file include implementation of protected API + * for the sound sources(OpenAL buffers) as part of SoundPool. + */ + +#include "internal/source.h" +#include "internal/stream.h" + +#include + +static int __sound_pool_add_source(sound_pool_t *pool, sound_source_t *src); +static int __sound_pool_remove_source(sound_pool_t *pool, sound_source_t *src); + +static int __sound_pool_add_source(sound_pool_t *pool, sound_source_t *src) +{ + SP_DEBUF_FENTER(); + SP_INST_CHECK(pool, SOUND_POOL_ERROR_INVALID_PARAMETER); + SP_INST_CHECK(src, SOUND_POOL_ERROR_INVALID_PARAMETER); + SP_RETVM_IF(!pool->sources, SOUND_POOL_ERROR_INVALID_OPERATION, "Sound " + "pool to add the source is corrupted: NULL sources hash table"); + SP_RETVM_IF(!src->tag_name, SOUND_POOL_ERROR_INVALID_OPERATION, + "Source to be added to sound pool is corrupted: NULL tag name"); + SP_RETVM_IF(src->parent_pool != pool, SOUND_POOL_ERROR_INVALID_OPERATION, + "Sound source can't be added to the pool different to the pool for" + " which sound source was created"); + SP_RETVM_IF(g_hash_table_contains(pool->sources, src->tag_name), + SOUND_POOL_ERROR_INVALID_PARAMETER, + "Tag already exists in sources hash table."); + + SP_RETVM_IF(!g_hash_table_insert(pool->sources, src->tag_name, src), + SOUND_POOL_ERROR_INVALID_OPERATION, "Error occurred when adding " + "the source tagged [%s] to the sound pool", src->tag_name); + + SP_DEBUG_FLEAVE(); + return SOUND_POOL_ERROR_NONE; +} + +static int __sound_pool_remove_source(sound_pool_t *pool, sound_source_t *src) +{ + SP_DEBUF_FENTER(); + SP_INST_CHECK(pool, SOUND_POOL_ERROR_INVALID_PARAMETER); + SP_INST_CHECK(pool->sources, SOUND_POOL_ERROR_INVALID_PARAMETER); + SP_INST_CHECK(src, SOUND_POOL_ERROR_INVALID_PARAMETER); + + if (src->parent_pool == pool && src->tag_name) { + if (!g_hash_table_steal(pool->sources, src->tag_name)) { + SP_DEBUG("Tag [%s] doesn't exist in sources hash table", + src->tag_name); + SP_DEBUG_FLEAVE(); + return SOUND_POOL_ERROR_NONE; + } + GHashTableIter iter; + gpointer key, value; + g_hash_table_iter_init(&iter, pool->streams); + while (g_hash_table_iter_next(&iter, &key, &value)) { + guint size_before = g_hash_table_size(pool->streams); + gchar *tag = (gchar*)key; + sound_stream_t *stream = (sound_stream_t*)value; + SP_INFO("(%s)(%s)", src->tag_name, stream->parent_source->tag_name); + if (src == stream->parent_source) + _sound_stream_destroy((sound_stream_t*)value); + guint size_after = g_hash_table_size(pool->streams); + if (size_before != size_after) + g_hash_table_iter_init(&iter, pool->streams); + } + } else { + SP_DEBUG("Source wasn't removed from sound pool as it isn't known by " + "the pool for which operation is performed, or tag corrupted"); + } + + SP_DEBUG_FLEAVE(); + return SOUND_POOL_ERROR_NONE; +} + +sound_pool_error_e _sound_source_create(sound_pool_t *pool, const char *tag, + sound_source_t **src) +{ + SP_DEBUF_FENTER(); + SP_RETVM_IF(!pool, SOUND_POOL_ERROR_INVALID_PARAMETER, + "Can't create sound source in NULL sound pool"); + SP_RETVM_IF(!tag, SOUND_POOL_ERROR_INVALID_PARAMETER, + "Can't create sound source with NULL tag name"); + SP_RETVM_IF(!src, SOUND_POOL_ERROR_INVALID_PARAMETER, + "Can't create sound source. Source pointer is NULL"); + SP_RETVM_IF(strnlen(tag, MAX_TAG_LEN + 1) == MAX_TAG_LEN + 1, + SOUND_POOL_ERROR_INVALID_PARAMETER, "Can't create sound source " + "with tag name longer than %u", MAX_TAG_LEN); + SP_RETVM_IF(!pool->sources, SOUND_POOL_ERROR_INVALID_OPERATION, "Sound " + "pool to create source is corrupted: NULL sources hash table."); + SP_RETVM_IF(g_hash_table_contains(pool->sources, tag), + SOUND_POOL_ERROR_INVALID_PARAMETER, + "Tag already exists in sources hash table."); + + SP_RETVM_IF(!alcMakeContextCurrent(pool->al_context), + SOUND_POOL_ERROR_INVALID_OPERATION, + "Can't set current context."); + + sound_source_t *_src = NULL; + SP_RETVM_IF(!(_src = g_try_malloc0(sizeof(*_src))), + SOUND_POOL_ERROR_OUT_OF_MEMORY, + "Memory alloc failure. Can't create sound _src"); + + int ret = SOUND_POOL_ERROR_NONE; + alGenBuffers(1, &_src->al_buffer); + if (alGetError() != AL_NO_ERROR) { + ret = SOUND_POOL_ERROR_OUT_OF_MEMORY; + GOTO_FAIL("OpenAL error occurred when trying to generate Buffer id", cfail); + } + + _src->parent_pool = pool; + _src->tag_name = NULL; + _src->tag_name = g_strndup(tag, MAX_TAG_LEN); + + ret = __sound_pool_add_source(pool, _src); + if (ret != SOUND_POOL_ERROR_NONE) + GOTO_FAIL("Error occurred when trying to add source to pool", cfail); + + *src = _src; + SP_DEBUG_FLEAVE(); + return ret; + +cfail: + ret = _sound_source_destroy(_src); + SP_RETVM_IF(SOUND_POOL_ERROR_NONE != ret, ret, "Error occurred during removal " + "of sound source[%s].", tag); + SP_DEBUG_FLEAVE(); + return ret; +} + +sound_pool_error_e _sound_source_destroy(sound_source_t *src) +{ + SP_DEBUF_FENTER(); + SP_RETVM_IF(!src, SOUND_POOL_ERROR_INVALID_PARAMETER, + "Can't destroy NULL sound source"); + + /* If parent pool exists, then source has to be removed from the pool */ + if (src->parent_pool && __sound_pool_remove_source(src->parent_pool, src) + != SOUND_POOL_ERROR_NONE) + SP_DEBUG("Source wasn't removed from sound pool."); + + SP_DEBUG("Deleting OpenAL buffer with id [%u]", src->al_buffer); + alDeleteBuffers(1, &src->al_buffer); + + SP_SAFE_GFREE(src->tag_name); + SP_SAFE_GFREE(src); + + SP_DEBUG_FLEAVE(); + return SOUND_POOL_ERROR_NONE; +} + +sound_pool_error_e _sound_source_load_from_file(sound_source_t *src, + const char *fname) +{ + SP_DEBUF_FENTER(); + SP_INST_CHECK(src, SOUND_POOL_ERROR_INVALID_PARAMETER); + SP_INST_CHECK(fname, SOUND_POOL_ERROR_INVALID_PARAMETER); + SP_INST_CHECK(src->parent_pool, SOUND_POOL_ERROR_INVALID_OPERATION); + SP_RETVM_IF(!alcMakeContextCurrent(src->parent_pool->al_context), + SOUND_POOL_ERROR_INVALID_OPERATION, "Can't set current AL context."); + + int ret = SOUND_POOL_ERROR_NONE; + ALenum format; + ALsizei size; + ALvoid* data = NULL; + ALsizei freq; + ALboolean loop; + + alutLoadWAVFile((ALbyte*)fname, &format, &data, &size, &freq, &loop); + SP_RETVM_IF(alGetError() != AL_NO_ERROR || !data, + SOUND_POOL_ERROR_NO_SUCH_FILE, "Can't load audio file. No such " + "file [%s]", fname); + + alBufferData(src->al_buffer, format, data, size, freq); + if (alGetError() != AL_NO_ERROR) { + SP_ERROR("Can't create audio buffer from file [%s]", fname); + ret = SOUND_POOL_ERROR_INVALID_OPERATION; + } + + alutUnloadWAV(format, data, size, freq); + + SP_DEBUG_FLEAVE(); + return ret; +} + +sound_pool_error_e _sound_pool_get_source_by_tag(sound_pool_t *pool, + const char *tag, sound_source_t **src) +{ + SP_DEBUF_FENTER(); + SP_INST_CHECK(pool, SOUND_POOL_ERROR_INVALID_PARAMETER); + SP_INST_CHECK(tag, SOUND_POOL_ERROR_INVALID_PARAMETER); + SP_INST_CHECK(src, SOUND_POOL_ERROR_INVALID_PARAMETER); + SP_RETVM_IF(!pool->sources, SOUND_POOL_ERROR_INVALID_PARAMETER, "Corrupted " + "sound pool. Sources hash table is NULL"); + + *src = (sound_source_t *)g_hash_table_lookup(pool->sources, tag); + SP_RETVM_IF(!(*src), SOUND_POOL_ERROR_KEY_NOT_AVAILABLE, "Tag doesn't " + "exist in sources hash table"); + + SP_DEBUG_FLEAVE(); + return SOUND_POOL_ERROR_NONE; +} diff --git a/src/stream.c b/src/stream.c new file mode 100644 index 0000000..aa5fa68 --- /dev/null +++ b/src/stream.c @@ -0,0 +1,540 @@ +/* + * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file stream.c + * @brief This file include implementation of protected API + * for the sound streams(OpenAL sources) as part of SoundPool. + */ + +#include "internal/stream.h" +#include "internal/priority.h" + +#include "internal/stream_cb_manager.h" + +#define DEFAULT_STREAM_PRIORITY 255U +#define DEFAULT_STREAM_LOOP 1U +#define DEFAULT_STREAM_VOLUME 1.0f + +static const char *__stringify_stream_state(sound_pool_stream_state_e state); +static void __al_source_state_cb(ALuint source, ALenum state, ALvoid *data); +static int __sound_pool_add_stream(sound_pool_t *pool, sound_stream_t *stream); +static int __sound_pool_remove_stream(sound_pool_t *pool, sound_stream_t *stream); + +static const char *__stringify_stream_state(sound_pool_stream_state_e state) +{ + switch (state) { + case SOUND_POOL_STREAM_STATE_NONE: + return "SOUND_POOL_STREAM_STATE_NONE"; + case SOUND_POOL_STREAM_STATE_PLAYING: + return "SOUND_POOL_STREAM_STATE_PLAYING"; + case SOUND_POOL_STREAM_STATE_PAUSED: + return "SOUND_POOL_STREAM_STATE_PAUSED"; + case SOUND_POOL_STREAM_STATE_SUSPENDED: + return "SOUND_POOL_STREAM_STATE_SUSPENDED"; + case SOUND_POOL_STREAM_STATE_STOPPED: + return "SOUND_POOL_STREAM_STATE_STOPPED"; + case SOUND_POOL_STREAM_STATE_FINISHED: + return "SOUND_POOL_STREAM_STATE_FINISHED"; + default: + return ""; + } +} + +static void __al_source_state_cb(ALuint source, ALenum state, ALvoid *data) +{ + SP_DEBUF_FENTER(); + + sound_stream_t *stream = (sound_stream_t *)data; + SP_RETM_IF(!stream, "Can't handle stream in OpenAL callback, it is NULL"); + sound_pool_t *pool = stream->parent_source->parent_pool; + SP_RETM_IF(!pool, "Can't handle stream in OpenAL callback, stream's sound " + "pool is NULL"); + + stream->state_previous = stream->state; + + switch (state) { + case AL_INITIAL: + stream->state = SOUND_POOL_STREAM_STATE_NONE; + break; + case AL_PLAYING: + stream->state = SOUND_POOL_STREAM_STATE_PLAYING; + break; + case AL_PAUSED: + if (pool->state == SOUND_POOL_STATE_INACTIVE) + stream->state = SOUND_POOL_STREAM_STATE_SUSPENDED; + else if (pool->state == SOUND_POOL_STATE_ACTIVE) + stream->state = SOUND_POOL_STREAM_STATE_PAUSED; + break; + case AL_STOPPED: + if (stream->stopped) + stream->state = SOUND_POOL_STREAM_STATE_STOPPED; + else + stream->state = SOUND_POOL_STREAM_STATE_FINISHED; + break; + default: + SP_ERROR("Invalid OpenAl state value [%#04x]", state); + return; + } + + SP_INFO("Stream [%s:%d] state changed from [%s] to [%s]", + stream->parent_source->tag_name, stream->id, + __stringify_stream_state(stream->state_previous), + __stringify_stream_state(stream->state)); + + SP_RETM_IF(_stream_cb_manager_register_event(pool->cbmgr, stream) != + SOUND_POOL_ERROR_NONE, "State changing event wasn't registered." + "Callbacks will be not called"); + + SP_DEBUG_FLEAVE(); +} + +static int __sound_pool_add_stream(sound_pool_t *pool, sound_stream_t *stream) +{ + SP_DEBUF_FENTER(); + SP_INST_CHECK(pool, SOUND_POOL_ERROR_INVALID_PARAMETER); + SP_INST_CHECK(stream, SOUND_POOL_ERROR_INVALID_PARAMETER); + SP_RETVM_IF(!pool->streams, SOUND_POOL_ERROR_INVALID_OPERATION, "Sound " + "pool to add the stream is corrupted: NULL streams hash table"); + stream->id = ++(pool->max_stream_index); + SP_DEBUG("Id assigned to the stream is [%u]", stream->id); + SP_RETVM_IF(g_hash_table_contains(pool->streams, (gpointer)&stream->id), + SOUND_POOL_ERROR_INVALID_PARAMETER, + "Stream ID already exists in streams hash table."); + SP_RETVM_IF(stream->parent_source->parent_pool != pool, + SOUND_POOL_ERROR_INVALID_OPERATION, "Sound stream can't be added " + "to the pool different to the pool for which sound stream was created"); + + SP_RETVM_IF(!g_hash_table_insert(pool->streams, (gpointer)&stream->id, stream), + SOUND_POOL_ERROR_INVALID_OPERATION, "Error occurred when adding " + "the stream [%u] to the sound pool", stream->id); + + int ret = _sound_stream_priority_add_stream(pool->mgr_priority, stream); + SP_RETVM_IF(SOUND_POOL_ERROR_NONE != ret, ret, "Error occurred when adding " + "the stream [%u] to the priority queue.", stream->id); + + SP_DEBUG_FLEAVE(); + return SOUND_POOL_ERROR_NONE; +} + +static int __sound_pool_remove_stream(sound_pool_t *pool, sound_stream_t *stream) +{ + SP_DEBUF_FENTER(); + SP_INST_CHECK(pool, SOUND_POOL_ERROR_INVALID_PARAMETER); + SP_INST_CHECK(pool->mgr_priority, SOUND_POOL_ERROR_INVALID_PARAMETER); + SP_INST_CHECK(stream, SOUND_POOL_ERROR_INVALID_PARAMETER); + SP_INST_CHECK(stream->parent_source, SOUND_POOL_ERROR_INVALID_PARAMETER); + + if (stream->parent_source->parent_pool == pool) { + if (!g_hash_table_remove(pool->streams, &stream->id) + || SOUND_POOL_ERROR_NONE + != _sound_stream_priority_remove_stream(pool->mgr_priority, + stream)) + SP_DEBUG("Id [%u] doesn't exist in streams hash table", stream->id); + else + SP_INFO("[%u] sound stream was removed from pool", stream->id); + } else { + SP_DEBUG("Stream wasn't removed from sound pool as it isn't known by " + "the pool for which operation is performed"); + } + + SP_DEBUG_FLEAVE(); + return SOUND_POOL_ERROR_NONE; +} + +sound_pool_error_e _sound_stream_create(sound_source_t *src, + sound_stream_t **stream) +{ + SP_DEBUF_FENTER(); + SP_RETVM_IF(!src, SOUND_POOL_ERROR_INVALID_PARAMETER, + "Can't create sound stream in NULL sound source"); + SP_RETVM_IF(!stream, SOUND_POOL_ERROR_INVALID_PARAMETER, + "Can't create sound stream. Stream pointer is NULL"); + SP_RETVM_IF(!src->parent_pool, SOUND_POOL_ERROR_INVALID_PARAMETER, + "Can't create sound stream. Source's pool is NULL"); + + SP_RETVM_IF(!alcMakeContextCurrent(src->parent_pool->al_context), + SOUND_POOL_ERROR_INVALID_OPERATION, "Can't set current context."); + + sound_stream_t *_stream = NULL; + SP_RETVM_IF(!(_stream = g_try_malloc0(sizeof(*_stream))), + SOUND_POOL_ERROR_OUT_OF_MEMORY, + "Memory alloc failure. Can't create sound stream"); + + _stream->parent_source = src; + _stream->priority = DEFAULT_STREAM_PRIORITY; + _stream->loop = DEFAULT_STREAM_LOOP; + _stream->volume = DEFAULT_STREAM_VOLUME; + _stream->state_previous = SOUND_POOL_STREAM_STATE_NONE; + _stream->state = SOUND_POOL_STREAM_STATE_NONE; + _stream->stopped = FALSE; + _stream->state_cb_info.callback = NULL; + _stream->state_cb_info.user_data = NULL; + + int ret = SOUND_POOL_ERROR_NONE; + alGenSources((ALsizei)1, &_stream->al_source); + if (alGetError() != AL_NO_ERROR) { + ret = SOUND_POOL_ERROR_OUT_OF_MEMORY; + GOTO_FAIL("OpenAL error occurred when trying to generate OpenAL Source", + cfail); + } + + alSourcei(_stream->al_source, AL_BUFFER, src->al_buffer); + if (alGetError() != AL_NO_ERROR) { + ret = SOUND_POOL_ERROR_INVALID_OPERATION; + GOTO_FAIL("OpenAL error occurred when trying to assign OpenAL Buffer " + "to OpenAL Source", cfail); + } + + alSourcep(_stream->al_source, AL_SOURCE_STATE_CALLBACK, + (ALvoid *)__al_source_state_cb); + if (alGetError() != AL_NO_ERROR) { + ret = SOUND_POOL_ERROR_INVALID_OPERATION; + GOTO_FAIL("OpenAL error occurred when trying to assign OpenAL Callback " + "to OpenAL Source", cfail); + } + + alSourcep(_stream->al_source, AL_SOURCE_STATE_CALLBACK_DATA, (ALvoid *)_stream); + if (alGetError() != AL_NO_ERROR) { + ret = SOUND_POOL_ERROR_INVALID_OPERATION; + GOTO_FAIL("OpenAL error occurred when trying to assign OpenAL Callback " + "Data to OpenAL Source", cfail); + } + + ret = __sound_pool_add_stream(src->parent_pool, _stream); + if (ret != SOUND_POOL_ERROR_NONE) + GOTO_FAIL("Error occurred when trying to add source to pool", cfail); + + *stream = _stream; + SP_DEBUG_FLEAVE(); + return ret; + +cfail: + _sound_stream_destroy(_stream); + SP_DEBUG_FLEAVE(); + return ret; +} + +sound_pool_error_e _sound_stream_destroy(sound_stream_t *stream) +{ + SP_DEBUF_FENTER(); + SP_RETVM_IF(!stream, SOUND_POOL_ERROR_INVALID_PARAMETER, + "Can't destroy NULL sound stream"); + SP_RETVM_IF(!stream->parent_source->parent_pool, SOUND_POOL_ERROR_INVALID_PARAMETER, + "Empty parent pool pointer."); + + if (stream->parent_source) { + if (__sound_pool_remove_stream(stream->parent_source->parent_pool, + stream) != SOUND_POOL_ERROR_NONE) + SP_DEBUG("Stream[%s] wasn't removed from sound pool. Continue destroying", + stream->parent_source->tag_name); + if (alcMakeContextCurrent(stream->parent_source->parent_pool->al_context)) { + alSourcei(stream->al_source, AL_BUFFER, 0); + alDeleteSources(1, &stream->al_source); + SP_DEBUG("Deleting OpenAL source with id [%u]", stream->al_source); + } else { + SP_DEBUG("Can't set current context for deleting OpenAL source with id [%u]", + stream->al_source); + } + } + + SP_SAFE_GFREE(stream); + + SP_DEBUG_FLEAVE(); + return SOUND_POOL_ERROR_NONE; +} + +sound_pool_error_e _sound_stream_play(sound_stream_t *stream) +{ + SP_DEBUF_FENTER(); + SP_INST_CHECK(stream, SOUND_POOL_ERROR_INVALID_PARAMETER); + SP_INST_CHECK(stream->parent_source, SOUND_POOL_ERROR_INVALID_PARAMETER); + sound_pool_t *pool = stream->parent_source->parent_pool; + SP_INST_CHECK(pool, SOUND_POOL_ERROR_INVALID_PARAMETER); + SP_RETVM_IF(stream->state != SOUND_POOL_STREAM_STATE_SUSPENDED && + stream->state != SOUND_POOL_STREAM_STATE_PAUSED && + stream->state != SOUND_POOL_STREAM_STATE_NONE, + SOUND_POOL_ERROR_INVALID_OPERATION, "Can't play stream[%s] in [%s] " + "state", stream->parent_source->tag_name, __stringify_stream_state(stream->state)); + SP_RETVM_IF(stream->state == SOUND_POOL_STREAM_STATE_SUSPENDED && + pool->state == SOUND_POOL_STATE_INACTIVE, + SOUND_POOL_ERROR_INVALID_OPERATION, "Can't play suspended stream " + "in inactive sound pool"); + + SP_RETVM_IF(!alcMakeContextCurrent(pool->al_context), + SOUND_POOL_ERROR_INVALID_OPERATION, "Can't set current context."); + + if (pool->state == SOUND_POOL_STATE_INACTIVE && + stream->state == SOUND_POOL_STREAM_STATE_NONE) { + stream->state_previous = stream->state; + stream->state = SOUND_POOL_STREAM_STATE_SUSPENDED; + if (_stream_cb_manager_register_event(pool->cbmgr, stream) != + SOUND_POOL_ERROR_NONE) + SP_DEBUG("State changing event wasn't registered." + "Callbacks will be not called"); + SP_DEBUG("Don't play due to SoundPool is in inactive state."); + } else { + alSourcePlay(stream->al_source); + } + + SP_DEBUG_FLEAVE(); + return SOUND_POOL_ERROR_NONE; +} + +sound_pool_error_e _sound_stream_pause(sound_stream_t *stream) +{ + SP_DEBUF_FENTER(); + SP_INST_CHECK(stream, SOUND_POOL_ERROR_INVALID_PARAMETER); + SP_INST_CHECK(stream->parent_source, SOUND_POOL_ERROR_INVALID_PARAMETER); + sound_pool_t *pool = stream->parent_source->parent_pool; + SP_INST_CHECK(pool, SOUND_POOL_ERROR_INVALID_PARAMETER); + SP_RETVM_IF(stream->state != SOUND_POOL_STREAM_STATE_SUSPENDED && + stream->state != SOUND_POOL_STREAM_STATE_PLAYING, + SOUND_POOL_ERROR_INVALID_OPERATION, "Can't pause stream in [%s] " + "state", __stringify_stream_state(stream->state)); + + SP_RETVM_IF(!alcMakeContextCurrent(pool->al_context), + SOUND_POOL_ERROR_INVALID_OPERATION, "Can't set current context."); + + if (pool->state == SOUND_POOL_STATE_INACTIVE && + stream->state == SOUND_POOL_STREAM_STATE_SUSPENDED) { + stream->state_previous = stream->state; + stream->state = SOUND_POOL_STREAM_STATE_PAUSED; + if (_stream_cb_manager_register_event(pool->cbmgr, stream) != + SOUND_POOL_ERROR_NONE) + SP_DEBUG("State changing event wasn't registered." + "Callbacks will be not called"); + SP_DEBUG("Don't paused due to SoundPool is in inactive state."); + } else { + alSourcePause(stream->al_source); + } + + SP_DEBUG_FLEAVE(); + return SOUND_POOL_ERROR_NONE; +} + +sound_pool_error_e _sound_stream_resume(sound_stream_t *stream) +{ + SP_DEBUF_FENTER(); + SP_INST_CHECK(stream, SOUND_POOL_ERROR_INVALID_PARAMETER); + SP_INST_CHECK(stream->parent_source, SOUND_POOL_ERROR_INVALID_PARAMETER); + sound_pool_t *pool = stream->parent_source->parent_pool; + SP_INST_CHECK(pool, SOUND_POOL_ERROR_INVALID_PARAMETER); + SP_RETVM_IF(stream->state != SOUND_POOL_STREAM_STATE_PAUSED, + SOUND_POOL_ERROR_INVALID_OPERATION, "Can't resume stream in [%s] " + "state", __stringify_stream_state(stream->state)); + + SP_RETVM_IF(!alcMakeContextCurrent(pool->al_context), + SOUND_POOL_ERROR_INVALID_OPERATION, "Can't set current context."); + + if (pool->state == SOUND_POOL_STATE_INACTIVE && + stream->state == SOUND_POOL_STREAM_STATE_PAUSED) + { + stream->state_previous = stream->state; + stream->state = SOUND_POOL_STREAM_STATE_SUSPENDED; + if (_stream_cb_manager_register_event(pool->cbmgr, stream) != + SOUND_POOL_ERROR_NONE) + SP_DEBUG("State changing event wasn't registered." + "Callbacks will be not called"); + SP_DEBUG("Don't resumed due to SoundPool is in inactive state."); + } else { + alSourcePlay(stream->al_source); + } + + SP_DEBUG_FLEAVE(); + return SOUND_POOL_ERROR_NONE; +} + +sound_pool_error_e _sound_stream_stop(sound_stream_t *stream) +{ + SP_DEBUF_FENTER(); + SP_INST_CHECK(stream, SOUND_POOL_ERROR_INVALID_PARAMETER); + SP_INST_CHECK(stream->parent_source, SOUND_POOL_ERROR_INVALID_PARAMETER); + sound_pool_t *pool = stream->parent_source->parent_pool; + SP_INST_CHECK(pool, SOUND_POOL_ERROR_INVALID_PARAMETER); + SP_RETVM_IF(stream->state != SOUND_POOL_STREAM_STATE_PLAYING && + stream->state != SOUND_POOL_STREAM_STATE_SUSPENDED && + stream->state != SOUND_POOL_STREAM_STATE_PAUSED, + SOUND_POOL_ERROR_INVALID_OPERATION, "Can't stop stream in [%s] " + "state", __stringify_stream_state(stream->state)); + + SP_RETVM_IF(!alcMakeContextCurrent(pool->al_context), + SOUND_POOL_ERROR_INVALID_OPERATION, "Can't set current context."); + + stream->stopped = TRUE; + alSourceStop(stream->al_source); + + SP_DEBUG_FLEAVE(); + return SOUND_POOL_ERROR_NONE; +} + +sound_pool_error_e _sound_stream_set_loop(sound_stream_t *stream, unsigned loop) +{ + SP_DEBUF_FENTER(); + SP_INST_CHECK(stream, SOUND_POOL_ERROR_INVALID_PARAMETER); + sound_pool_t *pool = stream->parent_source->parent_pool; + SP_INST_CHECK(pool, SOUND_POOL_ERROR_INVALID_PARAMETER); + SP_RETVM_IF(!alcMakeContextCurrent(pool->al_context), + SOUND_POOL_ERROR_INVALID_OPERATION, "Can't set current context."); + + alSourcei(stream->al_source, AL_LOOP_COUNT, (ALint)loop); + stream->loop = loop; + + SP_DEBUG_FLEAVE(); + return SOUND_POOL_ERROR_NONE; +} + +sound_pool_error_e _sound_stream_get_loop(sound_stream_t *stream, unsigned *loop) +{ + SP_DEBUF_FENTER(); + SP_INST_CHECK(stream, SOUND_POOL_ERROR_INVALID_PARAMETER); + SP_INST_CHECK(loop, SOUND_POOL_ERROR_INVALID_PARAMETER); + + *loop = stream->loop; + + SP_DEBUG_FLEAVE(); + return SOUND_POOL_ERROR_NONE; +} + +sound_pool_error_e _sound_stream_set_volume(sound_stream_t *stream, float volume) +{ + SP_DEBUF_FENTER(); + SP_INST_CHECK(stream, SOUND_POOL_ERROR_INVALID_PARAMETER); + SP_RETVM_IF((volume < 0.0f || volume > 1.0f), + SOUND_POOL_ERROR_INVALID_PARAMETER, + "Volume for sound stream should be in range 0.0..1.0."); + sound_pool_t *pool = stream->parent_source->parent_pool; + SP_INST_CHECK(pool, SOUND_POOL_ERROR_INVALID_PARAMETER); + SP_RETVM_IF(!alcMakeContextCurrent(pool->al_context), + SOUND_POOL_ERROR_INVALID_OPERATION, "Can't set current context."); + + alSourcef(stream->al_source, AL_GAIN, volume); + + stream->volume = volume; + + SP_DEBUG_FLEAVE(); + return SOUND_POOL_ERROR_NONE; +} + +sound_pool_error_e _sound_stream_get_volume(sound_stream_t *stream, float *volume) +{ + SP_DEBUF_FENTER(); + SP_INST_CHECK(stream, SOUND_POOL_ERROR_INVALID_PARAMETER); + SP_INST_CHECK(volume, SOUND_POOL_ERROR_INVALID_PARAMETER); + + *volume = stream->volume; + + SP_DEBUG_FLEAVE(); + return SOUND_POOL_ERROR_NONE; +} + +sound_pool_error_e _sound_stream_get_state(sound_stream_t *stream, + sound_pool_stream_state_e *state) +{ + SP_DEBUF_FENTER(); + SP_INST_CHECK(stream, SOUND_POOL_ERROR_INVALID_PARAMETER); + SP_INST_CHECK(state, SOUND_POOL_ERROR_INVALID_PARAMETER); + + *state = stream->state; + + SP_DEBUG_FLEAVE(); + return SOUND_POOL_ERROR_NONE; +} + +sound_pool_error_e _sound_stream_set_priority(sound_stream_t *stream, unsigned rank) +{ + SP_DEBUF_FENTER(); + SP_INST_CHECK(stream, SOUND_POOL_ERROR_INVALID_PARAMETER); + SP_INST_CHECK(stream->parent_source->parent_pool, SOUND_POOL_ERROR_INVALID_PARAMETER); + SP_INST_CHECK(stream->parent_source->parent_pool->mgr_priority, SOUND_POOL_ERROR_INVALID_PARAMETER); + + stream->priority = rank; + if (stream->parent_source->parent_pool->state == SOUND_POOL_STATE_INACTIVE) { + if (stream->state == SOUND_POOL_STREAM_STATE_NONE) { + stream->state_previous = stream->state; + stream->state = SOUND_POOL_STREAM_STATE_SUSPENDED; + if (_stream_cb_manager_register_event( + stream->parent_source->parent_pool->cbmgr, stream) + != SOUND_POOL_ERROR_NONE) + SP_DEBUG( + "State changing event wasn't registered." "Callbacks" + " will be not called"); + } + SP_DEBUG("No need to update priority, Sound pool is INACTIVE."); + return SOUND_POOL_ERROR_NONE; + } + + _sound_stream_priority_update_playback(stream->parent_source->parent_pool->mgr_priority); + + SP_DEBUG_FLEAVE(); + return SOUND_POOL_ERROR_NONE; +} + +sound_pool_error_e _sound_stream_get_priority(sound_stream_t *stream, unsigned *rank) +{ + SP_DEBUF_FENTER(); + SP_INST_CHECK(stream, SOUND_POOL_ERROR_INVALID_PARAMETER); + SP_INST_CHECK(rank, SOUND_POOL_ERROR_INVALID_PARAMETER); + + *rank = stream->priority; + + SP_DEBUG_FLEAVE(); + return SOUND_POOL_ERROR_NONE; +} + +sound_pool_error_e _sound_stream_set_callback(sound_stream_t *stream, + sound_pool_stream_state_change_cb callback, void *data) +{ + SP_DEBUF_FENTER(); + SP_INST_CHECK(stream, SOUND_POOL_ERROR_INVALID_PARAMETER); + SP_INST_CHECK(callback, SOUND_POOL_ERROR_INVALID_PARAMETER); + + stream->state_cb_info.callback = callback; + stream->state_cb_info.user_data = data; + + SP_DEBUG_FLEAVE(); + return SOUND_POOL_ERROR_NONE; +} + +sound_pool_error_e _sound_stream_unset_callback(sound_stream_t *stream) +{ + SP_DEBUF_FENTER(); + SP_INST_CHECK(stream, SOUND_POOL_ERROR_INVALID_PARAMETER); + + stream->state_cb_info.callback = NULL; + stream->state_cb_info.user_data = NULL; + + SP_DEBUG_FLEAVE(); + return SOUND_POOL_ERROR_NONE; +} + +sound_pool_error_e _sound_pool_get_stream_by_id(sound_pool_t *pool, unsigned id, + sound_stream_t **stream) +{ + SP_DEBUF_FENTER(); + SP_INST_CHECK(pool, SOUND_POOL_ERROR_INVALID_PARAMETER); + SP_INST_CHECK(stream, SOUND_POOL_ERROR_INVALID_PARAMETER); + + SP_RETVM_IF(!pool->streams, SOUND_POOL_ERROR_INVALID_OPERATION, "Corrupted " + "sound pool. Sources hash table is NULL"); + + *stream = (sound_stream_t *)g_hash_table_lookup(pool->streams, &id); + SP_RETVM_IF(!(*stream), SOUND_POOL_ERROR_KEY_NOT_AVAILABLE, "Tag doesn't " + "exist in sources hash table"); + + SP_DEBUG_FLEAVE(); + return SOUND_POOL_ERROR_NONE; +} diff --git a/src/stream_cb_manager.c b/src/stream_cb_manager.c new file mode 100644 index 0000000..9e08c2b --- /dev/null +++ b/src/stream_cb_manager.c @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file stream_cb_manager.c + * @brief This file include implementation of protected API for the stream + * callback manager as part of SoundPool. + */ + +#include "internal/stream_cb_manager.h" + +static gpointer __sound_pool_callback_isolator(gpointer user_data); +static void __thread_cancel_cleanup(); +static void __queue_destroy_item(gpointer data); + +static void __queue_destroy_item(gpointer data) +{ + SP_DEBUF_FENTER(); + + SP_SAFE_GFREE(data); + + SP_DEBUG_FLEAVE(); +} + +static void __thread_cancel_cleanup(void *user_data) +{ + SP_DEBUF_FENTER(); + + stream_cb_manager_t *cbmgr = (stream_cb_manager_t *)user_data; + g_async_queue_unref(cbmgr->isolator_callback_queue); + pthread_mutex_destroy(&cbmgr->isolator_data_mutex); + pthread_cond_destroy(&cbmgr->isolator_data_cond); + + SP_SAFE_GFREE(cbmgr); + + SP_DEBUG_FLEAVE(); +} + +static gpointer __sound_pool_callback_isolator(gpointer user_data) +{ + SP_DEBUF_FENTER(); + int err = pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); + if (err != 0) + SP_INFO("Can't setup cancel type for isolation thread with error [%d].", err); + err = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + if (err != 0) + SP_INFO("Can't setup cancel state for isolation thread with error [%d].", err); + SP_RETVM_IF(!user_data, NULL, "User data is NULL. Terminate callback thread"); + + stream_cb_manager_t *cbmgr = (stream_cb_manager_t *)user_data; + pthread_cleanup_push(__thread_cancel_cleanup, user_data); + pthread_mutex_lock(&cbmgr->isolator_data_mutex); + gboolean runing = cbmgr->isolator_loop_run; + pthread_mutex_unlock(&cbmgr->isolator_data_mutex); + + while (runing) { + pthread_mutex_lock(&cbmgr->isolator_data_mutex); + while (!cbmgr->isolator_state_changed) + pthread_cond_wait(&cbmgr->isolator_data_cond, &cbmgr->isolator_data_mutex); + pthread_mutex_unlock(&cbmgr->isolator_data_mutex); + + /* Iterate streams have been pushed to the queue and call for each + state changed callback for each stream */ + stream_cb_manager_event_data_t *event_data = NULL; + while ((event_data = (stream_cb_manager_event_data_t *) + g_async_queue_try_pop(cbmgr->isolator_callback_queue))) { + + sound_pool_t* _pool = cbmgr->pool; + if (NULL != event_data->stream->state_cb_info.callback) { + event_data->stream->state_cb_info.callback(_pool, + event_data->stream->parent_source->tag_name, + event_data->stream->id, event_data->state_previous, + event_data->state, + event_data->stream->state_cb_info.user_data); + } + /* If SoundPool final state is FINISHED or STOPPED, then release item */ + if (event_data->state == SOUND_POOL_STREAM_STATE_FINISHED || + event_data->state == SOUND_POOL_STREAM_STATE_STOPPED) + _sound_stream_destroy(event_data->stream); + SP_SAFE_GFREE(event_data); + } + + pthread_mutex_lock(&cbmgr->isolator_data_mutex); + cbmgr->isolator_state_changed = FALSE; + pthread_mutex_unlock(&cbmgr->isolator_data_mutex); + + pthread_mutex_lock(&cbmgr->isolator_data_mutex); + runing = cbmgr->isolator_loop_run; + pthread_mutex_unlock(&cbmgr->isolator_data_mutex); + } + + pthread_cleanup_pop(0); + SP_DEBUG_FLEAVE(); + return NULL; +} + +sound_pool_error_e _stream_cb_manager_create(sound_pool_t *pool, + stream_cb_manager_t **cbmgr) +{ + SP_DEBUF_FENTER(); + SP_RETVM_IF(!pool, SOUND_POOL_ERROR_INVALID_PARAMETER, "Stream callback " + "manager can't be created for NULL sound pool"); + SP_RETVM_IF(!cbmgr, SOUND_POOL_ERROR_INVALID_PARAMETER, "Can't create " + "stream state changing callback manager. Manager pointer is NULL"); + + stream_cb_manager_t *_cbmgr = NULL; + SP_RETVM_IF(!(_cbmgr = g_try_malloc0(sizeof(*_cbmgr))), + SOUND_POOL_ERROR_OUT_OF_MEMORY, + "Memory alloc failure. Can't create stream callback manager"); + + _cbmgr->isolator_callback_queue = g_async_queue_new_full(__queue_destroy_item); + + int err = pthread_mutex_init(&_cbmgr->isolator_data_mutex, NULL); + SP_RETVM_IF(err != 0, SOUND_POOL_ERROR_INVALID_PARAMETER, + "Error while initialize mutex for isolation thread with error[%d]", err); + + _cbmgr->isolator_state_changed = FALSE; + _cbmgr->isolator_loop_run = TRUE; + + err = pthread_cond_init(&_cbmgr->isolator_data_cond, NULL); + SP_RETVM_IF(err != 0, SOUND_POOL_ERROR_INVALID_PARAMETER, + "Error while initialize condition for isolation thread with error[%d]", err); + + err = pthread_create(&_cbmgr->isolator_thread, NULL, &__sound_pool_callback_isolator, (void*)_cbmgr); + SP_RETVM_IF(err != 0, SOUND_POOL_ERROR_OUT_OF_MEMORY, + "Error while thread creating for isolation thread with error[%d]", err); + + _cbmgr->pool = pool; + pool->cbmgr = _cbmgr; + *cbmgr = _cbmgr; + + SP_DEBUG_FLEAVE(); + return SOUND_POOL_ERROR_NONE; +} + +sound_pool_error_e _stream_cb_manager_destroy(stream_cb_manager_t *cbmgr) +{ + SP_DEBUF_FENTER(); + SP_RETVM_IF(!cbmgr, SOUND_POOL_ERROR_INVALID_PARAMETER, "Can't handle callback " + "manager, it is NULL."); + + + pthread_t thread = cbmgr->isolator_thread; + + int err = pthread_kill(thread, 0); + if (0 == err) { + err = pthread_cancel(thread); + if (0 != err) { + SP_ERROR("Error while cancelling of isolation thread[%d].", err); + g_async_queue_unref(cbmgr->isolator_callback_queue); + pthread_mutex_destroy(&cbmgr->isolator_data_mutex); + pthread_cond_destroy(&cbmgr->isolator_data_cond); + SP_SAFE_GFREE(cbmgr); + SP_DEBUG_FLEAVE(); + return SOUND_POOL_ERROR_INVALID_OPERATION; + } + } else { + SP_ERROR("Invalid isolation thread[%d].", err); + g_async_queue_unref(cbmgr->isolator_callback_queue); + pthread_mutex_destroy(&cbmgr->isolator_data_mutex); + pthread_cond_destroy(&cbmgr->isolator_data_cond); + SP_SAFE_GFREE(cbmgr); + SP_DEBUG_FLEAVE(); + return SOUND_POOL_ERROR_INVALID_OPERATION; + } + + void *ret; + err = pthread_join(thread, &ret); + if (0 != err) + SP_ERROR("Error while joining of isolation thread[%d].", err); + if (ret == PTHREAD_CANCELED) + SP_INFO("Isolation thread canceled."); + else + SP_INFO("Routine joining of isolation thread."); + + SP_DEBUG_FLEAVE(); + return SOUND_POOL_ERROR_NONE; +} + +sound_pool_error_e _stream_cb_manager_register_event(stream_cb_manager_t *cbmgr, + sound_stream_t *stream) +{ + SP_DEBUF_FENTER(); + SP_RETVM_IF(!cbmgr, SOUND_POOL_ERROR_INVALID_PARAMETER, + "Can't handle callback manager, it is NULL."); + SP_RETVM_IF(!stream, SOUND_POOL_ERROR_INVALID_PARAMETER, + "Can't handle stream in callback manager, it is NULL."); + + stream_cb_manager_event_data_t *event_data = NULL; + SP_RETVM_IF(!(event_data = g_try_malloc0(sizeof(stream_cb_manager_event_data_t))), + SOUND_POOL_ERROR_OUT_OF_MEMORY, + "Memory alloc failure. Can't create stream callback event data structure."); + event_data->stream = stream; + event_data->state = stream->state; + event_data->state_previous = stream->state_previous; + + g_async_queue_push(cbmgr->isolator_callback_queue, (gpointer)(event_data)); + pthread_mutex_lock(&cbmgr->isolator_data_mutex); + cbmgr->isolator_state_changed = TRUE; + pthread_cond_signal(&cbmgr->isolator_data_cond); + pthread_mutex_unlock(&cbmgr->isolator_data_mutex); + + SP_DEBUG_FLEAVE(); + return SOUND_POOL_ERROR_NONE; +}