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.
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.