CSC111 Lab 7

From CSclasswiki
Jump to: navigation, search

--Thiebaut 18:54, 23 March 2009 (UTC)

Playing with Sound Files


1. First, get the library of functions created this morning in class: , and paste it into your JES window.

2. Get the 3 sound files listed below from the CSC111 shared folder on the Novel network (\\rescent\pc\COURSES\CSC\CSC111\Sound Files). If you do not have access to them through your computer, just right-click on the link below and download them to your computer:

3. Plug in your earphones!

Playing sounds

  • Type in the following commands in the console window:
>>> file = pickAFile( )          (Pick the thisisatest.wav file)

>>> sound = makeSound( file )

>>> play( sound )
  • Verify that you can hear the speech in your earphones
  • Open the Media Tools/Sound Tool and pick sound as the object to observe
  • Verify that you can see the 4 words in the waveform. Using the cursor, record on a piece of paper the indexes of the beginning and end times of the four words ("this", "is", "a", and "test"), as indicated by the purple arrows. We will need them later.

CSC111 SoundTool1.png

  • Using the Sound Tool still, play with the highlight feature and the cursor to play only one word, or only the beginning of the sentence, or the end of it.
  • Check the other wav files up, playing them, and also looking at their waveform with the Sound Tool.

Using the library

Use the functions that are in the program (your new library), and assemble them together in a few lines of code to perform the following tasks (it may be easier to work in the console

  1. Double the amplitude of the waveform and play it several times. Each time restart the sound tool and reload the sound object to see how the waveform changes.

  2. Reload the sound with the original wav file. Now reduce the amplitude by half every time. How many times do you have to halve the amplitude before you cannot hear the words any longer? Assume that you must repeat the experiment 4 times before you lose the words. This is equivalent to dividing by 2 x 2 x 2 x 2 = 16. Why don't you multiply the last version of sound by 16 (or the equivalent of the number of division you found) to see if you regain the original sound.
    • What happens to the noise in the process?
    • Does it get increased?
    • Decreased?
    • Does it stay the same?
    • Did we lose any information by dividing the amplitude several times and then multiplying by the same factor?

  1. Reload the sound with the original wav file. Zero out the noise using a threshold of 100. Listen to the new sound. Keep on increasing the threshold by 100 as long as the the words still appear "intact".

  2. Add a new function to your library called myRemoveNoise( sound ) which will use the threshold you found in the previous step to reduce the noise of the sound object passed to it. Be efficient (and lazy)! Your new function should only be 3 lines long: one line for the def part, one line of comment, and one line for the body!

  3. Copy the first word (this) between the 2nd and 3rd words, and between the 3rd and 4th words of the sentence. Verify that you hear the correct new (strange sentence): "this is this a this test"

New functions

  • Add a function that will normalize the sound wave. By normalizing, we mean that the highest peak of the waveform will be set to 32767, the max allowed for a number, and all the other values will be increased by the same factor. This is a way of increasing the amplitude to the maximum value allowable without incurring clipping.
Unfortunately we cannot use max( getSamples( sound ) ) to get the largest value. Instead we can create a list of all the numbers and get its max as follows:
    # create an empty list 
    list = [ ]

    # put all the absolute values of the intensity of the samples in the list
    for sample in getSamples( sound ):
         value = getSampleValue( sample )
         list.append( abs( value ) )

    # get the largest value 
    largestValue = max( list )

Use this code in your new function which you will call normalize( sound ). Test it and verify that it normalizes your sound wave.
  • Reload the sound with the original wav file. Reduce the noise using your new function, then normalize the sound. (Is it better to normalize first and reduce the noise second, or vice versa?)

  • Add a function called zeroOutToEnd( sound, startX ) that sets the amplitude of the sound to 0 starting at Index startX. For example, if we were to call zeroOutToEnd( sound, 8100 ), where sound is a variable containing the waveform for "this is a test", we would clear the waveform right after the word this, and in effect remove the last 3 words.
To loop through all the samples of a waveform, you have two options, illustrated below:
for sample in getSamples( sound ):
     value = getValue( sample )       # get the value of the sample
     setSample( sample, value )       # set its value to the same number
for i in range( 1, getLength( sound )+1 ):
     value = getSampleValueAt( sound, i )    # get the value of the i-th sample
     setSampleValueAt( sound, i, value )     # set its value to the same number
Figure out which loop is best for your function zeroOutToEnd(), write the function and test it.

Making your functions robust

Robustness is the ability of a function not to crash, no matter what values are passed as parameters.

The function copyToOffset( sound, fromIndex, toIndex, whereIndex ) is not robust. It can crash with a range error. To verify this, try calling it as follows:

  copyToOffset( sound, 1, 8000, 60000 )

See the error?

JavaSoundException: You are trying to access the sample at index: 64514, but there are only 64513 samples in the file!

Modify the function copyToOffset so that it will copy as much of the interval defined by [fromIndex, toIndex] to the location specified, and will not copy past the end of the waveform.

The diagram below should allow you how to better understand the problem:

CSC111 copyToOffset.png

If fromIndex is 1, and toIndex is 4, then the furthest point where we can copy all 3 samples is at Index whereIndex=18. If whereIndex is larger, we have to somehow stop the loop before its normal end.

Here's an example of how we can accomplish this feature. Try this code in the console area of JES:

   for i in range( 1,  20 ):
       print "i = ", i
   print "done!"

Now modify it as follows:

   for i in range( 1,  20 ):
       if i > 15:
       print "i = ", i
   print "done!"

The break statement allows us to break out of the loop we are in.

Use this feature to make your function robust.

Check that your function is now robust.

Will it pass the following tests?

  copyToOffset( sound, 10, 1, 60000 )

  copyToOffset( sound, 1, 80000, 60000 )
  copyToOffset( sound, 0, 8000, 60000 )

  copyToOffset( sound, 1, 8000, 80000 )

If not, modify the file until it passes the tests without errors!

Ready for an echo?

We can now create an echo effect.

Modify the original sound wave file as follows:

  1. zero-out the words is a test
  2. normalize the result
  3. copy the word this to the offsets 10000, 20000, 30000, 40000, 50000 (use a loop!)
  4. play the resulting waveform.

You are ready for the homework assignment!

Solution Programs for this lab

Important note: There are different versions of JES running around, and some versions of JES use getValue() to get the value of a sample, while others use getSampleValue(). If you get an error when using the functions in the file attached to the link below, it is because your version of JES is not the same I used to test the functions.