From c57eca3f0b254497649ac2474036ccbe406de8c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Fri, 7 Aug 2009 06:41:43 +0200 Subject: spc: Rename SPC plugin to GME as preparation for enabling support for non-SPC gaming console sound files. --- ext/gme/Makefile.am | 10 + ext/gme/gstgme.c | 612 ++++++++++++++++++++++++++++++++++++++++++++++++++++ ext/gme/gstgme.h | 68 ++++++ ext/gme/tag.c | 353 ++++++++++++++++++++++++++++++ ext/gme/tag.h | 37 ++++ 5 files changed, 1080 insertions(+) create mode 100644 ext/gme/Makefile.am create mode 100644 ext/gme/gstgme.c create mode 100644 ext/gme/gstgme.h create mode 100644 ext/gme/tag.c create mode 100644 ext/gme/tag.h (limited to 'ext/gme') diff --git a/ext/gme/Makefile.am b/ext/gme/Makefile.am new file mode 100644 index 00000000..c2bdbfea --- /dev/null +++ b/ext/gme/Makefile.am @@ -0,0 +1,10 @@ +plugin_LTLIBRARIES = libgstgme.la + +libgstgme_la_SOURCES = gstgme.c tag.c + +libgstgme_la_CFLAGS = $(GST_CFLAGS) $(GME_CFLAGS) +libgstgme_la_LIBADD = $(GST_LIBS) $(GME_LIBS) +libgstgme_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) +libgstgme_la_LIBTOOLFLAGS = --tag=disable-static + +noinst_HEADERS = gstgme.h tag.h diff --git a/ext/gme/gstgme.c b/ext/gme/gstgme.c new file mode 100644 index 00000000..0315d6d0 --- /dev/null +++ b/ext/gme/gstgme.c @@ -0,0 +1,612 @@ +/* Copyright (C) 2004-2005,2009 Michael Pyne + * Copyright (C) 2004-2006 Chris Lee + * Copyright (C) 2007 Brian Koropoff + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gstgme.h" + +#include +#include +#include + +static const GstElementDetails gst_spc_dec_details = +GST_ELEMENT_DETAILS ("SNES SPC 700 decoder", + "Codec/Audio/Decoder", + "Uses Blargg's libgme to emulate an SPC processor", + "Chris Lee , Brian Koropoff , Michael Pyne "); + +static GstStaticPadTemplate sink_factory = +GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-spc")); + +static GstStaticPadTemplate src_factory = +GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-raw-int, " + "endianness = (int) BYTE_ORDER, " + "signed = (boolean) TRUE, " + "width = (int) 16, " + "depth = (int) 16, " "rate = (int) 32000, " "channels = (int) 2")); + +GST_BOILERPLATE (GstSpcDec, gst_spc_dec, GstElement, GST_TYPE_ELEMENT); + +static GstFlowReturn gst_spc_dec_chain (GstPad * pad, GstBuffer * buffer); +static gboolean gst_spc_dec_sink_event (GstPad * pad, GstEvent * event); +static gboolean gst_spc_dec_src_event (GstPad * pad, GstEvent * event); +static gboolean gst_spc_dec_src_query (GstPad * pad, GstQuery * query); +static GstStateChangeReturn gst_spc_dec_change_state (GstElement * element, + GstStateChange transition); +static void gst_spc_play (GstPad * pad); +static void gst_spc_dec_dispose (GObject * object); +static gboolean spc_setup (GstSpcDec * spc); + +static gboolean +spc_negotiate (GstSpcDec * spc) +{ + GstCaps *allowed, *caps; + GstStructure *structure; + gint width = 16, depth = 16; + gboolean sign; + int rate = 32000; + int channels = 2; + + allowed = gst_pad_get_allowed_caps (spc->srcpad); + if (!allowed) { + GST_DEBUG_OBJECT (spc, "couldn't get allowed caps"); + return FALSE; + } + + GST_DEBUG_OBJECT (spc, "allowed caps: %" GST_PTR_FORMAT, allowed); + + structure = gst_caps_get_structure (allowed, 0); + gst_structure_get_int (structure, "width", &width); + gst_structure_get_int (structure, "depth", &depth); + + if (width && depth && width != depth) { + GST_DEBUG_OBJECT (spc, "width %d and depth %d are different", width, depth); + gst_caps_unref (allowed); + return FALSE; + } + + gst_structure_get_boolean (structure, "signed", &sign); + gst_structure_get_int (structure, "rate", &rate); + gst_structure_get_int (structure, "channels", &channels); + + caps = gst_caps_new_simple ("audio/x-raw-int", + "endianness", G_TYPE_INT, G_BYTE_ORDER, + "signed", G_TYPE_BOOLEAN, TRUE, + "width", G_TYPE_INT, width, + "depth", G_TYPE_INT, depth, + "rate", G_TYPE_INT, rate, "channels", G_TYPE_INT, channels, NULL); + gst_pad_set_caps (spc->srcpad, caps); + + gst_caps_unref (caps); + gst_caps_unref (allowed); + + return TRUE; +} + +static void +gst_spc_dec_base_init (gpointer g_class) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); + + gst_element_class_set_details (element_class, &gst_spc_dec_details); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&sink_factory)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&src_factory)); +} + +static void +gst_spc_dec_class_init (GstSpcDecClass * klass) +{ + GstElementClass *element_class = (GstElementClass *) klass; + GObjectClass *gobject_class = (GObjectClass *) klass; + + element_class->change_state = GST_DEBUG_FUNCPTR (gst_spc_dec_change_state); + gobject_class->dispose = gst_spc_dec_dispose; +} + +static const GstQueryType * +gst_spc_dec_src_query_type (GstPad * pad) +{ + static const GstQueryType query_types[] = { + GST_QUERY_DURATION, + GST_QUERY_POSITION, + (GstQueryType) 0 + }; + + return query_types; +} + + +static void +gst_spc_dec_init (GstSpcDec * spc, GstSpcDecClass * klass) +{ + spc->sinkpad = gst_pad_new_from_static_template (&sink_factory, "sink"); + /* gst_pad_set_query_function (spc->sinkpad, NULL); */ + gst_pad_set_event_function (spc->sinkpad, gst_spc_dec_sink_event); + gst_pad_set_chain_function (spc->sinkpad, gst_spc_dec_chain); + gst_element_add_pad (GST_ELEMENT (spc), spc->sinkpad); + + spc->srcpad = gst_pad_new_from_static_template (&src_factory, "src"); + gst_pad_set_event_function (spc->srcpad, gst_spc_dec_src_event); + gst_pad_set_query_function (spc->srcpad, gst_spc_dec_src_query); + gst_pad_set_query_type_function (spc->srcpad, gst_spc_dec_src_query_type); + gst_pad_use_fixed_caps (spc->srcpad); + gst_element_add_pad (GST_ELEMENT (spc), spc->srcpad); + + spc->buf = NULL; + spc->player = NULL; + spc->initialized = FALSE; + spc_tag_clear (&spc->tag_info); +} + +static void +gst_spc_dec_dispose (GObject * object) +{ + GstSpcDec *spc = GST_SPC_DEC (object); + + if (spc->buf) { + gst_buffer_unref (spc->buf); + spc->buf = NULL; + } + + spc_tag_free (&spc->tag_info); +} + +static GstFlowReturn +gst_spc_dec_chain (GstPad * pad, GstBuffer * buffer) +{ + GstSpcDec *spc = GST_SPC_DEC (gst_pad_get_parent (pad)); + + /* Accumulate SPC data until end-of-stream, then commence playback. */ + if (spc->buf) { + spc->buf = gst_buffer_join (spc->buf, buffer); + } else { + spc->buf = buffer; + } + + gst_object_unref (spc); + + return GST_FLOW_OK; +} + +static gboolean +gst_spc_dec_sink_event (GstPad * pad, GstEvent * event) +{ + GstSpcDec *spc = GST_SPC_DEC (gst_pad_get_parent (pad)); + gboolean result = TRUE; + gboolean forward = FALSE; + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_EOS: + /* we get EOS when we loaded the complete file, now try to initialize the + * decoding */ + if (!(result = spc_setup (spc))) { + /* can't start, post an ERROR and push EOS downstream */ + GST_ELEMENT_ERROR (spc, STREAM, DEMUX, (NULL), + ("can't start playback")); + forward = TRUE; + } + break; + default: + break; + } + if (forward) + result = gst_pad_push_event (spc->srcpad, event); + else + gst_event_unref (event); + + gst_object_unref (spc); + + return result; +} + +static gint64 +gst_spc_duration (GstSpcDec * spc) +{ + gint64 total_ticks = + spc->tag_info.time_intro + + spc->tag_info.time_loop * spc->tag_info.loop_count + + spc->tag_info.time_end; + if (total_ticks) { + return (gint64) gst_util_uint64_scale (total_ticks, GST_SECOND, 64000); + } else if (spc->tag_info.time_seconds) { + gint64 time = (gint64) spc->tag_info.time_seconds * GST_SECOND; + + return time; + } else { + return (gint64) (3 * 60) * GST_SECOND; + } +} + +static gint64 +gst_spc_fadeout (GstSpcDec * spc) +{ + if (spc->tag_info.time_fade) { + return (gint64) gst_util_uint64_scale ((guint64) spc->tag_info.time_fade, + GST_SECOND, 64000); + } else if (spc->tag_info.time_fade_milliseconds) { + return (gint64) (spc->tag_info.time_fade_milliseconds * GST_MSECOND); + } else { + return 10 * GST_SECOND; + } +} + + +static gboolean +gst_spc_dec_src_event (GstPad * pad, GstEvent * event) +{ + GstSpcDec *spc = GST_SPC_DEC (gst_pad_get_parent (pad)); + gboolean result = FALSE; + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_SEEK: + { + gdouble rate; + GstFormat format; + GstSeekFlags flags; + GstSeekType start_type, stop_type; + gint64 start, stop; + gboolean flush; + + gst_event_parse_seek (event, &rate, &format, &flags, &start_type, &start, + &stop_type, &stop); + + gst_event_unref (event); + + if (format != GST_FORMAT_TIME) { + GST_DEBUG_OBJECT (spc, "seeking is only supported in TIME format"); + break; + } + + if (start_type != GST_SEEK_TYPE_SET || stop_type != GST_SEEK_TYPE_NONE) { + GST_DEBUG_OBJECT (spc, "unsupported seek type"); + break; + } + + if (stop_type == GST_SEEK_TYPE_NONE) + stop = GST_CLOCK_TIME_NONE; + + if (start_type == GST_SEEK_TYPE_SET) { + guint64 cur = gme_tell (spc->player) * GST_MSECOND; + guint64 dest = (guint64) start; + + dest = CLAMP (dest, 0, gst_spc_duration (spc) + gst_spc_fadeout (spc)); + + if (dest == cur) + break; + + flush = (flags & GST_SEEK_FLAG_FLUSH) == GST_SEEK_FLAG_FLUSH; + + if (flush) { + gst_pad_push_event (spc->srcpad, gst_event_new_flush_start ()); + } else { + gst_pad_stop_task (spc->srcpad); + } + + GST_PAD_STREAM_LOCK (spc->srcpad); + + if (flags & GST_SEEK_FLAG_SEGMENT) { + gst_element_post_message (GST_ELEMENT (spc), + gst_message_new_segment_start (GST_OBJECT (spc), format, cur)); + } + + if (flush) { + gst_pad_push_event (spc->srcpad, gst_event_new_flush_stop ()); + } + + if (stop == GST_CLOCK_TIME_NONE) + stop = (guint64) (gst_spc_duration (spc) + gst_spc_fadeout (spc)); + + gst_pad_push_event (spc->srcpad, gst_event_new_new_segment (FALSE, rate, + GST_FORMAT_TIME, dest, stop, dest)); + + spc->seekpoint = dest / GST_MSECOND; /* nsecs to msecs */ + spc->seeking = TRUE; + + gst_pad_start_task (spc->srcpad, (GstTaskFunction) gst_spc_play, + spc->srcpad); + + GST_PAD_STREAM_UNLOCK (spc->srcpad); + result = TRUE; + } + break; + } + default: + result = gst_pad_push_event (spc->sinkpad, event); + break; + } + + gst_object_unref (spc); + + return result; +} + +static gboolean +gst_spc_dec_src_query (GstPad * pad, GstQuery * query) +{ + GstSpcDec *spc = GST_SPC_DEC (gst_pad_get_parent (pad)); + gboolean result = TRUE; + + switch (GST_QUERY_TYPE (query)) { + case GST_QUERY_DURATION: + { + GstFormat format; + + gst_query_parse_duration (query, &format, NULL); + if (!spc->initialized || format != GST_FORMAT_TIME) { + result = FALSE; + break; + } + gst_query_set_duration (query, GST_FORMAT_TIME, + gst_spc_duration (spc) + gst_spc_fadeout (spc)); + break; + } + case GST_QUERY_POSITION: + { + GstFormat format; + + gst_query_parse_position (query, &format, NULL); + if (!spc->initialized || format != GST_FORMAT_TIME) { + result = FALSE; + break; + } + gst_query_set_position (query, GST_FORMAT_TIME, + (gint64) gme_tell (spc->player) * GST_MSECOND); + break; + } + default: + result = gst_pad_query_default (pad, query); + break; + } + + gst_object_unref (spc); + + return result; +} + +static void +gst_spc_play (GstPad * pad) +{ + GstSpcDec *spc = GST_SPC_DEC (gst_pad_get_parent (pad)); + GstFlowReturn flow_return; + GstBuffer *out; + gboolean seeking = spc->seeking; + gme_err_t gme_err = NULL; + gint64 duration, fade, end, position; + const int NUM_SAMPLES = 1600; /* 4 bytes (stereo 16-bit) per sample */ + + if (!seeking) { + out = gst_buffer_new_and_alloc (NUM_SAMPLES * 4); + gst_buffer_set_caps (out, GST_PAD_CAPS (pad)); + GST_BUFFER_TIMESTAMP (out) = gme_tell (spc->player) * GST_MSECOND; + + gme_err = + gme_play (spc->player, NUM_SAMPLES * 2, + (short *) GST_BUFFER_DATA (out)); + if (gme_err) { + GST_ELEMENT_ERROR (spc, STREAM, DEMUX, (NULL), (gme_err)); + gst_pad_pause_task (pad); + gst_pad_push_event (pad, gst_event_new_eos ()); + gst_object_unref (spc); + return; + } + } else { + gme_seek (spc->player, spc->seekpoint); + spc->seeking = FALSE; + + out = gst_buffer_new (); + gst_buffer_set_caps (out, GST_PAD_CAPS (pad)); + } + + duration = gst_spc_duration (spc); + fade = gst_spc_fadeout (spc); + end = duration + fade; + position = gme_tell (spc->player) * GST_MSECOND; + + if (position >= duration) { + gint16 *data = (gint16 *) GST_BUFFER_DATA (out); + guint32 size = GST_BUFFER_SIZE (out) / sizeof (gint16); + unsigned int i; + + gint64 num = (fade - (position - duration)); + + for (i = 0; i < size; i++) { + /* Apply a parabolic volume envelope */ + data[i] = (gint16) (data[i] * num / fade * num / fade); + } + } + + if ((flow_return = gst_pad_push (spc->srcpad, out)) != GST_FLOW_OK) { + GST_DEBUG_OBJECT (spc, "pausing task, reason %s", + gst_flow_get_name (flow_return)); + + gst_pad_pause_task (pad); + + if (GST_FLOW_IS_FATAL (flow_return) || flow_return == GST_FLOW_NOT_LINKED) { + gst_pad_push_event (pad, gst_event_new_eos ()); + } + } + + if (position >= end) { + gst_pad_pause_task (pad); + gst_pad_push_event (pad, gst_event_new_eos ()); + } + + gst_object_unref (spc); + + return; +} + +static gboolean +spc_setup (GstSpcDec * spc) +{ + spc_tag_info *info; + gme_err_t gme_err = NULL; + GstTagList *taglist; + guint64 total_duration; + + if (!spc->buf || !spc_negotiate (spc)) { + return FALSE; + } + + info = &(spc->tag_info); + + spc_tag_get_info (GST_BUFFER_DATA (spc->buf), GST_BUFFER_SIZE (spc->buf), + info); + + taglist = gst_tag_list_new (); + + if (info->title) + gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_TITLE, + info->title, NULL); + if (info->artist) + gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_ARTIST, + info->artist, NULL); + /* Prefer the name of the official soundtrack over the name of the game (since this is + * how track numbers are derived) + */ + if (info->album) + gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_ALBUM, + info->album, NULL); + else if (info->game) + gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_ALBUM, info->game, + NULL); + if (info->year) { + GDate *date = g_date_new_dmy (1, 1, info->year); + + gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_DATE, date, NULL); + g_date_free (date); + } + if (info->track) { + gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_TRACK_NUMBER, + info->track, NULL); + } + if (info->comment) + gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_COMMENT, + info->comment, NULL); + if (info->disc) + gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, + GST_TAG_ALBUM_VOLUME_NUMBER, info->disc, NULL); + if (info->publisher) + gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_ORGANIZATION, + info->publisher, NULL); + if (info->dumper) + gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_CONTACT, + info->dumper, NULL); + if (info->emulator) + gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_ENCODER, + info->emulator == EMU_ZSNES ? "ZSNES" : "Snes9x", NULL); + + total_duration = (guint64) (gst_spc_duration (spc) + gst_spc_fadeout (spc)); + + gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, + GST_TAG_DURATION, total_duration, + GST_TAG_GENRE, "Game", GST_TAG_CODEC, "SPC700", NULL); + + gst_element_found_tags_for_pad (GST_ELEMENT (spc), spc->srcpad, taglist); + + /* spc_tag_info_free(&info); */ + + gme_err = + gme_open_data (GST_BUFFER_DATA (spc->buf), GST_BUFFER_SIZE (spc->buf), + &spc->player, 32000); + if (gme_err || !spc->player) { + if (spc->player) { + gme_delete (spc->player); + spc->player = NULL; + } + + GST_ELEMENT_ERROR (spc, STREAM, DEMUX, (NULL), (gme_err)); + + return FALSE; + } +#ifdef HAVE_LIBGME_ACCURACY + /* TODO: Is it worth it to make this optional? */ + gme_enable_accuracy (spc->player, 1); +#endif + gme_start_track (spc->player, 0); + + gst_pad_push_event (spc->srcpad, gst_event_new_new_segment (FALSE, 1.0, + GST_FORMAT_TIME, 0, -1, 0)); + + gst_pad_start_task (spc->srcpad, (GstTaskFunction) gst_spc_play, spc->srcpad); + + /* We can't unreference this buffer because we might need to re-initialize + * the emulator with the original data during a reverse seek + * gst_buffer_unref (spc->buf); + * spc->buf = NULL; + */ + spc->initialized = TRUE; + spc->seeking = FALSE; + spc->seekpoint = 0; + return spc->initialized; +} + +static GstStateChangeReturn +gst_spc_dec_change_state (GstElement * element, GstStateChange transition) +{ + GstStateChangeReturn result; + GstSpcDec *dec; + + dec = GST_SPC_DEC (element); + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_PAUSED: + break; + default: + break; + } + + result = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + if (result == GST_STATE_CHANGE_FAILURE) + return result; + + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + if (dec->buf) { + gst_buffer_unref (dec->buf); + dec->buf = NULL; + } + break; + default: + break; + } + + return result; +} + +static gboolean +plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "spcdec", GST_RANK_PRIMARY, + GST_TYPE_SPC_DEC); +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "spcdec", + "OpenSPC Audio Decoder", + plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN); diff --git a/ext/gme/gstgme.h b/ext/gme/gstgme.h new file mode 100644 index 00000000..3d7e4899 --- /dev/null +++ b/ext/gme/gstgme.h @@ -0,0 +1,68 @@ +/* Copyright (C) 2004-2005, 2009 Michael Pyne + * Copyright (C) 2004-2006 Chris Lee + * Copyright (C) 2007 Brian Koropoff + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GST_SPC_DEC_H__ +#define __GST_SPC_DEC_H__ + +#include + +#include +#include "tag.h" + +G_BEGIN_DECLS + +#define GST_TYPE_SPC_DEC \ + (gst_spc_dec_get_type()) +#define GST_SPC_DEC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SPC_DEC,GstSpcDec)) +#define GST_SPC_DEC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_SPC_DEC,GstSpcDecClass)) +#define GST_IS_SPC_DEC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SPC_DEC)) +#define GST_IS_SPC_DEC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SPC_DEC)) + +typedef struct _GstSpcDec GstSpcDec; +typedef struct _GstSpcDecClass GstSpcDecClass; + +struct _GstSpcDec +{ + GstElement element; + + GstPad *sinkpad; + GstPad *srcpad; + + GstBuffer *buf; + Music_Emu *player; + gboolean initialized; + gboolean seeking; + int seekpoint; + + spc_tag_info tag_info; +}; + +struct _GstSpcDecClass +{ + GstElementClass parent_class; +}; + +G_END_DECLS + +#endif /* __GST_SPC_DEC_H__ */ diff --git a/ext/gme/tag.c b/ext/gme/tag.c new file mode 100644 index 00000000..41ce812f --- /dev/null +++ b/ext/gme/tag.c @@ -0,0 +1,353 @@ +/* Copyright (C) 2007 Brian Koropoff + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include + +#include +#include +#include + + +#define EXTENDED_OFFSET 0x10200 +#define EXTENDED_MAGIC ((guint32) ('x' << 0 | 'i' << 8 | 'd' << 16 | '6' << 24)) + +#define TYPE_LENGTH 0x0 +#define TYPE_STRING 0x1 +#define TYPE_INTEGER 0x4 + +#define TAG_TITLE 0x01 +#define TAG_GAME 0x02 +#define TAG_ARTIST 0x03 +#define TAG_DUMPER 0x04 +#define TAG_DUMP_DATE 0x05 +#define TAG_EMULATOR 0x06 +#define TAG_COMMENT 0x07 +#define TAG_ALBUM 0x10 +#define TAG_DISC 0x11 +#define TAG_TRACK 0x12 +#define TAG_PUBLISHER 0x13 +#define TAG_YEAR 0x14 +#define TAG_INTRO 0x30 +#define TAG_LOOP 0x31 +#define TAG_END 0x32 +#define TAG_FADE 0x33 +#define TAG_MUTED 0x34 +#define TAG_COUNT 0x35 +#define TAG_AMP 0x36 + +#define READ_INT8(data, offset) (data[offset]) +#define READ_INT16(data, offset) ((data[offset] << 0) + (data[offset+1] << 8)) +#define READ_INT24(data, offset) ((data[offset] << 0) + (data[offset+1] << 8) + (data[offset+2] << 16)) +#define READ_INT32(data, offset) ((data[offset] << 0) + (data[offset+1] << 8) + (data[offset+2] << 16) + (data[offset+3] << 24)) + +static inline gboolean +spc_tag_is_extended (guchar * data, guint length) +{ + // Extended tags come at the end of the file (at a known offset) + // and start with "xid6" + return (length > EXTENDED_OFFSET + 4 + && READ_INT32 (data, EXTENDED_OFFSET) == EXTENDED_MAGIC); +} + +static inline gboolean +spc_tag_is_text_format (guchar * data, guint length) +{ + // Because the id666 format is brain dead, there's + // no definite way to decide if it is in text + // format. This function implements a set of + // heuristics to make a best-effort guess. + + // If the date field contains separators, it is probably text + if (data[0xA0] == '/' || data[0xA0] == '.') + return TRUE; + // If the first byte of the date field is small (but not 0, + // which could indicate an empty string), it's probably binary + if (data[0x9E] >= 1 && data[0x9E] <= 31) + return FALSE; + + // If all previous tests turned up nothing, assume it's text + return TRUE; +} + +static inline gboolean +spc_tag_is_present (guchar * data, guint length) +{ + return data[0x23] == 26; +} + +static inline GDate * +spc_tag_unpack_date (guint32 packed) +{ + guint dump_year = packed / 10000; + guint dump_month = (packed % 10000) / 100; + guint dump_day = packed % 100; + + if (dump_month == 0) + dump_month = 1; + if (dump_day == 0) + dump_day = 1; + + if (dump_year != 0) + return g_date_new_dmy (dump_day, dump_month, dump_year); + else + return NULL; +} + +void +spc_tag_clear (spc_tag_info * info) +{ + info->title = info->game = info->publisher = info->artist = info->album = + info->comment = info->dumper = NULL; + info->dump_date = NULL; + info->time_seconds = 0; + info->time_fade_milliseconds = 0; + info->time_intro = 0; + info->time_end = 0; + info->time_loop = 0; + info->time_fade = 0; + info->loop_count = 0; + info->muted = 0; + info->disc = 0; + info->amplification = 0; +} + +void +spc_tag_get_info (guchar * data, guint length, spc_tag_info * info) +{ + spc_tag_clear (info); + + if (spc_tag_is_present (data, length)) { + gboolean text_format = spc_tag_is_text_format (data, length); + + info->title = g_new0 (gchar, 0x21); + info->game = g_new0 (gchar, 0x21); + info->artist = g_new0 (gchar, 0x21); + info->dumper = g_new0 (gchar, 0x10); + info->comment = g_new0 (gchar, 0x32); + + strncpy (info->title, (gchar *) & data[0x2E], 32); + strncpy (info->artist, (gchar *) & data[(text_format ? 0xB1 : 0xB0)], 32); + strncpy (info->game, (gchar *) & data[0x4E], 32); + strncpy (info->dumper, (gchar *) & data[0x6E], 16); + strncpy (info->comment, (gchar *) & data[0x7E], 32); + + if (text_format) { + gchar time[4]; + gchar fade[6]; + guint dump_year, dump_month, dump_day; + + strncpy (time, (gchar *) data + 0xA9, 3); + strncpy (fade, (gchar *) data + 0xAC, 5); + + time[3] = fade[5] = 0; + + info->time_seconds = atoi (time); + info->time_fade_milliseconds = atoi (fade); + + dump_year = (guint) atoi ((gchar *) data + 0x9E); + dump_month = (guint) atoi ((gchar *) data + 0x9E + 3); + dump_day = (guint) atoi ((gchar *) data + 0x9E + 3 + 3); + + if (dump_month == 0) + dump_month = 1; + if (dump_day == 0) + dump_day = 1; + if (dump_year != 0) + info->dump_date = g_date_new_dmy (dump_day, dump_month, dump_year); + + info->muted = READ_INT8 (data, 0xD1); + info->emulator = READ_INT8 (data, 0xD2); + } else { + info->time_seconds = READ_INT24 (data, 0xA9); + info->time_fade_milliseconds = READ_INT32 (data, 0xAC); + info->dump_date = spc_tag_unpack_date (READ_INT32 (data, 0x9E)); + info->muted = READ_INT8 (data, 0xD0); + info->emulator = READ_INT8 (data, 0xD1); + } + } + + if (spc_tag_is_extended (data, length)) { + guchar *chunk = data + EXTENDED_OFFSET + 8; + guint32 chunk_size = *((guint32 *) (data + EXTENDED_OFFSET + 4)); + + guchar *subchunk, *subchunk_next; + + for (subchunk = chunk; subchunk < chunk + chunk_size; + subchunk = subchunk_next) { + guint8 tag = READ_INT8 (subchunk, 0); + guint8 type = READ_INT8 (subchunk, 1); + guint16 length = READ_INT16 (subchunk, 2); + guchar *value = subchunk + 4; + + switch (type) { + case TYPE_LENGTH: + { + switch (tag) { + case TAG_TRACK: + info->track = READ_INT8 (subchunk, 2 + 1); + break; + case TAG_YEAR: + info->year = READ_INT16 (subchunk, 2); + break; + case TAG_COUNT: + info->loop_count = READ_INT8 (subchunk, 2); + break; + case TAG_EMULATOR: + info->emulator = READ_INT8 (subchunk, 2); + break; + case TAG_DISC: + info->disc = READ_INT8 (subchunk, 2); + break; + case TAG_MUTED: + info->muted = READ_INT8 (subchunk, 2); + break; + default: + break; + } + + subchunk_next = subchunk + 4; + break; + } + case TYPE_STRING: + { + gchar *dest; + + if (length <= 1) + dest = NULL; + else + switch (tag) { + case TAG_TITLE: + dest = info->title = g_renew (gchar, info->title, length); + break; + case TAG_GAME: + dest = info->game = g_renew (gchar, info->game, length); + break; + case TAG_ARTIST: + dest = info->artist = g_renew (gchar, info->artist, length); + break; + case TAG_ALBUM: + dest = info->album = g_renew (gchar, info->album, length); + break; + case TAG_DUMPER: + dest = info->dumper = g_renew (gchar, info->dumper, length); + break; + case TAG_COMMENT: + dest = info->comment = g_renew (gchar, info->comment, length); + break; + case TAG_PUBLISHER: + dest = info->publisher = + g_renew (gchar, info->publisher, length); + break; + default: + dest = NULL; + break; + } + + if (dest) + strncpy (dest, (gchar *) value, length); + + subchunk_next = value + length; + break; + } + case TYPE_INTEGER: + { + switch (tag) { + case TAG_INTRO: + info->time_intro = READ_INT32 (value, 0); + break; + case TAG_END: + info->time_end = READ_INT32 (value, 0); + break; + case TAG_FADE: + info->time_fade = READ_INT32 (value, 0); + break; + case TAG_LOOP: + info->time_loop = READ_INT32 (value, 0); + break; + case TAG_DUMP_DATE: + info->dump_date = spc_tag_unpack_date (READ_INT32 (value, 0)); + break; + case TAG_AMP: + info->amplification = READ_INT32 (value, 0); + break; + default: + break; + } + subchunk_next = value + length; + break; + } + default: + subchunk_next = value + length; + break; + } + } + } + + if (info->title && !*info->title) { + g_free (info->title); + info->title = NULL; + } + if (info->game && !*info->game) { + g_free (info->game); + info->game = NULL; + } + if (info->artist && !*info->artist) { + g_free (info->artist); + info->artist = NULL; + } + if (info->album && !*info->album) { + g_free (info->album); + info->album = NULL; + } + if (info->publisher && !*info->publisher) { + g_free (info->publisher); + info->publisher = NULL; + } + if (info->comment && !*info->comment) { + g_free (info->comment); + info->comment = NULL; + } + if (info->dumper && !*info->dumper) { + g_free (info->dumper); + info->dumper = NULL; + } +} + +void +spc_tag_free (spc_tag_info * info) +{ + if (info->title) + g_free (info->title); + if (info->game) + g_free (info->game); + if (info->artist) + g_free (info->artist); + if (info->album) + g_free (info->album); + if (info->publisher) + g_free (info->publisher); + if (info->comment) + g_free (info->comment); + if (info->dumper) + g_free (info->dumper); + if (info->dump_date) + g_date_free (info->dump_date); +} diff --git a/ext/gme/tag.h b/ext/gme/tag.h new file mode 100644 index 00000000..5b795893 --- /dev/null +++ b/ext/gme/tag.h @@ -0,0 +1,37 @@ +/* Copyright (C) 2007 Brian Koropoff + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include + +typedef struct +{ + gchar *title, *game, *artist, *album, *publisher; + gchar *dumper, *comment; + enum { EMU_SNES9X = 2, EMU_ZSNES = 1, EMU_UNKNOWN = 0 } emulator; + guint8 track, disc, muted, loop_count; + guint16 year; + guint32 time_seconds, time_fade_milliseconds; + guint32 time_intro, time_loop, time_end, time_fade; + guint32 amplification; + GDate *dump_date; +} spc_tag_info; + +void spc_tag_clear(spc_tag_info* info); +void spc_tag_get_info(guchar* data, guint length, spc_tag_info* info); +void spc_tag_free(spc_tag_info* info); -- cgit v1.2.1 From e3cc12769288a4cdd4ba06c501451ebfec942ee6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Fri, 7 Aug 2009 06:56:54 +0200 Subject: gme: Use gme for tags and duration parsing --- ext/gme/Makefile.am | 4 +- ext/gme/gstgme.c | 149 +++++++--------------- ext/gme/gstgme.h | 3 +- ext/gme/tag.c | 353 ---------------------------------------------------- ext/gme/tag.h | 37 ------ 5 files changed, 45 insertions(+), 501 deletions(-) delete mode 100644 ext/gme/tag.c delete mode 100644 ext/gme/tag.h (limited to 'ext/gme') diff --git a/ext/gme/Makefile.am b/ext/gme/Makefile.am index c2bdbfea..c84d340e 100644 --- a/ext/gme/Makefile.am +++ b/ext/gme/Makefile.am @@ -1,10 +1,10 @@ plugin_LTLIBRARIES = libgstgme.la -libgstgme_la_SOURCES = gstgme.c tag.c +libgstgme_la_SOURCES = gstgme.c libgstgme_la_CFLAGS = $(GST_CFLAGS) $(GME_CFLAGS) libgstgme_la_LIBADD = $(GST_LIBS) $(GME_LIBS) libgstgme_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) libgstgme_la_LIBTOOLFLAGS = --tag=disable-static -noinst_HEADERS = gstgme.h tag.h +noinst_HEADERS = gstgme.h diff --git a/ext/gme/gstgme.c b/ext/gme/gstgme.c index 0315d6d0..0efb8d8a 100644 --- a/ext/gme/gstgme.c +++ b/ext/gme/gstgme.c @@ -158,8 +158,8 @@ gst_spc_dec_init (GstSpcDec * spc, GstSpcDecClass * klass) spc->buf = NULL; spc->player = NULL; + spc->total_duration = GST_CLOCK_TIME_NONE; spc->initialized = FALSE; - spc_tag_clear (&spc->tag_info); } static void @@ -171,8 +171,6 @@ gst_spc_dec_dispose (GObject * object) gst_buffer_unref (spc->buf); spc->buf = NULL; } - - spc_tag_free (&spc->tag_info); } static GstFlowReturn @@ -223,38 +221,6 @@ gst_spc_dec_sink_event (GstPad * pad, GstEvent * event) return result; } -static gint64 -gst_spc_duration (GstSpcDec * spc) -{ - gint64 total_ticks = - spc->tag_info.time_intro + - spc->tag_info.time_loop * spc->tag_info.loop_count + - spc->tag_info.time_end; - if (total_ticks) { - return (gint64) gst_util_uint64_scale (total_ticks, GST_SECOND, 64000); - } else if (spc->tag_info.time_seconds) { - gint64 time = (gint64) spc->tag_info.time_seconds * GST_SECOND; - - return time; - } else { - return (gint64) (3 * 60) * GST_SECOND; - } -} - -static gint64 -gst_spc_fadeout (GstSpcDec * spc) -{ - if (spc->tag_info.time_fade) { - return (gint64) gst_util_uint64_scale ((guint64) spc->tag_info.time_fade, - GST_SECOND, 64000); - } else if (spc->tag_info.time_fade_milliseconds) { - return (gint64) (spc->tag_info.time_fade_milliseconds * GST_MSECOND); - } else { - return 10 * GST_SECOND; - } -} - - static gboolean gst_spc_dec_src_event (GstPad * pad, GstEvent * event) { @@ -293,7 +259,10 @@ gst_spc_dec_src_event (GstPad * pad, GstEvent * event) guint64 cur = gme_tell (spc->player) * GST_MSECOND; guint64 dest = (guint64) start; - dest = CLAMP (dest, 0, gst_spc_duration (spc) + gst_spc_fadeout (spc)); + if (spc->total_duration != GST_CLOCK_TIME_NONE) + dest = CLAMP (dest, 0, spc->total_duration); + else + dest = MAX (0, dest); if (dest == cur) break; @@ -317,8 +286,9 @@ gst_spc_dec_src_event (GstPad * pad, GstEvent * event) gst_pad_push_event (spc->srcpad, gst_event_new_flush_stop ()); } - if (stop == GST_CLOCK_TIME_NONE) - stop = (guint64) (gst_spc_duration (spc) + gst_spc_fadeout (spc)); + if (stop == GST_CLOCK_TIME_NONE + && spc->total_duration != GST_CLOCK_TIME_NONE) + stop = spc->total_duration; gst_pad_push_event (spc->srcpad, gst_event_new_new_segment (FALSE, rate, GST_FORMAT_TIME, dest, stop, dest)); @@ -356,12 +326,12 @@ gst_spc_dec_src_query (GstPad * pad, GstQuery * query) GstFormat format; gst_query_parse_duration (query, &format, NULL); - if (!spc->initialized || format != GST_FORMAT_TIME) { + if (!spc->initialized || format != GST_FORMAT_TIME + || spc->total_duration == GST_CLOCK_TIME_NONE) { result = FALSE; break; } - gst_query_set_duration (query, GST_FORMAT_TIME, - gst_spc_duration (spc) + gst_spc_fadeout (spc)); + gst_query_set_duration (query, GST_FORMAT_TIME, spc->total_duration); break; } case GST_QUERY_POSITION: @@ -395,7 +365,6 @@ gst_spc_play (GstPad * pad) GstBuffer *out; gboolean seeking = spc->seeking; gme_err_t gme_err = NULL; - gint64 duration, fade, end, position; const int NUM_SAMPLES = 1600; /* 4 bytes (stereo 16-bit) per sample */ if (!seeking) { @@ -421,24 +390,6 @@ gst_spc_play (GstPad * pad) gst_buffer_set_caps (out, GST_PAD_CAPS (pad)); } - duration = gst_spc_duration (spc); - fade = gst_spc_fadeout (spc); - end = duration + fade; - position = gme_tell (spc->player) * GST_MSECOND; - - if (position >= duration) { - gint16 *data = (gint16 *) GST_BUFFER_DATA (out); - guint32 size = GST_BUFFER_SIZE (out) / sizeof (gint16); - unsigned int i; - - gint64 num = (fade - (position - duration)); - - for (i = 0; i < size; i++) { - /* Apply a parabolic volume envelope */ - data[i] = (gint16) (data[i] * num / fade * num / fade); - } - } - if ((flow_return = gst_pad_push (spc->srcpad, out)) != GST_FLOW_OK) { GST_DEBUG_OBJECT (spc, "pausing task, reason %s", gst_flow_get_name (flow_return)); @@ -450,7 +401,7 @@ gst_spc_play (GstPad * pad) } } - if (position >= end) { + if (gme_track_ended (spc->player)) { gst_pad_pause_task (pad); gst_pad_push_event (pad, gst_event_new_eos ()); } @@ -463,7 +414,7 @@ gst_spc_play (GstPad * pad) static gboolean spc_setup (GstSpcDec * spc) { - spc_tag_info *info; + gme_info_t *info; gme_err_t gme_err = NULL; GstTagList *taglist; guint64 total_duration; @@ -472,77 +423,60 @@ spc_setup (GstSpcDec * spc) return FALSE; } - info = &(spc->tag_info); + gme_err = + gme_open_data (GST_BUFFER_DATA (spc->buf), GST_BUFFER_SIZE (spc->buf), + &spc->player, 32000); + if (gme_err || !spc->player) { + if (spc->player) { + gme_delete (spc->player); + spc->player = NULL; + } + + GST_ELEMENT_ERROR (spc, STREAM, DEMUX, (NULL), (gme_err)); + + return FALSE; + } - spc_tag_get_info (GST_BUFFER_DATA (spc->buf), GST_BUFFER_SIZE (spc->buf), - info); + gme_err = gme_track_info (spc->player, &info, 0); taglist = gst_tag_list_new (); - if (info->title) + if (info->song) gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_TITLE, - info->title, NULL); - if (info->artist) + info->song, NULL); + if (info->author) gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_ARTIST, - info->artist, NULL); + info->author, NULL); /* Prefer the name of the official soundtrack over the name of the game (since this is * how track numbers are derived) */ - if (info->album) - gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_ALBUM, - info->album, NULL); - else if (info->game) + if (info->game) gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_ALBUM, info->game, NULL); - if (info->year) { - GDate *date = g_date_new_dmy (1, 1, info->year); - gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_DATE, date, NULL); - g_date_free (date); - } - if (info->track) { - gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_TRACK_NUMBER, - info->track, NULL); - } if (info->comment) gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_COMMENT, info->comment, NULL); - if (info->disc) - gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, - GST_TAG_ALBUM_VOLUME_NUMBER, info->disc, NULL); - if (info->publisher) - gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_ORGANIZATION, - info->publisher, NULL); if (info->dumper) gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_CONTACT, info->dumper, NULL); - if (info->emulator) + if (info->copyright) + gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_COPYRIGHT, + info->copyright, NULL); + if (info->system) gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_ENCODER, - info->emulator == EMU_ZSNES ? "ZSNES" : "Snes9x", NULL); + info->system, NULL); - total_duration = (guint64) (gst_spc_duration (spc) + gst_spc_fadeout (spc)); + spc->total_duration = total_duration = + gst_util_uint64_scale_int (info->play_length, GST_MSECOND, 1); gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, - GST_TAG_DURATION, total_duration, - GST_TAG_GENRE, "Game", GST_TAG_CODEC, "SPC700", NULL); + GST_TAG_DURATION, total_duration, NULL); gst_element_found_tags_for_pad (GST_ELEMENT (spc), spc->srcpad, taglist); - /* spc_tag_info_free(&info); */ + g_free (info); - gme_err = - gme_open_data (GST_BUFFER_DATA (spc->buf), GST_BUFFER_SIZE (spc->buf), - &spc->player, 32000); - if (gme_err || !spc->player) { - if (spc->player) { - gme_delete (spc->player); - spc->player = NULL; - } - - GST_ELEMENT_ERROR (spc, STREAM, DEMUX, (NULL), (gme_err)); - - return FALSE; - } #ifdef HAVE_LIBGME_ACCURACY /* TODO: Is it worth it to make this optional? */ gme_enable_accuracy (spc->player, 1); @@ -575,6 +509,7 @@ gst_spc_dec_change_state (GstElement * element, GstStateChange transition) switch (transition) { case GST_STATE_CHANGE_READY_TO_PAUSED: + dec->total_duration = GST_CLOCK_TIME_NONE; break; default: break; diff --git a/ext/gme/gstgme.h b/ext/gme/gstgme.h index 3d7e4899..b4ab17a5 100644 --- a/ext/gme/gstgme.h +++ b/ext/gme/gstgme.h @@ -24,7 +24,6 @@ #include #include -#include "tag.h" G_BEGIN_DECLS @@ -55,7 +54,7 @@ struct _GstSpcDec gboolean seeking; int seekpoint; - spc_tag_info tag_info; + GstClockTime total_duration; }; struct _GstSpcDecClass diff --git a/ext/gme/tag.c b/ext/gme/tag.c deleted file mode 100644 index 41ce812f..00000000 --- a/ext/gme/tag.c +++ /dev/null @@ -1,353 +0,0 @@ -/* Copyright (C) 2007 Brian Koropoff - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#include -#include -#include -#include - -#include -#include -#include - - -#define EXTENDED_OFFSET 0x10200 -#define EXTENDED_MAGIC ((guint32) ('x' << 0 | 'i' << 8 | 'd' << 16 | '6' << 24)) - -#define TYPE_LENGTH 0x0 -#define TYPE_STRING 0x1 -#define TYPE_INTEGER 0x4 - -#define TAG_TITLE 0x01 -#define TAG_GAME 0x02 -#define TAG_ARTIST 0x03 -#define TAG_DUMPER 0x04 -#define TAG_DUMP_DATE 0x05 -#define TAG_EMULATOR 0x06 -#define TAG_COMMENT 0x07 -#define TAG_ALBUM 0x10 -#define TAG_DISC 0x11 -#define TAG_TRACK 0x12 -#define TAG_PUBLISHER 0x13 -#define TAG_YEAR 0x14 -#define TAG_INTRO 0x30 -#define TAG_LOOP 0x31 -#define TAG_END 0x32 -#define TAG_FADE 0x33 -#define TAG_MUTED 0x34 -#define TAG_COUNT 0x35 -#define TAG_AMP 0x36 - -#define READ_INT8(data, offset) (data[offset]) -#define READ_INT16(data, offset) ((data[offset] << 0) + (data[offset+1] << 8)) -#define READ_INT24(data, offset) ((data[offset] << 0) + (data[offset+1] << 8) + (data[offset+2] << 16)) -#define READ_INT32(data, offset) ((data[offset] << 0) + (data[offset+1] << 8) + (data[offset+2] << 16) + (data[offset+3] << 24)) - -static inline gboolean -spc_tag_is_extended (guchar * data, guint length) -{ - // Extended tags come at the end of the file (at a known offset) - // and start with "xid6" - return (length > EXTENDED_OFFSET + 4 - && READ_INT32 (data, EXTENDED_OFFSET) == EXTENDED_MAGIC); -} - -static inline gboolean -spc_tag_is_text_format (guchar * data, guint length) -{ - // Because the id666 format is brain dead, there's - // no definite way to decide if it is in text - // format. This function implements a set of - // heuristics to make a best-effort guess. - - // If the date field contains separators, it is probably text - if (data[0xA0] == '/' || data[0xA0] == '.') - return TRUE; - // If the first byte of the date field is small (but not 0, - // which could indicate an empty string), it's probably binary - if (data[0x9E] >= 1 && data[0x9E] <= 31) - return FALSE; - - // If all previous tests turned up nothing, assume it's text - return TRUE; -} - -static inline gboolean -spc_tag_is_present (guchar * data, guint length) -{ - return data[0x23] == 26; -} - -static inline GDate * -spc_tag_unpack_date (guint32 packed) -{ - guint dump_year = packed / 10000; - guint dump_month = (packed % 10000) / 100; - guint dump_day = packed % 100; - - if (dump_month == 0) - dump_month = 1; - if (dump_day == 0) - dump_day = 1; - - if (dump_year != 0) - return g_date_new_dmy (dump_day, dump_month, dump_year); - else - return NULL; -} - -void -spc_tag_clear (spc_tag_info * info) -{ - info->title = info->game = info->publisher = info->artist = info->album = - info->comment = info->dumper = NULL; - info->dump_date = NULL; - info->time_seconds = 0; - info->time_fade_milliseconds = 0; - info->time_intro = 0; - info->time_end = 0; - info->time_loop = 0; - info->time_fade = 0; - info->loop_count = 0; - info->muted = 0; - info->disc = 0; - info->amplification = 0; -} - -void -spc_tag_get_info (guchar * data, guint length, spc_tag_info * info) -{ - spc_tag_clear (info); - - if (spc_tag_is_present (data, length)) { - gboolean text_format = spc_tag_is_text_format (data, length); - - info->title = g_new0 (gchar, 0x21); - info->game = g_new0 (gchar, 0x21); - info->artist = g_new0 (gchar, 0x21); - info->dumper = g_new0 (gchar, 0x10); - info->comment = g_new0 (gchar, 0x32); - - strncpy (info->title, (gchar *) & data[0x2E], 32); - strncpy (info->artist, (gchar *) & data[(text_format ? 0xB1 : 0xB0)], 32); - strncpy (info->game, (gchar *) & data[0x4E], 32); - strncpy (info->dumper, (gchar *) & data[0x6E], 16); - strncpy (info->comment, (gchar *) & data[0x7E], 32); - - if (text_format) { - gchar time[4]; - gchar fade[6]; - guint dump_year, dump_month, dump_day; - - strncpy (time, (gchar *) data + 0xA9, 3); - strncpy (fade, (gchar *) data + 0xAC, 5); - - time[3] = fade[5] = 0; - - info->time_seconds = atoi (time); - info->time_fade_milliseconds = atoi (fade); - - dump_year = (guint) atoi ((gchar *) data + 0x9E); - dump_month = (guint) atoi ((gchar *) data + 0x9E + 3); - dump_day = (guint) atoi ((gchar *) data + 0x9E + 3 + 3); - - if (dump_month == 0) - dump_month = 1; - if (dump_day == 0) - dump_day = 1; - if (dump_year != 0) - info->dump_date = g_date_new_dmy (dump_day, dump_month, dump_year); - - info->muted = READ_INT8 (data, 0xD1); - info->emulator = READ_INT8 (data, 0xD2); - } else { - info->time_seconds = READ_INT24 (data, 0xA9); - info->time_fade_milliseconds = READ_INT32 (data, 0xAC); - info->dump_date = spc_tag_unpack_date (READ_INT32 (data, 0x9E)); - info->muted = READ_INT8 (data, 0xD0); - info->emulator = READ_INT8 (data, 0xD1); - } - } - - if (spc_tag_is_extended (data, length)) { - guchar *chunk = data + EXTENDED_OFFSET + 8; - guint32 chunk_size = *((guint32 *) (data + EXTENDED_OFFSET + 4)); - - guchar *subchunk, *subchunk_next; - - for (subchunk = chunk; subchunk < chunk + chunk_size; - subchunk = subchunk_next) { - guint8 tag = READ_INT8 (subchunk, 0); - guint8 type = READ_INT8 (subchunk, 1); - guint16 length = READ_INT16 (subchunk, 2); - guchar *value = subchunk + 4; - - switch (type) { - case TYPE_LENGTH: - { - switch (tag) { - case TAG_TRACK: - info->track = READ_INT8 (subchunk, 2 + 1); - break; - case TAG_YEAR: - info->year = READ_INT16 (subchunk, 2); - break; - case TAG_COUNT: - info->loop_count = READ_INT8 (subchunk, 2); - break; - case TAG_EMULATOR: - info->emulator = READ_INT8 (subchunk, 2); - break; - case TAG_DISC: - info->disc = READ_INT8 (subchunk, 2); - break; - case TAG_MUTED: - info->muted = READ_INT8 (subchunk, 2); - break; - default: - break; - } - - subchunk_next = subchunk + 4; - break; - } - case TYPE_STRING: - { - gchar *dest; - - if (length <= 1) - dest = NULL; - else - switch (tag) { - case TAG_TITLE: - dest = info->title = g_renew (gchar, info->title, length); - break; - case TAG_GAME: - dest = info->game = g_renew (gchar, info->game, length); - break; - case TAG_ARTIST: - dest = info->artist = g_renew (gchar, info->artist, length); - break; - case TAG_ALBUM: - dest = info->album = g_renew (gchar, info->album, length); - break; - case TAG_DUMPER: - dest = info->dumper = g_renew (gchar, info->dumper, length); - break; - case TAG_COMMENT: - dest = info->comment = g_renew (gchar, info->comment, length); - break; - case TAG_PUBLISHER: - dest = info->publisher = - g_renew (gchar, info->publisher, length); - break; - default: - dest = NULL; - break; - } - - if (dest) - strncpy (dest, (gchar *) value, length); - - subchunk_next = value + length; - break; - } - case TYPE_INTEGER: - { - switch (tag) { - case TAG_INTRO: - info->time_intro = READ_INT32 (value, 0); - break; - case TAG_END: - info->time_end = READ_INT32 (value, 0); - break; - case TAG_FADE: - info->time_fade = READ_INT32 (value, 0); - break; - case TAG_LOOP: - info->time_loop = READ_INT32 (value, 0); - break; - case TAG_DUMP_DATE: - info->dump_date = spc_tag_unpack_date (READ_INT32 (value, 0)); - break; - case TAG_AMP: - info->amplification = READ_INT32 (value, 0); - break; - default: - break; - } - subchunk_next = value + length; - break; - } - default: - subchunk_next = value + length; - break; - } - } - } - - if (info->title && !*info->title) { - g_free (info->title); - info->title = NULL; - } - if (info->game && !*info->game) { - g_free (info->game); - info->game = NULL; - } - if (info->artist && !*info->artist) { - g_free (info->artist); - info->artist = NULL; - } - if (info->album && !*info->album) { - g_free (info->album); - info->album = NULL; - } - if (info->publisher && !*info->publisher) { - g_free (info->publisher); - info->publisher = NULL; - } - if (info->comment && !*info->comment) { - g_free (info->comment); - info->comment = NULL; - } - if (info->dumper && !*info->dumper) { - g_free (info->dumper); - info->dumper = NULL; - } -} - -void -spc_tag_free (spc_tag_info * info) -{ - if (info->title) - g_free (info->title); - if (info->game) - g_free (info->game); - if (info->artist) - g_free (info->artist); - if (info->album) - g_free (info->album); - if (info->publisher) - g_free (info->publisher); - if (info->comment) - g_free (info->comment); - if (info->dumper) - g_free (info->dumper); - if (info->dump_date) - g_date_free (info->dump_date); -} diff --git a/ext/gme/tag.h b/ext/gme/tag.h deleted file mode 100644 index 5b795893..00000000 --- a/ext/gme/tag.h +++ /dev/null @@ -1,37 +0,0 @@ -/* Copyright (C) 2007 Brian Koropoff - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 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 - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#include -#include - -typedef struct -{ - gchar *title, *game, *artist, *album, *publisher; - gchar *dumper, *comment; - enum { EMU_SNES9X = 2, EMU_ZSNES = 1, EMU_UNKNOWN = 0 } emulator; - guint8 track, disc, muted, loop_count; - guint16 year; - guint32 time_seconds, time_fade_milliseconds; - guint32 time_intro, time_loop, time_end, time_fade; - guint32 amplification; - GDate *dump_date; -} spc_tag_info; - -void spc_tag_clear(spc_tag_info* info); -void spc_tag_get_info(guchar* data, guint length, spc_tag_info* info); -void spc_tag_free(spc_tag_info* info); -- cgit v1.2.1 From 359c780830c048766534cd931a3b60f89fc823c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Fri, 7 Aug 2009 06:59:55 +0200 Subject: gme: Rename everything from SPC to GME --- ext/gme/gstgme.c | 272 +++++++++++++++++++++++++++---------------------------- ext/gme/gstgme.h | 34 +++---- 2 files changed, 152 insertions(+), 154 deletions(-) (limited to 'ext/gme') diff --git a/ext/gme/gstgme.c b/ext/gme/gstgme.c index 0efb8d8a..45fa04db 100644 --- a/ext/gme/gstgme.c +++ b/ext/gme/gstgme.c @@ -28,15 +28,9 @@ #include #include -static const GstElementDetails gst_spc_dec_details = -GST_ELEMENT_DETAILS ("SNES SPC 700 decoder", - "Codec/Audio/Decoder", - "Uses Blargg's libgme to emulate an SPC processor", - "Chris Lee , Brian Koropoff , Michael Pyne "); - static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, - GST_STATIC_CAPS ("audio/x-spc")); + GST_STATIC_CAPS ("audio/x-gme")); static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, @@ -46,20 +40,20 @@ GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, "width = (int) 16, " "depth = (int) 16, " "rate = (int) 32000, " "channels = (int) 2")); -GST_BOILERPLATE (GstSpcDec, gst_spc_dec, GstElement, GST_TYPE_ELEMENT); +GST_BOILERPLATE (GstGmeDec, gst_gme_dec, GstElement, GST_TYPE_ELEMENT); -static GstFlowReturn gst_spc_dec_chain (GstPad * pad, GstBuffer * buffer); -static gboolean gst_spc_dec_sink_event (GstPad * pad, GstEvent * event); -static gboolean gst_spc_dec_src_event (GstPad * pad, GstEvent * event); -static gboolean gst_spc_dec_src_query (GstPad * pad, GstQuery * query); -static GstStateChangeReturn gst_spc_dec_change_state (GstElement * element, +static GstFlowReturn gst_gme_dec_chain (GstPad * pad, GstBuffer * buffer); +static gboolean gst_gme_dec_sink_event (GstPad * pad, GstEvent * event); +static gboolean gst_gme_dec_src_event (GstPad * pad, GstEvent * event); +static gboolean gst_gme_dec_src_query (GstPad * pad, GstQuery * query); +static GstStateChangeReturn gst_gme_dec_change_state (GstElement * element, GstStateChange transition); -static void gst_spc_play (GstPad * pad); -static void gst_spc_dec_dispose (GObject * object); -static gboolean spc_setup (GstSpcDec * spc); +static void gst_gme_play (GstPad * pad); +static void gst_gme_dec_dispose (GObject * object); +static gboolean gme_setup (GstGmeDec * gme); static gboolean -spc_negotiate (GstSpcDec * spc) +gme_negotiate (GstGmeDec * gme) { GstCaps *allowed, *caps; GstStructure *structure; @@ -68,20 +62,20 @@ spc_negotiate (GstSpcDec * spc) int rate = 32000; int channels = 2; - allowed = gst_pad_get_allowed_caps (spc->srcpad); + allowed = gst_pad_get_allowed_caps (gme->srcpad); if (!allowed) { - GST_DEBUG_OBJECT (spc, "couldn't get allowed caps"); + GST_DEBUG_OBJECT (gme, "couldn't get allowed caps"); return FALSE; } - GST_DEBUG_OBJECT (spc, "allowed caps: %" GST_PTR_FORMAT, allowed); + GST_DEBUG_OBJECT (gme, "allowed caps: %" GST_PTR_FORMAT, allowed); structure = gst_caps_get_structure (allowed, 0); gst_structure_get_int (structure, "width", &width); gst_structure_get_int (structure, "depth", &depth); if (width && depth && width != depth) { - GST_DEBUG_OBJECT (spc, "width %d and depth %d are different", width, depth); + GST_DEBUG_OBJECT (gme, "width %d and depth %d are different", width, depth); gst_caps_unref (allowed); return FALSE; } @@ -96,7 +90,7 @@ spc_negotiate (GstSpcDec * spc) "width", G_TYPE_INT, width, "depth", G_TYPE_INT, depth, "rate", G_TYPE_INT, rate, "channels", G_TYPE_INT, channels, NULL); - gst_pad_set_caps (spc->srcpad, caps); + gst_pad_set_caps (gme->srcpad, caps); gst_caps_unref (caps); gst_caps_unref (allowed); @@ -105,11 +99,15 @@ spc_negotiate (GstSpcDec * spc) } static void -gst_spc_dec_base_init (gpointer g_class) +gst_gme_dec_base_init (gpointer g_class) { GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); - gst_element_class_set_details (element_class, &gst_spc_dec_details); + gst_element_class_set_details_simple (element_class, + "Gaming console music file decoder", "Codec/Audio/Decoder", + "Uses libgme to emulate a gaming console sound processors", + "Chris Lee , Brian Koropoff , " + "Michael Pyne , Sebastian Dröge "); gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&sink_factory)); @@ -118,17 +116,17 @@ gst_spc_dec_base_init (gpointer g_class) } static void -gst_spc_dec_class_init (GstSpcDecClass * klass) +gst_gme_dec_class_init (GstGmeDecClass * klass) { GstElementClass *element_class = (GstElementClass *) klass; GObjectClass *gobject_class = (GObjectClass *) klass; - element_class->change_state = GST_DEBUG_FUNCPTR (gst_spc_dec_change_state); - gobject_class->dispose = gst_spc_dec_dispose; + element_class->change_state = GST_DEBUG_FUNCPTR (gst_gme_dec_change_state); + gobject_class->dispose = gst_gme_dec_dispose; } static const GstQueryType * -gst_spc_dec_src_query_type (GstPad * pad) +gst_gme_dec_src_query_type (GstPad * pad) { static const GstQueryType query_types[] = { GST_QUERY_DURATION, @@ -141,59 +139,59 @@ gst_spc_dec_src_query_type (GstPad * pad) static void -gst_spc_dec_init (GstSpcDec * spc, GstSpcDecClass * klass) +gst_gme_dec_init (GstGmeDec * gme, GstGmeDecClass * klass) { - spc->sinkpad = gst_pad_new_from_static_template (&sink_factory, "sink"); - /* gst_pad_set_query_function (spc->sinkpad, NULL); */ - gst_pad_set_event_function (spc->sinkpad, gst_spc_dec_sink_event); - gst_pad_set_chain_function (spc->sinkpad, gst_spc_dec_chain); - gst_element_add_pad (GST_ELEMENT (spc), spc->sinkpad); - - spc->srcpad = gst_pad_new_from_static_template (&src_factory, "src"); - gst_pad_set_event_function (spc->srcpad, gst_spc_dec_src_event); - gst_pad_set_query_function (spc->srcpad, gst_spc_dec_src_query); - gst_pad_set_query_type_function (spc->srcpad, gst_spc_dec_src_query_type); - gst_pad_use_fixed_caps (spc->srcpad); - gst_element_add_pad (GST_ELEMENT (spc), spc->srcpad); - - spc->buf = NULL; - spc->player = NULL; - spc->total_duration = GST_CLOCK_TIME_NONE; - spc->initialized = FALSE; + gme->sinkpad = gst_pad_new_from_static_template (&sink_factory, "sink"); + /* gst_pad_set_query_function (gme->sinkpad, NULL); */ + gst_pad_set_event_function (gme->sinkpad, gst_gme_dec_sink_event); + gst_pad_set_chain_function (gme->sinkpad, gst_gme_dec_chain); + gst_element_add_pad (GST_ELEMENT (gme), gme->sinkpad); + + gme->srcpad = gst_pad_new_from_static_template (&src_factory, "src"); + gst_pad_set_event_function (gme->srcpad, gst_gme_dec_src_event); + gst_pad_set_query_function (gme->srcpad, gst_gme_dec_src_query); + gst_pad_set_query_type_function (gme->srcpad, gst_gme_dec_src_query_type); + gst_pad_use_fixed_caps (gme->srcpad); + gst_element_add_pad (GST_ELEMENT (gme), gme->srcpad); + + gme->buf = NULL; + gme->player = NULL; + gme->total_duration = GST_CLOCK_TIME_NONE; + gme->initialized = FALSE; } static void -gst_spc_dec_dispose (GObject * object) +gst_gme_dec_dispose (GObject * object) { - GstSpcDec *spc = GST_SPC_DEC (object); + GstGmeDec *gme = GST_GME_DEC (object); - if (spc->buf) { - gst_buffer_unref (spc->buf); - spc->buf = NULL; + if (gme->buf) { + gst_buffer_unref (gme->buf); + gme->buf = NULL; } } static GstFlowReturn -gst_spc_dec_chain (GstPad * pad, GstBuffer * buffer) +gst_gme_dec_chain (GstPad * pad, GstBuffer * buffer) { - GstSpcDec *spc = GST_SPC_DEC (gst_pad_get_parent (pad)); + GstGmeDec *gme = GST_GME_DEC (gst_pad_get_parent (pad)); - /* Accumulate SPC data until end-of-stream, then commence playback. */ - if (spc->buf) { - spc->buf = gst_buffer_join (spc->buf, buffer); + /* Accumulate GME data until end-of-stream, then commence playback. */ + if (gme->buf) { + gme->buf = gst_buffer_join (gme->buf, buffer); } else { - spc->buf = buffer; + gme->buf = buffer; } - gst_object_unref (spc); + gst_object_unref (gme); return GST_FLOW_OK; } static gboolean -gst_spc_dec_sink_event (GstPad * pad, GstEvent * event) +gst_gme_dec_sink_event (GstPad * pad, GstEvent * event) { - GstSpcDec *spc = GST_SPC_DEC (gst_pad_get_parent (pad)); + GstGmeDec *gme = GST_GME_DEC (gst_pad_get_parent (pad)); gboolean result = TRUE; gboolean forward = FALSE; @@ -201,9 +199,9 @@ gst_spc_dec_sink_event (GstPad * pad, GstEvent * event) case GST_EVENT_EOS: /* we get EOS when we loaded the complete file, now try to initialize the * decoding */ - if (!(result = spc_setup (spc))) { + if (!(result = gme_setup (gme))) { /* can't start, post an ERROR and push EOS downstream */ - GST_ELEMENT_ERROR (spc, STREAM, DEMUX, (NULL), + GST_ELEMENT_ERROR (gme, STREAM, DEMUX, (NULL), ("can't start playback")); forward = TRUE; } @@ -212,19 +210,19 @@ gst_spc_dec_sink_event (GstPad * pad, GstEvent * event) break; } if (forward) - result = gst_pad_push_event (spc->srcpad, event); + result = gst_pad_push_event (gme->srcpad, event); else gst_event_unref (event); - gst_object_unref (spc); + gst_object_unref (gme); return result; } static gboolean -gst_spc_dec_src_event (GstPad * pad, GstEvent * event) +gst_gme_dec_src_event (GstPad * pad, GstEvent * event) { - GstSpcDec *spc = GST_SPC_DEC (gst_pad_get_parent (pad)); + GstGmeDec *gme = GST_GME_DEC (gst_pad_get_parent (pad)); gboolean result = FALSE; switch (GST_EVENT_TYPE (event)) { @@ -243,12 +241,12 @@ gst_spc_dec_src_event (GstPad * pad, GstEvent * event) gst_event_unref (event); if (format != GST_FORMAT_TIME) { - GST_DEBUG_OBJECT (spc, "seeking is only supported in TIME format"); + GST_DEBUG_OBJECT (gme, "seeking is only supported in TIME format"); break; } if (start_type != GST_SEEK_TYPE_SET || stop_type != GST_SEEK_TYPE_NONE) { - GST_DEBUG_OBJECT (spc, "unsupported seek type"); + GST_DEBUG_OBJECT (gme, "unsupported seek type"); break; } @@ -256,11 +254,11 @@ gst_spc_dec_src_event (GstPad * pad, GstEvent * event) stop = GST_CLOCK_TIME_NONE; if (start_type == GST_SEEK_TYPE_SET) { - guint64 cur = gme_tell (spc->player) * GST_MSECOND; + guint64 cur = gme_tell (gme->player) * GST_MSECOND; guint64 dest = (guint64) start; - if (spc->total_duration != GST_CLOCK_TIME_NONE) - dest = CLAMP (dest, 0, spc->total_duration); + if (gme->total_duration != GST_CLOCK_TIME_NONE) + dest = CLAMP (dest, 0, gme->total_duration); else dest = MAX (0, dest); @@ -270,54 +268,54 @@ gst_spc_dec_src_event (GstPad * pad, GstEvent * event) flush = (flags & GST_SEEK_FLAG_FLUSH) == GST_SEEK_FLAG_FLUSH; if (flush) { - gst_pad_push_event (spc->srcpad, gst_event_new_flush_start ()); + gst_pad_push_event (gme->srcpad, gst_event_new_flush_start ()); } else { - gst_pad_stop_task (spc->srcpad); + gst_pad_stop_task (gme->srcpad); } - GST_PAD_STREAM_LOCK (spc->srcpad); + GST_PAD_STREAM_LOCK (gme->srcpad); if (flags & GST_SEEK_FLAG_SEGMENT) { - gst_element_post_message (GST_ELEMENT (spc), - gst_message_new_segment_start (GST_OBJECT (spc), format, cur)); + gst_element_post_message (GST_ELEMENT (gme), + gst_message_new_segment_start (GST_OBJECT (gme), format, cur)); } if (flush) { - gst_pad_push_event (spc->srcpad, gst_event_new_flush_stop ()); + gst_pad_push_event (gme->srcpad, gst_event_new_flush_stop ()); } if (stop == GST_CLOCK_TIME_NONE - && spc->total_duration != GST_CLOCK_TIME_NONE) - stop = spc->total_duration; + && gme->total_duration != GST_CLOCK_TIME_NONE) + stop = gme->total_duration; - gst_pad_push_event (spc->srcpad, gst_event_new_new_segment (FALSE, rate, + gst_pad_push_event (gme->srcpad, gst_event_new_new_segment (FALSE, rate, GST_FORMAT_TIME, dest, stop, dest)); - spc->seekpoint = dest / GST_MSECOND; /* nsecs to msecs */ - spc->seeking = TRUE; + gme->seekpoint = dest / GST_MSECOND; /* nsecs to msecs */ + gme->seeking = TRUE; - gst_pad_start_task (spc->srcpad, (GstTaskFunction) gst_spc_play, - spc->srcpad); + gst_pad_start_task (gme->srcpad, (GstTaskFunction) gst_gme_play, + gme->srcpad); - GST_PAD_STREAM_UNLOCK (spc->srcpad); + GST_PAD_STREAM_UNLOCK (gme->srcpad); result = TRUE; } break; } default: - result = gst_pad_push_event (spc->sinkpad, event); + result = gst_pad_push_event (gme->sinkpad, event); break; } - gst_object_unref (spc); + gst_object_unref (gme); return result; } static gboolean -gst_spc_dec_src_query (GstPad * pad, GstQuery * query) +gst_gme_dec_src_query (GstPad * pad, GstQuery * query) { - GstSpcDec *spc = GST_SPC_DEC (gst_pad_get_parent (pad)); + GstGmeDec *gme = GST_GME_DEC (gst_pad_get_parent (pad)); gboolean result = TRUE; switch (GST_QUERY_TYPE (query)) { @@ -326,12 +324,12 @@ gst_spc_dec_src_query (GstPad * pad, GstQuery * query) GstFormat format; gst_query_parse_duration (query, &format, NULL); - if (!spc->initialized || format != GST_FORMAT_TIME - || spc->total_duration == GST_CLOCK_TIME_NONE) { + if (!gme->initialized || format != GST_FORMAT_TIME + || gme->total_duration == GST_CLOCK_TIME_NONE) { result = FALSE; break; } - gst_query_set_duration (query, GST_FORMAT_TIME, spc->total_duration); + gst_query_set_duration (query, GST_FORMAT_TIME, gme->total_duration); break; } case GST_QUERY_POSITION: @@ -339,12 +337,12 @@ gst_spc_dec_src_query (GstPad * pad, GstQuery * query) GstFormat format; gst_query_parse_position (query, &format, NULL); - if (!spc->initialized || format != GST_FORMAT_TIME) { + if (!gme->initialized || format != GST_FORMAT_TIME) { result = FALSE; break; } gst_query_set_position (query, GST_FORMAT_TIME, - (gint64) gme_tell (spc->player) * GST_MSECOND); + (gint64) gme_tell (gme->player) * GST_MSECOND); break; } default: @@ -352,46 +350,46 @@ gst_spc_dec_src_query (GstPad * pad, GstQuery * query) break; } - gst_object_unref (spc); + gst_object_unref (gme); return result; } static void -gst_spc_play (GstPad * pad) +gst_gme_play (GstPad * pad) { - GstSpcDec *spc = GST_SPC_DEC (gst_pad_get_parent (pad)); + GstGmeDec *gme = GST_GME_DEC (gst_pad_get_parent (pad)); GstFlowReturn flow_return; GstBuffer *out; - gboolean seeking = spc->seeking; + gboolean seeking = gme->seeking; gme_err_t gme_err = NULL; const int NUM_SAMPLES = 1600; /* 4 bytes (stereo 16-bit) per sample */ if (!seeking) { out = gst_buffer_new_and_alloc (NUM_SAMPLES * 4); gst_buffer_set_caps (out, GST_PAD_CAPS (pad)); - GST_BUFFER_TIMESTAMP (out) = gme_tell (spc->player) * GST_MSECOND; + GST_BUFFER_TIMESTAMP (out) = gme_tell (gme->player) * GST_MSECOND; gme_err = - gme_play (spc->player, NUM_SAMPLES * 2, + gme_play (gme->player, NUM_SAMPLES * 2, (short *) GST_BUFFER_DATA (out)); if (gme_err) { - GST_ELEMENT_ERROR (spc, STREAM, DEMUX, (NULL), (gme_err)); + GST_ELEMENT_ERROR (gme, STREAM, DEMUX, (NULL), (gme_err)); gst_pad_pause_task (pad); gst_pad_push_event (pad, gst_event_new_eos ()); - gst_object_unref (spc); + gst_object_unref (gme); return; } } else { - gme_seek (spc->player, spc->seekpoint); - spc->seeking = FALSE; + gme_seek (gme->player, gme->seekpoint); + gme->seeking = FALSE; out = gst_buffer_new (); gst_buffer_set_caps (out, GST_PAD_CAPS (pad)); } - if ((flow_return = gst_pad_push (spc->srcpad, out)) != GST_FLOW_OK) { - GST_DEBUG_OBJECT (spc, "pausing task, reason %s", + if ((flow_return = gst_pad_push (gme->srcpad, out)) != GST_FLOW_OK) { + GST_DEBUG_OBJECT (gme, "pausing task, reason %s", gst_flow_get_name (flow_return)); gst_pad_pause_task (pad); @@ -401,43 +399,43 @@ gst_spc_play (GstPad * pad) } } - if (gme_track_ended (spc->player)) { + if (gme_track_ended (gme->player)) { gst_pad_pause_task (pad); gst_pad_push_event (pad, gst_event_new_eos ()); } - gst_object_unref (spc); + gst_object_unref (gme); return; } static gboolean -spc_setup (GstSpcDec * spc) +gme_setup (GstGmeDec * gme) { gme_info_t *info; gme_err_t gme_err = NULL; GstTagList *taglist; guint64 total_duration; - if (!spc->buf || !spc_negotiate (spc)) { + if (!gme->buf || !gme_negotiate (gme)) { return FALSE; } gme_err = - gme_open_data (GST_BUFFER_DATA (spc->buf), GST_BUFFER_SIZE (spc->buf), - &spc->player, 32000); - if (gme_err || !spc->player) { - if (spc->player) { - gme_delete (spc->player); - spc->player = NULL; + gme_open_data (GST_BUFFER_DATA (gme->buf), GST_BUFFER_SIZE (gme->buf), + &gme->player, 32000); + if (gme_err || !gme->player) { + if (gme->player) { + gme_delete (gme->player); + gme->player = NULL; } - GST_ELEMENT_ERROR (spc, STREAM, DEMUX, (NULL), (gme_err)); + GST_ELEMENT_ERROR (gme, STREAM, DEMUX, (NULL), (gme_err)); return FALSE; } - gme_err = gme_track_info (spc->player, &info, 0); + gme_err = gme_track_info (gme->player, &info, 0); taglist = gst_tag_list_new (); @@ -467,45 +465,45 @@ spc_setup (GstSpcDec * spc) gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_ENCODER, info->system, NULL); - spc->total_duration = total_duration = + gme->total_duration = total_duration = gst_util_uint64_scale_int (info->play_length, GST_MSECOND, 1); gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_DURATION, total_duration, NULL); - gst_element_found_tags_for_pad (GST_ELEMENT (spc), spc->srcpad, taglist); + gst_element_found_tags_for_pad (GST_ELEMENT (gme), gme->srcpad, taglist); g_free (info); #ifdef HAVE_LIBGME_ACCURACY /* TODO: Is it worth it to make this optional? */ - gme_enable_accuracy (spc->player, 1); + gme_enable_accuracy (gme->player, 1); #endif - gme_start_track (spc->player, 0); + gme_start_track (gme->player, 0); - gst_pad_push_event (spc->srcpad, gst_event_new_new_segment (FALSE, 1.0, + gst_pad_push_event (gme->srcpad, gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_TIME, 0, -1, 0)); - gst_pad_start_task (spc->srcpad, (GstTaskFunction) gst_spc_play, spc->srcpad); + gst_pad_start_task (gme->srcpad, (GstTaskFunction) gst_gme_play, gme->srcpad); /* We can't unreference this buffer because we might need to re-initialize * the emulator with the original data during a reverse seek - * gst_buffer_unref (spc->buf); - * spc->buf = NULL; + * gst_buffer_unref (gme->buf); + * gme->buf = NULL; */ - spc->initialized = TRUE; - spc->seeking = FALSE; - spc->seekpoint = 0; - return spc->initialized; + gme->initialized = TRUE; + gme->seeking = FALSE; + gme->seekpoint = 0; + return gme->initialized; } static GstStateChangeReturn -gst_spc_dec_change_state (GstElement * element, GstStateChange transition) +gst_gme_dec_change_state (GstElement * element, GstStateChange transition) { GstStateChangeReturn result; - GstSpcDec *dec; + GstGmeDec *dec; - dec = GST_SPC_DEC (element); + dec = GST_GME_DEC (element); switch (transition) { case GST_STATE_CHANGE_READY_TO_PAUSED: @@ -536,12 +534,12 @@ gst_spc_dec_change_state (GstElement * element, GstStateChange transition) static gboolean plugin_init (GstPlugin * plugin) { - return gst_element_register (plugin, "spcdec", GST_RANK_PRIMARY, - GST_TYPE_SPC_DEC); + return gst_element_register (plugin, "gmedec", GST_RANK_PRIMARY, + GST_TYPE_GME_DEC); } GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR, - "spcdec", - "OpenSPC Audio Decoder", + "gmedec", + "GME Audio Decoder", plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN); diff --git a/ext/gme/gstgme.h b/ext/gme/gstgme.h index b4ab17a5..f1ba39d9 100644 --- a/ext/gme/gstgme.h +++ b/ext/gme/gstgme.h @@ -18,8 +18,8 @@ * Boston, MA 02111-1307, USA. */ -#ifndef __GST_SPC_DEC_H__ -#define __GST_SPC_DEC_H__ +#ifndef __GST_GME_DEC_H__ +#define __GST_GME_DEC_H__ #include @@ -27,21 +27,21 @@ G_BEGIN_DECLS -#define GST_TYPE_SPC_DEC \ - (gst_spc_dec_get_type()) -#define GST_SPC_DEC(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SPC_DEC,GstSpcDec)) -#define GST_SPC_DEC_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_SPC_DEC,GstSpcDecClass)) -#define GST_IS_SPC_DEC(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SPC_DEC)) -#define GST_IS_SPC_DEC_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SPC_DEC)) +#define GST_TYPE_GME_DEC \ + (gst_gme_dec_get_type()) +#define GST_GME_DEC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_GME_DEC,GstGmeDec)) +#define GST_GME_DEC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_GME_DEC,GstGmeDecClass)) +#define GST_IS_GME_DEC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_GME_DEC)) +#define GST_IS_GME_DEC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_GME_DEC)) -typedef struct _GstSpcDec GstSpcDec; -typedef struct _GstSpcDecClass GstSpcDecClass; +typedef struct _GstGmeDec GstGmeDec; +typedef struct _GstGmeDecClass GstGmeDecClass; -struct _GstSpcDec +struct _GstGmeDec { GstElement element; @@ -57,11 +57,11 @@ struct _GstSpcDec GstClockTime total_duration; }; -struct _GstSpcDecClass +struct _GstGmeDecClass { GstElementClass parent_class; }; G_END_DECLS -#endif /* __GST_SPC_DEC_H__ */ +#endif /* __GST_GME_DEC_H__ */ -- cgit v1.2.1 From cf0c65b72cc7c4fb5a1db3d0d918e3321cfc01d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Fri, 7 Aug 2009 07:01:11 +0200 Subject: gme: Fix caps name again --- ext/gme/gstgme.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'ext/gme') diff --git a/ext/gme/gstgme.c b/ext/gme/gstgme.c index 45fa04db..9da38028 100644 --- a/ext/gme/gstgme.c +++ b/ext/gme/gstgme.c @@ -30,7 +30,7 @@ static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, - GST_STATIC_CAPS ("audio/x-gme")); + GST_STATIC_CAPS ("audio/x-spc")); static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, -- cgit v1.2.1 From 9d9fa0a31d983530befe1ab9f3c701d5fa82c8be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Fri, 7 Aug 2009 07:23:07 +0200 Subject: gme: Don't add empty string tags --- ext/gme/gstgme.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'ext/gme') diff --git a/ext/gme/gstgme.c b/ext/gme/gstgme.c index 9da38028..fb8485a8 100644 --- a/ext/gme/gstgme.c +++ b/ext/gme/gstgme.c @@ -439,29 +439,29 @@ gme_setup (GstGmeDec * gme) taglist = gst_tag_list_new (); - if (info->song) + if (info->song && *info->song) gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_TITLE, info->song, NULL); - if (info->author) + if (info->author && *info->author) gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_ARTIST, info->author, NULL); /* Prefer the name of the official soundtrack over the name of the game (since this is * how track numbers are derived) */ - if (info->game) + if (info->game && *info->game) gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_ALBUM, info->game, NULL); - if (info->comment) + if (info->comment && *info->comment) gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_COMMENT, info->comment, NULL); - if (info->dumper) + if (info->dumper && *info->dumper) gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_CONTACT, info->dumper, NULL); - if (info->copyright) + if (info->copyright && *info->copyright) gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_COPYRIGHT, info->copyright, NULL); - if (info->system) + if (info->system && *info->system) gst_tag_list_add (taglist, GST_TAG_MERGE_REPLACE, GST_TAG_ENCODER, info->system, NULL); -- cgit v1.2.1 From 27efe0067c864baa57053bd7ce45ffb33aa6a6ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Fri, 7 Aug 2009 09:21:04 +0200 Subject: gmedec: Add caps for all supported file formats to the srcpad template --- ext/gme/gstgme.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'ext/gme') diff --git a/ext/gme/gstgme.c b/ext/gme/gstgme.c index fb8485a8..ff4e41d6 100644 --- a/ext/gme/gstgme.c +++ b/ext/gme/gstgme.c @@ -29,8 +29,13 @@ #include static GstStaticPadTemplate sink_factory = -GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, - GST_STATIC_CAPS ("audio/x-spc")); + GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-ay; " + "audio/x-gbs; " + "audio/x-gym; " + "audio/x-hes; " + "audio/x-kss; " + "audio/x-nsf; " "audio/x-sap; " "audio/x-spc; " "audio/x-vgm")); static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, -- cgit v1.2.1 From 782965be9f6442155a3754def8dc05d8f4db0394 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Fri, 7 Aug 2009 14:41:31 +0200 Subject: gmedec: Use GstAdapter instead of many buffer joins This reduces the number of reallocations and memcpys drastically. Also free the input data as soon as it's complete and passed to GME as it's not needed anymore. --- ext/gme/Makefile.am | 4 ++-- ext/gme/gstgme.c | 35 +++++++++++++++-------------------- ext/gme/gstgme.h | 3 ++- 3 files changed, 19 insertions(+), 23 deletions(-) (limited to 'ext/gme') diff --git a/ext/gme/Makefile.am b/ext/gme/Makefile.am index c84d340e..f9a7b139 100644 --- a/ext/gme/Makefile.am +++ b/ext/gme/Makefile.am @@ -2,8 +2,8 @@ plugin_LTLIBRARIES = libgstgme.la libgstgme_la_SOURCES = gstgme.c -libgstgme_la_CFLAGS = $(GST_CFLAGS) $(GME_CFLAGS) -libgstgme_la_LIBADD = $(GST_LIBS) $(GME_LIBS) +libgstgme_la_CFLAGS = $(GST_BASE_CFLAGS) $(GST_CFLAGS) $(GME_CFLAGS) +libgstgme_la_LIBADD = $(GST_BASE_LIBS) $(GST_LIBS) $(GME_LIBS) libgstgme_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) libgstgme_la_LIBTOOLFLAGS = --tag=disable-static diff --git a/ext/gme/gstgme.c b/ext/gme/gstgme.c index ff4e41d6..e3f145c5 100644 --- a/ext/gme/gstgme.c +++ b/ext/gme/gstgme.c @@ -159,7 +159,7 @@ gst_gme_dec_init (GstGmeDec * gme, GstGmeDecClass * klass) gst_pad_use_fixed_caps (gme->srcpad); gst_element_add_pad (GST_ELEMENT (gme), gme->srcpad); - gme->buf = NULL; + gme->adapter = gst_adapter_new (); gme->player = NULL; gme->total_duration = GST_CLOCK_TIME_NONE; gme->initialized = FALSE; @@ -170,9 +170,9 @@ gst_gme_dec_dispose (GObject * object) { GstGmeDec *gme = GST_GME_DEC (object); - if (gme->buf) { - gst_buffer_unref (gme->buf); - gme->buf = NULL; + if (gme->adapter) { + gst_object_unref (gme->adapter); + gme->adapter = NULL; } } @@ -182,11 +182,7 @@ gst_gme_dec_chain (GstPad * pad, GstBuffer * buffer) GstGmeDec *gme = GST_GME_DEC (gst_pad_get_parent (pad)); /* Accumulate GME data until end-of-stream, then commence playback. */ - if (gme->buf) { - gme->buf = gst_buffer_join (gme->buf, buffer); - } else { - gme->buf = buffer; - } + gst_adapter_push (gme->adapter, buffer); gst_object_unref (gme); @@ -421,14 +417,21 @@ gme_setup (GstGmeDec * gme) gme_err_t gme_err = NULL; GstTagList *taglist; guint64 total_duration; + GstBuffer *buffer; - if (!gme->buf || !gme_negotiate (gme)) { + if (!gst_adapter_available (gme->adapter) || !gme_negotiate (gme)) { return FALSE; } + buffer = + gst_adapter_take_buffer (gme->adapter, + gst_adapter_available (gme->adapter)); + gme_err = - gme_open_data (GST_BUFFER_DATA (gme->buf), GST_BUFFER_SIZE (gme->buf), + gme_open_data (GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer), &gme->player, 32000); + gst_buffer_unref (buffer); + if (gme_err || !gme->player) { if (gme->player) { gme_delete (gme->player); @@ -491,11 +494,6 @@ gme_setup (GstGmeDec * gme) gst_pad_start_task (gme->srcpad, (GstTaskFunction) gst_gme_play, gme->srcpad); - /* We can't unreference this buffer because we might need to re-initialize - * the emulator with the original data during a reverse seek - * gst_buffer_unref (gme->buf); - * gme->buf = NULL; - */ gme->initialized = TRUE; gme->seeking = FALSE; gme->seekpoint = 0; @@ -524,10 +522,7 @@ gst_gme_dec_change_state (GstElement * element, GstStateChange transition) switch (transition) { case GST_STATE_CHANGE_PAUSED_TO_READY: - if (dec->buf) { - gst_buffer_unref (dec->buf); - dec->buf = NULL; - } + gst_adapter_clear (dec->adapter); break; default: break; diff --git a/ext/gme/gstgme.h b/ext/gme/gstgme.h index f1ba39d9..5fe969d3 100644 --- a/ext/gme/gstgme.h +++ b/ext/gme/gstgme.h @@ -22,6 +22,7 @@ #define __GST_GME_DEC_H__ #include +#include #include @@ -48,7 +49,7 @@ struct _GstGmeDec GstPad *sinkpad; GstPad *srcpad; - GstBuffer *buf; + GstAdapter *adapter; Music_Emu *player; gboolean initialized; gboolean seeking; -- cgit v1.2.1