COMP 310
Fall 2011

HW06:  ABC Music Player

Home  Info  Owlspace  Java Resources  Eclipse Resources

The Demo

ABC Music Files

An abc music file is a text based transcription of a musical score.  The abc music notation is relatively simple, enabling anyone to transcribe music into a text file which can be parsed and played by a variety of open source software.  There are many pieces of music that have been transcribed into abc notation.  You can find out more about abc notation and find many simple tunes here.  For this assignment, we will deal with a subset of abc music notation.

ABC music files consist of headers and notes.  You will only need to handle files that have headers first and then music. You do not need to handle headers that are interspersed within notes.  Furthermore, you will not need to handle music that has repeats in it. The provided parser ignores repeat symbols, so you only need to play the notes once, even if the score says a particular phrase should be repeated.

Resources:

 

Music Basics

For this assignment, you will need to learn a little bit about music if you do not already know it.  This assignment is about programming using the visitor design pattern, not about music.  But you must be at least passingly familiar with terms like "tempo", "key signature", "sharp", "quarter note", and the like.  Below are a few tutorials.  You can and should also discuss anything that is confusing with the staff.  Please don't struggle over musical jargon or concepts - ask for help!  Also, Google is your friend, use it!

You do not need to read the above sites from start to finish in order to do this assignment.  Skim parts of them quickly if you feel like you don't know anything about music and refer back to them as you do the assignment if you find them helpful.

The Assignment

In this assignment, you will implement visitors to print and play parsed abc music files.  We will provide you with a parser for that subset that reads abc music files and gives them to you in a data structure that you can work with.

Parsed Music

The parser returns the music in the following data structure (click here for docs):

IPhrase UML Diagram

As the UML diagram shows, all of the elements of the abc file will be parsed into IPhrase elements.  The parsed file will therefore yield a single IPhrase object, which you can then perform operations on.  Each "phrase" consists of either a header element, a sequence of phrases, a collection of notes, or a note:

Playing Music in Java

We will be using MIDI to play music.  MIDI is a somewhat dated standard that allows you to play notes using computer generated instrument sounds. Most music these days is sampled, but that is slightly harder to deal with for our purposes when generating simple music from a score.

The Java MIDI library uses ticks to indicate time.  You specify the number of ticks in a quarter note and all note durations are then specified in ticks.  A typical number of ticks per quarter note is 16, enabling you to play notes are short as 64th notes (which would then be 1 tick long).  Internally, all notes begin and end on a timer tick.  The Java MIDI library (and/or your sound card) provides a set of instruments that you can use to play your music.  You select a particular instrument and notes are played in such a way as to (loosely) mimic that instrument.

To simplify the process, we have provided a SequencePlayer class.  The following are the important methods of the class:

  1. Constructor: the constructor takes the number of ticks you want per quarter note and the instrument you want to use.  You can find out the available instruments by calling the getInstruments method which returns a String.  Instruments are specified as a number between 0 and 127. getInstruments shows the mapping from numbers to instruments, one per line.  There is also an init method (which is called by the constructor) that takes the same arguments.  You can reinitialize the sequence player as often as you like by calling the init method.
  2. addNote: the addNote method takes a Note and a starting tick.  The note is scheduled to be played at that tick.  The method returns the tick at which the note will stop playing (which you can then use to schedule the next subsequent note).   What does this say about the input parameter and the return value of a visitor to process an IPhrase?
  3. play: when you have finished scheduling notes to be played using the addNote method, you can call the play method to actually play the music.  The music is played asynchronously, so this method will return immediately. When the music is done playing, it will print "Finished playing" to the console and close all MIDI resources.
  4. close: you can stop the music at any time by calling the close method.
  5. There are also getter and setter methods to read and update the tempo and the number of ticks in a "default note". abc files use the notion of a default note length. The Note class specifies the duration of a note in terms of this default (a length of 1.0 means to use the default).  The tempo and ticks per default note must be udpated from the headers of the abc file.  In case the music does not specify these (some abc files do not specify a tempo, for instance), you should initialize the default note length and the tempo to something you find reasonable before processing any music.  If you then process an L (default note) or Q (tempo) header, you would then update these values appropriately.

The sequence player does not currently allow you to change the tempo in the middle of the piece. The player uses the last tempo that was set before the play method is invoked to play back the entire piece.

Processing Music

For this assignment, you will be writing two visitors to process the parsed music.  You will be using Dr. Wong's extended visitor design pattern implementation, whose code will be supplied.

Read the extended visitor documentation here.

UML Diagram of the Extended Visitor Design Pattern Implementation
Extended visitor UML

The two visitors you must write will do the following:

  1. Convert the entire piece of music into a String so that you can print it.
  2. Enqueue the entire peice to be played using the provided SequencePlayer class and its addNote method.

You should perform these tasks in order.  The first visitor is a relatively straightforward traversal of the sequence lists and will allow you to see and get used to the structure of the parsed music.  The second visitor is more complex as it requires you to install many extended visitor commands and properly deal with the musical structure.

We have provided several classes to make your task easier:

  1. ABCParser: this class is will parse an abc file into an IPhrase data structure.
  2. SequencePlayer: this class is a wrapper to access MIDI sound in Java.  It enables you to add a sequence of notes to a MIDI track and then play it when you are through.
  3. ABCUtil: this class has several static methods that will enable you to parse things a little easier.
    1. parseFraction can be used to convert the default note length from a String to a double.
    2. parseTempo can be used to convert the tempo from a String to a double.
    3. getFileContents can be used to return the contents of a file as a String.
  4. KeySignature: this class keeps track of which notes are sharp/flat for the particular key signature you are using.  The adjust method returns a note whose pitch has been properly adjusted for the given key signature.

Your final program should always enqueue notes only after they have been adjusted for the key signature of the piece (simply via a call to KeySignature.adjust on a properly initialized KeySignature object).  While you are initially developing and debugging your program, if you only play music that is in the key of C, you can ignore the key signature (as no notes need to be adjusted in that key).

Be sure to call the SequencePlayer's init() method beforeprocessing a song or you may hear the previous song as well.   The init method will clear out any old music in the sequencer.

 

GUI

You may make your GUI operate however you think is appropriate.  However, you must be able to load, parse, play, and stop abc music files.  You must also display the currently loaded file and use the toString method to display the music after it is parsed.

The demo GUI makes use of a JSplitPane component.  Each pane then has a JScrollPane which contains a JTextArea.  However, you may organize things differently if you prefer.

If you want the text to wrap inside of the text area, look at the setLineWrap and setWrapStyleWord methods.

Assignment Notes

You should implement a very simple GUI that enables you to parse files, print them (using your toString visitor), and play them (using your player visitor). You should also add whatever debugging support you desire to your GUI.

In this program, the model is relatively simple, and contains only a few things (including a parser, a phrase, and a player).  Similarly, the view is also relatively simple.  But, you should still properly use the MVC architecture.

We recommend approaching the assignment as follows:

  1. Implement the basic GUI and your MVC architecture.  As you work on the following steps, it will be easier if you can easily display your results in the GUI.
  2. Implement the toString visitor first.   Look at the code for the toString() methods of MTSeqList and NESeqList and you will see that those two methods comprise the two cases of the main (outer) algorithm of a forward accumulation algorithm to generate a string representation of the list.  What is missing is the recursive forward accumulating helper (inner) algorithm.   A handy, setToStringAlgo method is provided in NESeqList that enables you to write that helper algorithm and install it into NESeqList.  (Why doesn't MTSeqList need this?)
  3. Create an IPhrase object that consists of a single note.  Create a visitor with a command for Note hosts and make sure you can play a single note.  You will need to initialize a SequencePlayer object with some defaults for this to work.  (This was the exercise from Lab07.)  Think about what parameters need to be passed to the Note's visitor in order to use the SequencePlayer and the return value of that visitor.
  4. Once you can play a Note, add support to play a single Chord or Triplet.
  5. Create an IPhrase object that is a sequence of notes (ISeqList). Add commands to your visitor to handle sequences and make sure you can play them properly. 
  6. Finally, add support in your visitor for Header objects. If you are adventurous, try to play a parsed abc file. Otherwise, construct your own IPhrase object that has a sequence that contains a sequence of headers and a sequence of notes. Try to play that musical phrase first.
  7. There are a lot of classes in the IPhrase hierarchy.   Of these classes, how many of them are distinct extended visitor hosts?  Is it necessary to install commands to handle all of those classes?   Can a visitor even tell if certain classes are serving as its host?  Why?
  8. Anonymous inner classes are crucial for this assignment.   To learn about how to do complex initializations of an anonymous inner class, see the Anonymous Inner Class Techniques page in the Java Resources site.  Especially pay attention to the discussion on "initialization blocks" and on "Accessing Enclosing Anonymous Inner Classes".
  9. In order to properly play abc files, you will need to install commands to deal with headers you do not care about.  Consider the following code for that purpose:
  10. String headerString = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    for (int i = 0; i < headerString.length(); i++) {
         myVisitor.addCmd("" + headerString.charAt(i), defaultHeaderCmd);
    }

    You should install commands for the headers you care about after you do this, so that those commands will replace the default command being used for a header with an arbitrary letter identifier.

  11. Tuplets are very difficult to describe concisely, requiring fairly advanced musical knowledge.  In such, Tuplets should be treated as an unknown host type.  If you think that you can write a reasonable tuplet handler, document your work carefully, let the staff know that you are attempting this and be sure that you include test songs for it.  You might be rewarded with some extra credit points!

Weird Potential Java MIDI bug:  If your program sounds as if it plays the first note of a musical piece twice, try starting adding the notes to the SequencePlayer starting at tick #1 rather than at tick #0.

Provided Code

The provided code is available via subversion.  You should follow the directions to acquire the provided code and keep it up to date.  All of the provided code will be contained within a package called provided.  Do not modify, remove, or add anything in the provided package.

Library documentation

Sample ABC music files:   You can access several sample abc music files at https://svn.rice.edu/r/comp310/course/HW06/songs.  You can access these files directly via that URL or add them to your project in the same way you added the provided package (the svn:externals property can have multiple lines, one per external repository).

But don't limit yourself to your instructors' boring musical tastes, go find your own files or make your own music!    If you have problems playing a particular file, let the staff know right away to make sure that there isn't a problem in the libraries or that the file is using an unsupported feature.

Assignment requirements:

  1. All code must be properly commented. (10 pts)
  2. The GUI should support all of the required operations, be intuitive to use, and all buttons should contain tool tips. (10 pts)
  3. MVC architecture (5 pts)
  4. You must write an extended visitor to implement toString for music sequence lists.  (15 pts)
  5. You must write an extended visitor to traverse an IPhrase object and add the appropriate notes to the sequence player so that the music may be played.  Required features (60 pts total):
    1. Key signatures ("K" header) (5 pts)
    2. Tempo ("Q" header) (5 pts)
    3. Default note ("L" header)  (5 pts)
    4. Ignored headers (5 pts)
    5. Individual notes  (10 pts)
    6. Chords (5 pts)
    7. Triplets (5 pts)
    8. Non-empty sequence list  (10 pts)
    9. Empty sequence list (end of song)  (5 pts)
    10. Unknown host   (5 pts)

You are NOT required to handle mutiple instrument choices as the demo does, though it would be more fun if you did.  ;-)

 


© 2011 by Stephen Wong and Scott Rixner