Tunes Library
The Tunes Library
Designing sound effects and musical themes
The tunes library handles the design of the musical sequences and sound effects and provides the connection to the standard MIDI synthesizer that generates the sounds.
The MIDI system provides a synthesizer that can play up to 15 different musical instruments at a time, plus the percussion instruments. The selection of the instruments that play on the different channels of the MIDI synthesizer is called a program. The library provides the default program, but the programmer may change the MIDI program at any time during the game.
The library can be used without the graphics component just to generate music and sound effects. The sample programs illustrate a variety of ways the music and sound effects can be composed.
When programming he interactive games, the programmer designs sound effects or a musical theme that is to be played as the game goes on, or when some special events happen in the game. The programmer then includes in the definition of the inTick events and the onKeyEvent events the collection of the tunes that should be played.
SoundConstants interface
This interface gives names to all MIDI instruments, provides simple names for the notes in the middle range of the piano keyboard. It also defines the default program for the synthesizer and supports mapping between instrument names and the numbers assigned to them.
When a student’s class implements SoundConstants, the name noteC denotes the middle C note, the name Piano represents the integer 1, that is the instrument number for the Acoustic Grand Piano MIDI instrument.
The Note class
The Note class represents one MIDI note that can be played by the program.
A note is defined by its MIDI pitch in the range from 0 to 127, and its duration. We decided to limit the duration to the range from 1 to 16. A note of duration 1 plays for one tick. It is up to the programmer to decide what represents a whole note and how to manage the time signatures of the tunes.
The Note class is a wonderful illustration of the power of the constructors to give the user a number of choices how the note can be defined. The foollwing four constructors:
new Note(noteC)
new Note("C4n1")
new Note(60)
new Note(60, 1)
define the same note, namely the C natural in 4th octave of duration 1.
The class also serves as an illustration of the use of constructors to assure integrity of data (we make sure the programmer supplies a valid pitch and duration).
There are several methods that verify the integrity of data and adjust the values to fall within the desired range:
int adjustPitch(int); |
int adjustDuration(int); |
Character adjustNoteName(Character); |
Character adjustModifier(Character); |
int adjustOctave(int); |
int computePitch(Character, int, Character); |
void initNote(inttt, int); |
Two methods are used when the tunes are being played:
void nextBeat(); |
void skipBeat(); |
Methods
String toString(); |
String toIndentedString(String); |
display the note information in readable format and the toIndentedString method is also used by the tester library for the display of the test results.
Furthermore, this class brings up an interesting issue related to the definition of equality: A-flat is the same note as G-sharp. How do we handle this? There are two methods that compare notes for equality, same and equals. The following tests illustrates the differences:
Note D4f4 = new Note("D4f4"); |
Note C4s4 = new Note("C4s4"); |
|
t.checkExpect(this.C4s4.sameNote(this.D4f4), true); |
t.checkExpect(this.C4s4.equals(this.D4f4), false); |
(Of course, the definition of the equals method is coupled with a corresponding definition of the hashCode method.)
The Chord class
The Chord class represents a collection (multiset) of MIDI notes that can be played all at once in a musical theme.
The constructors accept an arbitrary long sequence of notes, given either just by their pitch, by their #tt{String} representation, or by the actual Note objects (i.e., there are three versions of the constructor, each accepting a sequence of data of one of the given tyles).
The following methods
void addNote(Note) |
void addNote(String) |
void addNote(int, int) |
void nextBeat() |
void skipBeat() |
boolean isSilent() |
void removeSilent() |
int size() |
boolean contains(Note) |
allow the programmer to add a Note to a Chord, advance to the next beat or skip a beat, check if all notes in the Chord are silent, i.e. their duration has been decereased to zero or less, and can check the size of the Chord given as the number of notes in the Chord. Note, that if the programmer adds the same note value to the Chord more than once, the note will appear in the multiset of notes twice (or more times).
The programmer may also remove all notes that are now silent, and check whether the Chord contains the given Note. The comparison uses the method same defined in the Note class to check for a match.
The copy method
The class implements the copy method by producing a new Chord with new instances of Notes. That way, if the original Chord advances to the next beat, the copy is not changed.
Methods
String toString(); |
String toIndentedString(String); |
display the note information in readable format and the toIndentedString method is also used by the tester library for the display of the test results.
The class implements the equals method that verifies that the two Chords are equal as multisets of Notes, i.e. contttain the same notes with the same multiplicity, but their order is irrelevant.
(Of course, the definition of the equals method is coupled with a corresponding definition of the hashCode method.)
The Tune class
The Tune class represents the sound that is played on one of the MIDI channels. It consists of the MIDI program number and a Chord.
To play a chord, we need an instrument. The Tune class represents one chord that is played on one instrument. When a MIDI synthesizer is initialized it is given a soundbank of the available instruments and the programmer must select 16 instruments that will be played. One of them represents a collection of percussion instruments, the rest of them can be keyboard instruments, string instruments, winds, brass, some folk instruments (bagpipe, sitar, kalimba, ...), as well as one of a collection of special effect sounds (bird tweets, gun shot, ...).
Most of the time the programmer will not need to deal with Tunes other than adding Tunes to the TuneBuckets: the tickTunes and the keyTunes buckets when designing interactive games with graphics. The javalib.impsoundworld and the javalib.appletsoundworld libraries handle the duration of all notes and turns them off appropriately – with no action required of the programmer.
However, it is possible to use the javalib.tunes library without the world libraries as long as the programmer handles the timing for when the synthesizer should start and stop playing specific tunes. The examples in the ExamplesTuneBucketTests class illustrate this option.
Methods
String toString(); |
String toIndentedString(String); |
display the note information in readable format and the toIndentedString method is also used by the tester library for the display of the test results.
The class implements the equals method that verifies that the MIDI program choices and the Chords are equal.
(Of course, the definition of the equals method is coupled with a corresponding definition of the hashCode method.)
The TuneBucket class
The TuneBucket class represents what our orchestra is currently playing. It contains 16 Tunes, one for each MIDI instrument. The class comes with a number of convenience methods that allow the programmer to add one Note to be played on a given instrument, one Chord to be played at a given instrument, one Tune, or an Iterable collection of Tunes.
The constructor for the TuneBucket consumes an instance of the class MusicBox that handles the MIDI Synthesizer initialization and its actions.
The method playTune starts playing all tunes in the TuneBucket, the method nextBeat stops playing the notes that have run out of time and decreases the duration of all other notes.
The method clearBucket first stops playing all notes in the TuneBucket, then clears its contents of all Tunes. The method clearTunes just removes all Tunes from the TuneBucke.
To support testing, there are methods to check the size of the TuneBucket, to verify that it contains a specific note playing on a specific instrument, as well as the equals method that verifies that both instances are using the same MusicBox and that the Tunes for each instrument match.
This class illustrates the need for a deep copy of a data structure, as we need to make sure the data that describes the desired melody is not destroyed as the tunes are played.
Methods
String toString(); |
String toIndentedString(String); |
display the note information in readable format and the toIndentedString method is also used by the tester library for the display of the test results.
The class implements the equals method that verifies that the Tunes for all MIDI program channels are equal.
(Of course, the definition of the equals method is coupled with a corresponding definition of the hashCode method.)