Move files from gst-plugins-ugly into the "subprojects/gst-plugins-ugly/" subdir
[platform/upstream/gstreamer.git] / subprojects / gst-plugins-ugly / ext / sidplay / gstsiddec.cc
1 /* GStreamer
2  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3  *           (C) <2006> Wim Taymans <wim@fluendo.com>
4  *
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.
9  *
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.
14  *
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.
19  */
20
21 /**
22  * SECTION:element-siddec
23  *
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.
27  *
28  * This plugin will first load the complete program into memory before starting
29  * the emulator and producing output.
30  *
31  * Seeking is not (and cannot be) implemented.
32  *
33  * ## Example pipelines
34  *
35  * |[
36  * gst-launch-1.0 -v filesrc location=Hawkeye.sid ! siddec ! audioconvert ! audioresample ! autoaudiosink
37  * ]| Decode a sid file and play it back.
38  */
39
40 #ifdef HAVE_CONFIG_H
41 #include "config.h"
42 #endif
43
44 #include <string.h>
45 #include <gst/audio/audio.h>
46 #include "gstsiddec.h"
47
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
56
57 enum
58 {
59   PROP_0,
60   PROP_TUNE,
61   PROP_CLOCK,
62   PROP_MEMORY,
63   PROP_FILTER,
64   PROP_MEASURED_VOLUME,
65   PROP_MOS8580,
66   PROP_FORCE_SPEED,
67   PROP_BLOCKSIZE,
68   PROP_METADATA
69 };
70
71 static GstStaticPadTemplate sink_templ = GST_STATIC_PAD_TEMPLATE ("sink",
72     GST_PAD_SINK,
73     GST_PAD_ALWAYS,
74     GST_STATIC_CAPS ("audio/x-sid")
75     );
76
77 #define FORMATS "{ " GST_AUDIO_NE(S16) "," GST_AUDIO_NE(U16) ", S8, U8 }"
78
79 static GstStaticPadTemplate src_templ = GST_STATIC_PAD_TEMPLATE ("src",
80     GST_PAD_SRC,
81     GST_PAD_ALWAYS,
82     GST_STATIC_CAPS ("audio/x-raw, "
83         "format = (string) " FORMATS ", "
84         "layout = (string) interleaved, "
85         "rate = (int) [ 8000, 48000 ], " "channels = (int) [ 1, 2 ]")
86     );
87
88 GST_DEBUG_CATEGORY_STATIC (gst_siddec_debug);
89 #define GST_CAT_DEFAULT gst_siddec_debug
90
91 #define GST_TYPE_SID_CLOCK (gst_sid_clock_get_type())
92 static GType
93 gst_sid_clock_get_type (void)
94 {
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"},
99     {0, NULL, NULL},
100   };
101
102   if (!sid_clock_type) {
103     sid_clock_type = g_enum_register_static ("GstSidClock", sid_clock);
104   }
105   return sid_clock_type;
106 }
107
108 #define GST_TYPE_SID_MEMORY (gst_sid_memory_get_type())
109 static GType
110 gst_sid_memory_get_type (void)
111 {
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"},
117     {0, NULL, NULL},
118   };
119
120   if (!sid_memory_type) {
121     sid_memory_type = g_enum_register_static ("GstSidMemory", sid_memory);
122   }
123   return sid_memory_type;
124 }
125
126 static void gst_siddec_finalize (GObject * object);
127
128 static GstFlowReturn gst_siddec_chain (GstPad * pad, GstObject * parent,
129     GstBuffer * buffer);
130 static gboolean gst_siddec_sink_event (GstPad * pad, GstObject * parent,
131     GstEvent * event);
132
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,
136     GstEvent * event);
137 static gboolean gst_siddec_src_query (GstPad * pad, GstObject * parent,
138     GstQuery * query);
139
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);
144
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,
148     GST_TYPE_SIDDEC);
149
150 static void
151 gst_siddec_class_init (GstSidDecClass * klass)
152 {
153   GObjectClass *gobject_class;
154   GstElementClass *gstelement_class;
155
156   gobject_class = (GObjectClass *) klass;
157   gstelement_class = (GstElementClass *) klass;
158
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;
162
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,
173           DEFAULT_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",
187           DEFAULT_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,
192           DEFAULT_BLOCKSIZE,
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)));
197
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>");
201
202   gst_element_class_add_static_pad_template (gstelement_class, &src_templ);
203   gst_element_class_add_static_pad_template (gstelement_class, &sink_templ);
204
205   GST_DEBUG_CATEGORY_INIT (gst_siddec_debug, "siddec", 0,
206       "C64 sid song player");
207
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));
210 }
211
212 static void
213 gst_siddec_init (GstSidDec * siddec)
214 {
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);
219
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);
225
226   siddec->engine = new emuEngine ();
227   siddec->tune = new sidTune (0);
228   siddec->config = (emuConfig *) g_malloc (sizeof (emuConfig));
229
230   /* get default config parameters */
231   siddec->engine->getConfig (*siddec->config);
232
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
237
238   siddec->engine->setConfig (*siddec->config);
239
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;
245
246   siddec->have_group_id = FALSE;
247   siddec->group_id = G_MAXUINT;
248 }
249
250 static void
251 gst_siddec_finalize (GObject * object)
252 {
253   GstSidDec *siddec = GST_SIDDEC (object);
254
255   g_free (siddec->config);
256   g_free (siddec->tune_buffer);
257
258   delete (siddec->tune);
259   delete (siddec->engine);
260
261   G_OBJECT_CLASS (parent_class)->finalize (object);
262 }
263
264 static void
265 update_tags (GstSidDec * siddec)
266 {
267   sidTuneInfo info;
268   GstTagList *list;
269
270   if (siddec->tune->getInfo (info)) {
271     list = gst_tag_list_new_empty ();
272
273     if (info.nameString) {
274       gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
275           GST_TAG_TITLE, info.nameString, (void *) NULL);
276     }
277     if (info.authorString) {
278       gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
279           GST_TAG_ARTIST, info.authorString, (void *) NULL);
280     }
281     if (info.copyrightString) {
282       gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
283           GST_TAG_COPYRIGHT, info.copyrightString, (void *) NULL);
284     }
285     gst_pad_push_event (siddec->srcpad, gst_event_new_tag (list));
286   }
287 }
288
289 static gboolean
290 siddec_negotiate (GstSidDec * siddec)
291 {
292   GstCaps *allowed;
293   GstStructure *structure;
294   int rate = 44100;
295   int channels = 1;
296   GstCaps *caps;
297   const gchar *str;
298   GstAudioFormat format;
299   GstEvent *event;
300   gchar *stream_id;
301
302   allowed = gst_pad_get_allowed_caps (siddec->srcpad);
303   if (!allowed)
304     goto nothing_allowed;
305
306   GST_DEBUG_OBJECT (siddec, "allowed caps: %" GST_PTR_FORMAT, allowed);
307
308   allowed = gst_caps_normalize (allowed);
309
310   structure = gst_caps_get_structure (allowed, 0);
311
312   str = gst_structure_get_string (structure, "format");
313   if (str == NULL)
314     goto invalid_format;
315
316   format = gst_audio_format_from_string (str);
317   switch (format) {
318     case GST_AUDIO_FORMAT_S8:
319       siddec->config->bitsPerSample = 8;
320       siddec->config->sampleFormat = SIDEMU_SIGNED_PCM;
321       break;
322     case GST_AUDIO_FORMAT_U8:
323       siddec->config->bitsPerSample = 8;
324       siddec->config->sampleFormat = SIDEMU_UNSIGNED_PCM;
325       break;
326     case GST_AUDIO_FORMAT_S16:
327       siddec->config->bitsPerSample = 16;
328       siddec->config->sampleFormat = SIDEMU_SIGNED_PCM;
329       break;
330     case GST_AUDIO_FORMAT_U16:
331       siddec->config->bitsPerSample = 16;
332       siddec->config->sampleFormat = SIDEMU_UNSIGNED_PCM;
333       break;
334     default:
335       goto invalid_format;
336   }
337
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;
342
343   stream_id =
344       gst_pad_create_stream_id (siddec->srcpad, GST_ELEMENT_CAST (siddec),
345       NULL);
346
347   event = gst_pad_get_sticky_event (siddec->sinkpad, GST_EVENT_STREAM_START, 0);
348   if (event) {
349     if (gst_event_parse_group_id (event, &siddec->group_id))
350       siddec->have_group_id = TRUE;
351     else
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 ();
357   }
358
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);
362
363   gst_pad_push_event (siddec->srcpad, event);
364   g_free (stream_id);
365
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);
373
374   gst_caps_unref (allowed);
375
376   siddec->engine->setConfig (*siddec->config);
377
378   return TRUE;
379
380   /* ERRORS */
381 nothing_allowed:
382   {
383     GST_DEBUG_OBJECT (siddec, "could not get allowed caps");
384     return FALSE;
385   }
386 invalid_format:
387   {
388     GST_DEBUG_OBJECT (siddec, "invalid audio caps");
389     gst_caps_unref (allowed);
390     return FALSE;
391   }
392 }
393
394 static void
395 play_loop (GstPad * pad)
396 {
397   GstFlowReturn ret;
398   GstSidDec *siddec;
399   GstBuffer *out;
400   GstMapInfo outmap;
401   gint64 value, offset, time = 0;
402   GstFormat format;
403
404   siddec = GST_SIDDEC (gst_pad_get_parent (pad));
405
406   out = gst_buffer_new_and_alloc (siddec->blocksize);
407
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);
412
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;
418
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;
424
425   /* update position and get new timestamp to calculate duration */
426   siddec->total_bytes += siddec->blocksize;
427
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;
433
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;
438
439   if ((ret = gst_pad_push (siddec->srcpad, out)) != GST_FLOW_OK)
440     goto pause;
441
442 done:
443   gst_object_unref (siddec);
444
445   return;
446
447   /* ERRORS */
448 pause:
449   {
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 ());
457     }
458
459     GST_INFO_OBJECT (siddec, "pausing task, reason: %s",
460         gst_flow_get_name (ret));
461     gst_pad_pause_task (pad);
462     goto done;
463   }
464 }
465
466 static gboolean
467 start_play_tune (GstSidDec * siddec)
468 {
469   gboolean res;
470   GstSegment segment;
471
472   if (!siddec->tune->load (siddec->tune_buffer, siddec->tune_len))
473     goto could_not_load;
474
475   update_tags (siddec);
476
477   if (!siddec_negotiate (siddec))
478     goto could_not_negotiate;
479
480   if (!sidEmuInitializeSong (*siddec->engine, *siddec->tune,
481           siddec->tune_number))
482     goto could_not_init;
483
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;
489
490   res = gst_pad_start_task (siddec->srcpad,
491       (GstTaskFunction) play_loop, siddec->srcpad, NULL);
492   return res;
493
494   /* ERRORS */
495 could_not_load:
496   {
497     GST_ELEMENT_ERROR (siddec, LIBRARY, INIT,
498         ("Could not load tune"), ("Could not load tune"));
499     return FALSE;
500   }
501 could_not_negotiate:
502   {
503     GST_ELEMENT_ERROR (siddec, CORE, NEGOTIATION,
504         ("Could not negotiate format"), ("Could not negotiate format"));
505     return FALSE;
506   }
507 could_not_init:
508   {
509     GST_ELEMENT_ERROR (siddec, LIBRARY, INIT,
510         ("Could not initialize song"), ("Could not initialize song"));
511     return FALSE;
512   }
513 }
514
515 static gboolean
516 gst_siddec_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
517 {
518   GstSidDec *siddec;
519   gboolean res;
520
521   siddec = GST_SIDDEC (parent);
522
523   switch (GST_EVENT_TYPE (event)) {
524     case GST_EVENT_EOS:
525       res = start_play_tune (siddec);
526       break;
527     case GST_EVENT_SEGMENT:
528       res = TRUE;
529       break;
530     default:
531       res = TRUE;
532       break;
533   }
534   gst_event_unref (event);
535
536   return res;
537 }
538
539 static GstFlowReturn
540 gst_siddec_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
541 {
542   GstSidDec *siddec;
543   guint64 size;
544
545   siddec = GST_SIDDEC (parent);
546
547   size = gst_buffer_get_size (buffer);
548   if (siddec->tune_len + size > maxSidtuneFileLen)
549     goto overflow;
550
551   gst_buffer_extract (buffer, 0, siddec->tune_buffer + siddec->tune_len, size);
552
553   siddec->tune_len += size;
554
555   gst_buffer_unref (buffer);
556
557   return GST_FLOW_OK;
558
559   /* ERRORS */
560 overflow:
561   {
562     GST_ELEMENT_ERROR (siddec, STREAM, DECODE,
563         (NULL), ("Input data bigger than allowed buffer size"));
564     return GST_FLOW_ERROR;
565   }
566 }
567
568 static gboolean
569 gst_siddec_src_convert (GstPad * pad, GstFormat src_format, gint64 src_value,
570     GstFormat * dest_format, gint64 * dest_value)
571 {
572   gboolean res = TRUE;
573   guint scale = 1;
574   GstSidDec *siddec;
575   gint bytes_per_sample;
576
577   siddec = GST_SIDDEC (gst_pad_get_parent (pad));
578
579   if (src_format == *dest_format) {
580     *dest_value = src_value;
581     return TRUE;
582   }
583
584   bytes_per_sample =
585       (siddec->config->bitsPerSample >> 3) * siddec->config->channels;
586
587   switch (src_format) {
588     case GST_FORMAT_BYTES:
589       switch (*dest_format) {
590         case GST_FORMAT_DEFAULT:
591           if (bytes_per_sample == 0)
592             return FALSE;
593           *dest_value = src_value / bytes_per_sample;
594           break;
595         case GST_FORMAT_TIME:
596         {
597           gint byterate = bytes_per_sample * siddec->config->frequency;
598
599           if (byterate == 0)
600             return FALSE;
601           *dest_value =
602               gst_util_uint64_scale_int (src_value, GST_SECOND, byterate);
603           break;
604         }
605         default:
606           res = FALSE;
607       }
608       break;
609     case GST_FORMAT_DEFAULT:
610       switch (*dest_format) {
611         case GST_FORMAT_BYTES:
612           *dest_value = src_value * bytes_per_sample;
613           break;
614         case GST_FORMAT_TIME:
615           if (siddec->config->frequency == 0)
616             return FALSE;
617           *dest_value =
618               gst_util_uint64_scale_int (src_value, GST_SECOND,
619               siddec->config->frequency);
620           break;
621         default:
622           res = FALSE;
623       }
624       break;
625     case GST_FORMAT_TIME:
626       switch (*dest_format) {
627         case GST_FORMAT_BYTES:
628           scale = bytes_per_sample;
629           /* fallthrough */
630         case GST_FORMAT_DEFAULT:
631           *dest_value =
632               gst_util_uint64_scale_int (src_value,
633               scale * siddec->config->frequency, GST_SECOND);
634           break;
635         default:
636           res = FALSE;
637       }
638       break;
639     default:
640       res = FALSE;
641   }
642
643   return res;
644 }
645
646 static gboolean
647 gst_siddec_src_event (GstPad * pad, GstObject * parent, GstEvent * event)
648 {
649   gboolean res = FALSE;
650
651   switch (GST_EVENT_TYPE (event)) {
652     default:
653       break;
654   }
655   gst_event_unref (event);
656
657   return res;
658 }
659
660 static gboolean
661 gst_siddec_src_query (GstPad * pad, GstObject * parent, GstQuery * query)
662 {
663   gboolean res = TRUE;
664   GstSidDec *siddec;
665
666   siddec = GST_SIDDEC (parent);
667
668   switch (GST_QUERY_TYPE (query)) {
669     case GST_QUERY_POSITION:
670     {
671       GstFormat format;
672       gint64 current;
673
674       gst_query_parse_position (query, &format, NULL);
675
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, &current);
679       if (res) {
680         gst_query_set_position (query, format, current);
681       }
682       break;
683     }
684     default:
685       res = gst_pad_query_default (pad, parent, query);
686       break;
687   }
688
689   return res;
690 }
691
692 static void
693 gst_siddec_set_property (GObject * object, guint prop_id, const GValue * value,
694     GParamSpec * pspec)
695 {
696   GstSidDec *siddec = GST_SIDDEC (object);
697
698   switch (prop_id) {
699     case PROP_TUNE:
700       siddec->tune_number = g_value_get_int (value);
701       break;
702     case PROP_CLOCK:
703       siddec->config->clockSpeed = g_value_get_enum (value);
704       break;
705     case PROP_MEMORY:
706       siddec->config->memoryMode = g_value_get_enum (value);
707       break;
708     case PROP_FILTER:
709       siddec->config->emulateFilter = g_value_get_boolean (value);
710       break;
711     case PROP_MEASURED_VOLUME:
712       siddec->config->measuredVolume = g_value_get_boolean (value);
713       break;
714     case PROP_MOS8580:
715       siddec->config->mos8580 = g_value_get_boolean (value);
716       break;
717     case PROP_BLOCKSIZE:
718       siddec->blocksize = g_value_get_uint (value);
719       break;
720     case PROP_FORCE_SPEED:
721       siddec->config->forceSongSpeed = g_value_get_boolean (value);
722       break;
723     default:
724       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
725       return;
726   }
727   siddec->engine->setConfig (*siddec->config);
728 }
729
730 static void
731 gst_siddec_get_property (GObject * object, guint prop_id, GValue * value,
732     GParamSpec * pspec)
733 {
734   GstSidDec *siddec = GST_SIDDEC (object);
735
736   switch (prop_id) {
737     case PROP_TUNE:
738       g_value_set_int (value, siddec->tune_number);
739       break;
740     case PROP_CLOCK:
741       g_value_set_enum (value, siddec->config->clockSpeed);
742       break;
743     case PROP_MEMORY:
744       g_value_set_enum (value, siddec->config->memoryMode);
745       break;
746     case PROP_FILTER:
747       g_value_set_boolean (value, siddec->config->emulateFilter);
748       break;
749     case PROP_MEASURED_VOLUME:
750       g_value_set_boolean (value, siddec->config->measuredVolume);
751       break;
752     case PROP_MOS8580:
753       g_value_set_boolean (value, siddec->config->mos8580);
754       break;
755     case PROP_FORCE_SPEED:
756       g_value_set_boolean (value, siddec->config->forceSongSpeed);
757       break;
758     case PROP_BLOCKSIZE:
759       g_value_set_uint (value, siddec->blocksize);
760       break;
761     case PROP_METADATA:
762       g_value_set_boxed (value, NULL);
763       break;
764     default:
765       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
766       break;
767   }
768 }
769
770 static gboolean
771 plugin_init (GstPlugin * plugin)
772 {
773   return GST_ELEMENT_REGISTER (siddec, plugin);
774 }
775
776 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
777     GST_VERSION_MINOR,
778     sid,
779     "Uses libsidplay to decode .sid files",
780     plugin_init, VERSION, "GPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN);