#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <stdint.h>
-#include <semaphore.h>
-#include <unistd.h>
#include <mm_error.h>
#include <mm_debug.h>
-#include <pthread.h>
-#include <avsys-audio.h>
+#include <mm_sound_pa_client.h>
-#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_hal.h"
-#include "../../../include/mm_sound_private.h"
-
-
-#define SAMPLE_COUNT 128
-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'))
-
-enum {
- STATE_NONE = 0,
- STATE_READY,
- STATE_BEGIN,
- STATE_PLAY,
- STATE_STOP,
-};
-
-typedef struct
-{
- char *ptr_current;
- int size;
- int transper_size;
- avsys_handle_t audio_handle;
- int period;
- int tone;
- int keytone;
+#include "../../../include/mm_sound_common.h"
+#include <unistd.h>
+
+#include <sndfile.h>
+#include <pthread.h>
+#include <pulse/pulseaudio.h>
+
+typedef struct {
+ int handle;
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;
- MMSourceType *source;
- char buffer[48000 / 1000 * SAMPLE_COUNT * 2 *2];//segmentation fault when above 22.05KHz stereo
- int handle_route;
+ 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
- {
- 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);
+/* 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;
- tSize = sizeof(struct __riff_chunk);
- pdata = (struct __data_chunk*)(data+tSize);
-
- 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;
- debug_msg("info->size:%d\n", info->size);
-
- debug_leave("\n");
- return MM_ERROR_NONE;
+
+ pthread_exit(NULL);
}
+static void _cleanup_threaded_mainloop(pa_threaded_mainloop *m)
+{
+ int ret = 0;
+ pthread_t thread_id;
+ pthread_attr_t attr;
+ ret = pthread_attr_init(&attr);
+ if (ret != 0) {
+ debug_error("failed to init pthread attr!!! errno=%d", ret);
+ return;
+ }
-int MMSoundPlugCodecWaveCreate(mmsound_codec_param_t *param, mmsound_codec_info_t *info, MMHandleType *handle)
-{
- int ret = MM_ERROR_NONE;
- wave_info_t* p = NULL;
+ ret = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ if (ret != 0) {
+ debug_error("failed to set detach state!!! errno=%d", ret);
+ goto finish;
+ }
- avsys_audio_param_t audio_param;
+ ret = pthread_create(&thread_id, &attr, _cleanup_thread_func, m);
+ if (ret != 0)
+ debug_error("failed to create _cleanup_thread_func!!! errno=%d", ret);
- static avsys_handle_t keytone_handle = (avsys_handle_t)-1;
- MMSourceType *source;
+finish:
+ ret = pthread_attr_destroy(&attr);
+ if (ret != 0)
+ debug_error("failed to destroy pthread attr!!! errno=%d", ret);
- static int keytone_period = 0;
+ return;
+}
- debug_enter("\n");
- debug_msg("[CODEC WAV] Local keytone_period : %d\n", keytone_period);
- debug_msg("[CODEC WAV] Local keytone_handle : %d\n", (int)keytone_handle);
-
- debug_msg("[CODEC WAV] Type %s\n", info->codec == MM_SOUND_SUPPORTED_CODEC_WAVE ? "PCM Wave" : "Unknown");
- debug_msg("[CODEC WAV] channels : %d\n", info->channels);
- debug_msg("[CODEC WAV] format : %d\n", info->format);
- debug_msg("[CODEC WAV] samplerate : %d\n", info->samplerate);
- debug_msg("[CODEC WAV] doffset : %d\n", info->doffset);
- debug_msg("[CODEC WAV] priority : %d\n", param->priority);
- debug_msg("[CODEC WAV] repeat : %d\n", param->repeat_count);
- debug_msg("[CODEC WAV] volume type : %d\n", param->volume);
- debug_msg("[CODEC WAV] callback : %p\n", param->stop_cb);
- debug_msg("[CODEC WAV] Keytonemode : %08x\n", param->keytone);
- debug_msg("[CODEC WAV] handle route : %d\n", param->handle_route);
+static void _pa_context_drain_complete_callback(pa_context *c, void *userdata)
+{
+ debug_msg("context drain completed, cleanup context and mainloop");
- source = param->source;
+ pa_context_disconnect(c);
+ pa_context_unref(c);
- if (g_thread_pool_func == NULL) {
- debug_error("[CODEC WAV] Need thread pool!\n");
- return MM_ERROR_SOUND_INTERNAL;
- }
+ _cleanup_threaded_mainloop((pa_threaded_mainloop *)userdata);
+}
- p = (wave_info_t *) malloc(sizeof(wave_info_t));
+/* Stream Callbacks */
+static void _pa_stream_state_callback(pa_stream *s, void *userdata)
+{
+ pa_threaded_mainloop *m = (pa_threaded_mainloop *)userdata;
- if (p == NULL) {
- debug_error("[CODEC WAV] memory allocation failed\n");
- return MM_ERROR_OUT_OF_MEMORY;
- }
+ assert(s);
- memset(p, 0, sizeof(wave_info_t));
- p->audio_handle = (avsys_handle_t)-1;
-
- p->ptr_current = MMSourceGetPtr(source) + info->doffset;
-
- 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;
- // pthread_mutex_init(&p->mutex, NULL);
-
- debug_msg("[CODEC WAV] transper_size : %d\n", p->transper_size);
- debug_msg("[CODEC WAV] size : %d\n", p->size);
-
-
- /* audio param setting */
- memset (&audio_param, 0, sizeof(avsys_audio_param_t));
- audio_param.mode = AVSYS_AUDIO_MODE_OUTPUT;
- audio_param.priority = param->priority;
- audio_param.vol_type = param->volume_config;
- audio_param.channels = info->channels;
- audio_param.samplerate = info->samplerate;
- if(param->handle_route == MM_SOUND_HANDLE_ROUTE_USING_CURRENT) /* normal, solo */
- audio_param.handle_route = AVSYS_AUDIO_HANDLE_ROUTE_FOLLOWING_POLICY;
- else /* loud solo */
- audio_param.handle_route = AVSYS_AUDIO_HANDLE_ROUTE_HANDSET_ONLY;
- p->handle_route = param->handle_route;
-
- switch(info->format)
- {
- case 8:
- audio_param.format = AVSYS_AUDIO_FORMAT_8BIT;
+ 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:
- audio_param.format = AVSYS_AUDIO_FORMAT_16BIT;
+ 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:
- audio_param.format = AVSYS_AUDIO_FORMAT_16BIT;
break;
}
+}
- debug_msg("[CODEC WAV] PARAM mode : [%d]\n", audio_param.mode);
- debug_msg("[CODEC WAV] PARAM priority: [%d]\n", audio_param.priority);
- debug_msg("[CODEC WAV] PARAM channels : [%d]\n", audio_param.channels);
- debug_msg("[CODEC WAV] PARAM samplerate : [%d]\n", audio_param.samplerate);
- debug_msg("[CODEC WAV] PARAM format : [%d]\n", audio_param.format);
- debug_msg("[CODEC WAV] PARAM volume type : [%x]\n", audio_param.vol_type);
+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;
- ret = avsys_audio_open(&audio_param, &p->audio_handle, &p->period);
- if (ret != MM_ERROR_NONE)
- debug_critical("[CODEC WAV] Can not open audio handle\n");
+ debug_msg("stream(%p) : drain completed(%d)", s, success);
+ if (!success) {
+ debug_error("stream(%p) : drain failed(%d)", s, success);
+ //pa_threaded_mainloop_signal(h->m, 0);
+ }
+ pa_stream_disconnect(h->s);
+ pa_stream_unref(h->s);
+ h->s = NULL;
- if (p->audio_handle == (avsys_handle_t)-1) {
- debug_critical("[CODEC WAV] audio_handle is not created !! \n");
- if (p)
- free(p);
- return MM_ERROR_SOUND_INTERNAL;
+ 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);
+}
- p->state = STATE_READY;
+static void _pa_stream_moved_callback(pa_stream *s, void *userdata)
+{
+ assert(s);
+ debug_msg("stream(%p)", s);
+}
- g_thread_pool_func(p, _runing);
- debug_msg("[CODEC WAV] Thread pool start\n");
- *handle = (MMHandleType)p;
+static void _pa_stream_underflow_callback(pa_stream *s, void *userdata)
+{
+ wave_info_t *h = (wave_info_t *)userdata;
+ assert(s);
- debug_leave("\n");
+ debug_msg("stream(%p) : file(%s)", s, h->filename);
+}
- return MM_ERROR_NONE;
+static void _pa_stream_buffer_attr_callback(pa_stream *s, void *userdata)
+{
+ assert(s);
+ debug_msg("stream(%p)", s);
}
+static void _pa_stream_started_callback(pa_stream *s, void *userdata)
+{
+ assert(s);
+ debug_msg("stream(%p)", s);
+}
-int MMSoundPlugCodecWavePlay(MMHandleType handle)
+static void _pa_stream_write_callback(pa_stream *s, size_t length, void *userdata)
{
- wave_info_t *p = (wave_info_t *) handle;
+ 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("(handle %x)\n", handle);
+ frame_size = pa_frame_size(&h->spec);
+ data_length = length;
- if (p->size <= 0) {
- debug_error("[CODEC WAV] end of file\n");
- return MM_ERROR_END_OF_FILE;
+ if (frame_size == 0) {
+ debug_error("stream(%p) : frame size can't be 0", s);
+ return;
}
- debug_msg("[CODEC WAV] send start signal\n");
- p->state = STATE_BEGIN;
- debug_leave("\n");
+ if (pa_stream_begin_write(s, &data, &data_length) < 0) {
+ debug_error("stream(%p) : failed to pa_stream_begin_write()", s);
+ return;
+ }
- return MM_ERROR_NONE;
- }
+ 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);
-static void _runing(void *param)
+ 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);
+ }
+}
+
+static int _pa_context_connect(wave_info_t *h)
{
- wave_info_t *p = (wave_info_t*) param;
- int nread = 0;
- char *org_cur = NULL;
- int org_size = 0;
- char *dummy = NULL;
- int ret;
+ pa_threaded_mainloop *m = NULL;
+ pa_context *c = NULL;
- int gain, out, in, option;
- int gain_after, out_after, in_after, option_after;
+ /* Mainloop */
+ if (!(m = pa_threaded_mainloop_new())) {
+ debug_error("mainloop create failed");
+ return -1;
+ }
+ /* Context */
+ if (!(c = pa_context_new(pa_threaded_mainloop_get_api(m), NULL))) {
+ debug_error("context create failed");
+ goto error_context_new;
+ }
- debug_enter("[CODEC WAV] (Slot ID %d)\n", p->cb_param);
+ pa_context_set_state_callback(c, _pa_context_state_callback, m);
- /* Set the thread schedule */
- org_cur = p->ptr_current;
- org_size = p->size;
+ pa_threaded_mainloop_lock(m);
- dummy = malloc(p->period);
- if(!dummy) {
- debug_error("[CODEC WAV] not enough memory");
- return;
+ 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;
}
- memset(dummy, 0, p->period);
- p->transper_size = p->period;
-
- debug_msg("[CODEC WAV] Wait start signal\n");
-
- while(p->state == STATE_READY)
- usleep(4);
-
- /*
- * 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");
- avsys_audio_get_path_ex(&gain, &out, &in, &option);
-
- /* if current out is not speaker, then force set path to speaker */
- if (out != AVSYS_AUDIO_PATH_EX_SPK) {
- debug_msg("[CODEC WAV] current out is not SPEAKER, set path to SPEAKER now!!!\n");
- audio_hal_set_sound_path(AVSYS_AUDIO_GAIN_EX_AUDIOPLAYER, AVSYS_AUDIO_PATH_EX_SPK, AVSYS_AUDIO_PATH_EX_NONE, AVSYS_AUDIO_PATH_OPTION_NONE);
+
+ 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;
}
- break;
- case MM_SOUND_HANDLE_ROUTE_USING_CURRENT:
- default:
- break;
+
+ pa_threaded_mainloop_wait(m);
}
- 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);
+ h->m = m;
+ h->c = c;
- 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_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);
- 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);
- avsys_audio_write(p->audio_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);
- avsys_audio_write(p->audio_handle, p->buffer, nread);
- avsys_audio_write(p->audio_handle, dummy, (p->transper_size-nread));
- //avsys_audio_drain(p->audio_handle);
- //debug_error("Drain is called\n");
- p->ptr_current += nread;
- p->size = 0;
- }
+ if (state == PA_STREAM_READY)
+ break;
+
+ if (!PA_STREAM_IS_GOOD(state)) {
+ debug_error("stream(%p) : state(%d)", s, state);
+ goto error;
}
- p->ptr_current = org_cur;
- p->size = org_size;
+
+ /* Wait until the stream is ready */
+ pa_threaded_mainloop_wait(h->m);
}
- debug_msg("[CODEC WAV] End playing\n");
- p->state = STATE_STOP;
+ h->s = s;
- if (p->audio_handle == (avsys_handle_t)-1) {
- usleep(200000);
- debug_warning("[CODEC WAV] audio already unrealize !!\n");
- } else {
- //usleep(75000);
+ pa_threaded_mainloop_unlock(h->m);
- if(AVSYS_FAIL(avsys_audio_drain(p->audio_handle)))
- {
- debug_error("avsys_audio_drain() failed\n");
- }
+ return 0;
- /*
- * Restore path here
- */
- if (p->handle_route == MM_SOUND_HANDLE_ROUTE_SPEAKER) {
- avsys_audio_get_path_ex(&gain_after, &out_after, &in_after, &option_after);
+error:
+ if (s)
+ pa_stream_unref(s);
- /* If current path is not same as before playing sound, restore the sound path */
- if (gain_after != gain || out_after != out || in_after != in || option_after != option) {
+ pa_threaded_mainloop_unlock(h->m);
- debug_msg("[CODEC WAV] Restore path to previous one\n");
- if (audio_hal_set_sound_path(gain, out, in, option)) {
- debug_error("[CODEC WAV] Can not restore sound path\n");
- }
- }
- }
+ return -1;
+}
- ret = avsys_audio_close(p->audio_handle);
- if (AVSYS_FAIL(ret)) {
- debug_critical("[CODEC WAV] Can not close audio handle\n");
- }
+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;
+ }
+ 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;
+}
+
+static int _mm_sound_plug_codec_wave_parse(const char *filename, mmsound_codec_info_t *info)
+{
+ 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;
}
- p->audio_handle = (avsys_handle_t)-1;
- p->state = STATE_NONE;
+ /* 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;
- free(dummy);
- if (p->stop_cb)
- {
- debug_msg("[CODEC WAV] Play is finished, Now start callback\n");
- p->stop_cb(p->cb_param);
+#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;
}
- debug_leave("\n");
+
+ *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;
}
-int MMSoundPlugCodecWaveStop(MMHandleType handle)
+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 *p = (wave_info_t*) handle;
- if (!p) {
- debug_error("The handle is NULL\n");
+ 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);
+
+ if (h->stop_cb)
+ h->stop_cb(h->cb_param, true);
- return MM_ERROR_NONE;
+ 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 null\n");
+ 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);
- }
+ _sound_unprepare(h);
- free(p);
+ 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;
}