.

Gstreamer and SID

Some years ago I played around with Gstreamer quite a lot. Gstreamer is an open source multimedia framework which allows you to deal with video and audio sources of any kind and play, convert or mux them to your needs.

The nice thing with Gstreamer is the fact, that it is very powerful on the one hand, but very good to test and play around with on the other hand. For example it provides tools to run and inspect the various filters, sources and converters easily. Also it has bindings to e.g. Python, so you can use them in a native way, without the need to run shell commands from within your script. Yesterday I found, that Gstreamer also has siddec decoder, which allows you to deal with SID files. So this blog post is about playing retro C64 SID files with Gstreamer.

SID

SID are basically C64 music files. As the C64's sid tune music chip was quite popular, a whole community takes care of creating SID tunes or dumping tunes from popular games to be playable on PC.

lsrsid application

Some years ago I wrote a simple directory based music player for SID tunes, which made use of the shell tool sidplay2 to actually play the SID files - there was no Python SID binding around, so I had to remote control sidplay2 in order to play some sound. The project is quite old and not maintained for years - but with Gstreamer having a SID decoder, I might finally be able to play SID tunes directly from my Python application.

Gstreamer and siddec

First of all we can start playing around with the siddec decoder a bit. If you have downloaded e.g. the "High voltage SID collection" you will have plenty of SID files to play around with.

gst-launch filesrc location=23.sid ! siddec ! autoaudiosink

This command uses the gst-launch command to test some Gstreamer pipes. I will go into detail about this system in another blog post. In this case I instruct it to play media from file, so I am using the filesrc sink and point it to the file 23.sid in the current directory. Afterwards the processing pipeline is described. siddec will decode the audio stream - in my case a SID file. If I had provided a mp3 or video file, I would use other decoders. Finally I pipe the decoded SID audio to the autoaudiosink - I play it. I could also use another sink or convert it to mp3 and then put it to a file sink in order to save it.

Speed

The Gstreamer pipes usually follow the plug and play principle. So I can add other filters and effects to my pipe, if I want to.

gst-launch filesrc location=23.sid ! siddec ! speed speed=4 ! autoaudiosink

In this case I added the speed filter which run the audio file in 4x speed. There is also a pitch filter, that has some more options and is recommended by the docs.

More options

Most of the sinks and filters come with special options to configure them. Of course you can look for some docs - but usually gst-inspect is very useful to find such options. If I run gst-inspect siddec, it will render the following output:

Factory Details:
  Long name:    Sid decoder
  Class:    Codec/Decoder/Audio
  Description:  Use libsidplay to decode SID audio tunes
  Author(s):    Wim Taymans <wim.taymans@gmail.com>
  Rank:     primary (256)

Plugin Details:
  Name:         siddec
  Description:      Uses libsidplay to decode .sid files
  Filename:     /usr/lib/x86_64-linux-gnu/gstreamer-0.10/libgstsid.so
  Version:      0.10.19
  License:      GPL
  Source module:    gst-plugins-ugly
  Source release date:  2012-02-20
  Binary package:   GStreamer Ugly Plugins (Ubuntu)
  Origin URL:       https://launchpad.net/distros/ubuntu/+source/gst-plugins-ugly0.10

GObject
 +----GstObject
       +----GstElement
             +----GstSidDec

Pad Templates:
  SRC template: 'src'
    Availability: Always
    Capabilities:
      audio/x-raw-int
             endianness: 1234
                 signed: { true, false }
                  width: { 8, 16 }
                  depth: { 8, 16 }
                   rate: [ 8000, 48000 ]
               channels: [ 1, 2 ]

  SINK template: 'sink'
    Availability: Always
    Capabilities:
      audio/x-sid


Element Flags:
  no flags set

Element Implementation:
  Has change_state() function: gst_element_change_state_func
  Has custom save_thyself() function: gst_element_save_thyself
  Has custom restore_thyself() function: gst_element_restore_thyself

Element has no clocking capabilities.
Element has no indexing capabilities.
Element has no URI handling capabilities.

Pads:
  SRC: 'src'
    Implementation:
      Has custom eventfunc(): 0x7fa0f4ed4840
      Has custom queryfunc(): 0x7fa0f4ed5a70
      Has custom iterintlinkfunc(): gst_pad_iterate_internal_links_default
      Has getcapsfunc(): gst_pad_get_fixed_caps_func
      Has acceptcapsfunc(): gst_pad_acceptcaps_default
    Pad Template: 'src'
  SINK: 'sink'
    Implementation:
      Has chainfunc(): 0x7fa0f4ed4870
      Has custom eventfunc(): 0x7fa0f4ed49d0
      Has custom queryfunc(): gst_pad_query_default
      Has custom iterintlinkfunc(): gst_pad_iterate_internal_links_default
      Has acceptcapsfunc(): gst_pad_acceptcaps_default
    Pad Template: 'sink'

Element Properties:
  name                : The name of the object
                        flags: lesbar, schreibbar
                        String. Default: "siddec0"
  tune                : tune
                        flags: lesbar, schreibbar
                        Integer. Range: 0 - 100 Default: 0 
  clock               : clock
                        flags: lesbar, schreibbar
                        Enum "GstSidClock" Default: 1, "pal"
                           (1): pal              - PAL
                           (2): ntsc             - NTSC
  memory              : memory
                        flags: lesbar, schreibbar
                        Enum "GstSidMemory" Default: 32, "bank-switching"
                           (32): bank-switching   - Bank Switching
                           (33): transparent-rom  - Transparent ROM
                           (34): playsid-environment - Playsid Environment
  filter              : filter
                        flags: lesbar, schreibbar
                        Boolean. Default: true
  measured-volume     : measured_volume
                        flags: lesbar, schreibbar
                        Boolean. Default: true
  mos8580             : mos8580
                        flags: lesbar, schreibbar
                        Boolean. Default: false
  force-speed         : force_speed
                        flags: lesbar, schreibbar
                        Boolean. Default: false
  blocksize           : Size in bytes to output per buffer
                        flags: lesbar, schreibbar
                        Unsigned Long. Range: 1 - 18446744073709551615 Default: 4096 
  metadata            : Metadata
                        flags: lesbar
                        Caps (NULL)

I don't want to go into dedail about this too much, but as you can see, you can control the tune (SID files can have multiple subtunes), the clock or emulate another chip set called mos8580 with these configuration options.

Convert to MP3

When playing around with SIDs, you might want to convert them to another audio format, in order to be able to play it on your mobile. As mentioned above, its quite easy to do so - in this case, we just change our pipe in a way, that the output is not played, but written to an mp3 file:

gst-launch filesrc location=23.sid ! siddec ! audioconvert ! audioresample ! lame ! filesink location=out.mp3

So after the siddec we run audioconvert and audioresample to convert the raw audio from the SID file to a format the lame encoder can work with. The output of the lame encoder is than written to out.mp3 using the filesink.

Gstreamer will now silently "play" and record the SID tune - but we won't hear it, as there is no audio output. If you also want to listen to it, while converting, there is another useful command:

Tee

Similar to tee in the linux shell, Gstreamer's tee will allow you to "fork" the audio processing and do multiple things at once - e.g. "listen audio" and "convert audio".

gst-launch filesrc location=23.sid ! siddec ! audioconvert ! audioresample ! tee name=music ! queue ! lame ! filesink location=out.mp3 music. ! queue ! autoaudiosink

The first part gst-launch filesrc location=23.sid ! siddec ! audioconvert ! audioresample should be familiar from the earlier examples. After that we add the new tee element and give it a name. The tee element will do two things: it will output its input to the next element (queue in our case) an make it available by name later. So the next element in line is the queue element, which always should be used together with tee. It will basically create new threads that will deal with the input separately. The lame element will be fed by the first queue and convert the audio to mp3. The filesink will finally store the mp3 data. As you can see, there is a new music. command after the filesink. This will read from the named pipe we created with tee and write to the queue, which will be processed by the autoaudiosink.

For better readability, I could split the command like this:

# Read from 23.sid and convert it
gst-launch filesrc location=23.sid ! siddec ! audioconvert ! audioresample ! 
# Create a second stream called "music"
tee name=music !
# Convert the default stream to mp3 and save it as a file
queue ! lame ! filesink location=out.mp3
# Play the stream called "music"
music. ! queue ! autoaudiosink

Round up

As you can see, Gstreamer is pretty easy to use and supports a wide range of formats and codecs. Some years ago I even wrote a TV recorder, that grabbed TV shows from the video card and converted them to Xvid while showing them to me at the same tine. No rocket science, for sure - but I'd never expected those things to be so easy to implement in e.g. Python.

In a follow up blog post, I will have a look of how to implement the commands described above in a Python application.