CS 111 - Program Design I, Fall 2014

Project 4 - Sound Player

Due Date: Thursday 12/4/14 at 11:59pm

For this project, you are to write a program that will take a string containing "musical notes" and create a .wav file that plays those notes.

We will be using notes as played on a guitar:

guitar_neck.gif

We won't be using all of these notes, just the basic 18 notes for the guitar. These basic notes shown with the notes marked on sheet music are:

guitar_basic_notes.jpg

For this program will will need to be able to translate from the notes to the frequencies of sound each note creates when played on a guitar. Please note that the frequencies for guitar notes and piano notes differ by one octave.

Frequency Note String Fret Comments
82.4 E 6th string open
87.3 F 6th string 1st
92.5 F# 6th string 2nd
98.0 G 6th string 3rd
103.8 G# 6th string 4th
110.0 A 5th string open Not sure why the above images shows the fingering for this note twice
116.5 A# 5th string 1st
123.5 B 5th string 2nd
130.8 C 5th string 3rd Note position of "middle C"
138.6 C# 5th string 4th
146.8 D 4th string open
155.6 D# 4th string 1st
164.8 E 4th string 2nd
174.6 F 4th string 3rd
185.0 F# 4th string 4th
196.0 G 3rd string open
207.6 G# 3rd string 1st
220.0 A 3rd string 2nd
233.1 A# 3rd string 3rd
246.9 B 2nd string open
261.6 C 2nd string 1st Frequency of "middle C"
277.2 C# 2nd string 2nd
293.6 D 2nd string 3rd
311.1 D# 2nd string 4th
329.6 E 1st string open
349.2 F 1st string 1st
370.0 F# 1st string 2nd
392.0 G 1st string 3rd
415.3 G# 1st string 4th
440.0 A 1st string 5th
Since the input to the program will be a string of notes. Each note needs to be specified by a unique character. Each basic note on the guitar occurs 2 or 3 times, so we need to be able to distinguish between the A note at 110 hz, the A note at 220 hz and the A note at 440 hz.
  • We will use the digits of 7,8,9 to represent the 3 lowest notes of E, F, G that are played on the 6th string of the guitar.
  • We will use the lower case letters a through g to represent the notes of A on the 5th string to G on the 3rd string.
  • We will use the upper case letters A through G to represent the notes of A on the 3rd string to G on the 1st string.
  • We will use the upper case letter of H to represent the note of A on the 1st string.
The above representation would allow us to extend to higher notes by using additional upper case letters, but this project will NOT require that.

The one thing that this project will require is the representation of the sharp notes. This will be needed to mathematically translate from the notes to proper frequencies. The sharp notes will use the shifted keys of the digits.

So to relist the notes with frequencies and their corresponding character representation:

Frequency Note Character
Representation
82.4 E 7
87.3 F 8
92.5 F# !
98.0 G 9
103.8 G# @
110.0 A a
116.5 A# #
123.5 B b
130.8 C c
138.6 C# $
146.8 D d
155.6 D# %
164.8 E e
174.6 F f
185.0 F# ^ - the "up arrow" character at "Shift-6"
196.0 G g
207.6 G# &
220.0 A A
233.1 A# *
246.9 B B
261.6 C C
277.2 C# (
293.6 D D
311.1 D# )
329.6 E E
349.2 F F
370.0 F# _
392.0 G G
415.3 G# +
440.0 A H

To list all of the characters in order in a single Java String, we would use that following code:

String noteRepresentation = "78!9@a#bc$d%ef^g&A*BC(D)EF_G+H";

Once we have the above string, we can use it and a mathematical formula to calculate the frequency of each note.

    freq = 440.0 * 2.0((x - y)/12.0)      
    where:
       x = position in the String of the desired note
       y = position in the String of the A note with frequency of 440 hz  
With the use of the Java String Library method indexOf (int ch), we can easily find the position of our desired note and the position of the A note with frequency of 440 hz. For example, if we wanted to calculate the frequency of the G note played on the 3rd string. The "note character" is 'g'. The "note character" of the A note with frequency 440 hz is 'H'. We then have the following Java code:
    String noteRepresentation = "78!9@a#bc$d%ef^g&A*BC(D)EF_G+H";
     int x = noteRepresentation.indexOf('g');  // x becomes 15
     int y = noteRepresentation.indexOf('H');  // y becomes 29
     double exponent = (x - y)/12.0;
     double freq = 440.0 * Math.pow (2.0, exponent); // freq becomes 196.0 (if rounded) 

One more item that we will need to deal with is the length of the note being played and the amount of time between each note. To keep this as simple as possible, we will assume 4 beats per second. Thus each "beat" will last for 1/4 of a second. So:

  • A quarter note will take up 1/4 second in the resulting .wav file.
However, since we have to seperate each note, we will have a "half beat" of silence between notes. This restriction will only allow us to play notes of a quarter note or longer. To distinguish between two quarter notes and one half note, our input will need to specify what is being played during every 1/8 of a second.
  • A quarter note will be 1/8 of a second of sound followed by 1/8 of a second of silence.
  • A half note will be 3/8 of a second of sound followed by 1/8 of a second of silence.
  • A whole note will be 7/8 of a second of sound followed by 1/8 of a second of silence.
The input will then need a character to represent silence. The default character will be the period. However, if we encounter any "non-note" character, we are to treat it as "silence".

In order to keep the input as simple as possible, we will leave the length of each note up to how the user gives the input. Thus to have a quarter note of the 110.0 hz a note that will be "1/8 of a second of sound followed by 1/8 of a second of silence", the input would be the following string:

  • a. Where the a character in the string specifies the 1/8 of a second of sound and the . character in the string specifies the 1/8 of a second of silence.
The following string will be 4 quarter notes separated by an 1/8 of a second of silence (with an addition 1/8 of a second of silence at the end).
  • a.a.a.a.
While the following string will be 2 half notes separated by an 1/8 of a second of silence (with an addtional 1/8 of a second of silence at the end).
  • aaa.aaa.
While the following string will be 1 whole note (with an addtional 1/8 of a second of silence at the end).
  • aaaaaaa.
Note that each character specifies 1/8 of a second of the sound file. So a string of 8 characters will always create a one second longsound file. The notes played will be determined by the actual characters listed in the string.

Making Sine Waves

The next thing we need to do is to create the proper sine wave for each frequency. Our text book does discuss how to do this on page 327. The biggest issue that the book does is that it creates each sine wave to last for 1 second. We only want each since wave to last for 1/8 of a second. This information is also summarized below. Note that sine wave appears as:
sine_wave.jpg

First we need to determine how many sound samples are needed for one cycle of the wave at frequency f. If we want to create a sound at a certain frequency (like 440 Hz), we have to fit the sine wave into 1/440 of a second. If we have a sample file with 22050 sample per secend, that means we have about put the since wave in about 50 samples. To calculate the number of samples needed we can take the total number of samples per second (i.e. the sampling rate of a .wav file) and divide that by the frequency.

   samplesPerCycle = samplingRate / freq 

Second we want to fill in that number of samples with a sine wave. To do this we need to figure out at what point in the wave each sample is located. This can be done by taking the Index number for each sample and dividing this by the number of samples per cycle.

   wavePoint = sampleIndex / samplesPerCycle     
For this assignment, each frequency will be mantained for 1/8 of a second. So to determine the length of the sound and the range of the sampleIndex values, we divide the sampleRate by 8.

Now, we need to find the sine value of the wavePoint. Since the sin () method of the Java Math class takes input in radians, we need to first multiple the wavePoint value by 2 * PI before calling the sine() method. Fortunately, the Java Math class also gives us the value if PI.

    rawValue = Math.sin (wavePoint * 2.0 * Math.PI) 

The last thing that needs to be done is to determine the amplitude of our sine wave. The result from Math.sin() give us a value from -1 to 1. We need to multiple this value by some contant to give our sine wave some volume. Multiplying by a value of 5000 seems to work well.

User Input

Our input to the program will be a string that we will get from the user. It is up to the user to give us a properly formatted input. Example of this input are:
  • The beginning of Jingle Bells:
    E.E.EEE.E.E.EEE.E.G.C.D.EEEEEEE.

  • The beginning of Twinkle Twinkle Little Star
    g.g.D.D.E.E.DDD.C.C.B.B.A.A.ggg.

  • London Bridges:
    D.E.D.C.B.C.DDD.A.B.CCC.B.C.DDD.D.E.D.C.B.C.DDD.AAA.DDD.B.ggg.

  • Old MacDonald
    C.C.C.g.A.A.g...E.E.D.D.C.....g.C.C.C.g.A.A.g...E.E.D.D.C.

  • Go Team Go
    ggg.BBB.C.(((.D.ggg.BBB.C.(((.D.ggg.BBB.C.(((.D.G.G.G...g.g.g.

To get the input from the user, prompt the user for an input string using the getString () method from the SimpleInput class from the bookClasses.

While testing your program, you can simply copy and paste the above strings into this prompt. You job is to take the input and create the .wav file containing those notes.

Output of the Program

After the .wav file has been created, show the sound object using the explore() method.

Also prompt the user for a file and save the .wav file to the name given by the user.

Program Writing Style

Your program must be written using good programming style which includes:
  • Use of multiple methods
  • Good variable names
  • In-line commenting
  • Proper indentation of program statements
  • Use of blank lines to separate blocks of code.
  • Header block commenting for the program and each method written

    Your header block comment for the program must include the following:

    • Your Name,
    • Net-ID,
    • Course Name,
    • Assignment Name and
    • Day and time of your CS 101 lab section (i.e. Wednesday at 9:00)
    • A short description of the assignment.
Header block comments for each method must include the following:
    • A description of the purpose of the method
    • A listing of the name, type and purpose of every parameter
    • A description of the return value and its type

Project Collaboration

You are allowed to receive help on this project from other students who are also taking CS 111. Each student must still complete and submit his/her own project. You will be required to include a Collaboration Statement somewhere on your project if you receive help. This statement can simply be something like the following:
For this project, I received help from the following member of CS 111.
This statement should list each helping student's name in a comment in the "header comment" of your Java file that includes the main() method.

Submission of your Project

Submit your code via Blackboard. Use the assignment link for Project 4 to submit your .java code.

-- Main.troy - 2014-11-20

Topic revision: r1 - 2014-11-20 - 03:44:18 - Main.troy
 
Copyright 2016 The Board of Trustees
of the University of Illinois.webmaster@cs.uic.edu
WISEST
Helping Women Faculty Advance
Funded by NSF