Overview
JxMusic is a desktop music player application developed as a school project for the learning of Software Engineering principles. The project is built upon AddressBook(Level 4). The user interacts with it using a CLI, and it has a GUI created with JavaFX. It is written in Java, and has about 10 kLoC.
Summary of contributions
-
Major enhancement: Implemented Player component
-
What it does: Handles all audio related functionalities by interfacing with JavaFX Media.
-
Justification: This is the core feature of JxMusic as it handles audio related interactions in a music player application.
-
Highlights: This enhancement supports new media control commands. Existing mp3 player libraries for Java were briefly evaluated. Before settling on JavaFX Media, I created a prototype player to experiment on its API. However, the implementation of Player experienced some setbacks due to unforeseen issues with JavaFX Media.
-
Discovery of confirmed JavaFX Media bug
-
Resolution of another suspected JavaFX Media bug
-
Incompatibility of JavaFX Media and JUnit, and by extension, Travis and AppVeyor.
-
Asynchronous nature of JavaFX Media
-
-
-
Major enhancement: Coordinated refactoring process of entire code base
-
What it does: Transforms most of the original address book 4 codes to fit as a music player.
-
Justification: As the original code base was an address book application, a major overhaul was made to transform it into a music player application.
-
Highlights: Written elaborative and detailed issues and PRs. Guiding principle was to transform
AddressBook
toLibrary
,Person
toPlaylist
,Tag
toTrack
. Source refactored in 2 days and tests refactored in 3 days.
-
-
Major enhancement: Implemented
play
,playlist new
,duration
commands.-
What it does:
-
play
: Plays a playlist/track or resume from a pause -
playlist new
: Creates a new playlist -
duration
: Shows the duration of a playing track
-
-
Justification: These are just some of the many commands the team worked on as a whole.
-
Highlights:
play
command caters for 5 modes of usage -play p/
,play p/<playlist>
,play t/
,play t/<track>
, andplay
-
-
Other contributions:
-
Minor enhancement: Modified the parser regex to cater for command phrases instead of just single command word.
-
Minor enhancement: Implemented simple validation checks for tracks using mp3 file header bytes.
-
Test: Written test for
play
command
-
-
Project management:
-
Evidence of technical leadership: Occasional participation on the module forum
Contributions to the User Guide
Given below are sections I contributed to the User Guide. They showcase my ability to write documentation targeting end-users. |
Playing a track : play t/
Plays a track. If there is a playlist or track playing, it will be stopped and the track will be played.
Format: play t/[TRACK]
Examples:
-
play t/
Plays the first track in the library folder sorted by file name if there is any. -
play t/Some Song
Plays the track named "Some Song" if it exists in the library folder.
Playing a playlist : play p/
Plays a playlist. Similarly to playing a track, if there is a playlist or track playing, it will be stopped and the playlist will be played.
Format: play p/[PLAYLIST]
Examples:
-
play p/
Plays the first playlist in the library folder if there is any. -
play p/Favourites
Plays the playlist named "Favourites" if it exists in the library folder.
Continuing a paused track : play
Continues a paused track.
Format: play
Examples:
-
play t/Some Song
pause
play
"Some Song" will continue playing from where it is paused.
Creating a playlist : playlist new
Creates a new playlist with specific tracks and saves it into the library.
Format: playlist new p/PLAYLIST [t/TRACK]…
Examples:
-
playlist new p/Favourites t/Some Song t/Some Song 2
Creates a new playlist with the name Favourites and adds the tracks named Some Song and Some Song 2.
Contributions to the Developer Guide
Given below are sections I contributed to the Developer Guide. They showcase my ability to write technical documentation and the technical depth of my contributions to the project. |
Player component
API : Player.java
The Player
component,
-
interfaces with JavaFX Media to play sounds
-
handles media control with mp3 files
Player Component
Current Implementation
The Player component is a new component in addition to the existing 4 other components of AddressBook. It handles all audio related functionalities for some of the Command classes to use. Player mainly interacts with the JavaFX media library that is included in Java, so no third party library is involved.
The job of the Player is basically forwarding requests of media playback controls to the Playable object.
Singleton Player
Player is implemented as a singleton as only one instance of it is required at any time. While singleton brings about undesired implications such as tighter coupling and lower testability, we ensured that Player is only used by the Command classes and no other parts of the code touches Player. As for testability, we discover that JUnit is not compatible for JavaFX media (details in Notable Issues with JavaFX Media). On the plus side, singleton pattern makes adding dependency very easy which is very helpful as adding dependency into the Logic component of Address Book was tedious.
PlayableStatus
PlayableStatus
to represents the state of the player, effectively acts as a layer on top of JavaFX’s MediaPlayer.Status
.
-
UNINITIALIZED
- The initial state when no track or playlist has been played -
PLAYING
- User enters either theplay p/
orplay t/
command -
PAUSED
- User enters thepause
command -
STOPPED
- User enters thestop
command -
ERROR
- Any other states of theMediaPlayer.Status
which JxMusic is not concerned with
PlayableStatus
state chart diagramSkipped Tests
JUnit and JavaFX MediaPlayer does not work well together. Running any test that constructs a MediaPlayer object (ie new MediaPlayer
) will throw IllegalStateException: Toolkit not initialized
. In order to resolve the exception, it requires calling Platform.startup()
before new MediaPlayer
is called. Even so, the tests will not work on Travis nor Appveyor, throwing MediaException: Cannot create player!
thus failing the builds. It is suspected to be due to incompatibility or lack of support for JavaFX Media on their test servers since JavaFX Media has its own dependencies (at the bottom of link).
Therefore, any test that depends on MediaPlayer are skipped by using Assume.assumeNoException(mediaException)
.
Notable Issues with JavaFX Media
-
JavaFX Media does not work for mp3 files that has photoshopped album art.
-
This issue has been reported as a confirmed Java bug.
-
-
Playing a track on MacOS requires setting the MediaPlayer current time to 0 before calling
play()
as it jumps to the end of media for no reason. Whereas on Windows, callingMediaPlayer.play
works.-
This issue is addressed at #35.
-
Design Considerations
Aspect: Choice of media player library
-
Alternative 1 (current choice): Use JavaFX Media API
-
Pros:
-
No additional dependency (at least on common operating systems)
-
-
Cons:
-
See Skipped Tests
-
-
-
Alternative 2: Use 3rd party mp3 player library such as JLayer
-
Pros:
-
Possibly less issues than JavaFX Media.
-
-
Cons:
-
Additional dependency
-
Existing mp3 libraries for java are old and badly documented.
-
-
Play
PlayCommand caters for commands having the word play
in it, in particular, play
, play p/
and play t/
commands. It consists of 5 modes:
-
Continue from pause -
play
-
Default playlist -
play p/
-
Specific playlist -
play p/<playlist name>
-
Default track -
play t/
-
Specific track -
play t/<track name>
These modes are determined by the different PlayCommand constructors called by the PlayCommandParser after it parses the user input, as shown in the activity diagram below:
Playing a playlist constructs a PlayablePlaylist which is essentially playing a list of PlayableTrack. The currently playing track is pointed by the currentIndex
.
When a track finishes playing, the next track is played.
Future enhancement: implement prev()
for playing the previous track.
I also wrote the entire "Instructions for manual testing" section in developer guide which is too long to be included here.