Tuesday, July 31, 2007

End of July Status

Here's the skinny on the Banshee port to Windows:
  • Major re-factoring of the audio CD code. I abstracted a bunch of Linux-specific code and implemented classes for Windows. Made some interfaces into abstract classes where helpful and re-jiggered some of the architecture.
    • The Cool Thing: It's all really pretty and audio CD detection works.
    • The Not Cool Thing: CD playback does not work. This is because Gstreamer on Windows doesn't handle cdda:/ URIs. This is because libcdio doesn't build on Windows. This is because, while it used to build on Windows, the Visual Studio solution has terrible bitrot. Solutions:
      • Build with Cygwin and...
        • Live with a dependency on cygwin0.dll, or
        • Find some way of circumventing cygwin dependencies (ideal).
      • Fix the Visual Studio solution. This consumed much of my time recently. I emailed the guy who previously maintained the VS solution, but he hasn't gotten back to me.
      • Write a new cdda handler for Gstreamer using the Win32 API. Ug.
  • Implementation of CD burning for Windows.
    • The Cool Thing: It's partially done (and was loads of fun), but I need to be able to test it to continue with the work.
    • The Not Cool Thing: I can't test it. This is because Banshee's Gstreamer transcoding code doesn't work. This is because it references libgnomevfs-2-0.dll and there is no libgnomevfs-2-0.lib against which to link and when I link against libgnomevfs-2-0.a, Visual Studio produces a malformed DLL which crashes upon loading. Solutions:
      • Build gnome-vfs with MSBuild. I gave that a try and it looks like a lot of work before it will work.
      • Contact whoever maintains Mono's Windows binaries and ask for their advice (ideal).
      • Remove the gnome-vfs code from libbanshee.
  • Improvements to importing from iTunes.
    • The Cool Thing: Following Jonathan Pryor's post on monodocer performance, I switched from XmlDocument to XmlTextReader. Much memory savings. Not a lot of speed savings (XML handling isn't a bottleneck).
    • The Not Cool Thing: Nothing. The code is faster and prettier.
  • Misc. Other Things:
    • I'm hoping the Gstreamer people will add a function export that I need: _gst_plugin_register_static. It's in the code but it just isn't exported for Windows. Waiting to hear back.
    • The CD reading and burning code are from code example sites. Both are just encapsulation of COM interfaces. I'm 99% sure the CD reading code is license compatible (I'm still looking into it), but the CD burning stuff defiantly isn't. I've contacted both authors to see if they will release the code under MIT/X11. Waiting to hear back. If I can't get clearance, I'll have to do the COM wrappings myself. Ug.
    • Gtk is slow on Windows. Even when not debugging. Aaron's new view model should make things much better.
    • Gstreamer is slow on Windows. It's fine most of the time, but when the CPU gets hammered, the audio will become choppy. This doesn't happen when using DirectSound in similar situations. I don't know yet if it's a significant problem.
I find I'm spending most of my time struggling with unmanaged code. When I tackle a new problem, like audio CDs, I hammer out the C# lickety split and have a great time. But then I hit a snag in native land (like the cdda/libcdio problem) and spend days trying to sort out the C libraries which is no fun. After a few days of wrestling with unmanaged code, I'll put it on the back burner and start on something else (CD burning or iTunes importing, for instance) but I really need to address these native issues. Tomorrow I'll start emailing around and see where I can get with cdda support in Gstreamer or gnomevfs linking on Windows. If you have info on this stuff, especially using Cygwin to build Windows stuff which is not Cygwin-dependent, please leave a comment.


Wednesday, July 11, 2007

Using Glib Callbacks with Managed Delegates on Windows

After wrestling with GStreamer for a week on Windows, I was delighted and disgusted to learn that the solution to my problem involved a simple string manipulation. My grin was mighty toothy. Sharp teeth.

I then had a new problem: Native Banshee code needs to call managed Banshee code every now and again (every 200 milliseconds) while Gstreamer is GStreaming. These callbacks are handled on the unmanaged side with glib mechanisms such as g_timeout_add. The problem is, glib uses the cdecl calling convention. If you try to set up a callback to managed delegates, which do not use the cdecl convention on Windows, you'll hit a nasty error when you call a delegate. Specifically, you'll encounter my good friend, the every-cryptic, "An unhandled exception of type 'System.AccessViolationException' occurred in gtk-sharp.dll".

The solution (thanks Miguel) is round-about, but with some jiggering in Visual Studio 2005 you can Set it and Forget it©. Here's the step-by-step:
  • Download and install ActivePerl for Windows.
  • Grab gtk-sharp/gapi-cdecl-insert. I just put it in C:\
  • You may need to edit gapi-cdecl-insert depending on whether or not I've committed these changes to SVN by the time you read this. Look at line 36. If it looks like this:

    `ildasm $assembly /out:$basename.raw`;

    Then you need to change it to this:

    `ildasm "$assembly" /out:"$basename.raw"`;

    (Add quotes to $assembly and $basename.raw). Do the same for line 49 by putting quotes around $basename.il, if they aren't already there.
  • Specify the Glib.CDeclCallback attribute for each delegate definition you will be calling with glib. For example:

    internal delegate void GstPlaybackEosCallback(IntPtr engine);

  • Open the Properties for the C# library project with the delegates of interest.
  • Open the Build Events tab.
  • Click the Edit Post-build button.
  • First add the following:

    Call "C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\Bin\sdkvars.bat"

    Obviously, adjust the install path for Visual Studio as necessary (there is no sufficient macro; grrr).
  • On the next line, add this:

    Call C:\Perl\bin\perl.exe C:\gapi-cdecl-insert "$(TargetPath)"

    Again, adjust your Perl and gapi-cdecl-insert paths as appropriate.
  • For whatever reason, VS fucks up the Perl script at the end, so you need to add one final line to the post-build events:

    Call ilasm /DLL /QUIET /DEBUG "$(TargetDir)\$(TargetName).il"

    Remove the /DEBUG flag when you build for release.
Now try not to have too much fun fixing your glib-facilitated unmanaged-to-managed callback functions on Windows!

Great and terrible news! None of the above craziness is necessary. Thanks to Jonathan Pryor who points out in the comments that all you need to do is specify the [UnmanagedFunctionPointer(CallingConvention.Cdecl)] attribute for the delegate definitions. No Perl necessary! This only works for the 2.0 framework.