Saturday, April 30, 2011

0.11 w/ improved navigation and file browser

I am finally happy with the current build so I am cutting another release.   The first version of the file browser is complete.  It will only let you navigate paths you have added to the scanner.  I also added an about dialog to show some extra information. 







 I also recorded a video of the app on my archos.



The only issues i've ran into so far is the songs view can get extremely laggy when your collection is like 50k songs.  I will do some work in the future to make that view a bit more responsive for large collections.  Also on my thunderbolt I sometimes have to touch the buttons in the quicknav twice before it registers the press.  Otherwise I am not aware of any other bugs.  Please post if you find any issues.


Download 0.11

Thursday, April 28, 2011

0.11 Preview - Improved UI / Navigation

As I added more things to my player, I began to notice how cumbersome navigation was becoming.  I decided to rework the user interface to make switching between views to be much easier.  What I did was add a quick navigation bar with icons for each different view.  Its location can be set in the preferences (TOP, BOTTOM, LEFT, RIGHT).  I also change the title to show which view you are currently in as well

Edit: woops I changed the title from GoneMAD Music Player to Music Library on accident

Here are some screen shots:
The icons were mostly randomly picked from the built in ones.  They are just placeholders for now.

Tuesday, April 26, 2011

Android, sqlite, and foreign keys

I think its about time to do a post actually about android development.  One thing I discovered while setting up the database for gmml was that the older versions of the android os use a fairly outdated version of sqlite which do not implement foreign keys.  It will parse your statements fine but not actually do anything with the foreign keys you declare.

Sqlite version for each OS version

/*   
     *   Android 1.5 (Cupcake): 3.5.9
     *  Android 1.6 (Donut): 3.5.9
     *  Android 2.1 (Eclair): 3.5.9
     *  Android 2.2 (Froyo): 3.6.22 - First to support foreign keys
     *  Android 2.3 (Gingerbread): 3.6.22
     *  Android 3.0 (Honeycomb): 3.7.4
    */

Why is this important to know you may ask?  Well in version 0.10 of gmmp I experienced crashing on my thunderbolt (2.2) when trying to delete the database, but didn't experience it at all when testing in 1.6.  Turns out I was deleting tables out of order and violating some of the db constaints which are only present on 2.2+ causing an unhandled exception to be thrown.

I have corrected the issue so it shouldn't crash in the next version

Monday, April 25, 2011

Using the scanner


Access the Scanner through Menu -> Preferences -> Scanner

Setting up the scanner is fairly straightforward.  You first want to add the location(s) of your music.  Press the Edit Scan Paths to bring up the popup in the image above.  By default /mnt/sdcard and /mnt/sdcard/Music will be added to the list of paths/folders.  To remove a path from the list, simply click the list item and press the remove button.  To add a new path you can either type it in manually in the edit box, or press the browse storage button to bring up a list of paths found on the device.  Once selected press the add button to add it to the list.  Click save to save all the changes.

Once the scan paths are setup, you can add specific paths to ignore the same way.  Press the 'Edit Ignore Paths' to tell the scanner what paths to skip over.

Scan Options

Delete database before scan:  Wipes the entire database before running a scan.  This option is not typically needed but is present in case the user wants to start fresh without having to uninstall then reinstall.

Only scan new files:  If this is enabled, any file that was already scanned into the database will be automatically skipped over.  If you are using an external tagger to retag your music, disable this option to pick up all the changes.

Ignore folders with .nomedia:  This is a behavior that the default android music player follows.  Any folder with a file named .nomedia, will get skipped over.  This is useful if you have folders with ringtones or other sound bytes that you do not want showing up into GMMP.  Note: the ignore paths can also be used to skip these paths.

Format artist/album tags:  Artist names and album names will be formatted into proper case before being entered into the database.  This is useful if your music collection is not tagged to any standard.  Example:  I have 3 albums by "insert band name here".  The first album the artist is tagged "Insert Band Name Here", 2nd album "INSERT BAND NAME HERE", and 3rd album "insert band name here".  With this option off, each album would be recognized as being from a separate artist.  With this option on, all 3 albums are inserted into the database as the same artist "Insert Band Name Here".  If you have taken the time to properly tag all your music, you will not need to worry about this option.

Ignore AlbumArtist tag:  By default the scanner will use the albumartist tag if it exists instead of the artist tag to label albums.  Enable this if you want to ignore that tag

Scanner Menu Options (Press the menu button in the scanner)

Clean Database:  Removes any missing songs, artists, albums, or genres from the database.  This is typically done at the end of a scan, but it can be done independently as through this option.

Setup Auto Scan:  This sets the scanner to run at the given interval

Database Stats:  Shows the number of songs, artists, album artists, albums, and genres scanned into the database

0.10 with tag reader, file scanner, and file browser

It's late so I will keep this somewhat brief.  I have added a decent UI to the file scanner so everyone should be able to get their music into gmmp.  The first thing you should do is go in and edit the scan paths (there is a button in the scan page) and be sure its only pointing to places with music.  I accidentally tried a scan just on /sdcard/ and something caused the scanner to crash.  The paths set there are used for the file browser as well.

The file browser itself is not complete but it "works".  I need to sort the lists and finish populating all the information in the list.  Be sure to run a scan once before attempting to play a file from the browser, otherwise it will probably crash or give unexpected results.

One last neat little feature I threw in is to play a random song on playlist completion.  If enabled when your active playlist is finished, it will start playing a random song.. so the music never stops.  Enable that through the playlist options.

Download: 0.10

Edit: It appears if you scan with "delete database before scan" checked it will crash.  So erase the data through the android options if you want to clean the db.

Sunday, April 24, 2011

Working file scanner

So after getting taglib up and running I was able to implement a file scanner to scan your sdcard/hd/whatever to bring in music into gmmp. 


The UI is super simple right now.. mainly because its 3:30 in the morning.  Under the hood the scanner can accept multiple directories to scan, specific folders to ignore, and a flag to ignore folders with a .nomedia file in it.  I also added an option whether the nowplaying screen gets the song info from the actual file or from the database.  I plan on putting something in tomorrow that lets you choose what information is actually scanned and stored from the file, so for those who want to take the "minimum data stored" route.. you will be able to just store filenames and the rest can be scanned from the file when its being played.

Friday, April 22, 2011

My battle with the NDK

My next step in development is to code a file scanner to eliminate the dependency completely on the built in media store in order to use gmmp.  There are some tag libraries written for java I believe but from day 1 intended to use taglib. It supports many file types "Currently it supports both ID3v1 and ID3v2 for MP3 files, Ogg Vorbis comments and ID3 tags and Vorbis comments in FLAC, MPC, Speex, WavPack TrueAudio, WAV, AIFF, MP4 and ASF files." and is supposed to be pretty fast.

In order to use taglib, I would have to figure out how to compile C/C++ code using the Android NDK.  Let me tell you this was an adventure.  Taglib is setup to build with cmake so after googling around I found some others who were able to get their projects to build using cmake.  I followed all the tutorials/instructions using cygwin and ubuntu and got no where even close.  Cmake chokes when testing the android c compiler so I could not even make the makefiles.  I tried to trick cmake into passing those tests but the end result were some dll files... yea not useful at all.

So I caved and decided to use the suggested method in the docs and create a makefile by hand (Android.mk + Application.mk).  I wish I did this from the start because I was seeing progress almost immediately.  Besides having to add some flags to use stlport and link with zlib.. the whole process was painless.

So my lesson to everyone else trying the same thing... just follow the android instructions and don't bother with cmake.

Now to deal with JNI.. bleh

Monday, April 18, 2011

0.08 alpha

I finished making everything compatible with my new database aka the GoneMAD Media Library (gmml).  I also fixed some various bugs and its in a good enough state that I'll post it for others to try.  You will have to run the scan once in order for it to pick up your music.  Those with large collections I suggest plugging the device in and let the scan run for awhile.  My 300 GB collection took maybe an hour and a half on my archos to fully scan.  The slowness is on the android's mediastore's side so there isnt much I can do to speed the process up.  I will write a good scanner in the future which will eliminate the need to get the metadata from the media store.

Download 0.08

Edit: Rapidshare is a pretty bad site so please post some suggestions for better file hosts

Saturday, April 16, 2011

Large library speed tests

I just got finished coding up a new data layer for my player.  It allows you to use either the built in media store or the new database I setup specifically for the player.  It is highly optimized for our needs and also allows me to implement new features such as album/artist/song ratings, tagging album/artist/songs, dynamic "views", playcounts, multiple artists per song, and probably a bunch more.

Anyway one of the issues I saw early on in my testing on my archos 5IT was the default mediastore could barely handle massive libraries.  I have roughly 300 GB worth of music on it and want to be able to easily browse it all using a library.  So I have 2 videos showing the speed comparison between my app using the built in mediastore (version 0.05) and then it using my new database (version 0.07).  Response time for 0.07 is almost immediate, whereas 0.05 takes 5-6 seconds whenever you want to look at the albums for an artist.

This vid is 0.05.. sorry for it being sideways.



This is the much faster 0.07, ignore the crash at the end.. I have not implemented that feature using the new DB yet

Anyway.  This is good for any other archos 5IT users.  You will finally have a music player that can handle all your music.

Sunday, April 10, 2011

A week later

I was on travel for work most of this week so I didn't get to work on my app as much as I wanted to, but I still managed to get a good amount finished.  I am trying not to spend too much time on the looks right now.  I did separate the app into 3 main views: the library, now playing, and the playlist.  You can navigate between all 3 with the menu.


I also made some major improvements with the playlist.  The only thing visually different is the italics + white font for the current playing the song.  I also made the playlist persistent.  The current tracklist is saved off to a database along with the current track you are listening to. 

I also added some new context menu options for the library.
Play: Will erase the active playlist and play either the artist, album, song or genre depending on what was pressed.
Play Next: this will que up the artist, album, song or genre to play right after your current song finishes.  This feature is a must for people with music ADD like myself.
Enqueue: adds the selection to the end of the playlist

I did some other things under the hood like a massive code cleanup and added logging.

Download 0.06 alpha

Monday, April 4, 2011

Results: Coding an Android music player in a weekend

The full story with the challenge I gave myself can be read here: original post

TL;DR - I had no experience coding for the android but the API looked easy enough that I challenged myself to write a music player in a weekend.  I posted throughout the weekend showing my progress.  Here is a video of the end results.  I spent roughly 26 hours coding 1250 lines of code.
 
Note: I listen to more than just metal. The scanner only happened to get the portion of my collection that was death metal before I stopped the scan.


At the beginning of the challenge I gave myself the following goals:

Primary goals:
-Play any song that is a supported format (obviously) - DONE
-Basic controls: play/pause/fast forward/rewind/next/prev - DONE
-Create/View a simple playlist - DONE
-Ability to browse artists - DONE
-Ability to browse albums - DONE
-Show basic info on screen while playing - DONE


I would have to say this challenge was a success.  I didn't get around to doing any of my secondary goals because I decided to go out on Saturday night instead of staying in to code.  I plan on continuing development on this and eventually making it available on the market.

You can download what I've done so far here: gmmp-0.03-alpha.apk  It will crash if the orientation changes while the app is running, but otherwise it runs quite well on my Archos and in the emulator.  I recommend playing with it in an emulator because I honestly have no idea how it works with phones or any device not running 1.6.  Don't blame me if it messes up your device.  You have been warned.

Playlist

So with the addition of a playlist I have met my primary goal list for the weekend.  In the album/artist/song/genre views if you press a song it will do one of 2 things.  If a song is current playing, it will enqueue the song to the existing playlist.  If a song is not playing it will clear the current playlist and play the song.  The playlist can be accessed from the now playing view by hitting the menu button.  It should show all the songs in the playlist and will position the list so the song currently playing is in view (if the playlist is long enough to extend past the screen.  You can quick jump to a track in the playlist or remove a song all together via the context menu.


The playlist is not persisted (if you kill the app you lose the playlist).  I also still have to put in logic to automatically move to the next song in the playlist and hook up the next track and previous track buttons to change tracks.  I'll hopefully finish that up in the next hour and then I'll call it a night.

Sunday, April 3, 2011

Now Playing

I've been a bit distracted today so I haven't gotten as much done as I've liked.  I put in a new "now playing" tab that shows the info on whatever is being played.  There are also some buttons to control the playback and seek through the track.  The sdk made it fairly easy to add the media control buttons.  There is a MediaController widget that does a lot of the work already.

Music Service

Well it seemed I figured out how to get services working, so I have moved all the music playing over to the service and actually am using the MediaPlayer class instead of the AsyncPlayer.  Now I have everything I need to construct a now playing tab with the basic controls.

Saturday, April 2, 2011

Done for the day

Well I ran into issues with trying to connect to my service but I believe I figured it out.  It'll be interesting to see how much more I can get done because I probably wont be back on til afternoon tomorrow.  Oh well.

Vid of basic audio support

The controls are pretty basic right now.  You click a song to play and you click the same song to stop.  If you click a different song it will switch over.  It's not a very good setup but I'm going in small steps.  I'm approaching the 24 hour mark from when I started and I am pretty excited that I have something as functional as this.

Audio!

So just to get something working right away I used the AsyncPlayer which is not recommended.  With 3 lines of code I know have a working music player.  I did experience some hilarity when trying to change tracks.  I had multiple instances of AsyncPlayer going.. so right now im listening to a Naildown song and Scar Symmetry song playing at the same time.  Quite interesting.  I should probably implement a stop feature.

Navigation video

Ok I partially rescanned my library to something more reasonable.  It looks like 15-20 artists with 50+ albums?  On my archos its super responsive and the genre tab works quite well.  I'm happy with navigation so I will begin working on the fun stuff.. the actual audio portion.

Genre browsing

I can be stubborn when it comes to coding, so I continued to try to find a way to be able to browse Genre -> Artist -> Album -> Song and I believe I came up with a solution.  It is most likely quite inefficient since I am combine many many sql queries, but it will have to do for now.  I did some testing and it is mostly unusable on my archos due to there being so many artists for each genre, but it seems to work just fine on small libraries (in my emulator).  I think I will only partially scan my archos from now on to make testing a bit more realistic

Video

Alright I just recorded a video of my app running on an 500 gb Archos 5IT filled with about 300-350 gig of music.  That much music is definitely a good way to do stress tests.  Either way since the db is so large the queries take a bit.  I apologize for the mumbling.. i just woke up.

I'm tired

So I was able to refactor most of the common code between each tab and have the navigation completely working for artist, album, and song.  I'm having some issues with genre mainly because its done completely different in the SDK.  Even when I get it working there is still a bunch of extra work to be done so its actually useful.  By default it wants you to go Genre -> Song which is completely unusable.  I want it to be Genre -> Artist -> Album -> Song.  I"ll try tomorrow briefly to do that, but im not going to dwell on it too much.

I will post a video when I wake up showing the player live.. and hopefully comparing it to the pathetic android 1.6 player (my navigation is nearly identical after a half days work)

Accessing the data

At first when displaying the data from the media store I was basically doing a query and iterating the results to populate an ArrayList which got fed into the ListView. Something like this:


//--------------------------------------------------------------------------------------------------------------

cursor = managedQuery(Audio.Albums.EXTERNAL_CONTENT_URI, new String[] {
     Audio.Albums.ARTIST}, null, null, Audio.Albums.ARTIST + " ASC");

int artist_index = cursor.getColumnIndexOrThrow(Audio.Albums.ARTIST);
       
cursor.moveToFirst();
artist_list = new ArrayList<String>();
do
{
   String artist = cursor.getString(artist_index);
   artist.add(artist);
}
while (cursor.moveToNext());
       
setListAdapter(new ArrayAdapter<String>(this, R.id.def_listitem, artist_list));
//--------------------------------------------------------------------------------------------------------------

After looking into the api more I discovered there is actually a class that does this for you.  Awesome.
So my code is now much shorter

//--------------------------------------------------------------------------------------------------------------
cursor = managedQuery(Audio.Artists.EXTERNAL_CONTENT_URI, new String[] {
     Audio.Artists._ID, Audio.Artists.ARTIST }, null, null, Audio.Artists.ARTIST + " ASC");
        
adapter = new SimpleCursorAdapter(this, R.layout.list_item, m_cursor , new String[] { Audio.Artists.ARTIST }, new int[]{R.id.def_listitem});

setListAdapter(adapter);
//--------------------------------------------------------------------------------------------------------------

I am still unsure which approach I am going to use.  The second way is much cleaner but in order to get the information needed to make the album and song queries I had to extra it from text fields which is pretty ghetto.  Must investigate more.  Now for that beer.

Navigation

First thing I quickly added a genre tab which was pretty much a copy paste of the artist tab.  Nothing too special.


For the last few hours I been working on the navigation in the artist tabs.  The code will be nearly identical for the other tabs so I will worry about that later.  I put a toast to show the action of playing a song and clicking the back button.  I also messed with some of the formatting for the album and song view.
Selected Scar Symmetry -> Holographic Universe.  Tracks are in order by song number
Clicked on Quantumleaper then hit the back button twice
 I think I earned myself a beer. 

Friday, April 1, 2011

Auto updating lists

I had coded this before but I thought it was not working.  Turns out I uploaded a new track to the emulator incorrectly so it wasn't being picked up as the media scanner.  Once uploading it properly, my app will auto refresh the current view when a tab is selected and when the app is brought back into focus.

Mediastore

So I started back up around 7:15 or so.  It took me some time to figure out how to actually get some music on to the "sdcard" of the android emulator.  Eclipse has a gui to do it, but it limits you to transfering one file at a time and you cannot make folders.  Adb seems like the way to go.

adb connect localhost 5554
adb push "srcfolder" "/sdcard/destfolder"

The transfer takes FOREVER tho.  I literally coded the mediastore access in 15 minutes and tested it to make sure its working before a file transfer of 4 albums finished.

I decided to add in a song tab and make the font size slight smaller for the album list and even small for the song list.  I'm keeping the list displays simple for now.  It's trivial to add more info about the album or song.  So here is a screen of the results. 

Dark Matter Dimensions is in there twice because the media store considers the bonus track to be a separate album

Starting UI

I started off just making a simple UI to browse artists and albums.  With relatively little code I was able to create the basic artist/album tabs with list views displaying the contents.  90% of the players I looked at use this type of layout to browse your collection so I am using that as a starting point.


The contents of the lists are hardcoded.  So my next step is to feed those into a data source.

Now to hit the gym and get dinner before I settle down into a long night of coding.

Goals

In order to judge how success this experiment is, I should set some capability goals for the end of the weekend.

Primary goals:
-Play any song that is a supported format (obviously)
-Basic controls: play/pause/fast forward/rewind/next/prev
-Create/View a simple playlist
-Ability to browse artists
-Ability to browse albums
-Show basic info on screen while playing

Secondary goals:
-Show album art while playing
-Reorder songs in playlist
-Trigger a media scan
-?? I'll edit this if I think of more things

Challenge: Code an Android music player in a weekend

I will start with a little background as to why I am doing this.  About a month ago I purchased a 500 GB Archos 5IT which is running 1.6.  I have quite a large music collection and I wanted to be able to listen to it all in my car so this seemed like the perfect player for me. 

"With storage sizes reaching up to 500 GB, your ARCHOS 5 internet tablet can store up to 600 movies, 290,000 songs or 5 million photos. No need to be frugal, store everything on your ARCHOS."

They advertise that it can hold 290k songs, but the first thing I discovered was that the archos music player only supported roughly 32k songs in its library.  The default 1.6 player was complete garbage as well so I begin to search for a decent music player that could support my needs. My choices were limited for 1.6 but I found one app that seemed good enough for the time being.  It was a folder based and had a good amount of customization.  Folder based navigation is great if you know exactly what you want, but it is not ideal if you are just browsing for something to listen to.  It ended up being my best option for 1.6 but I figured maybe there was something better for 2.0+

A week or so ago I got a new phone that ran 2.2 (HTC Thunderbolt) so the first thing I did was start trying out all the other 2.0+ music players out there.  I tried out maybe 10 different players and still that one for 1.6 was the best.  Some players did have some nice features, but not one had everything I wanted.  I also discovered that some of them (both free/paid) were complete garbage.  I know for a fact that I could write something better.

I've been wanting to learn the android sdk for awhile and now that I have some android devices, this is the perfect time to start.  A music player seems like a good starting project for me and I could eventually mold it into something that suits all my needs .  This week I started setting up a dev environment and went through the simple hello world tutorial.  I also started reading the docs online to get an idea what I would be working with.  I was quite impressed with the API google has provided and the other day I casually said to a friend something along the lines of "this android sdk is great.. from the looks of it I could probably hammer out a decent music player in a weekend".

So all that leads me to today.  The statement of making a music player in a weekend stuck with me and sounded like it would be a pretty good challenge for an experienced programmer like myself.  So naturally:

My plan is to track my progress on here throughout the weekend and see what I end up with sunday night.  If I fail no one will have to see this right? ha

So I sit here at roughly 4pm on friday with my autogenerated project that consists of:

public class GMMPMainActivity extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }
}

and looks like:


its go time