Hotspots: Admin Pages | Turn-in Site |
Current Links: Cases Final Project Summer 2007
My name is Doug and this is my Who's Who Page. I really don't have much to say about myself, so I decided that our group could just use this page as a small case study for our mp3 player. For more information about me, visit my web site.
|Group Name:||Too Embarassing to Tell|
|Current Version:||SqueAmp v0.7|
The SqueAmp User Interface
|The Status Bar — SqueAmp often 'pauses' when attempting to perform operations such as downloading songs. Thus, we thought it would be wise to have a status bar. The status bar indicates which song is currently playing, whether or not the player is stopped or not, whether or not the next song is being downloaded, and whether or not a search is being formed.|
Two Windows — We chose to go with two windows for a couple of reasons. First, we were modeling our UI after the ever so popular WinAmp. Second, the playlist can take up a lot of desktop space, so we wanted the user to be able to optionally close the playlist without closing the player. We were careful to ensure that closing the player would also close the playlist. However, we overlooked the fact that there was no way to reopen the playlist if it had been closed :-(
The Buttons — What, did you expect to find something? I feel the buttons are pretty self explanatory so I won't go into detail about them here.
See squeamp for more on the UI
Playing over the webTo play our songs over the web, we chose to use an applet that harnessed the power of JMF (Java Media Framework). We chose to take this route for the following reasons:
1) It was an object oriented approach.
2) It was portable to any platform JAVA had been ported to.
3) It worked for all types of standard media including video
4) It works in any browser that supports JAVA Applets
IE, Netscape, Mozilla... just to name a few
5) It would stream the songs rather than require the entire
song to be downloaded first.
6) Olin offered Extra Credit for a JAVA Applet based solution :-)
You can download our applet here: streamingplayerapplet.class
Object Oriented DesignHere is our UML Class Diagram. I won't go into detail about our design here since it's discussed throughout this document.
Handling Multiple usersAt the time when we were originally designing the P4 milestone, there had been no clarifications on whether the system would have to support multiple users being able to use the Web-based JukeBox simultaneously.
So we decided to play on the safe side and create a flexible design to handle that requirement. And after many hours of heated deliberations in the TA lab, we finally came up with a neat design (which we later implemented) that efficiently solves the problem of not having unique identification system on the web in a browser-independent way. Here is how we did it:
1. We created an additional class, called JukeBoxCollection (extends Dictionary), which is really a hashtable of JukeBox instances, which stores the current state of the user, such as their playlist.
2. Whenever a user accesses our primary page for the first time, they get assigned a unique identifier (UID), which is used to track all of their movement through the site.
3. A new JukeBox instance gets created for that user and it is stored in the JukeBoxCollection based on that unique UID.
4. When the user clicks on various links and buttons to move through the site, their UID is sent on to the next page, along with the request to an instance of JukeBoxCollection (which handles all top-level requests for PWS)
5. That JukeBoxCollection instances looks for the UID in the request and if it doesn't find it, creates a new JukeBox for that user and stores it in its hashtable. Otherwise, it finds the user's JukeBox in the hashtable and tells it to process the specific request.
One major problem with this design, however, is that all these JukeBoxes will remain in memory and will not ever get garbage-collected because there is no way to detect the event of the user actually shutting down their browser or moving on to another site.
To solve this, we introduced a timeout value of 20 minutes for each JukeBox. Whenever the user is idle for more than 20 minutes, the system assumes that the user has gone away and removes the JukeBox from JukeBoxCollection, so that the garbage collector would remove it from memory.
Saving and Loading PlayListsWhen we first wrote the code to save and load playlists, we had the following goals in mind:
In order to validate a playlist when loading it, we inserted a header and a magic number into the file. These took up the first line of the file. If the header and magic number didn't match up correctly, then we could assume that the file was in a format that we couldn't recognize. After the header came a carriage return and the name of the playlist followed by two carriage returns. Then, the diskpath of each song was on each consecutive line.
- validate the file is a playlist before trying to load it
- be able to embed the name of the playlist into the disk
- the filename for the playlist doesn't have to be the same as the 'name' of the playlist. Thus, "Doug's Mix" could be saved as doug.pl
- filenames of playlists should have a common extension (pl)
When P7 rolled around, we noticed the following things
- we might want to save both the name of the song and the its path
- if we got the song from the web, we may want to save the URL where it was obtained from. Thus, you could send your playlist to a friend, and they could listen to all of your songs. This would be possible because the playlist would tell SqueAmp where to find the songs on the web.
- We might want to store more data later, so how can we make our file format generic enough so that it won't have to be changed for future updates to our system.
The solution was to embed within the playlist file directions on how a PlayList entry could be added to the playlist. Before I continue, let me describe a bit about our design:
| OrderedCollection |
| PlayListEntry | | PlayList |
|—————|0.. 1 |———–|
| diskPath |<-------------| name |
| name | |———–|
|—————| | next |
| diskPath: | | name: |
| diskPath | | name |
| name: | | save: |
| name | | load: |
| saveString | |___________|
| CachedEntry |
| url |
| diskPath |
| saveString |
A PlayList is nothing more than an OrderedCollection of PlayListEntries. The AudioPlayer knows which song to play by calling next on the PlayList. Each type of PlayList entry knows how to create a string (that can be written to disk) that represents itself. When PlayList load: is called, the PlayList reads in a file that contains these strings and then parses them apart to figure out how to create a PlayListEntry for each one. To explain it, I'll show a playlist file and describe what each part does. Each row in the following table represents a different line in the file:
| Line in file || Description |
|JUKEBOX#MAGIC:31337|| header and magic number|
| ||blank line|
|Doug's Mix||name of playlist|
|CachedEntry||name of class representing a PlayListEntry|
|name:url:diskPath:||class message that will create a new instance|
|Dave Latchaw - the_winding_trail.mp3||remaining lines contain parameters for the instance creation message|
| ||Blank Line|
|CachedEntry||name of class representing a PlayListEntry|
|name:url:||class message that will create a new instance|
|Dave Latchaw - grandmas_farm.mp3||remaining lines contain parameters for the instance creation message|
| ||Blank Line |
|PlayListEntry||name of class representing a PlayListEntry|
|name:diskPath:||class message that will create a new instance|
|Dave Matthew's - Crash||remaining lines contain parameters for the instance creation message|
Notice that the first and second entries are both CachedEntries but they have different message. The first one (which also contains a diskPath) has already been downloaded to disk. The second one hasn't. If a CachedEntry will be downloaded to disk when its diskPath is requested. The last entry is just a regular PlayList and contains a name and a location where it can be found on disk.
Problems we ran into:Our first few milestones went pretty smoothly, and we had a pretty solid design, but we did hit a few bumps along the way:
- we didn't have a very good system for keeping track of changes. We tended to file out the entire JukeBox category at once and mail it to the group, rather than filing out individual classes or messages. This caused us to almost turn in a milestone without any web-playing abilities for the web-player milestone. We eventually settled down the revision control a bit and didn't have any more serious troubles. Filing out individual messages is an excellent idea :-).
- In order for the java applet to play midi correctly it needs the web server to send a header with the content-length. For whatever reason PWS didn't serve up this particular header and thus midi would mysteriously not work. We spent several hours puzzling on this one.
- mp3.lycos.com changed their format the night before we turned in the milestone for P6, but luckily one of our group members caught it and fixed it.
- in general our group worked pretty well together, and although we had the occasional "heated discussion" over design we never got into an actual fight.
Links to this Page