Sunday, April 1, 2012

Instrutorials - Part 5 - Working prototype for Smartphones and Tablets

Hi all,

Last week I've finally come to the point where I have a functional prototype of my application. The code definitely needs some clean-up before going public, but hey, it works. One can browse the API for scores written for piano as well as a recorder. Scores are then played back with a visualization of the instrument (currently only for the recorder).

Since my last blog post, I had bumped into some issues which stalled my progress. I'm going to talk about those below.

Android MediaPlayer
Sigh. I already had a hard-coded prototype of the visualization working in my second post about this topic. It used a MIDI file which was included in the application's resources. However, when trying to bind this Activity to my search and browse activity from my previous post, the MediaPlayer class kept failing. I could not, in any way, let it play a MIDI file. I tried creating a MediaPlayer from the URI, downloading the file to a temporary location and loading it from there, but each and every time the MediaPlayer failed with the same error: "Unable to locate the file". When I used an URI of a MP3-file, everything worked fine. Because of these ridiculous errors, I decided to use both MP3 as MIDI files again. MP3 for audio, MIDI for the visualization. My own code could use a locally stored MIDI file, but the MediaPlayer class from Android could not.

Visualisation on Tablets
For development and testing, I always used my Samsung Galaxy S II with CyanogenMod 9. However, my application should also run perfectly on a Tablet, since it's using the Ice Cream Sandwich SDK. When I demoed my current progress to my Promotor, we noticed that the visualization of the red dots was quite off. I found this strange, because I already place the dots based on percentage levels, not on actual pixels. After some digging around, the fault was rather obvious. Android only allows three possible definitions for a width or height of an ImageView, these being "MATCH_PARENT", "WRAP_CONTENT", or a fixed size. I had defined the height as "MATCH_PARENT" (filling the whole screen in this case), and the width as "WRAP_CONTENT", thinking it would scale nicely. However this was not the case, as the width of my resulting ImageView was the width of the original image, independent of its scaled height.

To solve this, I defined my own ImageView "AspectRatioImageView" and overloaded the onMeasure(int, int) method, to let it calculate it's own height and then using the resulting height to calculate the corresponding width. In code, it looks like this:

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
if (getBackground() != null)
int height = MeasureSpec.getSize(heightMeasureSpec);
int width = height * getBackground().getIntrinsicWidth() / getBackground().getIntrinsicHeight();
setMeasuredDimension(width, height);
} else
super.onMeasure(widthMeasureSpec, heightMeasureSpec);

No syntax highlighting here, but I guess you'll get the point.

Next up, I'll finally implement piano visualization, clean-up my code (can take some time :p), and add some more functionality.


No comments:

Post a Comment