CS100J Fall 2006   Assignment A5.     Mozart's Musikalisches Würfelspiel.     Due Sunday, 5 November

The inspiration for this assigment comes from a similar assignment given by Kevin Wayne and Robert Sedgewick in Computer Science, Princeton. The assignment illustrates the use of two-dimensional arrays and also random-number generation in an interesting setting.

In 1787, Wolfgang Amadeus Mozart created a dice game (Mozart's Musikalisches Würfelspiel). In the game, one composes a two-part waltz by pasting together 32 of 272 precomposed measures (written by Mozart) at random. We will do a variation of Mozart's original Würfelspiel.

Your program will generate a waltz consisting of a trio followed by a minuet. Each is 16 measures long, and the measures are generated at random according to a fixed set of rules.

Example. This sample waltz is generated using mozart's original process; here is the accompanying musical score.

There are 6**16 * 11**16 different possible waltzes. Since this is over 10**23 different possibilities, each time you play the game you are likely to compose a piece of music that has never been heard before. Mozart carefully constructed the measures to obey a rigid harmonic structure, so each waltz reflects Mozart's distinct style.

About this assignment. You will use a number of ideas for the first time: generation of random numbers, two-dimensional arrays, playing music stored in a .wav file, saving in a file on your hard drive a double array, and more. To help you learn all this and also to give you more advice on how to go about developing and testing programs, we lead you through this assignment in a series of steps. Follow them carefully. When doing one step, don't start the next one until the current one is done and the methods you wrote for it work!

You will also have to submit a file MozartTester.java, which will contain test cases that you used in testing methods.

Step 1. Download the following and place them all in a new folder for the assignment.
File StdAudio.java. Class StdAudio contains methods for manipulating and playing music in wave (.wav) format.
File Mozart.java. You will be writing class Mozart. We have provided you with a few components to help out.
File waves.zip (34MB) OR waves.sitx (25MB). If you have a PC, you probably have to use waves.zip; mac people can use the smaller .sitx file. After downloading a file, unpack it into a folder waves that contains all the .wav files for the measures used in Mozart's Musikalisches Würfelspiel. Put folder waves in the folder with the two .java files.

Step 2. Generating rolls of a die. Your program will have to "roll a die" to produce a random number in the range 1..6. At the beginning of class Mozart, there is a declaration of a static Random variable generator. An object of class java.util.Random has methods for generating "random" numbers. Please bring up the Java 1.5 API package in your favorite browser, find class Random, and read about it. You will use function nextInt. Evaluation of a call generator.nextInt(t) yields an integer i that satisfies 0 <= i < t. Use function nextInt in writing a method with the following specification in class Mozart:

/** = a roll of a die --an int in the range 1..6 */
public static int throwDie()

Function throwDie has to be tested to make sure that it will (1) produce only integers in the range 1..6 and (2) will produce all integers in that range at least once. In JUnit class MozartTester, write a method that will test throwDie. In the method, have a loop that calls throwDie 200 times and tests each value it produces. In addition to method assertEquals, you can use method assertTrue(b), which passes the test if b is true and stops testing if b is false. For example, you could use something like this:

assertTrue(1 <= die && die <= 6);

Step 3. A method for printing a String array. Later, you will be writing a function that produces a String array of file names corresponding to measures to be played. In order to see the results of the method, you have to see the array of Strings. For that purpose, write and test the following function.

/** = a representation of array s --the list of Strings in the array, with each pair separated by ", " and the list delimited by "[" and "]".
Example: For array {"fi", "se", "th"}, return "[fi, se, th]" */
public static String arrayToString(String[] s)

Write a method in class MozartTester to test it. Be sure you test arrays of length 0, 1, and bigger.

Step 4. Generating a waltz. Before you work on creating a random waltz, first create a waltz assuming that each die thrown has the value 1, so that all the file names in a roll of 1 for the trio and a roll of 2 of the minuet are chosen. This will allow you to concentrate on constructing file names, as we discuss below. Thus, write a method with this specification:

/** = an array that contains the names of all the files
described by a roll of 1 for the trio and a roll of 2 for the minuet.
Each filename must be of the form

"waves/T<integer>.wav" or    "waves/M<integer>.wav",

where <integer> is some positive integer.
Thus, the files are expected to be in directory waves.*/
public static String[] create1Spiel()

We have given you arrays minuet and trio in class Mozart. Their meaning are given as comments on their declarations. As an example, minuet[0][2] = 22, representing file waves/M22.wav, is the number to use for the musical phrase to use for a roll of a 2 in measure 2. So, you have to put the String "waves/M22.wav" into the array returned by this function. Note that "/" is used to separate folder name waves from file name M22.wav. Even if you have a PC, you must use "/" and not a backslash.

After writing this function, check it out by executing s= Mozart.create1Spiel() in the interactions pane and then displaying the contents of s using Mozart.toString(s) and checking to make sure that the write file names are displayed. You do not have to write a test method in MozartTester to test create1Spiel; it would be too time consuming.

Make sure that the result of Mozart.create1Spiel() is an array of size 32!

Step 5. Listen to the music! Now write the following method to play all the files whose names are in an array. Look in class StdAudio for a method that will play the music in a file given by a file name. You will notice that there is a pause between measures when the music is played. Later, we investigate eliminating the pauses. Do NOT assume that parameter s is an array of 32 elements; it can be any length.

/** Play the music given by files whose names are in s, in order*/
public static void play(String[] s)

Playing your array should produce the same music as this file: mozart12.wav.

Step 6. Wouldn't you like to get rid of the pauses? One way to do this is to build a single file that contains the music in the files given by an array like s in the past two steps. Write the following method:

/** Put the measures given by the file names in s into
a new array and return the new array */
public static double[] build(String[] s)

To do this, you have to read a .wav file and place its contents into a double array; find a method to do this in class StdAudio.

Your method should do the following. First, determine the length of the output array --read all the .wav files given by s to do this. Then, declare the output array of the appropriate size. Finally, read the .wav files (again) and copy their values into the output array, one after the other. To help you out, we already placed a method copy in class Mozart .

Step 7. Creating a Mozart Musikalisches Würfelspiel. Write the following method:

/** = an array of random measure file names:16 for trios followed by16 for minuets. The filenames are of
the form "waves/M<integer>.wav" and "waves/T<integer>.wav", so the files are expected
to be in directory waves, which should be in the same folder as this class. */
public static String[] randomSpiel()

This method should produce a random waltz as described at the beginning of this document. For this method, you can use method throwDie, which you wrote earlier, to throw a die to get a number in the range 1..6. Also, use arrays minuet and trio in class Mozart when generating random names of files, as in method create1Spiel. You do not need a method in MozartTester to test it.

Step 8. Saving a file. We write a method to generate a waltz and save its .wav file so that you can play it later --or so that you can email it home to let your family know that you have been turned on by classical music and Musikalisches Würfelspiel. You will want to use method StdAudio.save in writing this procedure. The method body will create a waltz, build a double array that contains all the measures, and then save the waltz on your hard drive. Be careful in testing it. Make sure the name that you use is not already the name of a file on your desktop, or you will lose that file. You do not need a method in MozartTester to test it.

/** Create a random waltz (using function randomSpiel()) and store it on the desktop of this computer
under the name filename. If filename does not end in ".wav", then put that extension at the
end of the filename before creating the file.*/
public static void randomSpiel(String filename)

Step 9. Submitting your assignment. Submit your files Mozart and MozartTester on the CMS by the due date and time. As usual, your methods should have precise and complete specifications, written as javadoc comments. There will be severe deductions if these javadoc comments are not suitable. We suggest that you generate the javadoc API specs and look at them carefully.