Monday, April 23, 2012

Torus 1.02 Beta

Hi all,

For those who don't remember (or never played/downloaded Torus: Tower Rush), here's a screenshot:



I've recently noticed that my small Java game called Torus: Tower Rush was no longer available for download.
The reason for this was the closure of Megaupload, since the file was hosted there.

I didn't have version 1.01 Beta lying around, so I cleaned up the work-in-progress I had and reuploaded the game.

The result has no new features, but the code now provides a framework for recording games and playing back those games. You could record awesome games and share them with your friends this way.

The function isn't implemented yet, but expect it in version 1.02 Beta (whenever it comes).

Download: DROPBOX

Greets!

Thomas

Sunday, April 22, 2012

Instrutorials - Part 6 - Piano and more

Hi all,

Been a while since my last post; I've had a rather busy schedule in the previous weeks, but now I'm here to show some more progress on the implementation of my project.

I'll do this by starting with a video I recorded for a presentation last week, so you can show my application in action:



Firstly, audio and video isn't really 100% synced, but this is because the screenrecorder could not record audio. I've manually added the audio afterwards (well I actually let a friend do it since I'm a newbie in video editing).
You can see the user searching for scores containing "Pokemon". Once a score is selected, the score is played back to the user in real-time, by both audio and video. The user then searches for a score for the recorder, and then that score is played back to the user. It's the basics of the application, and it works. Yay!
Adding piano support to the app was very easy due to my current structure. I just needed to add a xml, an image, and just a few lines of code. If one provides me with a xml and an image for a different instrument, I can add it in less than a minute.

I have also gone back to MIDI for both my audio as visual feedback. The reason for all the MIDI bugs seemed to be problems with file-extensions. Android's MediaPlayer class can play every sort of file by just reading the header, but that fails with MIDI files. Since I first wrote my temporary files as .dat's to the internal file system, the MediaPlayer failed. However, now I'm back to using MIDI, loading times have been lowered exponentially. MIDI files are so much smaller in size than mp3 and I don't need those anymore!

Anyway, since the video I've added some things.
Firstly, I changed the search interface. I now presume that the user knows what score he's looking for, so I removed the browsing part of the GUI (it wasn't implemented anyway). The removal of those tabs gives me some more vertical space, which has a nice result:

old
new


























Next, I've also added an overlay when the app is searching for scores:


This solution is clean and makes clear to the user that the application is working.

I've also added another screen between the score list and the actual playback of the score. When a score is now selected, the user gets to see a screen like the one below, with more details about the score.


Now the user has the option to play the selected score, or download some files. Of course, the "Play score" option loads the activity as shown in the video. The download button allows the user to choose a file-type:


If the user selects one of these five options the application will download the corresponding file using the DownloadManager of Android. This means that the files will be stored in the default download folder, which is chosen by the user. It also shows a notification and allows the user to open the file directly when it's downloaded. Using the DownloadManager also allows the user to keep using Instrutorials when the files are being downloaded, since that happens in the background. If one pulls down the notification bar whilst downloading, he/she gets to see this:


The downloaded MIDI and MP3 files can be played back by the standard Media Player, while the user can use Adobe Reader to check out the PDF file.

All this functionality of the "Details" window as I call it, while not making the core of my application more potent (the actual playback), I personally feel that it makes the application much more complete. Even if a person does not need the playback functionality of my application. He/she still can use it just to search and download scores on his tablet and/or smartphone. If I didn't know better, I'd call it the official MuseScore app :P

To close this blogpost a quick screenshot of the playback Activity:


As you can see, I added functionality to the Activity to pause and continue playing. The "NEXT" button on the ActionBar is supposed to let the application play just the next note and then pause, so by pressing next one could go through the score step by step. However, the library I'm using for MIDI files on Android isn't really helping much. Between calling the pause button on the library and the actual pausing is at least a gap of 90 milliseconds. This is fixable for just manually allowing pause and play, but for real-time pausing and playing for every note it's undo-able. When the library encounters a note it should pause automatically, but before it actually pauses at least a few notes more are already played. I'll try to find a solution for this, but I can't guarantee finding one, since I'm really limited by the library I'm using in that aspect.

Anyway, that was a quick peek into the latest updates of my implementation.

See you next time,

Thomas

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 MuseScore.com 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:


@Override
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.

Thomas