Thursday, May 9, 2013

beaTunes 3.5.13

beaTunes2 logoI just pushed the latest beaTunes update to the server. It contains a plethora of little fixes and updates (listed below). Furthermore, instead of just uploading the zip of the JavaDocs, I also put an extracted, instantly browsable version right here. As a side-effect, this allows linking the plugin API documentation right to the corresponding interfaces.

Changes in 3.5.13

  • Updated bundled Jython to 2.5.3.
  • Updated bundled JRuby to 1.6.8.
  • Updated bundled Groovy to 1.8.9.
  • Updated bundled H2 to 1.3.171.
  • Removed XmlBeans dependency.
  • Added New Matchlist to context menu.
  • Improved handling of DRM files.
  • Added protected songs filter to analysis queue preferences.
  • Fixed 3rd party license link (OSX, Java7)
  • Fixed truncation of URLs with umlauts.
  • Fixed MatchList autocompletion issue.
  • Fixed failure to display all missing files in missing files inspection.
  • Latest version dialog now understands SNAPSHOTS versions.
  • Matchlistdialog now remembers the last match rule set used.
  • New playlists in FileSystem libraries now get a default name ("untitled").
  • Main song table is now updated after a library switch.
  • Increased robustness on Windows when memory is tight.
  • Improved LuckyLyrics.

Labels: ,

Monday, April 29, 2013

Creating Retina-capable Images with Apple's Java 6

beaTunes2 logoFor a while now, beaTunes supports Retina displays. It does this mostly through the NSImage hack. Any images loaded using Toolkit.getDefaultToolkit().getImage("NSImage://your_image_name_without_extension") that conform to the Apple @2x-naming scheme will be drawn in high resolution, if possible. Another trick to draw in high resolution is to apply an AffineTransform of (0.5, 0.5) to your Graphics2D object before drawing. But what do you do, when you cannot access the drawing code and your image is dynamically generated? For example a custom image for a JButton. Or something you loaded from the Internet. Then you somehow have to mimic what's happening when you are using the NSImage-hack. Unfortunately, Apple's JDK does not offer a public API for what it does. But the implementing classes are nevertheless accessible. So with a little bit of reflection magic, we can take advantage of the same mechanism.

Let's see... First we need to be able to figure out, what the current scale factor is. The scale factor is the factor between logical and physical pixels. Retina displays have a factor of 2.0f, regular displays have 1.0f. Luckily the factor is stored in a desktop property, so it's easily accessible:

private static final String APPLE_AWT_CONTENT_SCALE_FACTOR = "apple.awt.contentScaleFactor";

public static float getHiDPIScaleFactor() {
    final Float scaleFactor = (Float) Toolkit.getDefaultToolkit()
        .getDesktopProperty(APPLE_AWT_CONTENT_SCALE_FACTOR);
    return scaleFactor == null ? 1.0f : scaleFactor;
}

public static boolean useHiDPIScaling() {
    return getHiDPIScaleFactor() != 1f;
}

And now the real trickery (for brevity I skipped imports and proper error handling):

// Creates Retina version of the given image.
public static Image toHiDPIResolution(final Image image) {
    if (!useHiDPIScaling()) {
        return image;
    }

    Image hiDPICapableImage = image;
    try {
        final Class cImageClass = Class.forName("apple.awt.CImage");
        final Method getCreator = cImageClass.getDeclaredMethod("getCreator");
        getCreator.setAccessible(true);
        final Object creator = getCreator.invoke(null);
        final Image nativeImage = (Image)creator.getClass()
            .getMethod("createImage", Image.class).invoke(creator, image);

        final Method getNSImage = nativeImage.getClass()
            .getDeclaredMethod("getNSImage");
        getNSImage.setAccessible(true);
        final Object pointer = getNSImage.invoke(nativeImage);
        final float scaleFactor = getHiDPIScaleFactor();
        final int scaledWidth = (int) (image.getWidth(null) / scaleFactor);
        final int scaledHeight = (int) (image.getHeight(null) / scaleFactor);
        hiDPICapableImage = (BufferedImage)creator.getClass()
            .getMethod("createImageWithSize", Long.TYPE, Integer.TYPE, Integer.TYPE)
            .invoke(creator, pointer, scaledWidth, scaledHeight);

        // dereference first image, to avoid double release
        final Field fNSImage = nativeImage.getClass().getDeclaredField("fNSImage");
        fNSImage.setAccessible(true);
        fNSImage.setLong(nativeImage, 0L);
    } catch (Exception e) {
        // in real code - throw something!
        e.printStackTrace();
    }
    return hiDPICapableImage;
}

When called on a Retina capable Mac with Apple's Java 6 (no, it won't work on Java 7/8), this code generates an Image that will be drawn in high resolution without having to apply an AffineTransform on the graphics object. Naturally, logical pixel width and height are halved, i.e. a 200x200 pixel image becomes a 100x100 image, but on a Retina display all 200x200 pixel are still drawn.

Be warned: Since this hack relies on unpublished APIs, it may become unusable at any time due to changes in Apple's JVM.

Enjoy.

Labels: , ,

Sunday, April 21, 2013

beaTunes 3.5.12 - on Java7

beaTunes2 logoEven though Java 7 was originally released in 2011 (yes, pretty much two years ago!), there still isn't a version available for OS X that works as well as Apple's Java 6. However, we are getting very close to going prime time. As far as I know, only a couple of things are still missing. Most notable are Retina support and a flawlessly working full screen mode. But neither are really showstoppers. So, in addition to the regular beaTunes update, which can be found at the usual location, I decided to post another version of the update which comes with a bundled Java 7.
Why? Well, there really are two reasons:
  1. Feedback
  2. Speed!

Seriously, when playing around with it, it just left a really good impression. It feels a lot faster, more responsive. Neat. I figured, some of you might appreciate this :-)
So, take it for a spin and if you encounter any issues, please let me know in the support forum.
Thanks!
Requirement: OS X 10.7.3 or later
Download link: beaTunes-3-5-12-jre-osx.dmg (98mb)

Update

Due to this bug in Java 7, you cannot create new folder-based libraries with this release. beaTunes simply won't let you choose a folder (see also here). To work around this issue, you can first download the regular release, create the library and then install the Java 7 release.

Update 2

Unfortunately, the current Java 7 (1.7.0_21) for OS X still can't deal with Umlauts in file names. For beaTunes this means a lot of FileNotFoundExceptions. I was assured by the Java dev team, that the fix, which is already included in Java 8 EA versions, will be available soon.

Labels: ,

Saturday, January 26, 2013

beaTunes 3.5.11

beaTunes2 logoJust to let you guys know - there's a new beaTunes version out. This one is just a maintenance release. Mainly, I updated the bundled Windows JVM and improved the artist typo inspection a little. In other words: No big changes, just small improvements.

Labels:

Tuesday, October 30, 2012

Chordify!

Admittedly, this post is a bit off topic. But, if you enjoy playing guitar, please read on...!

A couple of weeks ago at ISMIR, I ran into Bas and Pedro, two of the guys behind Chordify. They gave me a little demo of what Chordify can already do and how simple and intuitive it is. I was impressed! Really cool stuff!

In essence, it lets you either upload your own music files or simply pick a YouTube video for it to analyze. After analysis, it spits out guitar tabs for the song in question. And to make it really easy for you, the user interface offers a player that visually shows you where in the piece you have to play which chord, i.e. the playback is synced to a kind of note/chord sheet. And it's synced both ways. In other words, you can start the player and an indicator will show you which chord to play. But you can also simply select a couple of bars and have the playback loop. This makes perfect sense, if you want to concentrate on practicing only a difficult section of the song.

Anyhow. I think this is great website. Check it out!

Wednesday, October 24, 2012

Import Cover Art Plugin

beaTunes2 logoAs promised yesterday, here's the follow-up post to the yesterday's release of beaTunes 3.5.8. While attending this year's ISMIR conference in Porto, I became aware of the joint MusicBrainz/InternetArchive effort to provide users with an open and public way to look up and download music cover art: The Cover Art Archive at coverartarchive.org.

This is exciting, because other cover art providers' terms (e.g. Amazon's) typically prohibit you from embedding downloaded images into your files. This is not the case with coverartarchive.org. The only caveat: Of course all images are copyrighted by their respective copyright owners.

In order to take advantage of the new service, two things had to happen:

  1. Better support for managing cover art in the beaTunes API
  2. A plugin that could take advantage of the API and also talk to coverartarchive.org
The first part was realized yesterday with the release of beaTunes 3.5.8. Its AudioSong interface now offers additional methods for dealing with cover art. Part two is what this post is about:

The new Import Cover Art plugin.

(for the curious, the sources are available as maven3 project)

How to install the plugin

To install the plugin, start beaTunes 3.5.8 and open the Plugin pane of the application's Preferences. Click on the Available plugins tab, select Import cover art from coverartarchive.org, and click on the +-button.

After restarting beaTunes, select some songs, and click on Analyze. This will open the Analysis Options dialog. Scroll down and you will see a new task called Import cover art. You can configure the task to either add or replace cover art. If you choose replace, an attempt is made to replace front covers with new front covers and back covers with new back covers. Depending on the audio format, the type specific replacement may not always work (but it always works for mp3/id3v2 files). If you are using iTunes as your main music manager, you might not care about the distinction anyway, as it is not capable of telling what's what.

Once you hit Analyze, beaTunes will attempt to look up MusicBrainz Release Ids for the selected tracks and use those to retrieve the image files. Notice that I write attempt. If beaTunes can't find the release id, it can't use coverartarchive.org. The other problem that you will most likely encounter, is, that the artwork may not be stored in the archive yet. Therefore, please contribute!

Contribute!

Contributing to coverartarchive.org is simple. The fine people over at MusicBrainz wrote a neat little guide. Just follow the guide!

Thanks!

Labels: ,

Tuesday, October 23, 2012

beaTunes 3.5.8

beaTunes2 logobeaTunes 3.5.8 is now available for download from the beaTunes.com website. The release features mostly small changes under the hood, including faster start-up, improved lucky lyrics, fixed handling of punctuation in file names, and better support for programmatic (i.e. via the API) adding of cover artwork. More about that last improvement tomorrow... ;-)

Labels: