forked from enlightenment/efl
ecore_audio: Add support for PulseAudio
Only outputs are supported at the moment Signed-off-by: Daniel Willmann <d.willmann@samsung.com> SVN revision: 80995
This commit is contained in:
parent
318a88b61f
commit
9293581c62
|
@ -76,6 +76,12 @@ ecore_audio_init(void)
|
||||||
ECORE_AUDIO_OUTPUT_INPUT_ADDED = ecore_event_type_new();
|
ECORE_AUDIO_OUTPUT_INPUT_ADDED = ecore_event_type_new();
|
||||||
ECORE_AUDIO_OUTPUT_INPUT_REMOVED = ecore_event_type_new();
|
ECORE_AUDIO_OUTPUT_INPUT_REMOVED = ecore_event_type_new();
|
||||||
|
|
||||||
|
#ifdef HAVE_PULSE
|
||||||
|
mod = ecore_audio_pulse_init();
|
||||||
|
if (mod)
|
||||||
|
ecore_audio_modules = eina_list_append(ecore_audio_modules, mod);
|
||||||
|
#endif
|
||||||
|
|
||||||
return _ecore_audio_init_count;
|
return _ecore_audio_init_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,6 +94,10 @@ ecore_audio_shutdown(void)
|
||||||
|
|
||||||
/* FIXME: Shutdown all the inputs and outputs first */
|
/* FIXME: Shutdown all the inputs and outputs first */
|
||||||
|
|
||||||
|
#ifdef HAVE_PULSE
|
||||||
|
ecore_audio_pulse_shutdown();
|
||||||
|
#endif
|
||||||
|
|
||||||
eina_list_free(ecore_audio_modules);
|
eina_list_free(ecore_audio_modules);
|
||||||
|
|
||||||
eina_log_domain_unregister(_ecore_audio_log_dom);
|
eina_log_domain_unregister(_ecore_audio_log_dom);
|
||||||
|
|
|
@ -9,6 +9,10 @@
|
||||||
#include <config.h>
|
#include <config.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_PULSE
|
||||||
|
#include <pulse/pulseaudio.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
@ -289,6 +293,25 @@ struct _Ecore_Audio_Callback {
|
||||||
|
|
||||||
extern Eina_List *ecore_audio_modules;
|
extern Eina_List *ecore_audio_modules;
|
||||||
|
|
||||||
|
#ifdef HAVE_PULSE
|
||||||
|
/* PA mainloop integration */
|
||||||
|
struct _Ecore_Audio_Pa_Private
|
||||||
|
{
|
||||||
|
pa_mainloop_api api;
|
||||||
|
pa_context *context;
|
||||||
|
pa_context_state_t state;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ecore_audio_pulse */
|
||||||
|
struct _Ecore_Audio_Pulse
|
||||||
|
{
|
||||||
|
pa_stream *stream;
|
||||||
|
};
|
||||||
|
|
||||||
|
Ecore_Audio_Module *ecore_audio_pulse_init(void);
|
||||||
|
void ecore_audio_pulse_shutdown(void);
|
||||||
|
#endif /* HAVE_PULSE */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @}
|
* @}
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -0,0 +1,570 @@
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include <config.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_PULSE
|
||||||
|
#include "Ecore.h"
|
||||||
|
#include "ecore_private.h"
|
||||||
|
#include "Ecore_Audio.h"
|
||||||
|
#include "ecore_audio_private.h"
|
||||||
|
|
||||||
|
#include <pulse/pulseaudio.h>
|
||||||
|
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
|
||||||
|
int _ecore_audio_pa_log_dom = -1;
|
||||||
|
static Ecore_Audio_Module *pulse_module = NULL;
|
||||||
|
|
||||||
|
/* Ecore mainloop integration start */
|
||||||
|
struct pa_io_event
|
||||||
|
{
|
||||||
|
struct _Ecore_Audio_Pa_Private *mainloop;
|
||||||
|
Ecore_Fd_Handler *handler;
|
||||||
|
|
||||||
|
void *userdata;
|
||||||
|
|
||||||
|
pa_io_event_flags_t flags;
|
||||||
|
pa_io_event_cb_t callback;
|
||||||
|
pa_io_event_destroy_cb_t destroy_callback;
|
||||||
|
};
|
||||||
|
|
||||||
|
static Ecore_Fd_Handler_Flags
|
||||||
|
map_flags_to_ecore(pa_io_event_flags_t flags)
|
||||||
|
{
|
||||||
|
return (Ecore_Fd_Handler_Flags)((flags & PA_IO_EVENT_INPUT ? ECORE_FD_READ : 0) | (flags & PA_IO_EVENT_OUTPUT ? ECORE_FD_WRITE : 0) | (flags & PA_IO_EVENT_ERROR ? ECORE_FD_ERROR : 0) | (flags & PA_IO_EVENT_HANGUP ? ECORE_FD_READ : 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
static Eina_Bool
|
||||||
|
_ecore_io_wrapper(void *data, Ecore_Fd_Handler *handler)
|
||||||
|
{
|
||||||
|
char buf[64];
|
||||||
|
pa_io_event_flags_t flags = 0;
|
||||||
|
pa_io_event *event = (pa_io_event *)data;
|
||||||
|
|
||||||
|
if (ecore_main_fd_handler_active_get(handler, ECORE_FD_READ))
|
||||||
|
{
|
||||||
|
flags |= PA_IO_EVENT_INPUT;
|
||||||
|
|
||||||
|
/* Check for HUP and report */
|
||||||
|
if (recv(ecore_main_fd_handler_fd_get(handler), buf, 64, MSG_PEEK))
|
||||||
|
{
|
||||||
|
if (errno == ESHUTDOWN || errno == ECONNRESET || errno == ECONNABORTED || errno == ENETRESET)
|
||||||
|
{
|
||||||
|
DBG("HUP condition detected");
|
||||||
|
flags |= PA_IO_EVENT_HANGUP;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ecore_main_fd_handler_active_get(handler, ECORE_FD_WRITE))
|
||||||
|
flags |= PA_IO_EVENT_OUTPUT;
|
||||||
|
if (ecore_main_fd_handler_active_get(handler, ECORE_FD_ERROR))
|
||||||
|
flags |= PA_IO_EVENT_ERROR;
|
||||||
|
|
||||||
|
event->callback(&event->mainloop->api, event, ecore_main_fd_handler_fd_get(handler), flags, event->userdata);
|
||||||
|
|
||||||
|
return ECORE_CALLBACK_RENEW;
|
||||||
|
}
|
||||||
|
|
||||||
|
static pa_io_event *
|
||||||
|
_ecore_pa_io_new(pa_mainloop_api *api, int fd, pa_io_event_flags_t flags, pa_io_event_cb_t cb, void *userdata)
|
||||||
|
{
|
||||||
|
pa_io_event *event;
|
||||||
|
struct _Ecore_Audio_Pa_Private *mloop;
|
||||||
|
|
||||||
|
mloop = api->userdata;
|
||||||
|
|
||||||
|
event = calloc(1, sizeof(pa_io_event));
|
||||||
|
event->mainloop = mloop;
|
||||||
|
event->userdata = userdata;
|
||||||
|
event->callback = cb;
|
||||||
|
event->flags = flags;
|
||||||
|
event->handler = ecore_main_fd_handler_add(fd, map_flags_to_ecore(flags), _ecore_io_wrapper, event, NULL, NULL);
|
||||||
|
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
_ecore_pa_io_enable(pa_io_event *event, pa_io_event_flags_t flags)
|
||||||
|
{
|
||||||
|
event->flags = flags;
|
||||||
|
ecore_main_fd_handler_active_set(event->handler, map_flags_to_ecore(flags));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
_ecore_pa_io_free(pa_io_event *event)
|
||||||
|
{
|
||||||
|
ecore_main_fd_handler_del(event->handler);
|
||||||
|
free(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
_ecore_pa_io_set_destroy(pa_io_event *event, pa_io_event_destroy_cb_t cb)
|
||||||
|
{
|
||||||
|
event->destroy_callback = cb;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Timed events */
|
||||||
|
struct pa_time_event
|
||||||
|
{
|
||||||
|
struct _Ecore_Audio_Pa_Private *mainloop;
|
||||||
|
Ecore_Timer *timer;
|
||||||
|
struct timeval tv;
|
||||||
|
|
||||||
|
void *userdata;
|
||||||
|
|
||||||
|
pa_time_event_cb_t callback;
|
||||||
|
pa_time_event_destroy_cb_t destroy_callback;
|
||||||
|
};
|
||||||
|
|
||||||
|
Eina_Bool
|
||||||
|
_ecore_time_wrapper(void *data)
|
||||||
|
{
|
||||||
|
pa_time_event *event = (pa_time_event *)data;
|
||||||
|
|
||||||
|
event->callback(&event->mainloop->api, event, &event->tv, event->userdata);
|
||||||
|
|
||||||
|
return ECORE_CALLBACK_CANCEL;
|
||||||
|
}
|
||||||
|
|
||||||
|
pa_time_event *
|
||||||
|
_ecore_pa_time_new(pa_mainloop_api *api, const struct timeval *tv, pa_time_event_cb_t cb, void *userdata)
|
||||||
|
{
|
||||||
|
pa_time_event *event;
|
||||||
|
struct _Ecore_Audio_Pa_Private *mloop;
|
||||||
|
struct timeval now;
|
||||||
|
double interval;
|
||||||
|
|
||||||
|
mloop = api->userdata;
|
||||||
|
|
||||||
|
event = calloc(1, sizeof(pa_time_event));
|
||||||
|
event->mainloop = mloop;
|
||||||
|
event->userdata = userdata;
|
||||||
|
event->callback = cb;
|
||||||
|
event->tv = *tv;
|
||||||
|
|
||||||
|
if (gettimeofday(&now, NULL) == -1)
|
||||||
|
{
|
||||||
|
ERR("Failed to get the current time!");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
interval = (tv->tv_sec - now.tv_sec) + (tv->tv_usec - now.tv_usec) / 1000;
|
||||||
|
event->timer = ecore_timer_add(interval, _ecore_time_wrapper, event);
|
||||||
|
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
_ecore_pa_time_restart(pa_time_event *event, const struct timeval *tv)
|
||||||
|
{
|
||||||
|
struct timeval now;
|
||||||
|
double interval;
|
||||||
|
|
||||||
|
/* If tv is NULL disable timer */
|
||||||
|
if (!tv)
|
||||||
|
{
|
||||||
|
ecore_timer_del(event->timer);
|
||||||
|
event->timer = NULL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
event->tv = *tv;
|
||||||
|
|
||||||
|
if (gettimeofday(&now, NULL) == -1)
|
||||||
|
{
|
||||||
|
ERR("Failed to get the current time!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
interval = (tv->tv_sec - now.tv_sec) + (tv->tv_usec - now.tv_usec) / 1000;
|
||||||
|
if (event->timer)
|
||||||
|
{
|
||||||
|
event->timer = ecore_timer_add(interval, _ecore_time_wrapper, event);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ecore_timer_interval_set(event->timer, interval);
|
||||||
|
ecore_timer_reset(event->timer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
_ecore_pa_time_free(pa_time_event *event)
|
||||||
|
{
|
||||||
|
if (event->timer)
|
||||||
|
ecore_timer_del(event->timer);
|
||||||
|
|
||||||
|
event->timer = NULL;
|
||||||
|
|
||||||
|
free(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
_ecore_pa_time_set_destroy(pa_time_event *event, pa_time_event_destroy_cb_t cb)
|
||||||
|
{
|
||||||
|
event->destroy_callback = cb;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Deferred events */
|
||||||
|
struct pa_defer_event
|
||||||
|
{
|
||||||
|
struct _Ecore_Audio_Pa_Private *mainloop;
|
||||||
|
Ecore_Idler *idler;
|
||||||
|
|
||||||
|
void *userdata;
|
||||||
|
|
||||||
|
pa_defer_event_cb_t callback;
|
||||||
|
pa_defer_event_destroy_cb_t destroy_callback;
|
||||||
|
};
|
||||||
|
|
||||||
|
Eina_Bool
|
||||||
|
_ecore_defer_wrapper(void *data)
|
||||||
|
{
|
||||||
|
pa_defer_event *event = (pa_defer_event *)data;
|
||||||
|
|
||||||
|
event->idler = NULL;
|
||||||
|
event->callback(&event->mainloop->api, event, event->userdata);
|
||||||
|
|
||||||
|
return ECORE_CALLBACK_CANCEL;
|
||||||
|
}
|
||||||
|
|
||||||
|
pa_defer_event *
|
||||||
|
_ecore_pa_defer_new(pa_mainloop_api *api, pa_defer_event_cb_t cb, void *userdata)
|
||||||
|
{
|
||||||
|
pa_defer_event *event;
|
||||||
|
struct _Ecore_Audio_Pa_Private *mloop;
|
||||||
|
|
||||||
|
mloop = api->userdata;
|
||||||
|
|
||||||
|
event = calloc(1, sizeof(pa_defer_event));
|
||||||
|
event->mainloop = mloop;
|
||||||
|
event->userdata = userdata;
|
||||||
|
event->callback = cb;
|
||||||
|
|
||||||
|
event->idler = ecore_idler_add(_ecore_defer_wrapper, event);
|
||||||
|
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
_ecore_pa_defer_enable(pa_defer_event *event, int b)
|
||||||
|
{
|
||||||
|
if (!b && event->idler)
|
||||||
|
{
|
||||||
|
ecore_idler_del(event->idler);
|
||||||
|
event->idler = NULL;
|
||||||
|
}
|
||||||
|
else if (b && !event->idler)
|
||||||
|
{
|
||||||
|
event->idler = ecore_idler_add(_ecore_defer_wrapper, event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
_ecore_pa_defer_free(pa_defer_event *event)
|
||||||
|
{
|
||||||
|
if (event->idler)
|
||||||
|
ecore_idler_del(event->idler);
|
||||||
|
|
||||||
|
event->idler = NULL;
|
||||||
|
|
||||||
|
free(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
_ecore_pa_defer_set_destroy(pa_defer_event *event, pa_defer_event_destroy_cb_t cb)
|
||||||
|
{
|
||||||
|
event->destroy_callback = cb;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
_ecore_pa_quit(pa_mainloop_api *api EINA_UNUSED, int retval EINA_UNUSED)
|
||||||
|
{
|
||||||
|
/* FIXME: Need to clean up timers, etc.? */
|
||||||
|
WRN("Not quitting mainloop, although PA requested it");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Function table for PA mainloop integration */
|
||||||
|
static const pa_mainloop_api functable = {
|
||||||
|
.userdata = NULL,
|
||||||
|
|
||||||
|
.io_new = _ecore_pa_io_new,
|
||||||
|
.io_enable = _ecore_pa_io_enable,
|
||||||
|
.io_free = _ecore_pa_io_free,
|
||||||
|
.io_set_destroy = _ecore_pa_io_set_destroy,
|
||||||
|
|
||||||
|
.time_new = _ecore_pa_time_new,
|
||||||
|
.time_restart = _ecore_pa_time_restart,
|
||||||
|
.time_free = _ecore_pa_time_free,
|
||||||
|
.time_set_destroy = _ecore_pa_time_set_destroy,
|
||||||
|
|
||||||
|
.defer_new = _ecore_pa_defer_new,
|
||||||
|
.defer_enable = _ecore_pa_defer_enable,
|
||||||
|
.defer_free = _ecore_pa_defer_free,
|
||||||
|
.defer_set_destroy = _ecore_pa_defer_set_destroy,
|
||||||
|
|
||||||
|
.quit = _ecore_pa_quit,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* *****************************************************
|
||||||
|
* Ecore mainloop integration end
|
||||||
|
*/
|
||||||
|
|
||||||
|
static Ecore_Audio_Object *
|
||||||
|
_pulse_input_new(Ecore_Audio_Object *input)
|
||||||
|
{
|
||||||
|
Ecore_Audio_Input *in = (Ecore_Audio_Input *)input;
|
||||||
|
return (Ecore_Audio_Object *)in;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Ecore_Audio_Object *
|
||||||
|
_pulse_output_new(Ecore_Audio_Object *output)
|
||||||
|
{
|
||||||
|
Ecore_Audio_Output *out = (Ecore_Audio_Output *)output;
|
||||||
|
struct _Ecore_Audio_Pulse *pulse;
|
||||||
|
|
||||||
|
pulse = calloc(1, sizeof(struct _Ecore_Audio_Pulse));
|
||||||
|
if (!pulse)
|
||||||
|
{
|
||||||
|
ERR("Could not allocate memory for private structure.");
|
||||||
|
free(out);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
out->module_data = pulse;
|
||||||
|
|
||||||
|
return (Ecore_Audio_Object *)out;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
_pulse_output_del(Ecore_Audio_Object *output)
|
||||||
|
{
|
||||||
|
Ecore_Audio_Output *out = (Ecore_Audio_Output *)output;
|
||||||
|
free(out->module_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
_pulse_output_volume_set(Ecore_Audio_Object *output, double vol)
|
||||||
|
{
|
||||||
|
Ecore_Audio_Output *out = (Ecore_Audio_Output *)output;
|
||||||
|
struct _Ecore_Audio_Pa_Private *priv = (struct _Ecore_Audio_Pa_Private *)out->module->priv;
|
||||||
|
Eina_List *input;
|
||||||
|
Ecore_Audio_Input *in;
|
||||||
|
pa_stream *stream;
|
||||||
|
uint32_t idx;
|
||||||
|
pa_cvolume volume;
|
||||||
|
pa_operation *op;
|
||||||
|
|
||||||
|
if (vol < 0)
|
||||||
|
vol = 0;
|
||||||
|
|
||||||
|
pa_cvolume_set(&volume, 2, vol * PA_VOLUME_NORM);
|
||||||
|
|
||||||
|
EINA_LIST_FOREACH(out->inputs, input, in)
|
||||||
|
{
|
||||||
|
stream = in->obj_data;
|
||||||
|
idx = pa_stream_get_index(stream);
|
||||||
|
op = pa_context_set_sink_input_volume(priv->context, idx, &volume, NULL, NULL);
|
||||||
|
pa_operation_unref(op);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
_pulse_output_write_cb(pa_stream *stream, size_t len, void *data)
|
||||||
|
{
|
||||||
|
Ecore_Audio_Input *in = (Ecore_Audio_Input *)data;
|
||||||
|
|
||||||
|
void *buf;
|
||||||
|
int bread;
|
||||||
|
|
||||||
|
buf = malloc(len);
|
||||||
|
|
||||||
|
bread = ecore_audio_input_read((Ecore_Audio_Object *)in, buf, len);
|
||||||
|
pa_stream_write(stream, buf, bread, free, 0, PA_SEEK_RELATIVE);
|
||||||
|
if (bread < len && !in->ended)
|
||||||
|
{
|
||||||
|
in->ended = EINA_TRUE;
|
||||||
|
pa_operation_unref(pa_stream_drain(stream, NULL, NULL));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Eina_Bool
|
||||||
|
_pulse_output_add_input(Ecore_Audio_Object *output, Ecore_Audio_Object *input)
|
||||||
|
{
|
||||||
|
Ecore_Audio_Output *out = (Ecore_Audio_Output *)output;
|
||||||
|
Ecore_Audio_Input *in = (Ecore_Audio_Input *)input;
|
||||||
|
Ecore_Audio_Module *outmod = out->module;
|
||||||
|
struct _Ecore_Audio_Pa_Private *priv = (struct _Ecore_Audio_Pa_Private *)outmod->priv;
|
||||||
|
pa_stream *stream;
|
||||||
|
|
||||||
|
pa_sample_spec ss = {
|
||||||
|
.format = PA_SAMPLE_FLOAT32LE,
|
||||||
|
.rate = in->samplerate,
|
||||||
|
.channels = in->channels,
|
||||||
|
};
|
||||||
|
|
||||||
|
stream = pa_stream_new(priv->context, in->name, &ss, NULL);
|
||||||
|
if (!stream)
|
||||||
|
{
|
||||||
|
ERR("Could not create stream");
|
||||||
|
return EINA_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
in->obj_data = stream;
|
||||||
|
|
||||||
|
pa_stream_set_write_callback(stream, _pulse_output_write_cb, in);
|
||||||
|
pa_stream_connect_playback(stream, NULL, NULL, PA_STREAM_NOFLAGS, NULL, NULL);
|
||||||
|
|
||||||
|
return EINA_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
_pulse_drain_cb(pa_stream *stream, int success, void *data)
|
||||||
|
{
|
||||||
|
// XXX: Check success?
|
||||||
|
pa_stream_disconnect(stream);
|
||||||
|
pa_stream_unref(stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Eina_Bool
|
||||||
|
_pulse_output_del_input(Ecore_Audio_Object *output, Ecore_Audio_Object *input)
|
||||||
|
{
|
||||||
|
Ecore_Audio_Input *in = (Ecore_Audio_Input *)input;
|
||||||
|
|
||||||
|
pa_stream *stream = (pa_stream *)in->obj_data;
|
||||||
|
in->obj_data = NULL;
|
||||||
|
|
||||||
|
pa_stream_set_write_callback(stream, NULL, NULL);
|
||||||
|
pa_operation_unref(pa_stream_drain(stream, _pulse_drain_cb, in));
|
||||||
|
|
||||||
|
return EINA_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
_pulse_output_update_input_format(Ecore_Audio_Object *output, Ecore_Audio_Object *input)
|
||||||
|
{
|
||||||
|
Ecore_Audio_Input *in = (Ecore_Audio_Input *)input;
|
||||||
|
pa_stream *stream = (pa_stream *)in->obj_data;
|
||||||
|
|
||||||
|
pa_operation_unref(pa_stream_update_sample_rate(stream, in->samplerate, NULL, NULL));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
_pulse_input_read(Ecore_Audio_Object *input, void *data, int len)
|
||||||
|
{
|
||||||
|
// XXX: Implement
|
||||||
|
Ecore_Audio_Input *in = (Ecore_Audio_Input *)input;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
_ecore_pa_state_cb(pa_context *context, void *data)
|
||||||
|
{
|
||||||
|
struct _Ecore_Audio_Pa_Private *priv = (struct _Ecore_Audio_Pa_Private *)data;
|
||||||
|
pa_context_state_t state;
|
||||||
|
|
||||||
|
state = pa_context_get_state(context);
|
||||||
|
|
||||||
|
if (state == PA_CONTEXT_READY)
|
||||||
|
{
|
||||||
|
DBG("PA context connected.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DBG("Connection state %i", state);
|
||||||
|
}
|
||||||
|
priv->state = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct input_api inops = {
|
||||||
|
.input_new = _pulse_input_new,
|
||||||
|
.input_read = _pulse_input_read,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct output_api outops = {
|
||||||
|
.output_new = _pulse_output_new,
|
||||||
|
.output_del = _pulse_output_del,
|
||||||
|
.output_volume_set = _pulse_output_volume_set,
|
||||||
|
.output_add_input = _pulse_output_add_input,
|
||||||
|
.output_del_input = _pulse_output_del_input,
|
||||||
|
.output_update_input_format = _pulse_output_update_input_format,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* externally accessible functions */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize the Ecore_Audio PA module
|
||||||
|
*
|
||||||
|
* @return the initialized module on success, NULL on error
|
||||||
|
*/
|
||||||
|
Ecore_Audio_Module *
|
||||||
|
ecore_audio_pulse_init(void)
|
||||||
|
{
|
||||||
|
struct _Ecore_Audio_Pa_Private *priv;
|
||||||
|
|
||||||
|
pulse_module = calloc(1, sizeof(Ecore_Audio_Module));
|
||||||
|
if (!pulse_module)
|
||||||
|
{
|
||||||
|
ERR("Could not allocate memory for module.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
priv = calloc(1, sizeof(struct _Ecore_Audio_Pa_Private));
|
||||||
|
if (!priv)
|
||||||
|
{
|
||||||
|
ERR("Could not allocate memory for private module region.");
|
||||||
|
free(pulse_module);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
priv->api = functable;
|
||||||
|
priv->api.userdata = priv;
|
||||||
|
/* FIXME: Get name from application */
|
||||||
|
priv->context = pa_context_new(&priv->api, "ecore_audio");
|
||||||
|
if (!priv->context)
|
||||||
|
{
|
||||||
|
ERR("Could not create PulseAudio context.");
|
||||||
|
free(priv);
|
||||||
|
free(pulse_module);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
pa_context_set_state_callback(priv->context, _ecore_pa_state_cb, priv);
|
||||||
|
pa_context_connect(priv->context, NULL, PA_CONTEXT_NOFLAGS, NULL);
|
||||||
|
|
||||||
|
ECORE_MAGIC_SET(pulse_module, ECORE_MAGIC_AUDIO_MODULE);
|
||||||
|
pulse_module->type = ECORE_AUDIO_TYPE_PULSE;
|
||||||
|
pulse_module->name = "pulse";
|
||||||
|
pulse_module->priv = priv;
|
||||||
|
pulse_module->inputs = NULL;
|
||||||
|
pulse_module->outputs = NULL;
|
||||||
|
pulse_module->in_ops = &inops;
|
||||||
|
pulse_module->out_ops = &outops;
|
||||||
|
|
||||||
|
return pulse_module;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Shut down the Ecore_Audio PA module
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
ecore_audio_pulse_shutdown(void)
|
||||||
|
{
|
||||||
|
struct _Ecore_Audio_Pa_Private *priv = (struct _Ecore_Audio_Pa_Private *)pulse_module->priv;
|
||||||
|
|
||||||
|
/* XXX: Make sure all pending events are freed */
|
||||||
|
priv->api.userdata = NULL;
|
||||||
|
free(priv);
|
||||||
|
free(pulse_module);
|
||||||
|
pulse_module = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @}
|
||||||
|
*/
|
||||||
|
|
||||||
|
#endif /* HAVE_PULSE */
|
Loading…
Reference in New Issue