Brian Nickel’s Online Journal

If you don’t C# you’ll B♭

Archive for March, 2007

TagLib#: Video, a work in progress…

Posted by Brian on March 28, 2007

This is just a start, I’ve replaced AudioProperties with Properties, which contains {Duration, AudioBitrate, AudioSampleRate, AudioChannels, VideoWidth, VideoHeight, MediaTypes}, and added MediaTypes {Unknown, Audio, Video}. All I’ve really done so far was the massive code rename, but it should be relatively fast from here. See what about 10 lines of code gives us in ASF:

brian ~/Programming/taglib-sharp/examples $ mono ReadFromUri.exe /media/shared/Movies/Clips/Clinton\ Interview.wmv
/media/shared/Movies/Clips/Clinton Interview.wmv
Title:
Artists:
Performers:
Composers:
Album:
Comment:
Genres:
Year:       0
Track:      0
TrackCount: 0
Disc:       0
DiscCount:  0
Lyrics:

Audio Properties
Length:     00:15:56.0840000
Bitrate:    32
SampleRate: 44100
Channels:   1

Video Properties
Length:     00:15:56.0840000
Width:      320
Height:     240

Embedded Pictures: 0

---------------------------------------
Total running time:    00:00:00.2000030
Total files read:      1
Average time per file: 00:00:00.2000030
brian ~/Programming/taglib-sharp/examples $

Posted in Programming, TagLib# | Leave a Comment »

Dock: More technical details on scaling, XInputShape, and icon highlighting

Posted by Brian on March 18, 2007

The dock, as of March 18th, 2007

Scaling:

While that beautiful scaling looks like it would be hard to calculate, it is actually super easy. At first glance, the curve created by the function looks like a simple Gaussian Distribution and believe it or not, that is exactly what it is. The base function for Gaussian Distribution is y = exp(-x^2), and the modified form for this is actually size[i] = normal_size + (big_size – normal_size) * exp (-((pointer.X – (x[i] + size[i]/2))^2). Since x[i] = sum(size[k]) for k = 0..i-1, and size[i] is dependent on itself, all we have to do is perform this calculation iteratively until the values converge (with a reasonable tolerance).

private void CalculateIconSizes (ref double [] sizes, double x, double pointer)
{
  double x_current = x;
  for (int i = 0; i < sizes.Length; i ++)
  {
     while (true)
     {
        double old_size = sizes [i];
        sizes [i] = StandardSize + (HoverSize - StandardSize) * Math.Exp (-Math.Pow ((pointer - (x_current + sizes [i] / 2)) / HoverSize, 2));
        if (Math.Abs (sizes [i] - old_size) <= 0.25)
           break;
     }
     x_current += sizes [i];
  }
}

Shaping:

InputShapeMask is a very nice feature, but it has a few weeknesses. The first thing that screwed me is that it doesn’t work with override redirected windows. In retrospect, that makes sense, but I don’t like it. The second thing is that you should not do any more masking than you need to. For example, if you wanted to mask an area like this:

Mask for entire window

The easiest thing to do would just be to make a pixmap with the same dimensions as the window, draw your rectangles, and set the mask. Unfortunately, this causes a funny problem. When you move over the window, in the hidden area, sometimes it focuses for a split second, causing all sorts of problems and annoyances. To overcome this, you should only render a mask for the area you’re interested in. For example, since you’re only interested in:

Region that needs masking

What I do is loop through all the rectangles finding the top-most, bottom-most, left-most, and right-most points, then create a pixmap (bottom – top) tall and (right – left) wide, rendering the pixmaps onto it offsetted by (top) and (left) so the end result looks like this:

Actual pixmap to use

Then I use win.InputShapeCombineMask (pixmap, left, top); to get it in the right place.

      private void DrawInputMask (int width, int height, Cairo.Rectangle [] rects)
      {
         int top = height;
         int bottom = 0;
         int right = 0;
         int left = width;

         foreach (Cairo.Rectangle rect in rects)
         {
            if (rect.X  right)  right  = (int) Math.Ceiling (rect.X + rect.Width);
            if (rect.Y + rect.Height > bottom) bottom = (int) Math.Ceiling (rect.Y + rect.Height);
         }

         Gdk.Pixmap pixmap = new Gdk.Pixmap (null, right - left, bottom - top, 1);
         Cairo.Context context =  Gdk.CairoHelper.Create (pixmap);
         foreach (Cairo.Rectangle rect in rects)
            context.Rectangle (rect.X - left, rect.Y - top, rect.Width, rect.Height);
         context.FillPreserve ();
         context.Stroke ();
         InputShapeCombineMask (pixmap, left, top);
      }

Icon highlighting

Icon highlighting is probably one of the trickiest things to do. While it is relatively easy to render SVG onto a context, it is pretty tricky to highlight just the image. The following is my solution:

  1. Figure out which region you want to work with on an unscaled cairo-context. It is very important that you use an unscaled context to avoid pixilizing the icon you’re working with.
  2. Create a new similar surface from the context’s target. In C#, this is context.Target.CreateSimilar (Cairo.Content.ColorAlpha, width, height);
  3. Create a new context on the surface and save it.
  4. Do whatever icon rendering you want with the new context.
  5. Restore the context.
  6. Create a new rectangle over the entire canvas, using whatever color you want with whatever alpha may be appropriate.
  7. Use icon_context.MaskSurface (icon_context.Target, 0, 0); This function takes whatever you were about to draw, in this case a colored rectangle, and prints it using the alpha channel of the surface as a mask. Thus every pixel of image will be covered with color proportional to its transparency.
      private double DrawIcon (Icon icon, Cairo.Context context, double x, double y, double size)
      {
         if (!LeftAligned) x -= size;
         if (!TopAligned)  y -= size;

         context.Save ();
         context.Translate (x + PaddingSize, y + PaddingSize);
         Cairo.Surface icon_surface = context.Target.CreateSimilar (Cairo.Content.ColorAlpha, (int) Math.Ceiling (size - 2*PaddingSize), (int) Math.Ceiling (size - 2*PaddingSize));
         Cairo.Context icon_context = new Cairo.Context (icon_surface);
         icon_context.Save ();
         icon_context.Scale ((size - 2*PaddingSize) / 100.0, (size - 2*PaddingSize) / 100.0);
         icon.Draw (icon_context, 0, 0);
         icon_context.Restore ();
         if (Mode == DisplayMode.Full && PointIsInRect (pointer, x, y, size, size))
         {
            icon_context.Rectangle (0, 0, size - 2*PaddingSize, size - 2*PaddingSize);
            icon_context.Color = new Cairo.Color (1, 1, 1, 0.6);
            icon_context.MaskSurface (icon_surface, 0, 0);
         }
         context.SetSourceSurface (icon_surface, 0, 0);
         context.Paint ();
         context.Restore ();

         return size;
      }

Posted in Cairo, Programming, XInputShape | Leave a Comment »

Cairo-row-row your boat… Bringing sexy back!

Posted by Brian on March 16, 2007

Brief note: New TagLib# released, adds lyrics support, fixes ASF writing, speeds up MPEG-4 (Not as fast as I previously boasted in my blog, but substantially.) Check out the website for more details and downloads.

Another brief note: I have time to mess around like this because I’m on spring break, I won’t have time to work on this again most likely until summer.

Now, for what everyone loves… SCREENSHOTS!

If you will it, it is not a dream…

Now that’s what I call scaling!

It’s transparent, it’s scaling, it’s fast, and it’s MANAGED! (Except for some RSVG work due to an incomplete API.)

The goal is to build my own Gimmie style application based on the mockup given when Gimmie was first announced. This would include four corners (applications, documents, system, and people) which would improve access to tasks and hopefully simplify the thought process for users. Some concepts I would like to include:

  • (From AWN) A DBus interface so applications could assign icons to windows, assign windows to applications, files, people, etc.
  • When the mouse leaves the dock, I want it to reduce to just the corner and shrink to a small size. Then, when an application wants to grab the user’s attention/input focus, it’s icon could appear, the corner could get bigger, and some eye-candy animation could begin.
  • Plugins: The library currently provides a generic Icon class which renders by receiving coordinates, dimensions, and a cairo-context. It would be pretty easy to render any sort of content
  • (From Cairo-Dock) I would really like to have a patterned background like Cairo-Dock. Looking at their code, it’ll take some DllImporting due to shortcomings in Mono.Cairo, but nothing too serious.

I’ll make a more technical post on the code in a while, once I’ve got the input mask worked out.

- Brian

Posted in Cairo, Programming | Leave a Comment »

TagLib#: Go Speed Racer…

Posted by Brian on March 12, 2007

Note: The changes discussed in this post have yet to be rolled in to Subversion. Things have changed that require me to recode the some of the rendering parts.

When I way a young lad, nary a few fortnights ago, Aaron Bockover posted a blog entry comparing TagLib#’s performance to Entagged#’s performance, and this comparison was by no means flattering. I, being entrapped in a stupor of my own greatness, and too busy making things work to consider such trivialities as performance, didn’t pay too much heed to this at the time. That all changed one fateful day, when I committed a mortal sin, revision 71776. Working diligently to remove the vestigial organs in the body that is the library, I went too far, pulling out a stop cap which lead to a flood of file operations, making life unbearable for those souls do not fancy Id3v2. While fixing this wound, I noticed a rather startling thing, that those who abstained from the wicked art of Id3v2 lives quicker and cheerier lives. With the scent of victory in my nostrils, I was off, like a bloodhound after a fox, determined that by sheer will alone, I would cure my library of the venom that ailed it. Tearing through the code, piece by piece, I slaughtered my foe, liberating all MP3’s from the bondage they were in. Again I struck, and again, until no difference could be seen between the sons of MP3 and the sons of Ogg. There was much rejoicing throughout the land, but it was not enough. For MPEG-4, on its mighty mountain, looking down from afar was still cursed with the poison. But today, this very day, there should be rejoicing in the kingdom once more. For MPEG-4 has been freed from captivity and may walk once more among his brothers…

I apologize for that. I started typing and then got carried away, but there is much to celebrate today, as I have made another great step forward, and reduced the reading time on MPEG-4 (AKA. iTunes files) but almost 70%, bringing the average read time to just more than a millisecond. But there is only so much words can say, perhaps this picture could best explain what has happened:

Warm file read times for my music directory with my current hacking version.

This happened when I tried, one more time, to tackle the problem of why exactly MPEG-4 was taking so long to read. I had figured the culperate was the many, many tiny file reads I was doing. Reading many, many tiny blocks was my best guess, but I had no really good way of stopping this (due to the whole “box inside a box” style), so I moved the entire thing to reading one big block, but it resulted in marginal gains. Then I recoded it to just read the “udta” box and the files loading near at Ogg speeds. So I changed the reading system again, this time doing tiny file read but ignoring a few gigantic boxes I didn’t need to read. This cut the read times in half. It was sweet, but still not good enough. So, I decided to go back to the orginal comparison of TagLib# vs. Entagged#, and peeked at how Entagged# does things. Essentially, it does a very quick recursive lookup of box header and for very standard container boxes ({header}{child1}{child2}…{childN}), it recurses through them. This exposes the four main boxes that TagLib# is interested in: ‘meta’, ’stco/co64′, ‘mvhd’, and ’stsd’. Using switch cases and our classic Box factory, we can extract the rest of the useful data. Presto! Everything we need, nothing we don’t. Why didn’t I think of that?

Now I need to rewrite some of the writing code, but once that is done, I’ll be able to roll it all into subversion. Look forward to yet another release in the next couple days!

PS. The results of the new TagLib# vs. Entagged# battle:

              File         Reader      Avg.     Total
-----------------------------------------------------
       sample.flac         TagLib  0.000322    3.2200
                         Entagged  0.000269    2.6900

     sample_v1.mp3         TagLib  0.000213    2.1300
                         Entagged  0.000250    2.5000

        sample.m4a         TagLib  0.000896    8.9601
                         Entagged  0.000619    6.1901

     sample_v2.mp3         TagLib  0.000717    7.1701
                         Entagged  0.000382    3.8201

        sample.wma         TagLib  0.000673    6.7301
                         Entagged  0.000398    3.9801

        sample.mpc         TagLib  0.000531    5.3101
                         Entagged  0.000324    3.2400

        sample.ogg         TagLib  0.000512    5.1201
                         Entagged  0.003756   37.5606

   sample_both.mp3         TagLib  0.000727    7.2701
                         Entagged  0.000469    4.6901

Considering that one of the main differences between TagLib# and Entagged# is that TagLib# reads everything and Entagged# reads just what is necessary, really, really quickly, I don’t think we’ll ever be as good as TagLib# on everything, but I think we’re doing a pretty decent job.

Posted in Programming, TagLib# | Leave a Comment »

Man, this was a bad idea… Id3v2.3 write support!

Posted by Brian on March 9, 2007

Well, adding write support for Id3v2.3 isn’t really a bad idea. The bad idea is staying up until 4 in the morning on a 6 hour coding spree. Revision 73998 contains a tonne of minor code changes and a single powerful static property:

TagLib.Id3v2.Tag.RenderVersion

This can be set to 2, 3, or 4, but it is very important to recognize that internally, all tag data is stored as Id3v2.4. This means that you may lose a few frames while reading and you may lose a few frames while writing. Use this with caution.

Posted in Programming, TagLib# | Leave a Comment »

Transmitting file data ……………………. Committed revision 73980.

Posted by Brian on March 9, 2007

Banshee bug 412273 should now be solved, as well as minor bugs that may show up in a few files here and there. To summarize the change, let me first present you with the Changelog:

2007-03-08 Brian Nickel <**********>
   * src/TagLib/Mpc/File.cs: Inherit NonContainer.File
   * src/TagLib/CombinedTag.cs: Add some useful features for NonContainer.*
   * src/TagLib/Id3v1/Tag.cs: Add Tag.Size instead of repeated hardcoding of
   128.
   * src/TagLib/Id3v2/Footer.cs:
   * src/TagLib/Id3v2/Header.cs: Throw proper CorruptFileExceptions
   * src/TagLib/Mpeg/File.cs: Inherit NonContainer.File
   * src/TagLib/WavPack/File.cs: Inherit NonContainer.File
   * src/TagLib/NonContainer:
   * src/TagLib/NonContainer/Tag.cs:
   * src/TagLib/NonContainer/File.cs:
   * src/TagLib/NonContainer/EndTag.cs:
   * src/TagLib/NonContainer/StartTag.cs: New generic abstracts for files
   that can start/end with an assortment of tags. (FLAC, WV, MPC, MP3)
   * src/TagLib/Ape/Tag.cs: Read backwards if a footer is found and forwards if
   a header is found.
   * src/TagLib/Ape/Footer.cs: FooterPresent and IsHeader were read
   incorrectly. Also throw proper exceptions while reading.
   * src/TagLib/Flac/File.cs: Inherit NonContainer.File
   * src/TagLib/Flac/BlockHeader.cs:
   * src/TagLib/Flac/Block.cs: Removed from Flac.File, made public.
   * src/TagLib/Mpeg4/File.cs: Remove old commented code.
   * src/Makefile.am:
   * src/taglib-sharp.csproj:
   * src/taglib-sharp.mdp:
   * taglib-sharp.mds:
   * tests/tests.mdp:
   * examples/SetPictures.mdp:
   * examples/ReadFromUri.mdp: Updates.

To solve this bug, I created a new namespace, TagLib.NonContainer, which provides an abstract file which does the following:

  1. Checks the very beginning of the file for a valid APE or ID3 tag, reads it, and then checks after that tag for another, until none is found.
  2. It then lets the child class read whatever it wants to at the beginning of the file. For most formats, this is just going to be the first header block, and for FLAC it’s a vorbis comment and embedded images.
  3. It then advanced to the end and searches backwards in similar manner.
  4. Same as 2, but at the end.
  5. Have the child class read the audio properties, if necessary.

So, now you can read ID3v2 tags at the end or APE tags at the front, not to mention fun combinations like (APE, APE, ID3v2, APE). The combinations are infinite! (sarcasm implied)

On the plus side, now the child classes are much cleaner and easier to work with internally.

Enjoy!

Posted in Programming, TagLib# | Leave a Comment »

TagLib#: Where are we at?

Posted by Brian on March 2, 2007

So, I’ve been getting a lot of emails lately about TagLib#. Apparently it’s become quite popular with over 200 downloads since I put up the latest revision on Feb 12. Anyway, the more popular the library becomes, the more errors appear and I am hoping this weekend will give me some time to hunt them down. Here are some problems I need to seek and destroy:

  1. TagLib# Bug 7: Some MP3 files are throwing “index out of range” and “division by zero” exceptions.
  2. Gnome Bugzilla Bug 412273: WavPack fails if APE tag is at the beginning of the file.
  3. From email: Id3v2 tags disappearing on “Save”.
  4. From email: Application crash on WMA TagLib.File.Create().
  5. From email: Images in MP3’s disappear in WMP10 when saved in TagLib#.

I’m waiting on files for 1 and 3, but 1 is most likely due to some minor code problems here and there and I think 3 will be due to an application’s inability to read ID3v2.4 tags.  2 is going to be a little sucky, as I’m going to need to write methods to read and write the hodgepodge mixture of tagging formats and arrangements that can be in MP3, WavPack, and MusePack formats, but the code will be more flexible because of it. 4 is most likely like 1 where I just made some mistakes.

5 is the real doosy as I’m going to need to rewrite the code so it can write Id3v2.3 tags. It was never that major of a problem when just dealing with Linux, as you could always say “My library is better,” “Id3v2.3 sucks. It doesn’t even have UTF-8 support,” “Why support a dead standard,” or “Id3lib hasn’t been maintained in 3 years. You really shouldn’t use EasyTag. Try Cowbell.” But now that I’m making in roads with Windows users, the fact that WMP does not support the Id3v2.4 standard is a really problem. Hopefully I can at least start making a dent in the problem but I’m going to have to seriously rework parts of TagLib.Id3v2.FrameFactory as well as all of TagLib.Id3v2.*Frame. I’m going to put this off until last and maybe branch (How do I do this?) TagLib#’s SVN to not mess up Banshee, which I think is close to a release. Release cycles… What a crazy world.

Posted in Programming, TagLib# | Leave a Comment »

Cairo, XShape, Transparency, and .NET: Together at last

Posted by Brian on March 1, 2007

Have ever you thought to yourself, “Man, I’d really like to make a transparent window using Gtk#?” I did. So I tried reading through the one really transparent program I had, MacSlow’s cairo-clock, and discovered the stuff going on under the hood was freakin’ nuts. Well, on February 17th, Ikke on Planet Gnome posted a swanky program called pycairoshape-clock.py which did pretty much the exact same thing with a few exceptions:

  • The code was a lot simpler.
  • It was witten in a much friendlier language.
  • It contained 100% Gtk/Gdk/Cairo functions thanks to some lovely additions in Gtk+-2.10

Well, I have some lovely news. That program was really easy to read and I C-sharped it into a friendly little program which is functionally identical. I give you CairoShapeClock.cs. In just 13^2 lines of code, you can have your very own transparent clock to wobble around the screen. I’d post screenshots but as the program is EXACTLY the same to the python one, you should just check out Ikke’s post.

Well, that’s a nice clock, but how does it work?

There are three parts: Checking if we can draw transparently, drawing the widget, and shaping the window.

The first part is straightforward. You create your window, then check that window.Screen.RgbaColormap != null. If the colormap is not null, then you have the much covetted “a” part of the Rgba and can draw transparently. window.Colormap should then be assigned to the RgbaColormap or RgbColormap based on whichever applied to the situation. Since the window may lose this power if your compositing engine (beryl or compiz) dies, you need to monitor the window.ScreenChanged event. Any time it is called, there’s a new screen at window.Screen for you to check out.

For the second part, you need to render the window’s background when its exposed. To do this, you need to monitor the window.ExposeEvent event. When this event is called, you get your chance to draw stuff on the screen. To draw the background, the program does the following:

private static void Expose (object o, Gtk.ExposeEventArgs args)
{
int width;
int height;

win.GetSize (out width, out height);

// Create the cairo context
Cairo.Context cr = Gdk.CairoHelper.Create (win.GdkWindow);

// Make the window transparent.
if (supports_alpha)
cr.Color = new Cairo.Color (1.0, 1.0, 1.0, 0.0);
else
cr.Color = new Cairo.Color (1.0, 1.0, 1.0);

cr.Operator = Cairo.Operator.Source;
cr.Paint ();

...

This function starts each expose event by creating a cairo context and a color, either transparent or plain, and painting the color onto the context. With the background set, the program can continue drawing everything else, business as usual.

The last part is shaping the window. This is done by creating a mask pixmap of equal size and shape to the window, drawing the clock’s shape on it, and calling (the new in Gtk# 2.10) win.InputShapeCombineMask (pixmap, 0, 0);. This method, restricts input signals to the window to the masked area with everything else falling through it to the window below. This is similar to ShapeCombineMask, but it doesn’t trim the visual, letting us keep our nice smooth edges.

Hopefully this will be helpful someone. Happy coding.

Posted in Programming | Leave a Comment »