PROJECT: JxMusic


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

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]

  • TRACK is an optional parameter, referring to the name of an existing track in the library folder.

  • If TRACK is not specified, the first track in the library folder sorted by file name will be played.

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]

  • PLAYLIST is an optional parameter, referring to the name of an existing playlist in the library folder.

  • If PLAYLIST is not specified, the first playlist in the library sorted by name will be played.

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]…​

  • PLAYLIST refers to the playlist’s name.

  • TRACK refers to the track’s name.

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.

Architecture
Figure 1. Architecture Diagram

Player component

PlayerClassDiagram
Figure 2. Structure of the 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.

PlayerCompleteClassDiagram
Figure 3. Player component complete class diagram
PlayableStatus

PlayableStatus to represents the state of the player, effectively acts as a layer on top of JavaFX’s MediaPlayer.Status.

  1. UNINITIALIZED - The initial state when no track or playlist has been played

  2. PLAYING - User enters either the play p/ or play t/ command

  3. PAUSED - User enters the pause command

  4. STOPPED - User enters the stop command

  5. ERROR - Any other states of the MediaPlayer.Status which JxMusic is not concerned with

PlayableStatusStateChartDiagram
Figure 4. PlayableStatus state chart diagram
Skipped 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
  1. JavaFX Media does not work for mp3 files that has photoshopped album art.

  2. 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, calling MediaPlayer.play works.

    • This issue is addressed at #35.

Design Considerations

Aspect: Choice of media player library
  • Alternative 1 (current choice): Use JavaFX Media API

  • 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:

  1. Continue from pause - play

  2. Default playlist - play p/

  3. Specific playlist - play p/<playlist name>

  4. Default track - play t/

  5. 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:

PlayCommandActivityDiagram
Figure 5. Play command activity diagram

Playing a playlist constructs a PlayablePlaylist which is essentially playing a list of PlayableTrack. The currently playing track is pointed by the currentIndex.

PlayablePlaylistObjectDiagram1

When a track finishes playing, the next track is played.

PlayablePlaylistObjectDiagram2

Future enhancement: implement prev() for playing the previous track.

PlayablePlaylistObjectDiagram3

I also wrote the entire "Instructions for manual testing" section in developer guide which is too long to be included here.