2 * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3 * (C) <2006> Wim Taymans <wim@fluendo.com>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
22 * SECTION:element-siddec
24 * This element decodes .sid files to raw audio. .sid files are in fact
25 * small Commodore 64 programs that are executed on an emulated 6502 CPU and a
26 * MOS 6581 sound chip.
28 * This plugin will first load the complete program into memory before starting
29 * the emulator and producing output.
31 * Seeking is not (and cannot be) implemented.
33 * ## Example pipelines
36 * gst-launch-1.0 -v filesrc location=Hawkeye.sid ! siddec ! audioconvert ! audioresample ! autoaudiosink
37 * ]| Decode a sid file and play it back.
45 #include <gst/audio/audio.h>
46 #include "gstsiddec.h"
48 #define DEFAULT_TUNE 0
49 #define DEFAULT_CLOCK SIDTUNE_CLOCK_PAL
50 #define DEFAULT_MEMORY MPU_BANK_SWITCHING
51 #define DEFAULT_FILTER TRUE
52 #define DEFAULT_MEASURED_VOLUME TRUE
53 #define DEFAULT_MOS8580 FALSE
54 #define DEFAULT_FORCE_SPEED FALSE
55 #define DEFAULT_BLOCKSIZE 4096
71 static GstStaticPadTemplate sink_templ = GST_STATIC_PAD_TEMPLATE ("sink",
74 GST_STATIC_CAPS ("audio/x-sid")
77 #define FORMATS "{ " GST_AUDIO_NE(S16) "," GST_AUDIO_NE(U16) ", S8, U8 }"
79 static GstStaticPadTemplate src_templ = GST_STATIC_PAD_TEMPLATE ("src",
82 GST_STATIC_CAPS ("audio/x-raw, "
83 "format = (string) " FORMATS ", "
84 "layout = (string) interleaved, "
85 "rate = (int) [ 8000, 48000 ], " "channels = (int) [ 1, 2 ]")
88 GST_DEBUG_CATEGORY_STATIC (gst_siddec_debug);
89 #define GST_CAT_DEFAULT gst_siddec_debug
91 #define GST_TYPE_SID_CLOCK (gst_sid_clock_get_type())
93 gst_sid_clock_get_type (void)
95 static GType sid_clock_type = 0;
96 static const GEnumValue sid_clock[] = {
97 {SIDTUNE_CLOCK_PAL, "PAL", "pal"},
98 {SIDTUNE_CLOCK_NTSC, "NTSC", "ntsc"},
102 if (!sid_clock_type) {
103 sid_clock_type = g_enum_register_static ("GstSidClock", sid_clock);
105 return sid_clock_type;
108 #define GST_TYPE_SID_MEMORY (gst_sid_memory_get_type())
110 gst_sid_memory_get_type (void)
112 static GType sid_memory_type = 0;
113 static const GEnumValue sid_memory[] = {
114 {MPU_BANK_SWITCHING, "Bank Switching", "bank-switching"},
115 {MPU_TRANSPARENT_ROM, "Transparent ROM", "transparent-rom"},
116 {MPU_PLAYSID_ENVIRONMENT, "Playsid Environment", "playsid-environment"},
120 if (!sid_memory_type) {
121 sid_memory_type = g_enum_register_static ("GstSidMemory", sid_memory);
123 return sid_memory_type;
126 static void gst_siddec_finalize (GObject * object);
128 static GstFlowReturn gst_siddec_chain (GstPad * pad, GstObject * parent,
130 static gboolean gst_siddec_sink_event (GstPad * pad, GstObject * parent,
133 static gboolean gst_siddec_src_convert (GstPad * pad, GstFormat src_format,
134 gint64 src_value, GstFormat * dest_format, gint64 * dest_value);
135 static gboolean gst_siddec_src_event (GstPad * pad, GstObject * parent,
137 static gboolean gst_siddec_src_query (GstPad * pad, GstObject * parent,
140 static void gst_siddec_get_property (GObject * object, guint prop_id,
141 GValue * value, GParamSpec * pspec);
142 static void gst_siddec_set_property (GObject * object, guint prop_id,
143 const GValue * value, GParamSpec * pspec);
145 #define gst_siddec_parent_class parent_class
146 G_DEFINE_TYPE (GstSidDec, gst_siddec, GST_TYPE_ELEMENT);
147 GST_ELEMENT_REGISTER_DEFINE (siddec, "siddec", GST_RANK_PRIMARY,
151 gst_siddec_class_init (GstSidDecClass * klass)
153 GObjectClass *gobject_class;
154 GstElementClass *gstelement_class;
156 gobject_class = (GObjectClass *) klass;
157 gstelement_class = (GstElementClass *) klass;
159 gobject_class->finalize = gst_siddec_finalize;
160 gobject_class->set_property = gst_siddec_set_property;
161 gobject_class->get_property = gst_siddec_get_property;
163 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_TUNE,
164 g_param_spec_int ("tune", "tune", "tune",
165 0, 100, DEFAULT_TUNE,
166 (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
167 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_CLOCK,
168 g_param_spec_enum ("clock", "clock", "clock",
169 GST_TYPE_SID_CLOCK, DEFAULT_CLOCK,
170 (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
171 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_MEMORY,
172 g_param_spec_enum ("memory", "memory", "memory", GST_TYPE_SID_MEMORY,
174 (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
175 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_FILTER,
176 g_param_spec_boolean ("filter", "filter", "filter", DEFAULT_FILTER,
177 (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
178 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_MEASURED_VOLUME,
179 g_param_spec_boolean ("measured-volume", "measured_volume",
180 "measured_volume", DEFAULT_MEASURED_VOLUME,
181 (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
182 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_MOS8580,
183 g_param_spec_boolean ("mos8580", "mos8580", "mos8580", DEFAULT_MOS8580,
184 (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
185 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_FORCE_SPEED,
186 g_param_spec_boolean ("force-speed", "force_speed", "force_speed",
188 (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
189 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_BLOCKSIZE,
190 g_param_spec_uint ("blocksize", "Block size",
191 "Size in bytes to output per buffer", 1, G_MAXUINT,
193 (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
194 g_object_class_install_property (gobject_class, PROP_METADATA,
195 g_param_spec_boxed ("metadata", "Metadata", "Metadata", GST_TYPE_CAPS,
196 (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
198 gst_element_class_set_static_metadata (gstelement_class, "Sid decoder",
199 "Codec/Decoder/Audio", "Use libsidplay to decode SID audio tunes",
200 "Wim Taymans <wim.taymans@gmail.com>");
202 gst_element_class_add_static_pad_template (gstelement_class, &src_templ);
203 gst_element_class_add_static_pad_template (gstelement_class, &sink_templ);
205 GST_DEBUG_CATEGORY_INIT (gst_siddec_debug, "siddec", 0,
206 "C64 sid song player");
208 gst_type_mark_as_plugin_api (GST_TYPE_SID_CLOCK, static_cast<GstPluginAPIFlags>(0));
209 gst_type_mark_as_plugin_api (GST_TYPE_SID_MEMORY, static_cast<GstPluginAPIFlags>(0));
213 gst_siddec_init (GstSidDec * siddec)
215 siddec->sinkpad = gst_pad_new_from_static_template (&sink_templ, "sink");
216 gst_pad_set_event_function (siddec->sinkpad, gst_siddec_sink_event);
217 gst_pad_set_chain_function (siddec->sinkpad, gst_siddec_chain);
218 gst_element_add_pad (GST_ELEMENT (siddec), siddec->sinkpad);
220 siddec->srcpad = gst_pad_new_from_static_template (&src_templ, "src");
221 gst_pad_set_event_function (siddec->srcpad, gst_siddec_src_event);
222 gst_pad_set_query_function (siddec->srcpad, gst_siddec_src_query);
223 gst_pad_use_fixed_caps (siddec->srcpad);
224 gst_element_add_pad (GST_ELEMENT (siddec), siddec->srcpad);
226 siddec->engine = new emuEngine ();
227 siddec->tune = new sidTune (0);
228 siddec->config = (emuConfig *) g_malloc (sizeof (emuConfig));
230 /* get default config parameters */
231 siddec->engine->getConfig (*siddec->config);
233 siddec->config->mos8580 = DEFAULT_MOS8580; // mos8580
234 siddec->config->memoryMode = DEFAULT_MEMORY; // memory mode
235 siddec->config->clockSpeed = DEFAULT_CLOCK; // clock speed
236 siddec->config->forceSongSpeed = DEFAULT_FORCE_SPEED; // force song speed
238 siddec->engine->setConfig (*siddec->config);
240 siddec->tune_buffer = (guchar *) g_malloc (maxSidtuneFileLen);
241 siddec->tune_len = 0;
242 siddec->tune_number = 0;
243 siddec->total_bytes = 0;
244 siddec->blocksize = DEFAULT_BLOCKSIZE;
246 siddec->have_group_id = FALSE;
247 siddec->group_id = G_MAXUINT;
251 gst_siddec_finalize (GObject * object)
253 GstSidDec *siddec = GST_SIDDEC (object);
255 g_free (siddec->config);
256 g_free (siddec->tune_buffer);
258 delete (siddec->tune);
259 delete (siddec->engine);
261 G_OBJECT_CLASS (parent_class)->finalize (object);
265 update_tags (GstSidDec * siddec)
270 if (siddec->tune->getInfo (info)) {
271 list = gst_tag_list_new_empty ();
273 if (info.nameString) {
274 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
275 GST_TAG_TITLE, info.nameString, (void *) NULL);
277 if (info.authorString) {
278 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
279 GST_TAG_ARTIST, info.authorString, (void *) NULL);
281 if (info.copyrightString) {
282 gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
283 GST_TAG_COPYRIGHT, info.copyrightString, (void *) NULL);
285 gst_pad_push_event (siddec->srcpad, gst_event_new_tag (list));
290 siddec_negotiate (GstSidDec * siddec)
293 GstStructure *structure;
298 GstAudioFormat format;
302 allowed = gst_pad_get_allowed_caps (siddec->srcpad);
304 goto nothing_allowed;
306 GST_DEBUG_OBJECT (siddec, "allowed caps: %" GST_PTR_FORMAT, allowed);
308 allowed = gst_caps_normalize (allowed);
310 structure = gst_caps_get_structure (allowed, 0);
312 str = gst_structure_get_string (structure, "format");
316 format = gst_audio_format_from_string (str);
318 case GST_AUDIO_FORMAT_S8:
319 siddec->config->bitsPerSample = 8;
320 siddec->config->sampleFormat = SIDEMU_SIGNED_PCM;
322 case GST_AUDIO_FORMAT_U8:
323 siddec->config->bitsPerSample = 8;
324 siddec->config->sampleFormat = SIDEMU_UNSIGNED_PCM;
326 case GST_AUDIO_FORMAT_S16:
327 siddec->config->bitsPerSample = 16;
328 siddec->config->sampleFormat = SIDEMU_SIGNED_PCM;
330 case GST_AUDIO_FORMAT_U16:
331 siddec->config->bitsPerSample = 16;
332 siddec->config->sampleFormat = SIDEMU_UNSIGNED_PCM;
338 gst_structure_get_int (structure, "rate", &rate);
339 siddec->config->frequency = rate;
340 gst_structure_get_int (structure, "channels", &channels);
341 siddec->config->channels = channels;
344 gst_pad_create_stream_id (siddec->srcpad, GST_ELEMENT_CAST (siddec),
347 event = gst_pad_get_sticky_event (siddec->sinkpad, GST_EVENT_STREAM_START, 0);
349 if (gst_event_parse_group_id (event, &siddec->group_id))
350 siddec->have_group_id = TRUE;
352 siddec->have_group_id = FALSE;
353 gst_event_unref (event);
354 } else if (!siddec->have_group_id) {
355 siddec->have_group_id = TRUE;
356 siddec->group_id = gst_util_group_id_next ();
359 event = gst_event_new_stream_start (stream_id);
360 if (siddec->have_group_id)
361 gst_event_set_group_id (event, siddec->group_id);
363 gst_pad_push_event (siddec->srcpad, event);
366 caps = gst_caps_new_simple ("audio/x-raw",
367 "format", G_TYPE_STRING, gst_audio_format_to_string (format),
368 "layout", G_TYPE_STRING, "interleaved",
369 "rate", G_TYPE_INT, siddec->config->frequency,
370 "channels", G_TYPE_INT, siddec->config->channels, NULL);
371 gst_pad_set_caps (siddec->srcpad, caps);
372 gst_caps_unref (caps);
374 gst_caps_unref (allowed);
376 siddec->engine->setConfig (*siddec->config);
383 GST_DEBUG_OBJECT (siddec, "could not get allowed caps");
388 GST_DEBUG_OBJECT (siddec, "invalid audio caps");
389 gst_caps_unref (allowed);
395 play_loop (GstPad * pad)
401 gint64 value, offset, time = 0;
404 siddec = GST_SIDDEC (gst_pad_get_parent (pad));
406 out = gst_buffer_new_and_alloc (siddec->blocksize);
408 gst_buffer_map (out, &outmap, GST_MAP_WRITE);
409 sidEmuFillBuffer (*siddec->engine, *siddec->tune,
410 outmap.data, siddec->blocksize);
411 gst_buffer_unmap (out, &outmap);
413 /* get offset in samples */
414 format = GST_FORMAT_DEFAULT;
415 if (gst_siddec_src_convert (siddec->srcpad,
416 GST_FORMAT_BYTES, siddec->total_bytes, &format, &offset))
417 GST_BUFFER_OFFSET (out) = offset;
419 /* get current timestamp */
420 format = GST_FORMAT_TIME;
421 if (gst_siddec_src_convert (siddec->srcpad,
422 GST_FORMAT_BYTES, siddec->total_bytes, &format, &time))
423 GST_BUFFER_TIMESTAMP (out) = time;
425 /* update position and get new timestamp to calculate duration */
426 siddec->total_bytes += siddec->blocksize;
428 /* get offset in samples */
429 format = GST_FORMAT_DEFAULT;
430 if (gst_siddec_src_convert (siddec->srcpad,
431 GST_FORMAT_BYTES, siddec->total_bytes, &format, &value))
432 GST_BUFFER_OFFSET_END (out) = value;
434 format = GST_FORMAT_TIME;
435 if (gst_siddec_src_convert (siddec->srcpad,
436 GST_FORMAT_BYTES, siddec->total_bytes, &format, &value))
437 GST_BUFFER_DURATION (out) = value - time;
439 if ((ret = gst_pad_push (siddec->srcpad, out)) != GST_FLOW_OK)
443 gst_object_unref (siddec);
450 if (ret == GST_FLOW_EOS) {
451 /* perform EOS logic, FIXME, segment seek? */
452 gst_pad_push_event (pad, gst_event_new_eos ());
453 } else if (ret < GST_FLOW_EOS || ret == GST_FLOW_NOT_LINKED) {
454 /* for fatal errors we post an error message */
455 GST_ELEMENT_FLOW_ERROR (siddec, ret);
456 gst_pad_push_event (pad, gst_event_new_eos ());
459 GST_INFO_OBJECT (siddec, "pausing task, reason: %s",
460 gst_flow_get_name (ret));
461 gst_pad_pause_task (pad);
467 start_play_tune (GstSidDec * siddec)
472 if (!siddec->tune->load (siddec->tune_buffer, siddec->tune_len))
475 update_tags (siddec);
477 if (!siddec_negotiate (siddec))
478 goto could_not_negotiate;
480 if (!sidEmuInitializeSong (*siddec->engine, *siddec->tune,
481 siddec->tune_number))
484 gst_segment_init (&segment, GST_FORMAT_TIME);
485 gst_pad_push_event (siddec->srcpad, gst_event_new_segment (&segment));
486 siddec->total_bytes = 0;
487 siddec->have_group_id = FALSE;
488 siddec->group_id = G_MAXUINT;
490 res = gst_pad_start_task (siddec->srcpad,
491 (GstTaskFunction) play_loop, siddec->srcpad, NULL);
497 GST_ELEMENT_ERROR (siddec, LIBRARY, INIT,
498 ("Could not load tune"), ("Could not load tune"));
503 GST_ELEMENT_ERROR (siddec, CORE, NEGOTIATION,
504 ("Could not negotiate format"), ("Could not negotiate format"));
509 GST_ELEMENT_ERROR (siddec, LIBRARY, INIT,
510 ("Could not initialize song"), ("Could not initialize song"));
516 gst_siddec_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
521 siddec = GST_SIDDEC (parent);
523 switch (GST_EVENT_TYPE (event)) {
525 res = start_play_tune (siddec);
527 case GST_EVENT_SEGMENT:
534 gst_event_unref (event);
540 gst_siddec_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
545 siddec = GST_SIDDEC (parent);
547 size = gst_buffer_get_size (buffer);
548 if (siddec->tune_len + size > maxSidtuneFileLen)
551 gst_buffer_extract (buffer, 0, siddec->tune_buffer + siddec->tune_len, size);
553 siddec->tune_len += size;
555 gst_buffer_unref (buffer);
562 GST_ELEMENT_ERROR (siddec, STREAM, DECODE,
563 (NULL), ("Input data bigger than allowed buffer size"));
564 return GST_FLOW_ERROR;
569 gst_siddec_src_convert (GstPad * pad, GstFormat src_format, gint64 src_value,
570 GstFormat * dest_format, gint64 * dest_value)
575 gint bytes_per_sample;
577 siddec = GST_SIDDEC (gst_pad_get_parent (pad));
579 if (src_format == *dest_format) {
580 *dest_value = src_value;
585 (siddec->config->bitsPerSample >> 3) * siddec->config->channels;
587 switch (src_format) {
588 case GST_FORMAT_BYTES:
589 switch (*dest_format) {
590 case GST_FORMAT_DEFAULT:
591 if (bytes_per_sample == 0)
593 *dest_value = src_value / bytes_per_sample;
595 case GST_FORMAT_TIME:
597 gint byterate = bytes_per_sample * siddec->config->frequency;
602 gst_util_uint64_scale_int (src_value, GST_SECOND, byterate);
609 case GST_FORMAT_DEFAULT:
610 switch (*dest_format) {
611 case GST_FORMAT_BYTES:
612 *dest_value = src_value * bytes_per_sample;
614 case GST_FORMAT_TIME:
615 if (siddec->config->frequency == 0)
618 gst_util_uint64_scale_int (src_value, GST_SECOND,
619 siddec->config->frequency);
625 case GST_FORMAT_TIME:
626 switch (*dest_format) {
627 case GST_FORMAT_BYTES:
628 scale = bytes_per_sample;
630 case GST_FORMAT_DEFAULT:
632 gst_util_uint64_scale_int (src_value,
633 scale * siddec->config->frequency, GST_SECOND);
647 gst_siddec_src_event (GstPad * pad, GstObject * parent, GstEvent * event)
649 gboolean res = FALSE;
651 switch (GST_EVENT_TYPE (event)) {
655 gst_event_unref (event);
661 gst_siddec_src_query (GstPad * pad, GstObject * parent, GstQuery * query)
666 siddec = GST_SIDDEC (parent);
668 switch (GST_QUERY_TYPE (query)) {
669 case GST_QUERY_POSITION:
674 gst_query_parse_position (query, &format, NULL);
676 /* we only know about our bytes, convert to requested format */
677 res &= gst_siddec_src_convert (pad,
678 GST_FORMAT_BYTES, siddec->total_bytes, &format, ¤t);
680 gst_query_set_position (query, format, current);
685 res = gst_pad_query_default (pad, parent, query);
693 gst_siddec_set_property (GObject * object, guint prop_id, const GValue * value,
696 GstSidDec *siddec = GST_SIDDEC (object);
700 siddec->tune_number = g_value_get_int (value);
703 siddec->config->clockSpeed = g_value_get_enum (value);
706 siddec->config->memoryMode = g_value_get_enum (value);
709 siddec->config->emulateFilter = g_value_get_boolean (value);
711 case PROP_MEASURED_VOLUME:
712 siddec->config->measuredVolume = g_value_get_boolean (value);
715 siddec->config->mos8580 = g_value_get_boolean (value);
718 siddec->blocksize = g_value_get_uint (value);
720 case PROP_FORCE_SPEED:
721 siddec->config->forceSongSpeed = g_value_get_boolean (value);
724 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
727 siddec->engine->setConfig (*siddec->config);
731 gst_siddec_get_property (GObject * object, guint prop_id, GValue * value,
734 GstSidDec *siddec = GST_SIDDEC (object);
738 g_value_set_int (value, siddec->tune_number);
741 g_value_set_enum (value, siddec->config->clockSpeed);
744 g_value_set_enum (value, siddec->config->memoryMode);
747 g_value_set_boolean (value, siddec->config->emulateFilter);
749 case PROP_MEASURED_VOLUME:
750 g_value_set_boolean (value, siddec->config->measuredVolume);
753 g_value_set_boolean (value, siddec->config->mos8580);
755 case PROP_FORCE_SPEED:
756 g_value_set_boolean (value, siddec->config->forceSongSpeed);
759 g_value_set_uint (value, siddec->blocksize);
762 g_value_set_boxed (value, NULL);
765 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
771 plugin_init (GstPlugin * plugin)
773 return GST_ELEMENT_REGISTER (siddec, plugin);
776 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
779 "Uses libsidplay to decode .sid files",
780 plugin_init, VERSION, "GPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN);