/** @file vocoder.c * Vocoder effect plugin * * Copyright (C) 2006-2007 XMMS2 Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include "xmms/xmms_xformplugin.h" #include "xmms/xmms_sample.h" #include "xmms/xmms_log.h" #include #include #include #include #include #include #include "pvocoder.h" typedef struct { pvocoder_t *pvoc; SRC_STATE *resampler; gint winsize; gint channels; gint bufsize; xmms_sample_t *iobuf; pvocoder_sample_t *procbuf; gfloat *resbuf; GString *outbuf; gfloat speed; gfloat pitch; SRC_DATA resdata; gint attack_detection; gboolean enabled; } xmms_vocoder_data_t; static gboolean xmms_vocoder_plugin_setup (xmms_xform_plugin_t *xform_plugin); static gboolean xmms_vocoder_init (xmms_xform_t *xform); static void xmms_vocoder_destroy (xmms_xform_t *xform); static void xmms_vocoder_config_changed (xmms_object_t *object, gconstpointer data, gpointer userdata); static gint xmms_vocoder_read (xmms_xform_t *xform, xmms_sample_t *buf, gint len, xmms_error_t *err); static gint64 xmms_vocoder_seek (xmms_xform_t *xform, gint64 offset, xmms_xform_seek_mode_t whence, xmms_error_t *err); /* * Plugin header */ XMMS_XFORM_PLUGIN ("vocoder", "Vocoder effect", XMMS_VERSION, "Phase vocoder effect plugin", xmms_vocoder_plugin_setup); static gboolean xmms_vocoder_plugin_setup (xmms_xform_plugin_t *xform_plugin) { xmms_xform_methods_t methods; XMMS_XFORM_METHODS_INIT (methods); methods.init = xmms_vocoder_init; methods.destroy = xmms_vocoder_destroy; methods.read = xmms_vocoder_read; methods.seek = xmms_vocoder_seek; xmms_xform_plugin_methods_set (xform_plugin, &methods); xmms_xform_plugin_config_property_register (xform_plugin, "speed", "100", NULL, NULL); xmms_xform_plugin_config_property_register (xform_plugin, "pitch", "100", NULL, NULL); xmms_xform_plugin_config_property_register (xform_plugin, "attack_detection", "0", NULL, NULL); xmms_xform_plugin_indata_add (xform_plugin, XMMS_STREAM_TYPE_MIMETYPE, "audio/pcm", XMMS_STREAM_TYPE_FMT_FORMAT, XMMS_SAMPLE_FORMAT_S16, XMMS_STREAM_TYPE_END); return TRUE; } static gboolean xmms_vocoder_init (xmms_xform_t *xform) { xmms_vocoder_data_t *priv; xmms_config_property_t *config; g_return_val_if_fail (xform, FALSE); priv = g_new0 (xmms_vocoder_data_t, 1); priv->winsize = 2048; priv->channels = xmms_xform_indata_get_int (xform, XMMS_STREAM_TYPE_FMT_CHANNELS); priv->bufsize = priv->winsize * priv->channels; priv->iobuf = g_malloc (priv->bufsize * sizeof (gint16)); priv->procbuf = g_malloc (priv->bufsize * sizeof (pvocoder_sample_t)); priv->resbuf = g_malloc (priv->bufsize * sizeof (gfloat)); priv->outbuf = g_string_new (NULL); priv->pvoc = pvocoder_init (priv->winsize, priv->channels); g_return_val_if_fail (priv->pvoc, FALSE); priv->resampler = src_new (SRC_LINEAR, priv->channels, NULL); g_return_val_if_fail (priv->resampler, FALSE); xmms_xform_private_data_set (xform, priv); config = xmms_xform_config_lookup (xform, "enabled"); g_return_val_if_fail (config, FALSE); xmms_config_property_callback_set (config, xmms_vocoder_config_changed, priv); priv->enabled = !!xmms_config_property_get_int (config); config = xmms_xform_config_lookup (xform, "speed"); g_return_val_if_fail (config, FALSE); xmms_config_property_callback_set (config, xmms_vocoder_config_changed, priv); priv->speed = (gfloat) xmms_config_property_get_int (config) / 100.0; config = xmms_xform_config_lookup (xform, "pitch"); g_return_val_if_fail (config, FALSE); xmms_config_property_callback_set (config, xmms_vocoder_config_changed, priv); priv->pitch = 100.0 / (gfloat) xmms_config_property_get_int (config); config = xmms_xform_config_lookup (xform, "attack_detection"); g_return_val_if_fail (config, FALSE); xmms_config_property_callback_set (config, xmms_vocoder_config_changed, priv); priv->attack_detection = !!xmms_config_property_get_int (config); pvocoder_set_scale (priv->pvoc, priv->speed * priv->pitch); pvocoder_set_attack_detection (priv->pvoc, priv->attack_detection); priv->resdata.data_in = NULL; priv->resdata.input_frames = 0; priv->resdata.data_out = priv->resbuf; priv->resdata.output_frames = priv->winsize; priv->resdata.src_ratio = priv->pitch; priv->resdata.end_of_input = 0; xmms_xform_outdata_type_copy (xform); return TRUE; } static void xmms_vocoder_destroy (xmms_xform_t *xform) { xmms_vocoder_data_t *data; xmms_config_property_t *config; g_return_if_fail (xform); data = xmms_xform_private_data_get (xform); g_return_if_fail (data); config = xmms_xform_config_lookup (xform, "enabled"); xmms_config_property_callback_remove (config, xmms_vocoder_config_changed); config = xmms_xform_config_lookup (xform, "speed"); xmms_config_property_callback_remove (config, xmms_vocoder_config_changed); config = xmms_xform_config_lookup (xform, "pitch"); xmms_config_property_callback_remove (config, xmms_vocoder_config_changed); config = xmms_xform_config_lookup (xform, "attack_detection"); xmms_config_property_callback_remove (config, xmms_vocoder_config_changed); pvocoder_close (data->pvoc); src_delete (data->resampler); g_string_free (data->outbuf, TRUE); g_free (data->resbuf); g_free (data->procbuf); g_free (data->iobuf); g_free (data); } static void xmms_vocoder_config_changed (xmms_object_t *object, gconstpointer objdata, gpointer userdata) { xmms_config_property_t *val; xmms_vocoder_data_t *data; const gchar *name; gint value; g_return_if_fail (object); g_return_if_fail (userdata); val = (xmms_config_property_t *) object; data = (xmms_vocoder_data_t *) userdata; name = xmms_config_property_get_name (val); value = xmms_config_property_get_int (val); XMMS_DBG ("config value changed! %s => %d", name, value); /* we are passed the full config key, not just the last token, * which makes this code kinda ugly. * fix when bug 97 has been resolved */ name = strrchr (name, '.') + 1; if (!strcmp (name, "enabled")) { data->enabled = !!value; } else if (!strcmp (name, "speed")) { data->speed = (gfloat) value / 100.0; pvocoder_set_scale (data->pvoc, data->speed * data->pitch); } else if (!strcmp (name, "pitch") && value != 0) { data->pitch = 100.0 / (gfloat) value; data->resdata.src_ratio = data->pitch; pvocoder_set_scale (data->pvoc, data->speed * data->pitch); } else if (!strcmp (name, "attack_detection") && value != 0) { data->attack_detection = value; pvocoder_set_attack_detection (data->pvoc, value); } } static gint xmms_vocoder_read (xmms_xform_t *xform, xmms_sample_t *buffer, gint len, xmms_error_t *error) { xmms_vocoder_data_t *data; guint size; g_return_val_if_fail (xform, -1); data = xmms_xform_private_data_get (xform); g_return_val_if_fail (data, -1); size = MIN (data->outbuf->len, len); while (size == 0) { int i, dpos; gint16 *samples = (gint16 *) data->iobuf; if (!data->enabled) { return xmms_xform_read (xform, buffer, len, error); } if (!data->resdata.input_frames) { dpos = pvocoder_get_chunk (data->pvoc, data->procbuf); while (dpos != 0) { int ret, read = 0; memset (data->procbuf, 0, data->bufsize * sizeof (pvocoder_sample_t)); while (read < data->bufsize * sizeof (gint16)) { ret = xmms_xform_read (xform, data->iobuf+read, data->bufsize * sizeof (gint16)-read, error); if (ret <= 0) { if (!ret && !read) { /* end of file */ return 0; } else if (ret < 0) { return ret; } break; } read += ret; } for (i=0; ibufsize; i++) { data->procbuf[i] = (pvocoder_sample_t) samples[i] / 32767; } pvocoder_add_chunk (data->pvoc, data->procbuf); dpos = pvocoder_get_chunk (data->pvoc, data->procbuf); } data->resdata.data_in = data->procbuf; data->resdata.input_frames = data->winsize; } src_process (data->resampler, &data->resdata); data->resdata.data_in += data->resdata.input_frames_used * data->channels; data->resdata.input_frames -= data->resdata.input_frames_used; for (i=0; iresdata.output_frames_gen * data->channels; i++) { samples[i] = data->resbuf[i] * 32767; } g_string_append_len (data->outbuf, data->iobuf, data->resdata.output_frames_gen * data->channels * sizeof (gint16)); size = MIN (data->outbuf->len, len); } memcpy (buffer, data->outbuf->str, size); g_string_erase (data->outbuf, 0, size); return size; } static gint64 xmms_vocoder_seek (xmms_xform_t *xform, gint64 offset, xmms_xform_seek_mode_t whence, xmms_error_t *err) { return xmms_xform_seek (xform, offset, whence, err); }