-
Extension 1 for this module (8 points):
Conway's Game of Life
Conway's Game of Life
is a biology simulation that was developed by British mathematician
John Horton Conway in 1970. It is designed to simulate cellular
automation by creating an initial configuration of living and dead cells and
observing how they evolve. Many interesting patterns have developed from the origins
of the original simulation--producing patterns that pulsate, exist into infinity, and even glide
like spaceships.
Directions
In this lab, you will be responsible for building the simulator portion of
Conway's Game of Life (henceforth known as Conway, or Life).
You can then run the game on your own patterns on on patterns that we provide.
The code for this work can be found
conway package of the extensions source folder.
Here is a tour of what you should find there:
Notes
- A Conway game has-a rectangular array of Cell
objects. The constructor for Conway specifies the number of
rows and columns in that array.
To complete the constructor, you must use what you already know about
arrays and objects. Get help as needed,
but be sure to understand what you are doing before you proceed to the next
steps.
A common mistake is to instantiate the array of Cell objects,
but fail to assign each the result from an actual Cell()
constructor call. Java initializes each element of an array of objects
as null. You must explicitly assign each elemnt an actual
Cell object.
- As given to you, the unit test will already pass testcellFunctions, because Cell is already implemented.
- You should proceed with implementation according to the unit tests.
-
This means your first task is to get the constructor to work, so that testinit passes.
What other methods must you complete so that testinit passes?
- From there, proceed with testConwayAccessors.
- From there, proceed with testStep.
- The testPrint test should work once your other methods
are complete. This test prints the board on the console.
Once you have completed all the methods, you can run the Main method to play Conway's Game of Life. There
are many patterns that can be used to test your simulation, some of which can be
found here.
To further debug your code, the visual interface allows you to take one step
at a time.
If the game is not working, use the debugger or print information helpful to
diagnosing the problems you see.
Sample Patterns
Still Lifes
Block
Beehive
Loaf
Boat
Oscillators
Blinker
Toad
Beacon
Pulsar
Spaceships
Glider
Lightweight Spaceship
Perpetual Patterns
Gosper Glider Gun
Block-Laying Switch Engine
When you done with this extension, you must be cleared by the TA to receive credit.
- Commit all your work to your repository
- Fill in the form below with the relevant information
- Have a TA check your work
- The TA should check your work and then fill in his or her name
- Click OK while the TA watches
- If you request propagation, it does not happen immediately,
but should be posted in the next day or so
End of extension 1
-
Extension 2 for this module (3 points):
Automatic generation of Conway initializations
For this extension, you will be working with automatic code generation, which will save you time and energy if
you want to save any of the cell mappings you create in Conway. In order to get credit for this extension, you must implement
the functionality of logAndCapture() in Conway
to capture the current living cells you have on the board and generate the appropriate Java code for creating those cells
in the console.
For example, the Four Blinkers code is captured already,
but if you were
to generate code for it using logAndCapture() the result would look something like this:
Beginning of Log and Capture
setLife(true, 1, 1);
setLife(true, 1, 2);
setLife(true, 1, 3);
setLife(true, 1, 5);
setLife(true, 1, 6);
setLife(true, 1, 7);
setLife(true, 5, 1);
setLife(true, 5, 2);
setLife(true, 5, 3);
setLife(true, 5, 5);
setLife(true, 5, 6);
setLife(true, 5, 7);
End of Log and Capture
The idea is that the code can be copied from the console, pasted into your
Conway class, and when you choose the right menu item from the
interface, the board will be initialized to replicate what you captured.
Once you have logAndCapture() working, use this new tool to automatically generate your own Conway
patterns in myDesignOne(), myDesignTwo(), and myDesignThree(). For credit for this
extension, these patterns should be both intriguing and potentially time-consuming to generate by hand.
When you done with this extension, you must be cleared by the TA to receive credit.
- Commit all your work to your repository
- Fill in the form below with the relevant information
- Have a TA check your work
- The TA should check your work and then fill in his or her name
- Click OK while the TA watches
- If you request propagation, it does not happen immediately,
but should be posted in the next day or so
End of extension 2
-
Extension 3 for this module (5 points):
In the matrix package of the extensions source folder,
implement the Matrix class with the following features.
- A constructor that takes a two-dimensional array of type double
and saves it (as an instance variable) as the values of the matrix.
To be safe, your instance variable must be a copy of the
parameter, so that the contents of your Matrix's array cannot be changed beyond
your control.
To copy the two-dimensional array, you must instantiate a new two-dimensional array
and copy the original array's contents into your new array.
- An arraysAreEqual(double[][] one, double[][] two) method that compares the
two arrays to see if they are the same. The two arrays are the same if:
- They agree in the size of both of their dimensions.
- The contents of the two arrays are the same.
The .equals(Object) method included with this lab calls your
arraysAreEqual method, so that Matrix equality of
two matrices depends on the contents of those matrices.
Until this method is working, the rest of the JUnit tests will not work properly.
- A toString method that neatly shows the contents of the matrix.
In your string use "\n" to insert a line break, and use "\t" where you want a tab between elements.
Include JUnit tests that print these strings to the console (using System.out.println) for visual inspection,
in addition to any automated tests you perform.
Your toString method will come in handy for debugging the other methods.
- A scaleRow method that takes a row number (an integer) and a scale factor (a double),
and multiplies that row by the given scale factor. This method does not return anything. It just modifies the matrix.
In this lab, rows are numbered as arrays are indexed. Thus,
the top row in the matrix is row 0, and the bottom row is numbered one less than
the number of rows in the matrix.
- An addRows method that takes two row numbers as parameters, and adds the two rows together, storing the
result in place of the second of the two rows.
- A plus method that takes another Matrix as its parameter and returns the
matrix that results from adding this matrix by the given one.
Matrix addition is only valid when the two matrices are the same size in both dimensions,
so your plus method should throw an
IllegalArgumentException when this is not the case.
- A transpose method that takes no parameters and returns a new matrix that is the transpose of
this one. Recall that the columns of the transposed matrix have the same values as the rows of the original matrix.
- A times method that takes another Matrix as its parameter and returns the
matrix that results from multiplying this matrix by the given one.
Recall that when you multiply two matrices A and B, the value in the
cell at row r and column c of the resulting matrix
is the dot product of A's row r with B's column c.
Also recall that matrix multiplication is only valid when the number
of columns of the first matrix is equal to the number of rows of the
second, so your times method should throw an
IllegalArgumentException when this is not the case.
When you done with this extension, you must be cleared by the TA to receive credit.
- Commit all your work to your repository
- Fill in the form below with the relevant information
- Have a TA check your work
- The TA should check your work and then fill in his or her name
- Click OK while the TA watches
- If you request propagation, it does not happen immediately,
but should be posted in the next day or so
End of extension 3
-
Extension 4 for this module (6 points):
Gaussian Elimination
We do not have unit tests for this extension, so you must write some
of your own and convince the TA, and possibly the instructor as well, that
your solution works.
Directions
-
Add a method to your Matrix class to perform Gaussian elimination, as described in this
Wikipedia article.
- Write JUnit tests that use your method to solve systems of equations.
- Demo your code using equations you have written out, whose solutions
you can compute on your own, showing that your code computes the correct
answers.
TAs, note! Be sure the student's code computes solutions to
actual equations. The student's code must work on the equations with which
he or she tests, but the code must also work on an equation that you devise.
When you done with this extension, you must be cleared by the TA to receive credit.
- Commit all your work to your repository
- Fill in the form below with the relevant information
- Have a TA check your work
- The TA should check your work and then fill in his or her name
- Click OK while the TA watches
- If you request propagation, it does not happen immediately,
but should be posted in the next day or so
End of extension 4
-
Extension 5 for this module (3 points):
Net Present Value
Note: This extension is slated to be rewritten Summer 2013 so as not to
use any objects, and it would then be moved earlier in the sequence.
Most students find this write-up confusing, so you may want to try something
else instead.
Calculating NPV:
The present value is a calculation used by investors as a baseline for comparison when making new investments. The present value is seen as the value of an amount of cash were it to be saved in a bank account (or other low risk venture) as opposed to being invested. It can be used to evaluate a cash flow in two different ways: the projection of a current cash flow into the future (capitalization) or the evaluation of expected future earnings (discounting).
The discount evaluation is especially useful when determining the viability of starting a new investment. For example, say that $30,000 has been invested into an opportunity that is expected to return $10,000 each year thereafter for three years. The present value of each year can be calculated by:
PV = C / (1 + r)^t
where C is the expected yearly return, r is the discount rate (typically the risk-free interest rate) and t is the time in years that have elapsed since the initial investment. Using a discount rate of 10% (.10), we can see that the present values for each of the three years are:
Year 1: $9090.90
Year 2: $8264.26
Year 3: $7513.15
It can be seen that the present value for each year is less than the actual return (it has been discounted), since the same amount of money invested at the outset (time = 0) would have accrued interest. The sum of the present values over a given time is called the Net Present Value. The NPV of the above example would be $24868.31. Since the initial investment can be considered a negative cash flow at time = 0, the actual NPV would be -$5131.69 making this a bad investment. More money could have been made by simply placing the original investment into a bank account for the same time period. Putting everything together, the formula for NPV is the sum of all present values from 1 to T minus the original investment.
To assist you with this work, a CashFlows class has been provided
(along with some other useful clases) in the business package in
the coursesupport source folder.
The CashFlows object represents the expected cash flows for a certain number of years. The CashFlows class contains the following methods:
- public CashFlows(Money ... f): takes in expected cash flows for each year and stores them. The amount of flows given can vary, and are stored in the order that they are given. The number of cash flows provided determines the maximum number of years for the investment.
- public int getNumberOfFlows(): returns the number of cash flows currently being stored. This number also represents the maxmum number of years for the investment.
- public Money getFlow(int year): returns the flow for the desired year for 0 < year <= getNumberOfFlows() (since the value at year = 0 is -investment).
Using the information provided above, your task is to complete the following methods within the NpvCalculator class found in the
netpresentvalue package of the extensions source
folder in your repository:
- public NpvCalculator(CashFlows flows, double investment, double discount): The constructor takes in an object of type CashFlows containing all of the estimated cash flows for an investment, the original investment amount expressed as a positive value, and the expected discount rate.
- public Money calculatePV(int year): returns the present value of a cash flow for a given year using the formula outlined above.
- public Money calculateNPV(int year): calculates the net present value from time = 1 to the given year. In other words, the year parameter = T from the above NPV formula. Don't forget to account for the initial investment when returning the NPV.
- public Money calulateTotalNPV(): returns the NPV for the entire set of cash flows provided.
- public Money findMaxNPV(): returns the largest NPV for the given cash flows. This NPV is not necessarily the same as the total NPV described above, and may even be a negative value.
To demo this work, run the NpvCalculator class as a Java Application
and look for the printed results to be similar to what is shown at the top
of this description.
When you done with this extension, you must be cleared by the TA to receive credit.
- Commit all your work to your repository
- Fill in the form below with the relevant information
- Have a TA check your work
- The TA should check your work and then fill in his or her name
- Click OK while the TA watches
- If you request propagation, it does not happen immediately,
but should be posted in the next day or so
End of extension 5
-
Extension 6 for this module (8 points):
Julia sets: fractal images
Overview:
Fractals are mathematical models that
can create seemingly complex drawings from very simple specifications.
Because they are not easily bored, computers are well-suited to
iterative tasks.
You will use iteration in this lab to compute
a fractal drawing.
This lab emphasizes how computation can be applied
with relative ease and speed to create what
would take
a human much longer to accomplish.
Before Starting:
- Update your workspace and look for the julia package in
the extensions source folder. To run
the program, run JuliaController as a Java application.
- Please read over the entire document before you start. Really.
It will save you time in the long run.
- If you are interested, see the
wallpaper a student made from
this lab. You can change constants and colors to get different patterns.
- Take a look at the
documentation supplied for this work.
Background Information:
Fractals are an amazing mechanism for describing complex systems in terms
of simple and recursive components.
The idea of fractals in the complex plane (where the x coordinate is real and the y coordinate is imaginary)
dates back to 1918, when
Gaston Julia began research that characterized
Julia Set
fractals. In 1979,
Benoit Mandelbrot defined a map over Julia sets that reflected
the expense of computing a specific point in the set. Such maps
generate interesting patterns, and we will in a sense reproduce
Mandelbrot's work in this lab.
Since Mandelbrot's original work, computers have become faster and Java
has made programming them easier. Thus,
our renditions will be more aesthetically pleasing
(and won't take overnight to render either).
Search
here
(using
Google) to find some pages with more background information.
Problem statement: Drawing a Julia Set
This lab involves computation over some points of a complex plane.
Recall that a complex number has two components:
a real and an imaginary part.
For each complex point c
that we want to display, we compute
the function rigor(c)
as follows.
Pseudocode for rigor(c):
z = -0.7795 + 0.134 i
iters = 0
while abs(c) < 2 and iters < maxIters
c = c*c + z
iters = iters + 1
end while
return iters
In other words, rigor(c)
is the number of iterations
that it takes to compute the value at c
.
The value of maxIters
is arbitrary, but let's assume
for now that
100 iterations are allowed for the computation. The function
abs(z)
computes the distance of z
from
the complex point (0,0).
To show complex numbers on an x-y axis system,
let us assume that the real component of a complex number is registered on
the x-axis; the imaginary component is thus registered on the y axis.
If we apply the above computation over the complex plane as x
ranges from -2 to 2 and y ranges from -2i to 2i,
we obtain the following picture:
(-2, 2i) | (0, 2i) | (2, 2i) |
(-2, 0i) |
 | (2, 0i) |
(-2, -2i) | (0, -2i) | (2, -2i) |
Figure 1: Initial display |
-
Note the complex coordinates of the lower-left and upper-right corners.
We use those two corners to describe the currently viewed area of the
display.
Zooming in, zooming out, and the selection of a box all affect those two
corners, which in turn frame the display that is shown.
- The
black areas represent points whose computation exceeded the limit set above (
maxIters
).
-
In the figures shown in this write-up, the color of a pixel p is
computed as follows, based on the value of iters that was
computed for the Complex coordinate associated with p:
Color color = Color.black;
if (iters < maxIters)
color = Color.getHSBColor((iters % 256)/255.0f, 1.0f, 1.0f);
image.setPixel(i, j, color);
-
The above code uses the HSB color model. For more information,
consult the Color.getHSBColor documentation. The basic idea is to pick a hue based on the number of iterations,
but leave the brightness and saturation at full.
Zooming around
What is really interesting about a fractal drawing is that one
can dive into the drawing and discover ever increasing detail.
Below you see the results of zooming into the picture.
(-1.6, -0.5i) |
|
(-0.6, 0.4i) |
|  | |
(-1.6, 0.5i) |
|
(-0.6, 0.5i) |
|
(-1.51755, 0.063544i) |
|
(-1.51210, 0.063544i) |
|  | |
(-1.51755, -0.0690i) |
|
(-1.51210, -0.0690i) |
|
Zoom in on left part of picture |
Another zoom in on left part of picture |
- Zooming into the picture changes the fractal plane coordinate of
the pixels.
Sedgewick's StdDraw allows you to set the coordinates of the
display.
-
The Julia object offers several ways to zoom, but the most
important method to consider is
setCoordinates.
The other zooming methods can accomplish their tasks by calling
setCoordinates with the appropriate parameters.
- The zoom methods are called by selecting entries on the JuliaController panel:
- ZoomIn and ZoomOut are available.
- Reset sets things back to the way they started.
- The Mouse panel button allows you to click and drag an area of
the display into which to zoom.
Approach
-
Since this may be your first experience implementing code
from a design, you should first peruse the
documentation supplied for
this extension.
- Work on the classes in the following order, testing as you go:
- Complex: The code for this class is already complete.
- Test Complex using the provided unit tests.
- Julia
Your code for Julia must use the Complex class. You will not get
credit for this extension if your code is sloppy!
- You are provided with JUnit tests for Complex.
When you done with this extension, you must be cleared by the TA to receive credit.
- Commit all your work to your repository
- Fill in the form below with the relevant information
- Have a TA check your work
- The TA should check your work and then fill in his or her name
- Click OK while the TA watches
- If you request propagation, it does not happen immediately,
but should be posted in the next day or so
End of extension 6
-
Extension 7 for this module (9 points):
Objects to support music
Overview:
There are several extensions that involve the use of music. Without some
nicely articulated objects in place, the code for those extensions would become
unwieldy.
This extension involves your completing some classes that have already
been designed. Each class has an API described here and in the class's
JavaDoc comments. A JUnit test case is written both to help you create
a correct implementation of the design and to demo your work for credit.
Fall 2013,
Note to students: this is a new extension. Your suggestions for
improving this assignment are welcome!
Before Starting:
- Update your workspace and look for the chords packages in
the extensions source folder, which include the following
- chords.gui
- This package has objects that are provided for your use. You do not
need to make any changes to these classes.
Later,
the class you
will most likely use from this package
is TwoDimensionsionalGUI, which helps applications
use the current
position of the mouse in the Sedgewick drawing panel as a control mechanism.
For now,
run the provided TwoDimensionalGUIExample and watch the effects
of moving
your mouse around in the window. This code uses the
TwoDimensionalGUI's update() method in an event loop,
such as the ones you have seen before that accomplish animation.
- chords.testing
- This package contains the tests for the code you will develop.
Most of them are JUnit tests, but one (PlayTest), is an application
that you will run to test your code.
- chords.music
- This package contains skeleton classes for the code you will develop.
Follow the instructions below, in the order given, to simplify your efforts.
- If necessary, review the slides presented in this course on sound and music. These were in Module 4.
Development Sequence
Some general guidelines follow:
-
Pay attention to the user story told about each class. Recall that
each has-a indicates the need for an instance variable.
- Name your variables appropriately, and protect them from access by
other classes by declaring them private.
- Where appropriate, include final on the declaration of instance
variables, so that their values cannot be changed after the constructor
finishes.
- Create meaningful toString() methods. Make sure these do
not produce too much information. For example, if a class contains
a large array of values, those should not be included in the toString() result. Instead, include the size of the array in the toString()
result if it could be useful.
OK, now follow the steps below, in order, to develop the classes for
this extension:
- Open the Samples class as we will complete this class first.
- Take a look at the first constructor in Samples–the one
whose signature is Samples(double[]).
A Samples object has-a double array of samples.
This constructor takes in such an array, and the constructor must capture
the array by making a copy of its values to be retained as an instance
variable.
This is a bit unusual: you would normally capture an instance variable
val by writing this.val = val, but for an array, that
would
retain the reference to the array without copying its values.
While the reference is sufficient to access the array's values, there is
no guarantee that code outside the Samples class won't change the
array after the constructor returns.
To guard against this, your constructor must make a copy of the array's
values. As a reminder, this involves:
- Declaring the instance variable to be an array of double values.
- In the constructor, instantiating the array the be the same size as
the parameter array's size.
- After instantiating the array, writing a loop to copy the values
one-by-one from the parameter into
the instance variable copy.
- Implement the double getSample(int) method, which should return
the value of sample i. Note that getSample(0) returns the
first value, because (like most things in Java), we begin our samples at index
0.
Run the unit tests, and the testConstructor() unit
test should pass at this point.
- The second constructor for Samples has signature Samples(int). It does not accept an input array, but instead
it should instantiate the instance variable to be an array of the specified
length, all zero. Such an array is not not directly useful or suitable
for play-back,
but is useful as a base for composing or extending other samples.
Java does initialize all newly instaitated double arrays to zero
automatically.
- Next complete the getNumSamples() method by returning the
length of the instance-variable array of samples.
Run the unit tests, and the testGetNumSamples() unit
test should pass at this point.
- Complete the play() method by having Sedgewick's StdAudio class play the instance-variable array of samples.
- Complete the toString() method to return a useful string to
describe the contained samples.
- Complete the getMax() and getMin() methods so
that they do what their comments say they should do.
Run the unit tests, and the testMaxAndMin() unit
test should pass at this point.
- Complete the concat(Samples) method, which takes
this sequence of samples and an other sequence of
samples and return a new sequence that is the concatenation of the two.
Note that this must
return a new Samples object. The current object must not
be changed!
Run the unit tests, and the testConcat() unit
test should pass at this point.
- Complete the combine(Samples) method, which takes
this sequence of samples and an other sequence
of samples, and sums them, element-by-element, to form a new sequence of samples.
If one sequence is shorter than the other, it is as if the shorter sequence
had zeros as the sums are computed.
Run the unit tests, and the testCombine() unit
test should pass at this point.
- OK after all that work, open Pitch and you will see that
this class is done for you.
Run the PitchTest unit tests, and they should pass at this point.
- Open the SingleTone class. A SingleTone
has-a frequency which should be retained as a double.
- Complete the constructor for SingleTone.
- Complete the method getSamples() by generating samples
at the specified amplitude and duration.
Recall that a SingleTone has-a a frequency, so the
frequency used for the samples should be stored as an instance variable in your
class.
- Complete the getOverTone(double) method, which returns
a new SingleTone object whose frequency is the specified
mutliple of this one's frequency.
- Complete the getOverTone(int,int) method, which should
return its result by reduction to the getOverTone(double) method.
This means that you should return the result of
getOverTones(int,int) by returning a suitable call of
getOverTone(double).
Run the unit tests, and the testOvertones() unit
test should pass at this point.
- Open DiatonicScale. This class represents notes that form
a major scale, such as the natural (usually, white) keys do on a piano. A
DiatonicScale has-a starting Pitch, which
should be retained as a Pitch reference.
We could have designed this class to retain the starting pitch as an
integer, but with the richer object Pitch we should use it instead.
Why?
A Pitch object can easily compute other related pitches, and
return its representation as a frequency. We could carry out these computations
on any integer, but by having it already programmed in Pitch, we
should use that object to avoid code duplication, avoid work, and increase
reliability.
- The first constructor is already completed in terms of the second one,
so you don't have to worry about the first constructor.
- The second constructor must be completed,
but it simply stores its incoming parameter in the instance variable you
provisioned above.
- Let's look at getPitch(int). This method returns the
Pitch that is the specified diatonic distance from this
DiatonicScale's starting pitch. What makes this tricky is that
the diatonic notes are not evenly spaced. This can be most easily understood
by looking at the chromatic distances between the natural keys on a piano.
The following table shows the relative chromatic distances between the
notes of a diatonic scale:
Example in key of C major | Diatonic offset | Chromatic offset from previous diatonic note |
C |
0 |
N/A |
D |
1 |
2 |
E |
2 |
2 |
F |
3 |
1 |
G |
4 |
2 |
A |
5 |
2 |
B |
6 |
2 |
C |
7 |
1 |
Moreover, the getPitch(int) method must accommodate values for
its parameter that are negative, zero, or positive, and those values may
be outside the range of a single octave.
This is the trickiest method: get help from the TAs or instructor if you
need it.
-
Run the unit tests for DiatonicScale and they should pass
at this point.
-
You can also run DiatonicScale as an applicaiton and it should
print out some information about some scales.
- Open Triad class. A Triad has three
SingleTones.
- Complete the constructor for Triad.
The constructor takes in a DiatonicScale and an int, which
is the diatonic offset of the root of the triad.
The other two pitches are 2 and 4 diatonic offsets away from the root.
Use the getPitch(int) method of the specified
DiatonicScale to find the root, second,
and third SingleTones of this Triad.
- Complete getTones() which returns an array consisting of the
SingleTones of this Triad, in order.
- Complete getSamples(double,double), which returns a Samples object
of the triad's tones at the specified amplitude and of the
specified duration.
- Note that the specified amplitude must be divided (evenly)
among the three SingleTones.
- All three SingleTones of
the triad should sound simultaneously and last for the specified duration.
- This method can be written in
a single line of code using objects you have already created and tested.
Run the unit tests for Triad and they should pass
at this point.
- For your final test, run PlayTest and choose a song. You should
be able to hear the song repeat and with the mouse you can change its
pitch and its speed.
When you done with this extension, you must be cleared by the TA to receive credit.
- Commit all your work to your repository
- Fill in the form below with the relevant information
- Have a TA check your work
- The TA should check your work and then fill in his or her name
- Click OK while the TA watches
- If you request propagation, it does not happen immediately,
but should be posted in the next day or so
End of extension 7
-
Extension 8 for this module (4 points):
Chord Organ
Fall 2013, Note to students:
this is a new extension. Your suggestions as to how to improve this
assignment are welcome!
You will use the Triad and other classes you have
developed to make a simple chord organ that resembles the following:
The goal of your work is to hear the
a diatonic triad play in the C major scale when your mouse enters
the correspondingly marked rectangle.
Procedure
- Open the ChordOrgan class in the chords package.
This is where you will write your code for this extension.
- Arrange for a screen such as the one shown above to be drawn,
and detect mouse entry into the specified rectangles. To help you
with this, consider the BoundingBoxGUIExample class
in the chords.gui package:
- Run BoundingBoxGUIExample as an application and observe
what happens when your mouse enters the displayed rectangle.
- Read the comments and the code for BoundingBoxGUIExample,
and get help if anything is unclear.
- Now try to create the GUI shown above.
Your code will be shorter and easier to write if you think through how
to represent the 8 keys shown in the GUI. You could use many separate
variables, but if you think abstractly, you will use
arrays to represent the values of interest.
For the steps of the development described below, however, you
must use the specified
classes.
- Construct a DiatonicScale instance in the key of C:
DiatonicScale ds = new DiatonicScale(3);
This makes a diatonic scale object with high C as its base note.
- To make a given triad at offset i, use
Triad t = new Triad(ds,i);
- You can get samples from that Triad (using
Triad's getSamples(double,double)) of an amplitude and
duration of your choosing and then cause them to be played using the
Samples class.
- Demonstrate your work to a TA using a computer with sound enabled.
When you done with this extension, you must be cleared by the TA to receive credit.
- Commit all your work to your repository
- Fill in the form below with the relevant information
- Have a TA check your work
- The TA should check your work and then fill in his or her name
- Click OK while the TA watches
- If you request propagation, it does not happen immediately,
but should be posted in the next day or so
End of extension 8
-
Extension 9 for this module (5 points):
Six Four One Five
Much has been written about the tendency for
musicians from
Pachelbel to
Pink
to write songs based on a common chord progression:
In response of this,
the Axis of Awesome has realized that
the only barrier to their becoming famous is their lack of a four-chord song,
as documented extensively in
this amazing video.
Axis of Awesome needs your help
to record their soon-to-be-hit four-chord song.
In this extension, you use the chords.music classes you developed above to
create a 4-chord song. The song consists of two Samples of music,
background and tune, which are
combined throughout to make a song. As the song plays, you
can control how much of what you hear is background and how much is tune.
Overview
- Open the SixFourOneFive class in the chords package.
Your work for this extension will be done in this class.
-
A TwoDimensionalGUI class is provided for your use in
the chords.gui package.
To see how it works
- Run the
TwoDimensionalGUIExample and move your mouse in the window to
control the size and shading of the displayed circle.
- Read the constructor and the code in the class.
- You can instantiate this class yourself
and use it to select the amount of background and tune present in your sound output.
- The code you write will resemble the example GUI and all other
programs based on an event loop.
- Each iteration of your infinite event loop prepares four seconds of
sound for output.
- These four seconds are one musical measure.
- Each measure has four beats
- Each second of time corresponds to one beat of the four-beat measure
- In other words, ♩=60 in 4/4 time
Thus, each measure goes through the four chords of your four-chord song,
with one second spent on each chord.
- The music you generate consists of background (chords) and a tune:
- Background
- The background of your song is a constant progression of Six-Four-One-Five
chords. These chords can be generated using the classes you have
above as follows:
- If you construct a DiatonicScale with starting pitch 3,
then its scale is C major starting on high C.
- Although the chord pattern is called Six Four One Five, this assumes
the root chord is One. For us, the root chord is 0, so the
diatonic offsets for our chords are really 5 3 0 4.
- That 5 chord is a bit high and sqeaky, so we lower it by
an octave, subtracting 7 diatonic steps to get -2.
The resulting chord sequence in terms of diatonic offsets is
-2 3 0 4
- If we name the DiatonicScale of interest ds, then the
triads of interest are as follows:
Chord | Name in C major | Triad |
Six | a minor | new Triad(ds, -2) |
Four | F major | new Triad(ds, 3) |
One | C major | new Triad(ds, 0) |
Five | G major | new Triad(ds, 4) |
Because Triads are constructed relative to a DiatonicScale,
the Triad instantiations above create Six Four One Five for whatever
key the DiatonicScale object was constructed.
The background consists of rotating among the above chords, with each chord playing for
one second (a quarter note).
- Tune
- The tune is generated randomly, with each note having random pitch and duration:
- Duration
-
- Each chord of the background lasts for one second, and you need
to generate a one-second tune to be combined and played with the background.
- You could pick a single note for the one second, but that would not
be rhythmically interesting.
- Instead, let's begin with a nominal beat that is
one second in duration.
- That beat could be subdivided into two beats of half the original
duration, so two half-second notes.
- The process of subdividing could continue to obtain smaller rhythmic
units such as four quarter-second notes, eight eighth-second notes, etc.
- No matter how much we subdivide the beat, the resulting rhythmic
units sum to one second when played continguously.
- If the above process is not somewhat random, the resulting rhythms
would still be quite regular and uninteresting.
Your challange is to devise a method for randomly breaking a one-second beat
into smaller units. The units should not always be the same. For example,
one way to divide one second might result in the following, played
consecutively:
- 2 eighth-second notes
- 1 eighth-second note
- 2 sixteenth-second notes
- 1 quarter-second note
- 1 quarter-second note
- Pitch
- Once one of the above rhythmic units is complete, its
pitch should be chosen from a diatonic scale in the key of C major. Construct a
DiatonicScale(3) object and use its getPitch(int) to
get a random diatonic pitch from the C-major scale.
When you done with this extension, you must be cleared by the TA to receive credit.
- Commit all your work to your repository
- Fill in the form below with the relevant information
- Have a TA check your work
- The TA should check your work and then fill in his or her name
- Click OK while the TA watches
- If you request propagation, it does not happen immediately,
but should be posted in the next day or so
End of extension 9