489 lines
12 KiB
C
489 lines
12 KiB
C
|
#ifdef HAVE_CONFIG_H
|
||
|
#include <config.h>
|
||
|
#endif
|
||
|
|
||
|
#ifdef HAVE_SNDFILE
|
||
|
#include "Ecore.h"
|
||
|
#include "ecore_private.h"
|
||
|
#include "Ecore_Audio.h"
|
||
|
#include "ecore_audio_private.h"
|
||
|
|
||
|
#include <sndfile.h>
|
||
|
|
||
|
static Ecore_Audio_Module *module = NULL;
|
||
|
|
||
|
/* Virtual IO wrapper functions */
|
||
|
|
||
|
static sf_count_t _sndfile_wrap_get_filelen(void *data)
|
||
|
{
|
||
|
Ecore_Audio_Object *obj = data;
|
||
|
struct _Ecore_Audio_Sndfile *sndfile = obj->module_data;
|
||
|
|
||
|
if (!sndfile->vio)
|
||
|
goto error;
|
||
|
|
||
|
if (sndfile->vio->get_length)
|
||
|
return sndfile->vio->get_length(obj);
|
||
|
|
||
|
error:
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
static sf_count_t _sndfile_wrap_seek(sf_count_t offset, int whence, void *data)
|
||
|
{
|
||
|
Ecore_Audio_Object *obj = data;
|
||
|
struct _Ecore_Audio_Sndfile *sndfile = obj->module_data;
|
||
|
|
||
|
if (!sndfile->vio)
|
||
|
goto error;
|
||
|
|
||
|
if (sndfile->vio->seek)
|
||
|
return sndfile->vio->seek(obj, offset, whence);
|
||
|
|
||
|
error:
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
static sf_count_t _sndfile_wrap_read(void *buffer, sf_count_t count, void *data)
|
||
|
{
|
||
|
Ecore_Audio_Object *obj = data;
|
||
|
struct _Ecore_Audio_Sndfile *sndfile = obj->module_data;
|
||
|
|
||
|
if (!sndfile->vio)
|
||
|
goto error;
|
||
|
|
||
|
if (sndfile->vio->read)
|
||
|
return sndfile->vio->read(obj, buffer, count);
|
||
|
|
||
|
error:
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static sf_count_t _sndfile_wrap_write(const void *buffer, sf_count_t count, void *data)
|
||
|
{
|
||
|
Ecore_Audio_Object *obj = data;
|
||
|
struct _Ecore_Audio_Sndfile *sndfile = obj->module_data;
|
||
|
|
||
|
if (!sndfile->vio)
|
||
|
goto error;
|
||
|
|
||
|
if (sndfile->vio->write)
|
||
|
return sndfile->vio->write(obj, buffer, count);
|
||
|
|
||
|
error:
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static sf_count_t _sndfile_wrap_tell(void *data)
|
||
|
{
|
||
|
Ecore_Audio_Object *obj = data;
|
||
|
struct _Ecore_Audio_Sndfile *sndfile = obj->module_data;
|
||
|
|
||
|
if (!sndfile->vio)
|
||
|
goto error;
|
||
|
|
||
|
if (sndfile->vio->tell)
|
||
|
return sndfile->vio->tell(obj);
|
||
|
|
||
|
error:
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
/* End virtual IO wrapper functions */
|
||
|
|
||
|
static Ecore_Audio_Object *
|
||
|
_sndfile_input_new(Ecore_Audio_Object *input)
|
||
|
{
|
||
|
Ecore_Audio_Input *in = (Ecore_Audio_Input *)input;
|
||
|
struct _Ecore_Audio_Sndfile *sndfile;
|
||
|
|
||
|
sndfile = calloc(1, sizeof(struct _Ecore_Audio_Sndfile));
|
||
|
if (!sndfile)
|
||
|
{
|
||
|
ERR("Could not allocate memory for private structure.");
|
||
|
free(in);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
in->module_data = sndfile;
|
||
|
|
||
|
sndfile->sfinfo.format = 0;
|
||
|
sndfile->volume = 1.0;
|
||
|
|
||
|
return (Ecore_Audio_Object *)in;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
_sndfile_input_del(Ecore_Audio_Object *input)
|
||
|
{
|
||
|
Ecore_Audio_Input *in = (Ecore_Audio_Input *)input;
|
||
|
struct _Ecore_Audio_Sndfile *sndfile = in->module_data;
|
||
|
|
||
|
eina_stringshare_del(sndfile->filename);
|
||
|
|
||
|
sf_close(sndfile->handle);
|
||
|
free(sndfile);
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
_sndfile_input_read(Ecore_Audio_Object *input, void *data, int len)
|
||
|
{
|
||
|
Ecore_Audio_Input *in = (Ecore_Audio_Input *)input;
|
||
|
struct _Ecore_Audio_Sndfile *sndfile = in->module_data;
|
||
|
int read;
|
||
|
|
||
|
/* FIXME: Nicer way to return bytes read instead of items */
|
||
|
read = sf_read_float(sndfile->handle, data, len / 4) * 4;
|
||
|
|
||
|
return read;
|
||
|
}
|
||
|
|
||
|
static double
|
||
|
_sndfile_input_seek(Ecore_Audio_Object *input, double offs, int mode)
|
||
|
{
|
||
|
Ecore_Audio_Input *in = (Ecore_Audio_Input *)input;
|
||
|
struct _Ecore_Audio_Sndfile *sndfile = in->module_data;
|
||
|
sf_count_t count, pos;
|
||
|
|
||
|
count = offs * sndfile->sfinfo.samplerate;
|
||
|
pos = sf_seek(sndfile->handle, count, mode);
|
||
|
return (double)pos / sndfile->sfinfo.samplerate;
|
||
|
}
|
||
|
|
||
|
static struct input_api inops = {
|
||
|
.input_new = _sndfile_input_new,
|
||
|
.input_del = _sndfile_input_del,
|
||
|
.input_read = _sndfile_input_read,
|
||
|
.input_seek = _sndfile_input_seek,
|
||
|
};
|
||
|
|
||
|
EAPI void ecore_audio_input_sndfile_format_set(Ecore_Audio_Object *input, int format)
|
||
|
{
|
||
|
Ecore_Audio_Output *in = (Ecore_Audio_Output *)input;
|
||
|
struct _Ecore_Audio_Sndfile *sndfile = in->module_data;
|
||
|
|
||
|
sndfile->sfinfo.format = format;
|
||
|
}
|
||
|
|
||
|
void ecore_audio_input_sndfile_filename_set(Ecore_Audio_Object *input, const char *filename)
|
||
|
{
|
||
|
Ecore_Audio_Input *in = (Ecore_Audio_Input *)input;
|
||
|
struct _Ecore_Audio_Sndfile *sndfile = in->module_data;
|
||
|
|
||
|
if (sndfile->handle)
|
||
|
sf_close(sndfile->handle);
|
||
|
|
||
|
if (sndfile->filename)
|
||
|
eina_stringshare_del(sndfile->filename);
|
||
|
|
||
|
if (sndfile->vio)
|
||
|
{
|
||
|
free(sndfile->vio);
|
||
|
sndfile->vio = NULL;
|
||
|
}
|
||
|
|
||
|
sndfile->filename = eina_stringshare_add(filename);
|
||
|
|
||
|
sndfile->handle = sf_open(sndfile->filename, SFM_READ, &sndfile->sfinfo);
|
||
|
|
||
|
in->length = (double)sndfile->sfinfo.frames / sndfile->sfinfo.samplerate;
|
||
|
|
||
|
// XXX: Propagate samplerate/channel update to output
|
||
|
in->samplerate = sndfile->sfinfo.samplerate;
|
||
|
in->channels = sndfile->sfinfo.channels;
|
||
|
}
|
||
|
|
||
|
void ecore_audio_input_sndfile_vio_set(Ecore_Audio_Object *input, Ecore_Audio_Vio *vio)
|
||
|
{
|
||
|
Ecore_Audio_Input *in = (Ecore_Audio_Input *)input;
|
||
|
struct _Ecore_Audio_Sndfile *sndfile = in->module_data;
|
||
|
struct _Ecore_Audio_Sndfile_Private *priv = in->module->priv;
|
||
|
|
||
|
if (sndfile->handle)
|
||
|
sf_close(sndfile->handle);
|
||
|
|
||
|
if (sndfile->filename)
|
||
|
{
|
||
|
eina_stringshare_del(sndfile->filename);
|
||
|
sndfile->filename = NULL;
|
||
|
}
|
||
|
|
||
|
if (sndfile->vio)
|
||
|
free(sndfile->vio);
|
||
|
|
||
|
sndfile->vio = calloc(1, sizeof(Ecore_Audio_Vio));
|
||
|
sndfile->vio->get_length = vio->get_length;
|
||
|
sndfile->vio->seek = vio->seek;
|
||
|
sndfile->vio->read = vio->read;
|
||
|
sndfile->vio->tell = vio->tell;
|
||
|
|
||
|
if (sndfile->sfinfo.format != 0) {
|
||
|
sndfile->sfinfo.samplerate = in->samplerate;
|
||
|
sndfile->sfinfo.channels = in->channels;
|
||
|
}
|
||
|
|
||
|
sndfile->handle = sf_open_virtual(&priv->vio_wrapper, SFM_READ, &sndfile->sfinfo, in);
|
||
|
|
||
|
in->length = (double)sndfile->sfinfo.frames / sndfile->sfinfo.samplerate;
|
||
|
|
||
|
// XXX: Propagate samplerate/channel update to output
|
||
|
in->samplerate = sndfile->sfinfo.samplerate;
|
||
|
in->channels = sndfile->sfinfo.channels;
|
||
|
}
|
||
|
|
||
|
static Ecore_Audio_Object *
|
||
|
_sndfile_output_new(Ecore_Audio_Object *output)
|
||
|
{
|
||
|
Ecore_Audio_Output *out = (Ecore_Audio_Output *)output;
|
||
|
struct _Ecore_Audio_Sndfile *sndfile;
|
||
|
|
||
|
sndfile = calloc(1, sizeof(struct _Ecore_Audio_Sndfile));
|
||
|
if (!sndfile)
|
||
|
{
|
||
|
ERR("Could not allocate memory for private structure.");
|
||
|
free(out);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
out->module_data = sndfile;
|
||
|
|
||
|
sndfile->sfinfo.format = SF_FORMAT_OGG | SF_FORMAT_VORBIS;
|
||
|
|
||
|
return (Ecore_Audio_Object *)out;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
_sndfile_output_del(Ecore_Audio_Object *output)
|
||
|
{
|
||
|
Ecore_Audio_Output *out = (Ecore_Audio_Output *)output;
|
||
|
struct _Ecore_Audio_Sndfile *sndfile = out->module_data;
|
||
|
|
||
|
eina_stringshare_del(sndfile->filename);
|
||
|
free(sndfile->vio);
|
||
|
|
||
|
sf_close(sndfile->handle);
|
||
|
free(sndfile);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
_sndfile_output_volume_set(Ecore_Audio_Object *output, double volume)
|
||
|
{
|
||
|
Ecore_Audio_Output *out = (Ecore_Audio_Output *)output;
|
||
|
struct _Ecore_Audio_Sndfile *sndfile = out->module_data;
|
||
|
|
||
|
if ((volume < 0) || (volume > 1.0))
|
||
|
return;
|
||
|
|
||
|
sndfile->volume = volume;
|
||
|
}
|
||
|
|
||
|
static double
|
||
|
_sndfile_output_volume_get(Ecore_Audio_Object *output)
|
||
|
{
|
||
|
Ecore_Audio_Output *out = (Ecore_Audio_Output *)output;
|
||
|
struct _Ecore_Audio_Sndfile *sndfile = out->module_data;
|
||
|
|
||
|
return sndfile->volume;
|
||
|
}
|
||
|
|
||
|
static Eina_Bool
|
||
|
_sndfile_output_write_cb(void *data)
|
||
|
{
|
||
|
Ecore_Audio_Output *out = (Ecore_Audio_Output *)data;
|
||
|
struct _Ecore_Audio_Sndfile *sndfile = out->module_data;
|
||
|
Ecore_Audio_Input *in;
|
||
|
int written, bread;
|
||
|
void *buf;
|
||
|
|
||
|
buf = malloc(1024);
|
||
|
|
||
|
in = eina_list_data_get(out->inputs);
|
||
|
|
||
|
bread = ecore_audio_input_read((Ecore_Audio_Object *)in, buf, 1024);
|
||
|
DBG("read: %i", bread);
|
||
|
if (bread == 0)
|
||
|
{
|
||
|
DBG("read done");
|
||
|
out->paused = EINA_TRUE;
|
||
|
return EINA_FALSE;
|
||
|
}
|
||
|
|
||
|
// XXX: Check for errors
|
||
|
written = sf_write_float(sndfile->handle, buf, bread/4)*4;
|
||
|
|
||
|
return EINA_TRUE;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
_sndfile_output_paused_set(Ecore_Audio_Object *output, Eina_Bool paused)
|
||
|
{
|
||
|
struct _Ecore_Audio_Sndfile *sndfile = output->module_data;
|
||
|
Ecore_Audio_Output *out = (Ecore_Audio_Output *)output;
|
||
|
|
||
|
if (paused && sndfile->timer)
|
||
|
ecore_timer_del(sndfile->timer);
|
||
|
else if (out->inputs)
|
||
|
sndfile->timer = ecore_timer_add(0.01, _sndfile_output_write_cb, output);
|
||
|
}
|
||
|
|
||
|
static Eina_Bool
|
||
|
_sndfile_output_add_input(Ecore_Audio_Object *output, Ecore_Audio_Object *input)
|
||
|
{
|
||
|
struct _Ecore_Audio_Sndfile *sndfile = output->module_data;
|
||
|
struct _Ecore_Audio_Sndfile_Private *priv = output->module->priv;
|
||
|
|
||
|
Ecore_Audio_Output *out = (Ecore_Audio_Output *)output;
|
||
|
Ecore_Audio_Input *in = (Ecore_Audio_Input *)input;
|
||
|
|
||
|
sndfile->sfinfo.samplerate = in->samplerate;
|
||
|
sndfile->sfinfo.channels = in->channels;
|
||
|
|
||
|
if (sndfile->vio)
|
||
|
sndfile->handle = sf_open_virtual(&priv->vio_wrapper, SFM_WRITE, &sndfile->sfinfo, out);
|
||
|
else if (sndfile->filename)
|
||
|
sndfile->handle = sf_open(sndfile->filename, SFM_WRITE, &sndfile->sfinfo);
|
||
|
|
||
|
if (output->paused)
|
||
|
return EINA_TRUE;
|
||
|
|
||
|
if (!out->inputs)
|
||
|
sndfile->timer = ecore_timer_add(0.01, _sndfile_output_write_cb, output);
|
||
|
|
||
|
return EINA_TRUE;
|
||
|
}
|
||
|
|
||
|
static Eina_Bool
|
||
|
_sndfile_output_del_input(Ecore_Audio_Object *out EINA_UNUSED, Ecore_Audio_Object *in EINA_UNUSED)
|
||
|
{
|
||
|
//XXX: Check if it's the last one and pause the timer
|
||
|
return EINA_TRUE;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
_sndfile_output_update_input_format(Ecore_Audio_Object *out, Ecore_Audio_Object *in)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
static struct output_api outops = {
|
||
|
.output_new = _sndfile_output_new,
|
||
|
.output_del = _sndfile_output_del,
|
||
|
.output_volume_set = _sndfile_output_volume_set,
|
||
|
.output_volume_get = _sndfile_output_volume_get,
|
||
|
.output_paused_set = _sndfile_output_paused_set,
|
||
|
.output_add_input = _sndfile_output_add_input,
|
||
|
.output_del_input = _sndfile_output_del_input,
|
||
|
.output_update_input_format = _sndfile_output_update_input_format,
|
||
|
};
|
||
|
|
||
|
EAPI void ecore_audio_output_sndfile_filename_set(Ecore_Audio_Object *output, const char *filename)
|
||
|
{
|
||
|
Ecore_Audio_Output *out = (Ecore_Audio_Output *)output;
|
||
|
struct _Ecore_Audio_Sndfile *sndfile = out->module_data;
|
||
|
|
||
|
if (sndfile->handle)
|
||
|
sf_close(sndfile->handle);
|
||
|
|
||
|
if (sndfile->filename)
|
||
|
eina_stringshare_del(sndfile->filename);
|
||
|
|
||
|
if (sndfile->vio)
|
||
|
{
|
||
|
free(sndfile->vio);
|
||
|
sndfile->vio = NULL;
|
||
|
}
|
||
|
|
||
|
sndfile->filename = eina_stringshare_add(filename);
|
||
|
}
|
||
|
|
||
|
EAPI void ecore_audio_output_sndfile_format_set(Ecore_Audio_Object *output, int format)
|
||
|
{
|
||
|
Ecore_Audio_Output *out = (Ecore_Audio_Output *)output;
|
||
|
struct _Ecore_Audio_Sndfile *sndfile = out->module_data;
|
||
|
|
||
|
sndfile->sfinfo.format = format;
|
||
|
}
|
||
|
|
||
|
EAPI void ecore_audio_output_sndfile_vio_set(Ecore_Audio_Object *output, Ecore_Audio_Vio *vio)
|
||
|
{
|
||
|
struct _Ecore_Audio_Sndfile *sndfile = output->module_data;
|
||
|
|
||
|
if (sndfile->handle)
|
||
|
sf_close(sndfile->handle);
|
||
|
|
||
|
if (sndfile->filename)
|
||
|
{
|
||
|
eina_stringshare_del(sndfile->filename);
|
||
|
sndfile->filename = NULL;
|
||
|
}
|
||
|
|
||
|
if (sndfile->vio)
|
||
|
free(sndfile->vio);
|
||
|
|
||
|
sndfile->vio = calloc(1, sizeof(Ecore_Audio_Vio));
|
||
|
sndfile->vio->get_length = vio->get_length;
|
||
|
sndfile->vio->seek = vio->seek;
|
||
|
sndfile->vio->write = vio->write;
|
||
|
sndfile->vio->tell = vio->tell;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief Initialize the Ecore_Audio sndfile module
|
||
|
*
|
||
|
* @return the initialized module on success, NULL on error
|
||
|
*/
|
||
|
Ecore_Audio_Module *
|
||
|
ecore_audio_sndfile_init(void)
|
||
|
{
|
||
|
struct _Ecore_Audio_Sndfile_Private *priv;
|
||
|
|
||
|
module = calloc(1, sizeof(Ecore_Audio_Module));
|
||
|
if (!module)
|
||
|
{
|
||
|
ERR("Could not allocate memory for module.");
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
priv = calloc(1, sizeof(struct _Ecore_Audio_Sndfile_Private));
|
||
|
if (!priv)
|
||
|
{
|
||
|
ERR("Could not allocate memory for private module region.");
|
||
|
free(module);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
priv->vio_wrapper.get_filelen = _sndfile_wrap_get_filelen;
|
||
|
priv->vio_wrapper.seek = _sndfile_wrap_seek;
|
||
|
priv->vio_wrapper.read = _sndfile_wrap_read;
|
||
|
priv->vio_wrapper.write = _sndfile_wrap_write;
|
||
|
priv->vio_wrapper.tell = _sndfile_wrap_tell;
|
||
|
|
||
|
ECORE_MAGIC_SET(module, ECORE_MAGIC_AUDIO_MODULE);
|
||
|
module->type = ECORE_AUDIO_TYPE_SNDFILE;
|
||
|
module->name = "sndfile";
|
||
|
module->priv = priv;
|
||
|
module->inputs = NULL;
|
||
|
module->outputs = NULL;
|
||
|
module->in_ops = &inops;
|
||
|
module->out_ops = &outops;
|
||
|
|
||
|
DBG("Initialized");
|
||
|
return module;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @brief Shut down the Ecore_Audio sndfile module
|
||
|
*/
|
||
|
void
|
||
|
ecore_audio_sndfile_shutdown(void)
|
||
|
{
|
||
|
struct _Ecore_Audio_Sndfile_Private *priv = (struct _Ecore_Audio_Sndfile_Private *)module->priv;
|
||
|
|
||
|
free(priv);
|
||
|
free(module);
|
||
|
module = NULL;
|
||
|
|
||
|
DBG("Shutting down");
|
||
|
}
|
||
|
|
||
|
#endif /* HAVE_SNDFILE */
|