My Life Is A House

My daily life with all things Libre and Open

Tuesday, August 21, 2012

Libwaveform final Google Summer Of Code report

Finally it's a last day of this year's Google Summer Of Code - it's when I need to calm down, look back what I have done and what's still in my todo list. As I plan to carry on with developing this library, end of summer project is merely a milestone, albeit very important one.

Before this summer I had been working with Gtk+ and GObject/GLib on various projects (but mainly on Jokosher, which I try to continue to port to Gst 1.0), however never so seriously as in this project (and never using C). It took some time to get full swing of how things are done in C/GLib/GObject combo. As usual, C requires no taking things for granted, as usual successful compiling doesn't mean it works really as it should. Seriously improving my knowledge of C, GObject, Gtk+ and how to debug all this is my major gain from this project.

My project aim was/is to create usable set of elements which together would allow provide a widget which would draw a waveform. Those elements are WaveformReader, WaveformData and WaveformDrawing (as I have named them in my code for now). Reader handles reading of the sound levels from files (also decoding them accordingly, all using Gstreamer 1.0), Data provides data model and method to add information to it (so far), and Drawing is a generic widget (derived from GtkDrawingArea) which goes trough data in data model and draws it using cairo library.

I have done most of stuff I initially planned for Reader, however new ideas and issues popped up. I have one method to read levels, which takes source URI, interval, start and finish time as parameters, which works as expected. However while testing this I got unexpected behaviour from 'level' element - as I found out, it can't parse data for smaller interval than buffer, so if you even set interval different than default, you can't expect to get results precisely for that buffer size. This was something too hard to fix for me during project time, but I plan to do so with a help from my mentor Sebastian. I also need to expand 'level' element with having sort of 'lowest peak' value for buffer, as it helps to draw nicer waveforms. This also is on my todo list for post-summer coding.
For error handling in library I choose GError. For Reader I added several errors and have tested it trough Python exception handling - you can see example in demo/demo.py script.

I had lot of ideas and theories how data model should look like, however I settled for quite simple now (see PDF here, I will add more improved version later). Most difficult part is related how to treat more detailed data which are needed for zooming in. I'm planning to return to improving data model when I will get fully working stack within theoretical application (like Pitivi or Jokosher). I have started to create TODO file, which naturally and slowly turns into design document. Currently Data has adding method which detects invervals and time and put readings in right places. For storing readings for each channel I use boxed structure (which is quite buggy I admit and will get cleanup treatement postsummer) named WaveformLevelReading. For GI annotations GList of readings is never transfered fully, as it's quite a chunk of data and copying it wouldn't be very smart resource wise. In current state of things Reader create GList of readings, and when adding it to data model, it practically becomes a part of data model, without copying.

For Drawing - widget part - I first stuck at creating widget itself as an object in Python. For some strange reason manually written annotation with (transfer full) for constructor method failed and object weren't transfered, ending it's run into segfault wall in next second I was trying to access it. Thankfully, just removing annotation worked. It was strange as it was copy pasted from other object were it worked. After that I tried to follow instructions from various tutorials - ways to create custom GtkWidgets have changed considerably over years, so probably it would be wise to create some general intro tutorial for doing it in C/Vala/Python - and finally succeeded in having created object which didn't segfaulted and which I could add to generic GtkWindow. After that I started to dig into Cairo stuff, which resulted in very simple waveform which I have now. I tried to replicate Jokosher waveform style with bottom part filled up, but didn't succeeded so far, I will have to look at that later. It's clear that widget part will need most work with style support, customization, and mostly allowing create subclasses  so people can draw overlays. Post-summer my first job will be making zooming work, also implementing async methods of updating waveform data and drawing.



As usual, biggest issues were theoretical ones - understanding how some of more complex stuff work required more time than I planned to. However acknowledging that it's better to dig trough them now than later I did several rewrites and studied lot of Gstreamer code (thanks to Sebastian about giving references and reviews with comments), as I will need to provide patches to Gst or understand how to write async methods. I also have to admit that I was overoptimistic about project's timetable - as usual, studies made things difficult at the beginning of the summer.

In overall, it was very refreshing experience (although I didn't finish lot of things I wanted to - especially implementing zoom logic) and I'm happy that I got some foundations (and beefed up knowledge) in place so I can continue to hack on this library. In more general sense it was definitely worth it because of increased knowledge which will help me to grow as a coder and will enable me to help with code and patches other projects like Gstreamer and Gtk+ in the future.

Project can be found in github.com/Pecisk/libwaveform, see 'postsummer' branch for futher changes for now (as long as I submit code to Google). I will post more updates after week break which I need for my studies. Stay tuned.