Present Perfect


Picture Gallery
Present Perfect

90 minutes of hacking

Filed under: GStreamer,Hacking,Python — Thomas @ 11:39 pm

11:39 pm

today give me, as a followup to yesterday’s task post:

  • an implementation of MultiTask that tracks progress across all tasks combined
  • a new task that calculates the MusicBrainz TRM id/fingerprint of a track
  • an example that uses the new MultiTask with the new TRMTask to calculate the fingerprints of a playlist given

Not bad for 90 minutes of hacking. I really like my expressiveness in Python. And all of this done while listening to beautifully mixed music with my current jukebox script. I’m actually enjoying hacking again!

The example doesn’t actually save the fingerprints yet. My mini-goal with this for DAD is to fingerprint all audio on all my devices, as a basis to uniquely identify audio tracks, and then layer the rating of tracks on various machines on top of that information.

Skype hack session

Filed under: GStreamer,Hacking,Python — Thomas @ 11:18 pm

11:18 pm

Today, Jan and I did some hacking over Skype! It was fun, worked out well. Jaime was off shopping and Kristien had her radio show and some interview with Flowrida.

Jan mostly helped me with stuff though, he didn’t seem to need any help from me. I started ripping CD’s to flac this week, and was very disappointed yesterday when my jukebox program didn’t seem to work at all with .flac files. With Jan’s help I narrowed down the problem, checked with Edward, then I fixed it on a git branch.

I also set up http publishing of my personal git branches, and a cgit installation. cgit was a little more difficult to configure than I’d like, the documentation isn’t much help. I’ll keep that for a separate post though.

Anyway, my jukebox is happily playing again with all the freshly and accurately ripped .flac files I have. My life just improved by 20% !

Although, I don’t know whether it was switching to uridecodebin for decoding, or some bug in .flac, but sometimes the composition just seems to jump a few seconds.

I’ll worry about that later, first fix some more bugs and make the jukebox example a bit more featureful. I’m wondering what to do on ‘next’ and ‘previous’ – rearrange the composition on the fly to match ? I hope gnonlin will be able to keep up…

Tip from our hacking session: add persistent history to your gdb session by putting the following in .gdbinit:

set history filename ~/.gdbhistory
set history save on

gst-python gotcha

Filed under: GStreamer,Hacking,Python — Thomas @ 9:39 am

9:39 am

Here’s an innocent little line of code that caused me to lose an hour of debugging and svn bisecting time yesterday:

gobject.timeout_add(0L, self._pipeline.set_state, gst.STATE_PLAYING)

That change was buried inbetween some other cosmetic changes, and caused the UI of my application to be frozen. It took me a while before I nailed it down to this line, and a little bit more to figure out exactly why.

The goal was to set the state of the pipeline to PLAYING ‘as soon as possible’, from the main thread.

Of course the line of code does something subtly different. timeout_add schedules a call, so that part’s fine. But, the call gets rescheduled automatically unless the call returns False. Since that returns a GST_STATE_CHANGE enum, as long as the state change doesn’t fail, it keeps getting rescheduled, over and over. So, no time for the UI to update.

I figure this is something I”m bound to run into again, so a little blog will increase my chances of figuring it out again next time.

Bug hunting

Filed under: GStreamer,Hacking,Python — Thomas @ 12:31 pm

12:31 pm

Yesterday, I was working some more on my jukebox code. It is now in the state where I can actually have it play a playlist and do the mixing properly. So I’m finally into dog food mode if you will, or in the mode where I can actually enjoy the result of the code I’ve written.

But I still had random failures that I couldn’t make sense of that I thought were due to gnonlin. I was getting random backtraces on startup when setting up my gnl objects, like so:

Traceback (most recent call last):
File "examples/jukebox.py", line 123, in
File "examples/jukebox.py", line 115, in main
main = Main(loop, tracks, playlist)
File "examples/jukebox.py", line 67, in __init__
self._jukebox.add_track(path, self._tracks[path][0])
File "/home/thomas/svn/src/dad/trunk/dad/gstreamer/jukebox.py", line 81, in add_track
File "/home/thomas/svn/src/dad/trunk/dad/gstreamer/jukebox.py", line 137, in _process
operation.props.duration = mix.duration
TypeError: could not convert argument to correct param type

I had been noticing them before but always thought “I’ll get to that later.” But yesterday it was getting on my nerves.

The problem was, there was nothing wrong with the duration I was setting. ‘duration’ is an INT64 property on a gnloperation, and the duration I was setting was a normal long. I spent hours sprinkling debugging code and diving deeper into the stack to figure out where it was going wrong, but couldn’t turn up anything.

I did see that in pygobject’s gobject/pygtype.c code, it was checking PyErr_Occurred, and just resetting the error and the GValue. I added a PyErr_Print to see why the conversion was failing, and it told me:

TypeError: can't convert negative long to unsigned

This made even less sense – I wasn’t setting a negative long, and I wasn’t setting it to an unsigned property either. It’s too bad the pygtype code wasn’t bubbling up this clear error up, instead obfuscating it with the generic error, but it still didn’t explain my bug.

We had invited friends over for dinner, so in the afternoon I was forced to take my mind off the keyboard, but as you all know that doesn’t mean your mind is off the problem. I’m sure you all know that feeling where you have lots of different pieces of the puzzle lying in front of your mind’s eye, and none of your theories made sense and you still see a bunch of different pieces.

Dinner was prepared, friends came, a good time was had by all, and my mind was off the problem. My only distraction was listening to the mixing points at the end of each song. And then when they left around 1 AM, Kristien and I sat on the couch, were watching some TV, it suddenly hit me. A classic bug pitfall.

What if the error was actually triggered before the line of code I was looking at ?

I’m sure you also all know that feeling where suddenly, a different way of looking at the problem makes all the pieces of the puzzle fall into place. It’s probably the most gratifying moment when doing bug hunting – after hours of frustrating undirected prodding, suddenly you get that one theory that explains both the problem and the lack of result in the prodding.

That’s when, instead of flying to the computer and trying it out, I prefer to savor the moment a bit. Because there’s still the possibility that I might be wrong, and just uncover another layer of fur on the yak you’re shaving. But no, everything seemed to add up, and I knew in my mind exactly the one line of code I needed to add to either prove or disprove your theory. And I also knew that this one line of code was the last thing I needed to try that day. Nothing else. Before, every ‘just 2 more minutes’ I asked for where valiant efforts borne from bug hunting optimism. Now, I see the problem crystal clear.

If the one line proves the theory, I can go to bed safely, be happy that I found the problem, that my stack ws unwound, and I was holding my juggling balls of my mental state in my hands. (Of course, if it disproved the theory, I was in for a night of bad sleep, going over the problem again and again in my head).

So, while Kristien got ready for bed, I moved back to the keyboard, and before the call that I thought was failing in the bindings, added another PyErr_Occurred check, and ran it. And there it went – and it confirmed that the error was actually generated before the line I was looking at, and coming from somewhere else.

So, off to bed, knowing that in the morning I knew exactly where to look.

After some futzing around, I realized that the call before was setting a negative value on a UINT64 property, and it was not erroring out. I was able to reproduce it from a python shell:

>>> import gst
>>> e = gst.element_factory_make('gnlsource')
>>> e.props.start = -1L
>>> import gobject
Traceback (most recent call last):
File "", line 1, in
TypeError: can't convert negative long to unsigned

And after digging through the history of that code, I found the revision where a change was made that removed the error checking for this particular case.

I filed a bug, and that was it. Hours of frustration boiling down to a simple commit labeled ‘guint64 property fix’. The patch is trivial, so I’m sure they will do a better job at it than I would.

Of course, the actual bug is still in my code – I need to figure out why I’m setting a negative start value.

But again I am reminded of a fundamental bug fixing rule that I broke again this time: before trying to fix the bug where it appears to be, first make sure that the bug is where it appears to be. Sometimes the bug is created before where it is detected, and it’s a lot easier to verify that first before anything else.

git workflow question

Filed under: GStreamer,Hacking,Python,Twisted — Thomas @ 10:34 pm

10:34 pm

I’m trying to integrate git into my workflow. I’ve been reading documentation and tutorials (there sure are a lot), but I’m not sure they cover the use case I want to try and implement. So I’m explaining it here in the hopes that some experienced gits will be able to show me the way.

The basic use case is simple: I want to hack on GStreamer, which uses git, from various computers (laptop, work machine, home machine), and have my private and/or public hacking in sync between those three machines.

Basically, when I create a branch on my laptop, hack in it, commit stuff, and push it out to my private or public repo, I then want to pull all those changes on my home desktop and continue hacking.

It looks like I should be able to do it with a magic combination of a bare repository on some server, and the right incantation of git remote add lines on all of my machines. But so far, my experiments have only led me to some abomination of a bare repository where my home machine sees a branch created on my laptop with the name ‘thomas’ as a branch named ‘private/thomas’. In other words, the names don’t match up. And for now it looks like the content doesn’t match up either; I somehow merged the thomas branch into my master on my home desktop. Also, it looks like pulls from that private bare repo end up as an actual commit, which seems a bit messy.

I’ll retry my experiment to see if I might have screwed something up, but in the meantime, if you recognize the use case I’m going for and know how to implement it, feel free to throw me a bone.

« Previous PageNext Page »