/* XMMS2 - X Music Multiplexer System * Copyright (C) 2003-2007 XMMS2 Team * * PLUGINS ARE NOT CONSIDERED TO BE DERIVED WORK !!! * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. */ #include "xmms/xmms_decoderplugin.h" #include "xmms/xmms_log.h" #include #include #include #include #include #include #include typedef struct xmms_speex_data_St { void *speex_state; SpeexBits speex_bits; SpeexHeader *speexheader; ogg_sync_state sync_state; ogg_stream_state stream_state; ogg_page ogg_page; ogg_packet ogg_packet; gchar *ogg_data; } xmms_speex_data_t; /* * Function prototypes */ static gboolean xmms_speex_new (xmms_decoder_t *decoder); static gboolean xmms_speex_init (xmms_decoder_t *decoder, gint mode); static gboolean xmms_speex_seek (xmms_decoder_t *decoder, guint samples); static gboolean xmms_speex_decode_block (xmms_decoder_t *decoder); static void xmms_speex_destroy (xmms_decoder_t *decoder); static void xmms_speex_get_mediainfo (xmms_decoder_t *decoder); /* * Plugin header */ xmms_plugin_t * xmms_plugin_get (void) { xmms_plugin_t *plugin; plugin = xmms_plugin_new (XMMS_PLUGIN_TYPE_DECODER, XMMS_DECODER_PLUGIN_API_VERSION, "speex", "Speex Decoder", XMMS_VERSION, "Speex decoder"); if (!plugin) { return NULL; } xmms_plugin_info_add (plugin, "URL", "http://www.speex.org/"); xmms_plugin_info_add (plugin, "Author", "XMMS Team"); xmms_plugin_method_add (plugin, XMMS_PLUGIN_METHOD_NEW, xmms_speex_new); xmms_plugin_method_add (plugin, XMMS_PLUGIN_METHOD_INIT, xmms_speex_init); xmms_plugin_method_add (plugin, XMMS_PLUGIN_METHOD_SEEK, xmms_speex_seek); xmms_plugin_method_add (plugin, XMMS_PLUGIN_METHOD_DESTROY, xmms_speex_destroy); xmms_plugin_method_add (plugin, XMMS_PLUGIN_METHOD_DECODE_BLOCK, xmms_speex_decode_block); xmms_plugin_method_add (plugin, XMMS_PLUGIN_METHOD_GET_MEDIAINFO, xmms_speex_get_mediainfo); //xmms_plugin_properties_add (plugin, XMMS_PLUGIN_PROPERTY_FAST_FWD); //xmms_plugin_properties_add (plugin, XMMS_PLUGIN_PROPERTY_REWIND); xmms_plugin_config_property_register (plugin, "perceptual_enhancer", "1", NULL, NULL); xmms_plugin_magic_add (plugin, "ogg/speex header", "audio/x-speex", "0 string OggS", ">4 byte 0", ">>28 string Speex ", NULL); return plugin; } static gboolean xmms_speex_new (xmms_decoder_t *decoder) { xmms_speex_data_t *data; g_return_val_if_fail (decoder, FALSE); data = g_new0 (xmms_speex_data_t, 1); xmms_decoder_private_data_set (decoder, data); return TRUE; } static gboolean xmms_speex_init (xmms_decoder_t *decoder, gint mode) { gint pe; xmms_config_property_t *val; xmms_speex_data_t *data; xmms_error_t error; xmms_transport_t *transport; g_return_val_if_fail (decoder, FALSE); data = xmms_decoder_private_data_get (decoder); g_return_val_if_fail (data, FALSE); transport = xmms_decoder_transport_get (decoder); g_return_val_if_fail (transport, FALSE); ogg_sync_init (&data->sync_state); speex_bits_init (&data->speex_bits); /* Find the speex header */ while (42) { gint ret; data->ogg_data = ogg_sync_buffer (&data->sync_state, 1024); ret = xmms_transport_read (transport, data->ogg_data, 1024, &error); ogg_sync_wrote (&data->sync_state, ret); if (ret <= 0) { return FALSE; } if (ogg_sync_pageout (&data->sync_state, &data->ogg_page) == 1) { break; } } ogg_stream_init (&data->stream_state, ogg_page_serialno (&data->ogg_page)); if (ogg_stream_pagein (&data->stream_state, &data->ogg_page) < 0) { return FALSE; } if (ogg_stream_packetout (&data->stream_state, &data->ogg_packet) != 1) { return FALSE; } data->speexheader = speex_packet_to_header (data->ogg_packet.packet, data->ogg_packet.bytes); data->speex_state = speex_decoder_init (speex_mode_list[data->speexheader->mode]); val = xmms_plugin_config_lookup (xmms_decoder_plugin_get (decoder), "perceptual_enhancer"); pe = xmms_config_property_get_int (val); speex_decoder_ctl (data->speex_state, SPEEX_SET_ENH, &pe); ogg_sync_pageout (&data->sync_state, &data->ogg_page); ogg_stream_pagein (&data->stream_state, &data->ogg_page); ogg_stream_packetout (&data->stream_state, &data->ogg_packet); xmms_decoder_format_add (decoder, XMMS_SAMPLE_FORMAT_S16, data->speexheader->nb_channels, data->speexheader->rate); /* we don't have to care about the return value other than NULL, as there is only one format (to rule them all) */ if (xmms_decoder_format_finish (decoder) == NULL) { return FALSE; } return TRUE; } static gboolean xmms_speex_seek (xmms_decoder_t *decoder, guint samples) { g_return_val_if_fail (decoder, FALSE); return FALSE; /* Seeking not supported right now */ } static gboolean xmms_speex_decode_block (xmms_decoder_t *decoder) { gint ret; gfloat outfloat [2000]; gint16 outshort [2000]; xmms_speex_data_t *data; xmms_error_t error; xmms_transport_t *transport; SpeexStereoState stereo = SPEEX_STEREO_STATE_INIT; g_return_val_if_fail (decoder, FALSE); data = xmms_decoder_private_data_get (decoder); g_return_val_if_fail (data, FALSE); transport = xmms_decoder_transport_get (decoder); g_return_val_if_fail (transport, FALSE); if (ogg_stream_packetout (&data->stream_state, &data->ogg_packet) > 0) { gint frame, cnt; speex_bits_read_from (&data->speex_bits, data->ogg_packet.packet, data->ogg_packet.bytes); for (frame = 0; frame < data->speexheader->frames_per_packet; frame++) { speex_decode (data->speex_state, &data->speex_bits, outfloat); if (data->speexheader->nb_channels == 2) { speex_decode_stereo (outfloat, data->speexheader->frame_size,&stereo); } for (cnt = 0; cnt < data->speexheader->frame_size * data->speexheader->nb_channels; cnt++) { outshort [cnt] = outfloat [cnt]; } xmms_decoder_write (decoder, (gchar *) outshort, data->speexheader->frame_size * data->speexheader->nb_channels * 2); } return TRUE; } /* Need more data */ data->ogg_data = ogg_sync_buffer (&data->sync_state, 200); ret = xmms_transport_read (transport, data->ogg_data, 200, &error); ogg_sync_wrote (&data->sync_state, ret); if ( ret <= 0 ) { return FALSE; } if (ogg_sync_pageout (&data->sync_state, &data->ogg_page) < 1) { return TRUE; } if (ogg_stream_pagein (&data->stream_state, &data->ogg_page) != 0) { return TRUE; } return TRUE; } static void xmms_speex_destroy (xmms_decoder_t *decoder) { xmms_speex_data_t *data; g_return_if_fail (decoder); data = xmms_decoder_private_data_get (decoder); g_return_if_fail (data); ogg_stream_clear (&data->stream_state); ogg_sync_clear (&data->sync_state); speex_decoder_destroy (data->speex_state); speex_bits_destroy (&data->speex_bits); g_free (data->speexheader); g_free (data); } static void xmms_speex_get_mediainfo (xmms_decoder_t *decoder) { xmms_medialib_entry_t entry; xmms_speex_data_t *data; g_return_if_fail (decoder); if (!xmms_speex_init (decoder, 0)) return; data = xmms_decoder_private_data_get (decoder); g_return_if_fail (data); entry = xmms_medialib_entry_new (NULL); xmms_medialib_entry_property_set_int (entry, XMMS_MEDIALIB_ENTRY_PROPERTY_BITRATE, data->speexheader->bitrate); xmms_medialib_entry_send_update (entry); }