#include <string.h>
#include <stdint.h>
-#include <semaphore.h>
#include <mm_error.h>
#include <mm_debug.h>
-#include <pthread.h>
#include <mm_sound_pa_client.h>
#include "../../include/mm_sound_plugin_codec.h"
#include "../../../include/mm_sound_common.h"
#include <unistd.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 ((uint32_t) MAKE_FOURCC('R', 'I', 'F', 'F'))
-#define RIFF_CHUNK_TYPE ((uint32_t) MAKE_FOURCC('W', 'A', 'V', 'E'))
-#define FMT_CHUNK_ID ((uint32_t) MAKE_FOURCC('f', 'm', 't', ' '))
-#define DATA_CHUNK_ID ((uint32_t) 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 <sndfile.h>
+#include <pthread.h>
+#include <pulse/pulseaudio.h>
+
+typedef struct {
int handle;
- int period;
- int tone;
- int keytone;
int repeat_count;
- int (*stop_cb)(int);
- int cb_param; // slotid
- 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[MM_SOUND_STREAM_TYPE_LEN];
+ int (*stop_cb)(int, bool);
+ char filename[MM_SOUND_MAX_FILENAME];
+ int cb_param;
+ 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);
+
+ h->spec.rate = h->si.samplerate;
+ h->spec.channels = h->si.channels;
+ h->spec.format = PA_SAMPLE_S16LE;
-static int (*g_thread_pool_func)(void*, void (*)(void*)) = NULL;
+ 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);
-int MMSoundPlugCodecWaveSetThreadPool(int (*func)(void*, void (*)(void*)))
+ 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
- {
- uint32_t chunkid;
- uint32_t chunksize;
- uint32_t rifftype;
- };
-
- struct __wave_chunk
- {
- uint32_t chunkid;
- uint32_t chunksize;
- uint16_t compression;
- uint16_t channels;
- uint32_t samplerate;
- uint32_t avgbytepersec;
- uint16_t blockkalign;
- uint16_t bitspersample;
- };
-
- struct __data_chunk
- {
- uint32_t chunkid;
- uint32_t 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[%f] callback[%p] 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->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);
+
+finish:
+ ret = pthread_attr_destroy(&attr);
+ if (ret != 0)
+ debug_error("failed to destroy pthread attr!!! errno=%d", ret);
- p->ptr_current = MMSourceGetPtr(source) + info->doffset;
+ 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;
- p->stream_index = param->stream_index;
- MMSOUND_STRNCPY(p->stream_type, param->stream_type, MM_SOUND_STREAM_TYPE_LEN);
- // 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;
- p->handle_route = param->handle_route;
+ 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 size;
-
- pa_sample_spec ss;
- mm_sound_handle_route_info route_info;
-
- 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);
+ frame_size = pa_frame_size(&h->spec);
+ data_length = length;
- route_info.policy = HANDLE_ROUTE_POLICY_OUT_AUTO;
-
- 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, p->stream_type, p->stream_index);
- if(!p->handle) {
- debug_critical("[CODEC WAV] Can not open audio handle\n");
- CODEC_WAVE_UNLOCK(p->codec_wave_mutex);
+ 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;
- dummy = malloc(p->period);
- if(!dummy) {
- debug_error("[CODEC WAV] not enough memory");
- CODEC_WAVE_UNLOCK(p->codec_wave_mutex);
- return;
+ 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--;
+
+ if (_sound_rewind(h) == 0)
+ return;
+
+ debug_error("stream(%p) : REWIND failed....", s);
+ /* can't loop anymore, fallback and do drain */
+ }
+
+ /* 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; */
+}
+
+static int _pa_context_connect(wave_info_t *h)
+{
+ pa_threaded_mainloop *m = NULL;
+ pa_context *c = NULL;
- debug_msg("[CODEC WAV] Wait start signal\n");
+ /* Mainloop */
+ if (!(m = pa_threaded_mainloop_new())) {
+ debug_error("mainloop create failed");
+ return -1;
+ }
- while(p->state == STATE_READY) {
- usleep(4);
+ /* Context */
+ if (!(c = pa_context_new(pa_threaded_mainloop_get_api(m), NULL))) {
+ debug_error("context create failed");
+ goto error_context_new;
}
- 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);
+ pa_context_set_state_callback(c, _pa_context_state_callback, m);
- 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_threaded_mainloop_lock(m);
+
+ if (pa_threaded_mainloop_start(m) < 0) {
+ debug_error("mainloop start failed");
+ goto error;
+ }
+
+ if (pa_context_connect(c, NULL, 0, NULL) < 0) {
+ debug_error("context connect 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;
- }
+ for (;;) {
+ pa_context_state_t state = pa_context_get_state(c);
+ if (state == PA_CONTEXT_READY)
+ break;
+
+ if (!PA_CONTEXT_IS_GOOD(state)) {
+ debug_error("Context error!!!! %d", pa_context_errno(c));
+ goto error;
}
- p->ptr_current = org_cur;
- p->size = org_size;
+
+ pa_threaded_mainloop_wait(m);
}
- debug_msg("[CODEC WAV] End playing\n");
- p->state = STATE_STOP;
+ h->m = m;
+ h->c = c;
- 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;
+ 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);
+
+ pa_threaded_mainloop_unlock(h->m);
+
+ return -1;
+}
+
+static void _pa_stream_uncork(wave_info_t *h)
+{
+ pa_operation *o = NULL;
+
+ assert(h);
+ assert(h->m);
+ assert(h->s);
- p->state = STATE_NONE;
+ 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);
+}
- 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);
+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 (!p) {
- debug_error("The handle is null\n");
+ 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);
+
+ 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;
}