CS1110 Fall 2008 Assignment A6. Mozart's Musikalisches Würfelspiel. Due date on the CMS.
This assignment uses two-dimensional arrays and random-number generation in an interesting setting. Inspiration for it comes from an assignment given by Kevin Wayne and Robert Sedgewick in CS at Princeton. We have used a similar assignment before in CS100J. This version has extensive modifications, but you can still cheat by looking at someone's solution in the past. We ask you not to do so. You won't learn anything that way, we have to do extra unnecessary grading, and if you are caught it makes more work and pain for all of us.
Please keep track of the time you spend on this assignment. At the end, we ask you to put a comment at the top of file WurfelSpiel.java
that indicates how much time you spent.
In 1787, Wolfgang Amadeus Mozart created a dice game (Mozart's Musikalisches Würfelspiel). One composes a two-part waltz by pasting together 32 of 272 pre-composed musical elements at random. The waltz consists of two parts: a minuet and a trio. Each has 16 measures, which are generated at random according to rules described below.
Minuet. The minuet consists of 16 measures. The 176 possible minuet measures are named m1.wav through m176.wav. To determine which one to play, roll two dice and use the following table. The dice roll, in 2..12, tells you what to play for a measure (a column number). For example, if you roll 12 for measure 3, then play wav file m165.wav.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ----------------------------------------------------------------------------------- 2 96 22 141 41 105 122 11 30 70 121 26 9 112 49 109 14 3 32 6 128 63 146 46 134 81 117 39 126 56 174 18 116 83 4 69 95 158 13 153 55 110 24 66 139 15 132 73 58 145 79 5 40 17 113 85 161 2 159 100 90 176 7 34 67 160 52 170 6 148 74 163 45 80 97 36 107 25 143 64 125 76 136 1 93 7 104 157 27 167 154 68 118 91 138 71 150 29 101 162 23 151 8 152 60 171 53 99 133 21 127 16 155 57 175 43 168 89 172 9 119 84 114 50 140 86 169 94 120 88 48 166 51 115 72 111 10 98 142 42 156 75 129 62 123 65 77 19 82 137 38 149 8 11 54 130 10 103 28 37 106 5 35 20 108 92 12 124 44 131 12 3 87 165 61 135 47 147 33 102 4 31 164 144 59 173 78
Trio. The trio consists of 16 measures. The 96 possible trio measures are named T1.wav through T96.wav. To determine which one to play, roll one die and use the following table. For example, if you roll 1 for measure 21, then play file T81.wav.
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 ------------------------------------------------------------------ 1 72 6 59 25 81 41 89 13 36 5 46 79 30 95 19 66 2 56 82 42 74 14 7 26 71 76 20 64 84 8 35 47 88 3 75 39 54 1 65 43 15 80 9 34 93 48 69 58 90 21 4 40 73 16 68 29 55 2 61 22 67 49 77 57 87 33 10 5 83 3 28 53 37 17 44 70 63 85 32 96 12 23 50 91 6 18 45 62 38 4 27 52 94 11 92 24 86 51 60 78 31
Example. Here is a sample waltz generated using this process and the accompanying musical score. There are 11^16 * 6^16 different possible waltzes. Since this is over 10^23 different possibilities, each time you play the game you are likely to produce 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. To help you learn as much as possible in a short amount of time as possible, and also to give you advice on how to go about writing and testing programs, we lead you through this assignment in a series of steps. Please study the code we have given you; be conscious of style, specifications, etc.
Follow the directions carefully. Many people have had more points off because they failed to follow instructions than because of programming errors. When doing one step, don't go on to the next one until the current one is done and the methods you wrote for it work!
Step 0. Download the following and place them all in a new folder for the assignment.
File StdAudio.java. Contains methods for manipulating and playing music in wave (.wav) format. It is complete; you don't have to do anything with it.
File WurfelGUI.java. Defines the GUI (Graphical User Interface) that you saw in class. It is complete; you don't have to do anything with it.
File MNumber.java. An instance contains a JLabel that can be clicked on. It is complete; you don't have to do anything with it.
File WurfelSpiel.java. You will write some methods in this
class. We have provided others.
File WurfelSpielTester.java.
We give you stubs of test methods and some testing instructions
in the bodies.
File measures.zip (34MB).
After downloading this file, unzip it —into a folder measures
that
contains all the .wav files for the measures used in the game.
Put folder measures
in the folder with the two .java files.
Then open StdAudio.java, WurfelGUI, MNumber.java, WurfelSpiel.java, WurfelSpielTester, and into DrJava. You are ready to begin.
Note how this program is broken into several classes: all code concerned with the GUI appears in one, code that does the audio appears in another, and code that relies on and uses the first two is a third one.
Take a look at the methods in StdAudio.java
. Call method playScale
just to see what
sounds come out.
Execute x= new WurfelGUI(); in the interactions pane. A GUI window will appear. Look it over. You can click one of the entries in the minuet and trio tables and listen to the measure play. But clicking the buttons near the top just prints a message in the ineractions pane. As you complete the tasks given below, more buttons will work.
Step 1. Get slightly familiar with the GUI and class WurfelGUI. You won't be changing the GUI class. We reveal pieces of GUIs to you over the next three weeks. But you can learn a little bit now. One thing we can show you now is how the GUI calls procedures in WurfelSpiel
.
Look at WurfelGUI
and search for procedure actionPerformed
. Hey, look! It is declared in a class that is declared in WurfelGUI
. You can indeed declare classes within a class. It is an inner class. Don't be concerned with this now.
The system calls procedure actionPerformed
when a button is clicked. In actionPerformed
, function call e.getSource()
returns the object that was clicked. You can understand this code. The code checks to see which button it is and calls a procedure —which generally calls the appropriate procedure in WurfelSpiel
to process the click. This is how button clicks are communicated to the procedures that can process them.
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 WurfelSpiel
,
there is a declaration of a static Random
variable generator
.
An object of class generator
has functions for generating "random" numbers.
You will use function nextInt
. Evaluation of a call generator.nextInt(t)
yields
an integer i
that satisfies 0 <= i < t
.
Write function WurfelSpiel.throwDie
, using function nextInt
.
The method body can be a single return statement.
Class Random
is
in API package java.util
.
Use link java.sun.com/javase/6/docs/api/java/util/Random.html
to obtain the API specs for this class and spend some time becoming familiar with it.
Function throwDie
seems extremely simple! Nevertheless,
it has to be tested to make sure that (1) it produces only integers in
the range 1..6 and (2) can produce all integers in 1..6. In order to help you
do this, we have written part of method WurfelSpielTester.testThrowDie
. Study
what we have written in the specification and the body of this method,
complete the method, and test throwDie
.
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 that the method works, you have to look at the array of Strings
.
For that purpose, we have partially written function toString(String[])
.
Study what we have written in the specification and function body and complete
the function. Then test it by completing test method WurfelSpielTester.testToString
.
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 row 0 of array minuet
and row 0 of array trio
are chosen. This allows you to concentrate on the idea of constructing
a file name. To do this, write the body of function WurfelSpiel.create0Spiel()
.
Class WurfelSpiel
contains arrays minuet
and trio
. Each element of these arrays is an integer that
indicates a file that represents a measure. For example, minuet[0][0]
= 96
,
which represents file measures/m96.wav
. So, you have to put the
String
"measures/m96.wav"
into the array that this
function returns.
minuet[0][0]
is one of the musical phrases that can be used in measure
1 of the minuet part. Note that "/" is used to separate
folder name measures
from file name m96.wav
. Even
if you have a PC, you must use "/" and not a backslash.
Write loops in this function: one to get info from array minuet
and the other to get info from array trio
. After writing this function, complete procedure WurfelSpielTester.testCreate0Spiel()
.
We put in a test to make sure that the first array element is correct so that
you have some idea how to do this. You must make sure that all array elements
are correct.
Study procedure WurfelGUI.create0()
. It is called when the top button in the GUI is clicked. You can see that field lastWaltzS
is used to contain the last waltz generated. Look at the definitions of lastWaltzS
and lastWaltzD
on their declarations and understand them completely.
Now, with the GUI open, click the button to create a waltz from the first rows of minuet
and trio
and then click the button to print the last waltz created. You should see the names of the files in the Interactions Pane.
Step 5. Listen to the music! Complete procedure WurfelSpiel.play(String[])
, which will
play all the files whose names are in an array such as that calculated by function create0Spiel
.
Now you should be able to hear the waltz when you click the create button and then the play button. You will notice pauses between measures when the music is played. We investigate eliminating the pauses later. Playing your array should produce the same music as this file: mozart12.wav.
Step 6. We can get rid of the pause between measures by building
a single file that contains the music in the files given by an array
like s
in the past two steps. To do this, complete the body of
function WurfelSpiel.build(String)
.
To do this, you have to read a .wav file and place its contents
into a
double array; use function StdAudio.read(String)
to
read one file at a time. To help you out, we give an outline for the body of
WurfelSpiel.build(String)
, and we also give you method WurfelSpiel.copy
.
You figure out how to check this function; as long at it is correct, we will
not look at your test cases.
You should now be able to compress the waltz and play the compressed form.
Step 7. Creating a Mozart Musikalisches Würfelspiel. Finally,
write the the body of function WurfelSpiel.createRandomSpiel()
,
which will create a random waltz. It should produce a random waltz as described
at the beginning of this document. In this method, you can use 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 WurfelSpiel
when generating
random names of files, as in method create0Spiel
.
The easiest way to write it is to copy and paste the body of
create0Spiel
and then modify it.
Step 8. Saving a file. We knew you would like to save a random waltz so that you can play it later ---or email it
home to let your family know that you have been turned on by classical music
and Musikalisches Würfelspiel. You are to save the compressed waltz. It has file name compressedWaltz.wav
, and it will be either on your desktop or in the directory along with the .java files for this program, depending on your operating system.
Step 9. Submitting your assignment. Place at the top of file WurfelSpiel.java
a comment that says how many hours you spent on this assignment. Submit files WurfelSpiel.java
and
WurfelSpielTester.java
on the CMS by the due date.