X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=server%2Fplugin%2Fwav%2Fmm_sound_plugin_codec_wave.c;h=65ea45990fccb630aea21370290e81aeacc8aa0a;hb=e8f1d4370fd7feb2fa0f3103a205f451d37eafa5;hp=93175d8eaeaa96b99add91e801bbe83b8a6258ab;hpb=cd707bc05b72e1de5456c0491f8798e6e4af11cb;p=platform%2Fcore%2Fmultimedia%2Flibmm-sound.git diff --git a/server/plugin/wav/mm_sound_plugin_codec_wave.c b/server/plugin/wav/mm_sound_plugin_codec_wave.c index 93175d8..65ea459 100644 --- a/server/plugin/wav/mm_sound_plugin_codec_wave.c +++ b/server/plugin/wav/mm_sound_plugin_codec_wave.c @@ -22,567 +22,635 @@ #include #include #include +#include -#include -#include #include #include -#include #include -#include "../../include/mm_sound.h" -#include "../../include/mm_ipc.h" -#include "../../include/mm_sound_thread_pool.h" #include "../../include/mm_sound_plugin_codec.h" -#include "../../../include/mm_sound_private.h" - - -#define SAMPLE_COUNT 128 -#define WAV_FILE_SAMPLE_PLAY_DURATION 350 /*Unit: ms*/ -enum { - WAVE_CODE_UNKNOWN = 0, - WAVE_CODE_PCM = 1, - WAVE_CODE_ADPCM = 2, - WAVE_CODE_G711 = 3, - WAVE_CODE_IMA_ADPCM = 17, - WAVE_CODE_G723_ADPCM = 20, - WAVE_CODE_GSM = 49, - WAVE_CODE_G721_ADPCM = 64, - WAVE_CODE_MPEG = 80, -}; - -#define MAKE_FOURCC(a, b, c, d) ((a) | (b) << 8) | ((c) << 16 | ((d) << 24)) -#define RIFF_CHUNK_ID ((unsigned long) MAKE_FOURCC('R', 'I', 'F', 'F')) -#define RIFF_CHUNK_TYPE ((unsigned long) MAKE_FOURCC('W', 'A', 'V', 'E')) -#define FMT_CHUNK_ID ((unsigned long) MAKE_FOURCC('f', 'm', 't', ' ')) -#define DATA_CHUNK_ID ((unsigned long) MAKE_FOURCC('d', 'a', 't', 'a')) - -#define CODEC_WAVE_LOCK(LOCK) do { pthread_mutex_lock(LOCK); } while (0) -#define CODEC_WAVE_UNLOCK(LOCK) do { pthread_mutex_unlock(LOCK); } while (0) - -enum { - STATE_NONE = 0, - STATE_READY, - STATE_BEGIN, - STATE_PLAY, - STATE_STOP, -}; - -typedef struct -{ - char *ptr_current; - int size; - int transper_size; +#include "../../../include/mm_sound_common.h" +#include + +#include +#include +#include + +typedef struct { int handle; - int period; - int tone; - int keytone; int repeat_count; - int (*stop_cb)(int); + int (*stop_cb)(int, bool); + char filename[MM_SOUND_MAX_FILENAME]; int cb_param; - int state; - pthread_mutex_t mutex; - pthread_mutex_t *codec_wave_mutex; - int mode; - int volume_config; - int channels; - int samplerate; - int format; - int handle_route; - int priority; - MMSourceType *source; - char buffer[48000 / 1000 * SAMPLE_COUNT * 2 *2];//segmentation fault when above 22.05KHz stereo - int gain, out, in, option; + char stream_type[MAX_STREAM_TYPE_LEN]; + int stream_index; + int client_pid; + + pa_threaded_mainloop *m; + pa_context *c; + pa_stream *s; + pa_sample_spec spec; + SNDFILE *sf; + SF_INFO si; + + size_t written; } wave_info_t; -static void _runing(void *param); +static int _sound_prepare(wave_info_t *h) +{ + memset(&h->si, 0, sizeof(SF_INFO)); + + h->sf = sf_open(h->filename, SFM_READ, &h->si); + if (!h->sf) { + debug_error("sf_open error. path(%s), error(%d,%s)", h->filename, sf_error(h->sf), sf_strerror(h->sf)); + return (sf_error(h->sf) == SF_ERR_SYSTEM) ? MM_ERROR_SOUND_INTERNAL : MM_ERROR_SOUND_UNSUPPORTED_MEDIA_TYPE; + } + + sf_command(h->sf, SFC_SET_SCALE_FLOAT_INT_READ, NULL, SF_TRUE); -static int (*g_thread_pool_func)(void*, void (*)(void*)) = NULL; + h->spec.rate = h->si.samplerate; + h->spec.channels = h->si.channels; + h->spec.format = PA_SAMPLE_S16LE; -int MMSoundPlugCodecWaveSetThreadPool(int (*func)(void*, void (*)(void*))) + debug_msg("SF_INFO : frames = %"PRId64", samplerate = %d, channels = %d, format = 0x%X, sections = %d, seekable = %d", + h->si.frames, h->si.samplerate, h->si.channels, h->si.format, h->si.sections, h->si.seekable); + + return 0; +} + +static int _sound_rewind(wave_info_t *h) { - debug_enter("(func : %p)\n", func); - g_thread_pool_func = func; - debug_leave("\n"); - return MM_ERROR_NONE; + return (sf_seek(h->sf, 0, SEEK_SET) != -1) ? 0 : -1; } -int* MMSoundPlugCodecWaveGetSupportTypes(void) +static int _sound_is_rewind_needed(wave_info_t *h) { - debug_enter("\n"); - static int suported[2] = {MM_SOUND_SUPPORTED_CODEC_WAVE, 0}; - debug_leave("\n"); - return suported; + return (h->repeat_count == -1 || h->repeat_count > 1); } -int MMSoundPlugCodecWaveParse(MMSourceType *source, mmsound_codec_info_t *info) +static void _sound_unprepare(wave_info_t *h) { - struct __riff_chunk - { - long chunkid; - long chunksize; - long rifftype; - }; - - struct __wave_chunk - { - long chunkid; - long chunksize; - unsigned short compression; - unsigned short channels; - unsigned long samplerate; - unsigned long avgbytepersec; - unsigned short blockkalign; - unsigned short bitspersample; - }; - - struct __data_chunk - { - long chunkid; - long chunkSize; - }; - - struct __riff_chunk *priff = NULL; - struct __wave_chunk *pwav = NULL; - struct __data_chunk *pdata = NULL; -// struct __fmt_chunk *pfmt = NULL; - - int datalen = -1; - char *data = NULL; - unsigned int tSize; - - debug_enter("\n"); - - data = MMSourceGetPtr(source); - debug_msg("[CODEC WAV] source ptr :[%p]\n", data); - - datalen = MMSourceGetCurSize(source); - debug_msg("[CODEC WAV] source size :[0x%08X]\n", datalen); - - priff = (struct __riff_chunk *) data; - - /* Must be checked, Just for wav or not */ - if (priff->chunkid != RIFF_CHUNK_ID ||priff->rifftype != RIFF_CHUNK_TYPE) - return MM_ERROR_SOUND_UNSUPPORTED_MEDIA_TYPE; - - if(priff->chunksize != datalen -8) - priff->chunksize = (datalen-8); - - if (priff->chunkid != RIFF_CHUNK_ID ||priff->chunksize != datalen -8 ||priff->rifftype != RIFF_CHUNK_TYPE) { - debug_msg("[CODEC WAV] This contents is not RIFF file\n"); - debug_msg("[CODEC WAV] cunkid : %ld, chunksize : %ld, rifftype : 0x%lx\n", priff->chunkid, priff->chunksize, priff->rifftype); - //debug_msg("[CODEC WAV] cunkid : %ld, chunksize : %d, rifftype : 0x%lx\n", RIFF_CHUNK_ID, datalen-8, RIFF_CHUNK_TYPE); - return MM_ERROR_SOUND_UNSUPPORTED_MEDIA_TYPE; + if (h->sf) { + sf_close(h->sf); + h->sf = NULL; } +} - debug_msg("[CODEC WAV] cunkid : %ld, chunksize : %ld, rifftype : 0x%lx\n", priff->chunkid, priff->chunksize, priff->rifftype); - //debug_msg("[CODEC WAV] cunkid : %ld, chunksize : %d, rifftype : 0x%lx\n", RIFF_CHUNK_ID, datalen-8, RIFF_CHUNK_TYPE); - - tSize = sizeof(struct __riff_chunk); - pdata = (struct __data_chunk*)(data+tSize); +/* Context Callbacks */ +static void _pa_context_state_callback(pa_context *c, void *userdata) +{ + pa_threaded_mainloop *m = (pa_threaded_mainloop *)userdata; + assert(c); + + switch (pa_context_get_state(c)) { + case PA_CONTEXT_CONNECTING: + case PA_CONTEXT_AUTHORIZING: + case PA_CONTEXT_SETTING_NAME: + debug_log("context(%p), state(%d)", c, pa_context_get_state(c)); + break; - while (pdata->chunkid != FMT_CHUNK_ID && tSize < datalen) { - tSize += (pdata->chunkSize+8); + case PA_CONTEXT_READY: + case PA_CONTEXT_TERMINATED: + case PA_CONTEXT_FAILED: + debug_warning("context(%p), state(%d)", c, pa_context_get_state(c)); + pa_threaded_mainloop_signal(m, 0); + break; - if (tSize >= datalen) { - debug_warning("[CODEC WAV] Parsing finished : unable to find the Wave Format chunk\n"); - return MM_ERROR_SOUND_UNSUPPORTED_MEDIA_TYPE; - } else { - pdata = (struct __data_chunk*)(data+tSize); - } - } - pwav = (struct __wave_chunk*)(data+tSize); - - if (pwav->chunkid != FMT_CHUNK_ID || - pwav->compression != WAVE_CODE_PCM || /* Only supported PCM */ - pwav->avgbytepersec != pwav->samplerate * pwav->blockkalign || - pwav->blockkalign != (pwav->bitspersample >> 3)*pwav->channels) { - debug_msg("[CODEC WAV] This contents is not supported wave file\n"); - debug_msg("[CODEC WAV] chunkid : 0x%lx, comp : 0x%x, av byte/sec : %lu, blockalign : %d\n", pwav->chunkid, pwav->compression, pwav->avgbytepersec, pwav->blockkalign); - return MM_ERROR_SOUND_UNSUPPORTED_MEDIA_TYPE; + default: + break; } +} - /* Only One data chunk support */ - - tSize += (pwav->chunksize+8); - pdata = (struct __data_chunk *)(data+tSize); +static void *_cleanup_thread_func(void *userdata) +{ + pa_threaded_mainloop *m = (pa_threaded_mainloop *)userdata; - while (pdata->chunkid != DATA_CHUNK_ID && tSize < datalen) { - tSize += (pdata->chunkSize+8); - if (tSize >= datalen) { - debug_warning("[CODEC WAV] Parsing finished : unable to find the data chunk\n"); - return MM_ERROR_SOUND_UNSUPPORTED_MEDIA_TYPE; - } else { - pdata = (struct __data_chunk*)(data+tSize); - } + if (m) { + debug_error("now stop and free threaded_mainloop(%p) here", m); + pa_threaded_mainloop_stop(m); + pa_threaded_mainloop_free(m); + } else { + debug_warning("thread mainloop is already null"); } - info->codec = MM_SOUND_SUPPORTED_CODEC_WAVE; - info->channels = pwav->channels; - info->format = pwav->bitspersample; - info->samplerate = pwav->samplerate; - info->doffset = (tSize+8); - info->size = pdata->chunkSize; - info->duration = ((info->size)*1000)/pwav->avgbytepersec; - debug_msg("info->size:%d info->duration: %d\n", info->size, info->duration); - - debug_leave("\n"); - return MM_ERROR_NONE; + pthread_exit(NULL); } - - -int MMSoundPlugCodecWaveCreate(mmsound_codec_param_t *param, mmsound_codec_info_t *info, MMHandleType *handle) +static void _cleanup_threaded_mainloop(pa_threaded_mainloop *m) { - wave_info_t* p = NULL; - MMSourceType *source; - static int keytone_period = 0; - -#ifdef DEBUG_DETAIL - debug_enter("\n"); -#endif - debug_msg("period[%d] type[%s] ch[%d] format[%d] rate[%d] doffset[%d] priority[%d] repeat[%d] volume[%d] callback[%p] keytone[%08x] route[%d]\n", - keytone_period, (info->codec == MM_SOUND_SUPPORTED_CODEC_WAVE) ? "Wave" : "Unknown", - info->channels, info->format, info->samplerate, info->doffset, param->priority, param->repeat_count, - param->volume, param->stop_cb, param->keytone, param->handle_route); - - source = param->source; + int ret = 0; + pthread_t thread_id; + pthread_attr_t attr; - if (g_thread_pool_func == NULL) { - debug_error("[CODEC WAV] Need thread pool!\n"); - return MM_ERROR_SOUND_INTERNAL; + ret = pthread_attr_init(&attr); + if (ret != 0) { + debug_error("failed to init pthread attr!!! errno=%d", ret); + return; } - p = (wave_info_t *) malloc(sizeof(wave_info_t)); - - if (p == NULL) { - debug_error("[CODEC WAV] memory allocation failed\n"); - return MM_ERROR_OUT_OF_MEMORY; + ret = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + if (ret != 0) { + debug_error("failed to set detach state!!! errno=%d", ret); + goto finish; } - memset(p, 0, sizeof(wave_info_t)); - p->handle = 0; + ret = pthread_create(&thread_id, &attr, _cleanup_thread_func, m); + if (ret != 0) + debug_error("failed to create _cleanup_thread_func!!! errno=%d", ret); - p->ptr_current = MMSourceGetPtr(source) + info->doffset; +finish: + ret = pthread_attr_destroy(&attr); + if (ret != 0) + debug_error("failed to destroy pthread attr!!! errno=%d", ret); + + return; +} - p->size = info->size; - p->transper_size = info->samplerate / 1000 * SAMPLE_COUNT * (info->format >> 3) * info->channels; - p->tone = param->tone; - p->repeat_count = param ->repeat_count; - p->stop_cb = param->stop_cb; - p->cb_param = param->param; - p->source = source; - p->codec_wave_mutex = param->codec_wave_mutex; - // pthread_mutex_init(&p->mutex, NULL); +static void _pa_context_drain_complete_callback(pa_context *c, void *userdata) +{ + debug_msg("context drain completed, cleanup context and mainloop"); - debug_msg("[CODEC WAV] transper_size : %d\n", p->transper_size); - debug_msg("[CODEC WAV] size : %d\n", p->size); + pa_context_disconnect(c); + pa_context_unref(c); - if(info->duration < WAV_FILE_SAMPLE_PLAY_DURATION) { - p->mode = HANDLE_MODE_OUTPUT_LOW_LATENCY; - } else { - p->mode = HANDLE_MODE_OUTPUT; - } + _cleanup_threaded_mainloop((pa_threaded_mainloop *)userdata); +} - p->priority = param->priority; - p->volume_config = param->volume_config; - p->channels = info->channels; - p->samplerate = info->samplerate; +/* Stream Callbacks */ +static void _pa_stream_state_callback(pa_stream *s, void *userdata) +{ + pa_threaded_mainloop *m = (pa_threaded_mainloop *)userdata; - if(param->handle_route == MM_SOUND_HANDLE_ROUTE_USING_CURRENT) /* normal, solo */ - p->handle_route = HANDLE_ROUTE_POLICY_OUT_AUTO; - else /* loud solo */ - p->handle_route = HANDLE_ROUTE_POLICY_OUT_HANDSET; + assert(s); - switch(info->format) - { - case 8: - p->format = PA_SAMPLE_U8; + switch (pa_stream_get_state(s)) { + case PA_STREAM_CREATING: + debug_log("stream(%p), state(%d)", s, pa_stream_get_state(s)); break; - case 16: - p->format = PA_SAMPLE_S16LE; + case PA_STREAM_READY: + case PA_STREAM_FAILED: + case PA_STREAM_TERMINATED: + debug_warning("stream(%p), state(%d)", s, pa_stream_get_state(s)); + pa_threaded_mainloop_signal(m, 0); break; default: - p->format = PA_SAMPLE_S16LE; break; } +} - debug_msg("[CODEC WAV] PARAM mode : [%d]\n", p->mode); - debug_msg("[CODEC WAV] PARAM channels : [%d]\n", p->channels); - debug_msg("[CODEC WAV] PARAM samplerate : [%d]\n", p->samplerate); - debug_msg("[CODEC WAV] PARAM format : [%d]\n", p->format); - debug_msg("[CODEC WAV] PARAM volume type : [%x]\n", p->volume_config); +static void _pa_stream_drain_complete_callback(pa_stream *s, int success, void *userdata) +{ + pa_operation *o = NULL; + wave_info_t *h = (wave_info_t *)userdata; - p->state = STATE_READY; + debug_msg("stream(%p) : drain completed(%d)", s, success); - g_thread_pool_func(p, _runing); - debug_msg("[CODEC WAV] Thread pool start\n"); - *handle = (MMHandleType)p; + if (!success) { + debug_error("stream(%p) : drain failed(%d)", s, success); + //pa_threaded_mainloop_signal(h->m, 0); + } -#ifdef DEBUG_DETAIL - debug_leave("\n"); -#endif + pa_stream_disconnect(h->s); + pa_stream_unref(h->s); + h->s = NULL; - return MM_ERROR_NONE; -} + if (!(o = pa_context_drain(h->c, _pa_context_drain_complete_callback, h->m))) { + debug_error("context(%p) failed to drain context!", h->c); + pa_context_disconnect(h->c); + pa_context_unref(h->c); + h->c = NULL; + } else { + pa_operation_unref(o); + } + debug_msg("Invoke stop callback(%p, %d) of mgr_codec", h->stop_cb, h->cb_param); + if (h->stop_cb) + h->stop_cb(h->cb_param, false); +} -int MMSoundPlugCodecWavePlay(MMHandleType handle) +static void _pa_stream_moved_callback(pa_stream *s, void *userdata) { - wave_info_t *p = (wave_info_t *) handle; + assert(s); + debug_msg("stream(%p)", s); +} - debug_enter("(handle %x)\n", handle); +static void _pa_stream_underflow_callback(pa_stream *s, void *userdata) +{ + wave_info_t *h = (wave_info_t *)userdata; + assert(s); - if (p->size <= 0) { - debug_error("[CODEC WAV] end of file\n"); - return MM_ERROR_END_OF_FILE; - } - debug_msg("[CODEC WAV] send start signal\n"); - p->state = STATE_BEGIN; + debug_msg("stream(%p) : file(%s)", s, h->filename); +} - debug_leave("\n"); +static void _pa_stream_buffer_attr_callback(pa_stream *s, void *userdata) +{ + assert(s); + debug_msg("stream(%p)", s); +} - return MM_ERROR_NONE; +static void _pa_stream_started_callback(pa_stream *s, void *userdata) +{ + assert(s); + debug_msg("stream(%p)", s); } -static void _runing(void *param) +static void _pa_stream_write_callback(pa_stream *s, size_t length, void *userdata) { - wave_info_t *p = (wave_info_t*) param; - int nread = 0; - char *org_cur = NULL; - int org_size = 0; - char *dummy = NULL; - int ret; - unsigned int volume_value = 0; - int size; - - pa_sample_spec ss; - mm_sound_handle_route_info route_info; - - mm_sound_device_in device_in_before = MM_SOUND_DEVICE_IN_NONE; - mm_sound_device_in device_in_after = MM_SOUND_DEVICE_IN_NONE; - mm_sound_device_out device_out_before = MM_SOUND_DEVICE_OUT_NONE; - mm_sound_device_out device_out_after = MM_SOUND_DEVICE_OUT_NONE; - - if (p == NULL) { - debug_error("[CODEC WAV] param is null\n"); + sf_count_t bytes = 0; + void *data = NULL; + size_t data_length = 0; + size_t frame_size; + pa_operation *o = NULL; + wave_info_t *h = (wave_info_t *)userdata; + + if (!s || length <= 0) { + debug_error("stream(%p) : length(%zu)", s, length); return; } - debug_enter("[CODEC WAV] (Slot ID %d)\n", p->cb_param); - CODEC_WAVE_LOCK(p->codec_wave_mutex); - - /* - * set path here - */ - switch(p->handle_route) - { - case MM_SOUND_HANDLE_ROUTE_SPEAKER: - case MM_SOUND_HANDLE_ROUTE_SPEAKER_NO_RESTORE: - debug_msg("[CODEC WAV] Save backup path\n"); - __mm_sound_lock(); - - /* get route info from pulseaudio */ - /* MMSoundMgrPulseGetActiveDevice(&p->in, &p->out); */ - mm_sound_get_audio_path(&device_in_before, &device_out_before); - /* if current out is not speaker, then force set path to speaker */ - if (device_out_before != MM_SOUND_DEVICE_OUT_SPEAKER) { - debug_msg("[CODEC WAV] current out is not SPEAKER, set path to SPEAKER now!!!\n"); - mm_sound_pa_corkall(1); - mm_sound_set_sound_path_for_active_device(MM_SOUND_DEVICE_OUT_SPEAKER, MM_SOUND_DEVICE_IN_NONE); - } - - /* set route info */ - route_info.device_in = MM_SOUND_DEVICE_IN_NONE; - route_info.device_out = MM_SOUND_DEVICE_OUT_SPEAKER; - route_info.policy = HANDLE_ROUTE_POLICY_OUT_HANDSET; - /* MMSoundMgrPulseSetActiveDevice(route_info_device_in, route_info.device_out); */ - break; - case MM_SOUND_HANDLE_ROUTE_USING_CURRENT: - default: - break; - } + frame_size = pa_frame_size(&h->spec); + data_length = length; - ss.rate = p->samplerate; - ss.channels = p->channels; - ss.format = p->format; - p->period = pa_sample_size(&ss) * ((ss.rate * 25) / 1000); - p->handle = mm_sound_pa_open(p->mode, &route_info, p->priority, p->volume_config, &ss, NULL, &size); - if(!p->handle) { - debug_critical("[CODEC WAV] Can not open audio handle\n"); - CODEC_WAVE_UNLOCK(p->codec_wave_mutex); - if (p->handle_route == MM_SOUND_HANDLE_ROUTE_SPEAKER || p->handle_route == MM_SOUND_HANDLE_ROUTE_SPEAKER_NO_RESTORE) { - __mm_sound_unlock(); - } + if (frame_size == 0) { + debug_error("stream(%p) : frame size can't be 0", s); return; } - - if (p->handle == 0) { - debug_critical("[CODEC WAV] audio_handle is not created !! \n"); - CODEC_WAVE_UNLOCK(p->codec_wave_mutex); - free(p); - p = NULL; + + if (pa_stream_begin_write(s, &data, &data_length) < 0) { + debug_error("stream(%p) : failed to pa_stream_begin_write()", s); return; } - /* Set the thread schedule */ - org_cur = p->ptr_current; - org_size = p->size; + if ((bytes = sf_readf_short(h->sf, data, (sf_count_t)(data_length / frame_size))) > 0) + bytes *= (sf_count_t)frame_size; + + debug_msg("stream(%p) : === %"PRId64" / %zu / %zu ===", s, bytes, data_length, h->written); + + if (bytes > 0) + pa_stream_write(s, data, (size_t)bytes, NULL, 0, PA_SEEK_RELATIVE); + else + pa_stream_cancel_write(s); + + h->written += bytes; + + /* If No more data, drain stream */ + if (bytes < (sf_count_t)data_length) { + debug_msg("stream(%p) : End Of Stream", s); + + /* Handle loop */ + if (_sound_is_rewind_needed(h)) { + debug_msg("stream(%p) : repeat count = %d", s, h->repeat_count); + /* do not decrease it in case of -1 for infinite play */ + if (h->repeat_count != -1) + h->repeat_count--; - dummy = malloc(p->period); - if(!dummy) { - debug_error("[CODEC WAV] not enough memory"); - CODEC_WAVE_UNLOCK(p->codec_wave_mutex); - if (p->handle_route == MM_SOUND_HANDLE_ROUTE_SPEAKER || p->handle_route == MM_SOUND_HANDLE_ROUTE_SPEAKER_NO_RESTORE) { - __mm_sound_unlock(); + if (_sound_rewind(h) == 0) + return; + + debug_error("stream(%p) : REWIND failed....", s); + /* can't loop anymore, fallback and do drain */ } - return; + + /* EOS callback will be notified after drain is completed */ + pa_stream_set_write_callback(s, NULL, NULL); + o = pa_stream_drain(s, _pa_stream_drain_complete_callback, h); + if (o) + pa_operation_unref(o); + else + debug_error("stream(%p) : failed to drain", s); + debug_msg("stream(%p) : reset write callback and drain requested", s); } - memset(dummy, 0, p->period); - p->transper_size = p->period; - /* stop_size = org_size > p->period ? org_size : p->period; */ +} - debug_msg("[CODEC WAV] Wait start signal\n"); +static int _pa_context_connect(wave_info_t *h) +{ + pa_threaded_mainloop *m = NULL; + pa_context *c = NULL; - while(p->state == STATE_READY) { - usleep(4); + /* Mainloop */ + if (!(m = pa_threaded_mainloop_new())) { + debug_error("mainloop create failed"); + return -1; } - debug_msg("[CODEC WAV] Recv start signal\n"); - debug_msg("[CODEC WAV] repeat : %d\n", p->repeat_count); - debug_msg("[CODEC WAV] transper_size : %d\n", p->transper_size); + /* Context */ + if (!(c = pa_context_new(pa_threaded_mainloop_get_api(m), NULL))) { + debug_error("context create failed"); + goto error_context_new; + } - if (p->state != STATE_STOP) { - debug_msg("[CODEC WAV] Play start\n"); - p->state = STATE_PLAY; - } else { - debug_warning ("[CODEC WAV] state is already STATE_STOP\n"); + pa_context_set_state_callback(c, _pa_context_state_callback, m); + + pa_threaded_mainloop_lock(m); + + if (pa_threaded_mainloop_start(m) < 0) { + debug_error("mainloop start failed"); + goto error; } - while (((p->repeat_count == -1)?(1):(p->repeat_count--)) && p->state == STATE_PLAY) { - while (p->state == STATE_PLAY && p->size > 0) { - if (p->size >= p->transper_size) { - nread = p->transper_size; - memcpy(p->buffer, p->ptr_current, nread); - mm_sound_pa_write(p->handle, p->buffer, nread); - p->ptr_current += nread; - p->size -= nread; - debug_msg("[CODEC WAV] Playing, nRead_data : %d Size : %d \n", nread, p->size); - } else { - /* Write remain size */ - nread = p->size; - memcpy(p->buffer, p->ptr_current, nread); - mm_sound_pa_write(p->handle, p->buffer, nread); - mm_sound_pa_write(p->handle, dummy, (p->transper_size-nread)); - p->ptr_current += nread; - p->size = 0; - } - } - p->ptr_current = org_cur; - p->size = org_size; + if (pa_context_connect(c, NULL, 0, NULL) < 0) { + debug_error("context connect failed"); + goto error; } - debug_msg("[CODEC WAV] End playing\n"); - p->state = STATE_STOP; + for (;;) { + pa_context_state_t state = pa_context_get_state(c); + if (state == PA_CONTEXT_READY) + break; - if (p->handle == 0) { - usleep(200000); - debug_warning("[CODEC WAV] audio already unrealize !!\n"); - } else { - /* usleep(75000); */ - if(MM_ERROR_NONE != mm_sound_pa_drain(p->handle)) - debug_error("mm_sound_pa_drain() failed\n"); - - if(MM_ERROR_NONE != mm_sound_pa_close(p->handle)) { - debug_error("[CODEC WAV] Can not close audio handle\n"); - } else { - p->handle = 0; - } - /* - * Restore path here - */ - if (p->handle_route == MM_SOUND_HANDLE_ROUTE_SPEAKER) { - /* If current path is not same as before playing sound, restore the sound path */ - if (device_out_before != MM_SOUND_DEVICE_OUT_SPEAKER) { - mm_sound_set_sound_path_for_active_device(device_out_before, device_in_before); - mm_sound_pa_corkall(0); - } + if (!PA_CONTEXT_IS_GOOD(state)) { + debug_error("Context error!!!! %d", pa_context_errno(c)); + goto error; } - if (p->handle_route == MM_SOUND_HANDLE_ROUTE_SPEAKER || p->handle_route == MM_SOUND_HANDLE_ROUTE_SPEAKER_NO_RESTORE) { - __mm_sound_unlock(); + + pa_threaded_mainloop_wait(m); + } + + h->m = m; + h->c = c; + + pa_threaded_mainloop_unlock(m); + + return 0; + +error: + pa_context_unref(c); + pa_threaded_mainloop_unlock(m); +error_context_new: + pa_threaded_mainloop_free(m); + + return -1; +} + +static int _pa_stream_connect(wave_info_t *h) +{ + int ret = PA_OK; + pa_stream *s = NULL; + pa_proplist *proplist = NULL; + pa_stream_state_t state; + + proplist = pa_proplist_new(); + if (!proplist) + return -1; + pa_proplist_sets(proplist, PA_PROP_MEDIA_ROLE, h->stream_type); + pa_proplist_setf(proplist, PA_PROP_APPLICATION_PROCESS_ID_ORIGIN, "%d", h->client_pid); + if (h->stream_index != -1) + pa_proplist_setf(proplist, PA_PROP_MEDIA_PARENT_ID, "%d", h->stream_index); + + pa_threaded_mainloop_lock(h->m); + + s = pa_stream_new_with_proplist(h->c, "wav-player", &h->spec, NULL, proplist); + pa_proplist_free(proplist); + if (!s) { + debug_error("pa_stream_new failed. file(%s)", h->filename); + goto error; + } + + pa_stream_set_state_callback(s, _pa_stream_state_callback, h->m); + pa_stream_set_write_callback(s, _pa_stream_write_callback, h); + pa_stream_set_moved_callback(s, _pa_stream_moved_callback, h); + pa_stream_set_underflow_callback(s, _pa_stream_underflow_callback, h); + pa_stream_set_buffer_attr_callback(s, _pa_stream_buffer_attr_callback, h); + pa_stream_set_started_callback(s, _pa_stream_started_callback, h); + + ret = pa_stream_connect_playback(s, NULL, NULL, + PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_ADJUST_LATENCY | PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_START_CORKED, + NULL, NULL); + if (ret < 0) { + debug_error("stream(%p) : failed to connect playback, ret(%d)", s, ret); + goto error; + } + for (;;) { + state = pa_stream_get_state(s); + + if (state == PA_STREAM_READY) + break; + + if (!PA_STREAM_IS_GOOD(state)) { + debug_error("stream(%p) : state(%d)", s, state); + goto error; } + + /* Wait until the stream is ready */ + pa_threaded_mainloop_wait(h->m); } - CODEC_WAVE_UNLOCK(p->codec_wave_mutex); - p->handle = 0; + h->s = s; + + pa_threaded_mainloop_unlock(h->m); + + return 0; + +error: + if (s) + pa_stream_unref(s); - p->state = STATE_NONE; + pa_threaded_mainloop_unlock(h->m); - free(dummy); dummy = NULL; - if (p->stop_cb) - { - debug_msg("[CODEC WAV] Play is finished, Now start callback\n"); - p->stop_cb(p->cb_param); + return -1; +} + +static void _pa_stream_uncork(wave_info_t *h) +{ + pa_operation *o = NULL; + + assert(h); + assert(h->m); + assert(h->s); + + pa_threaded_mainloop_lock(h->m); + + if ((o = pa_stream_cork(h->s, 0, NULL, NULL))) + pa_operation_unref(o); + else + debug_error("stream(%p) : uncork failed", h->s); + + pa_threaded_mainloop_unlock(h->m); +} + +static void _pa_stream_stop_disconnect(wave_info_t *h) +{ + assert(h); + assert(h->m); + + pa_threaded_mainloop_lock(h->m); + if (h->s) { + pa_stream_disconnect(h->s); + pa_stream_unref(h->s); + h->s = NULL; + } + if (h->c) { + pa_context_disconnect(h->c); + pa_context_unref(h->c); + h->c = NULL; } - debug_leave("\n"); + pa_threaded_mainloop_unlock(h->m); + + pa_threaded_mainloop_free(h->m); + h->m = NULL; } +static int * _mm_sound_plug_codec_wave_get_supported_types(void) +{ + static int suported[2] = { MM_SOUND_SUPPORTED_CODEC_WAVE, 0 }; + return suported; +} -int MMSoundPlugCodecWaveStop(MMHandleType handle) +static int _mm_sound_plug_codec_wave_parse(const char *filename, mmsound_codec_info_t *info) { - wave_info_t *p = (wave_info_t*) handle; + SNDFILE *sf = NULL; + SF_INFO si; + + if (!filename || !info) { + debug_error("filename(%p) or info(%p) is invalid...", filename, info); + return MM_ERROR_INVALID_ARGUMENT; + } + + /* FIXME : following sndfile code should be encapsulated */ + memset(&si, 0, sizeof(SF_INFO)); + sf = sf_open(filename, SFM_READ, &si); + if (!sf) { + debug_error("sf_open error. path(%s), error(%d, %s)", filename, sf_error(sf), sf_strerror(sf)); + if (sf_error(sf) == SF_ERR_SYSTEM) + return MM_ERROR_SOUND_INTERNAL; + else + return MM_ERROR_SOUND_UNSUPPORTED_MEDIA_TYPE; + } + + info->codec = MM_SOUND_SUPPORTED_CODEC_WAVE; + info->channels = si.channels; + info->samplerate = si.samplerate; + + debug_msg("filename[%s], frames[%"PRId64"], samplerate[%d], channels[%d], format[%x], sections[%d], seekable[%d]", + filename, si.frames, si.samplerate, si.channels, si.format, si.sections, si.seekable); + sf_close(sf); - if (!p) { - debug_error("The handle is null\n"); + return MM_ERROR_NONE; +} + +static int _mm_sound_plug_codec_wave_create(mmsound_codec_param_t *param, mmsound_codec_info_t *info, MMHandleType *handle) +{ + wave_info_t *h = NULL; + int ret = 0; + +#ifdef DEBUG_DETAIL + debug_enter(); +#endif + + h = (wave_info_t *)calloc(1, sizeof(wave_info_t)); + if (h == NULL) { + debug_error("memory allocation failed"); + return MM_ERROR_OUT_OF_MEMORY; + } + + h->handle = 0; + h->repeat_count = param->repeat_count; + h->stop_cb = param->stop_cb; + h->cb_param = param->param; + MMSOUND_STRNCPY(h->filename, param->pfilename, MM_SOUND_MAX_FILENAME); + h->client_pid = param->pid; + h->stream_index = param->stream_index; + MMSOUND_STRNCPY(h->stream_type, param->stream_type, MAX_STREAM_TYPE_LEN); + + ret = _sound_prepare(h); + if (ret < 0) { + debug_error("failed to prepare sound"); + goto error; + } + + ret = _pa_context_connect(h); + if (ret < 0) { + debug_error("failed to connect context..."); + goto error; + } + + ret = _pa_stream_connect(h); + if (ret < 0) { + debug_error("failed to connect stream..."); + goto error; + } + + *handle = (MMHandleType)h; + +#ifdef DEBUG_DETAIL + debug_leave("%p", h); +#endif + return MM_ERROR_NONE; + +error: + _sound_unprepare(h); + free(h); + return MM_ERROR_SOUND_INTERNAL; +} + + +static int _mm_sound_plug_codec_wave_play(MMHandleType handle) +{ + wave_info_t *h = (wave_info_t *)handle; + + debug_msg("Start handle(%p), stream(%p), written(%zu)", h, h->s, h->written); + _pa_stream_uncork(h); + + return MM_ERROR_NONE; +} + +static int _mm_sound_plug_codec_wave_stop(MMHandleType handle) +{ + wave_info_t *h = (wave_info_t *)handle; + if (!h) { + debug_error("The handle is null"); return MM_ERROR_SOUND_INTERNAL; } - debug_msg("[CODEC WAV] Current state is state %d\n", p->state); - debug_msg("[CODEC WAV] Handle 0x%08X stop requested\n", handle); - p->state = STATE_STOP; + debug_msg("Handle %p stop requested", h); + + _pa_stream_stop_disconnect(h); - return MM_ERROR_NONE; + if (h->stop_cb) + h->stop_cb(h->cb_param, true); + + return MM_ERROR_NONE; } -int MMSoundPlugCodecWaveDestroy(MMHandleType handle) +static int _mm_sound_plug_codec_wave_destroy(MMHandleType handle) { - wave_info_t *p = (wave_info_t*) handle; + wave_info_t *h = (wave_info_t *)handle; - if (!p) { - debug_warning("Can not destroy handle :: handle is invalid"); + if (!h) { + debug_error("Can not destroy handle :: handle is invalid"); return MM_ERROR_SOUND_INVALID_POINTER; } - if(p->source) { - mm_source_close(p->source); - free(p->source); p->source = NULL; - } + _sound_unprepare(h); - free(p); p = NULL; + free(h); + h = NULL; return MM_ERROR_NONE; } EXPORT_API -int MMSoundGetPluginType(void) +int MMSoundPlugCodecGetInterface(mmsound_codec_interface_t *intf) { - return MM_SOUND_PLUGIN_TYPE_CODEC; + assert(intf); + + intf->GetSupportTypes = _mm_sound_plug_codec_wave_get_supported_types; + intf->Parse = _mm_sound_plug_codec_wave_parse; + intf->Create = _mm_sound_plug_codec_wave_create; + intf->Play = _mm_sound_plug_codec_wave_play; + intf->Stop = _mm_sound_plug_codec_wave_stop; + intf->Destroy = _mm_sound_plug_codec_wave_destroy; + intf->SetThreadPool = NULL; + + return MM_ERROR_NONE; } EXPORT_API -int MMSoundPlugCodecGetInterface(mmsound_codec_interface_t *intf) +int MMSoundGetPluginType(void) { - intf->GetSupportTypes = MMSoundPlugCodecWaveGetSupportTypes; - intf->Parse = MMSoundPlugCodecWaveParse; - intf->Create = MMSoundPlugCodecWaveCreate; - intf->Destroy = MMSoundPlugCodecWaveDestroy; - intf->Play = MMSoundPlugCodecWavePlay; - intf->Stop = MMSoundPlugCodecWaveStop; - intf->SetThreadPool = MMSoundPlugCodecWaveSetThreadPool; - - return MM_ERROR_NONE; + return MM_SOUND_PLUGIN_TYPE_CODEC; }