CS 177 Week 8 Recitation Slides JES Sound functions and Modifying Sounds Increasing/Decreasing Volume Maximizing (Normalizing) Splicing Reversing Mirroring 1 ANY QUESTIONS? 2 Let’s remember Sound We store sounds as array of integer values Each value is stored as 16 bits The range is from –2(16-1) to +2(16-1) – 1 i.e from –32768 to 32767 59 28 -123 0 -32768 20456 32767 0 1 2 3 4 5 6 The length of this sound is 7 3 JES Functions about Sound pickAFile() makeSound(file) play(sound) Let the user pick a sound file (.wav file) Takes a filename as input, reads the file, and creates a sound from it. Returns the sound. Plays a sound provided as input. No return value. getLength(sound) Takes a sound as input and returns the number of samples in that sound. getSamplingRate(sound) Takes a sound as input and returns the number representing the number of samples in each second for the sound. getSamples(sound) Takes a sound as input and returns the Samples in that sound. writeSoundTo(sound, path) 4 Takes a sound and a filename (a string) and writes the sound to that file as a WAV file. (Make sure that the filename ends in '.wav' if you want the operating system to treat it right.) JES Functions about Sound getSampleValueAt(sound, index) Takes a sound and an index (an integer value), and returns the value of the sample (between -32768 and 32767) for that object. setSampleValueAt(sound, index, value) Takes a sound, an index, and a value (should be between -32768 and 32767), and sets the value of the sample at the given index in the given sound to the given value. getSampleObjectAt(sound, index) Takes a sound and an index (an integer value), and returns the Sample object at that index. getSampleValue(sample) getSample (sample) Takes a Sample object and returns its value (between -32768 and 32767). setSampleValue(sample, value) setSample (sample, value) Takes a Sample object and a value (should be between -32768 and 32767), and sets the sample to that value. getSound(sample) Takes a Sample object and returns the Sound that it belongs to. A sample object remembers its sound, so if you change the sample object, the sound gets changed. 5 Demonstrating Working with Sound in JES >>> filename=pickAFile() >>> print filename /Users/guzdial/mediasources/preamble.wav >>> sound=makeSound(filename) >>> print sound Sound of length 421109 >>> samples=getSamples(sound) >>> print samples Samples, length 421109 >>> print getSampleValueAt(sound,1) 36 >>> print getSampleValueAt(sound,2) 29 >>> explore(sound) 6 Demonstrating working with samples >>> print getLength(sound) 220568 >>> print getSamplingRate(sound) 22050.0 >>> print getSampleValueAt(sound,220567) 68 >>> print getSampleValueAt(sound,220568) I wasn't able to do what you wanted. The error java.lang.ArrayIndexOutOfBoundsException has occurred Please check line 0 of >>> print getSampleValueAt(sound,1) 36 >>> setSampleValueAt(sound,1,12) >>> print getSampleValueAt(sound,1) 12 7 Example: Changing Samples >>> soundfile=pickAFile() >>> sound=makeSound(soundfile) >>> sample=getSampleObjectAt(sound,1) >>> print sample Sample at 1 value at 59 >>> print sound Sound of length 387573 >>> print getSound(sample) Sound of length 387573 >>> print getSample(sample) 59 >>> setSample(sample,29) >>> print getSample(sample) 29 8 Increasing and Decreasing the Volume def increaseVolume(sound): for sample in getSamples(sound): value = getSampleValue(sample) setSampleValue(sample,value * 2) >>> f=pickAFile() >>> s=makeSound(f) >>> increaseVolume(s) def decreaseVolume(sound): for sample in getSamples(sound): value = getSampleValue(sample) setSampleValue(sample,value * 0.5) >>> f=pickAFile() >>> s=makeSound(f) >>> decreaseVolume(s) 9 Recognize some similarities? def increaseVolume(sound): for sample in getSamples(sound): value = getSampleValue(sample) setSampleValue(sample, value*2) def increaseRed(picture): for p in getPixels(picture): value=getRed(p) setRed(p,value*1.2) def decreaseVolume(sound): for sample in getSamples(sound): value = getSampleValue(sample) setSampleValue(sample, value*0.5) def decreaseRed(picture): for p in getPixels(picture): value=getRed(p) setRed(p,value*0.5) 10 Maximizing (Normalizing) sound First, figure out the loudest sound (largest sample). Next, figure out how much we have to increase/decrease that sound to fill the available space We want to find the amplification factor amp, where amp * loudest = 32767 11 In other words: amp = 32767/loudest Finally, amplify each sample by multiplying it by amp Maxing (normalizing) the sound This loop finds the loudest def normalize(sound): sample largest = 0 for s in getSamples(sound): largest = max(largest, getSampleValue(s)) amplification = 32767.0 / largest Why 32767.0 but not 32767? print "Largest sample value in original sound was", print "Amplification multiplier is", amplification largest for s in getSamples(sound): louder = amplification * getSampleValue(s) setSampleValue(s, louder) We’re making an assumption here that the maximum positive value is also the maximum negative value. 12 This loop amplifies the sound Note that the largest sample will be updated with 32767 Avoiding clipping 13 Why are we being so careful to stay within range? What if we just multiplied all the samples by some big number and let some of them go over 32,767? The result then is clipping Clipping: The awful, buzzing noise whenever the sound volume is beyond the maximum that your sound system can handle. Processing only part of the sound What if we wanted to increase or decrease the volume of only part of the sound? use a range() function with our for loop Let’s increase volume by sample index (using range) def increaseVolumeByRange(sound): for sampleNumber in range(0, getLength(sound)): value = getSampleValueAt(sound, sampleNumber) setSampleValueAt(sound, sampleNumber, value * 2) SAME AS 14 def increaseVolume(sound): for sample in getSamples(sound): value = getSample(sample) setSample(sample,value * 2) Modify different sound sections Here we increase the volume in the first half, and decrease it in the second half. def increaseAndDecrease(sound): length = getLength(sound) for index in range(0, length/2): value = getSampleValueAt(sound, index) setSampleValueAt(sound, index, value*2) for sampleIndex in range(length/2, length): value = getSampleValueAt(sound, index) setSampleValueAt(sound, index, value*0.2) 15 Splicing Sounds 16 Splicing gets its name from literally cutting and pasting pieces of magnetic tape together The easiest kind of splicing is when the component sounds are in separate files. All we need to do is copy each sound, in order, into a target sound. Here’s a recipe that creates the start of a sentence, “Guzdial is …” (You may complete the sentence.) Splicing whole sound files def merge(): guzdial = makeSound(getMediaPath("guzdial.wav")) isSound = makeSound(getMediaPath("is.wav")) target = makeSound(getMediaPath("sec3silence.wav")) index = 0 for source in range(0, getLength(guzdial)): value = getSampleValueAt(guzdial, source) setSampleValueAt(target, index, value) index = index + 1 for source in range(0, int(0.1*getSamplingRate(target))): setSampleValueAt(target, index, 0) index = index + 1 for source in range(0, getLength(isSound)): value = getSampleValueAt(isSound, source) setSampleValueAt(target, index, value) index = index + 1 normalize(target) play(target) return target 17 How it works Creates sound objects for the words “Guzdial”, “is” and the target silence Set target’s index to 0, then let each loop increment index and end the loop by leaving index at the next empty sample ready for the next loop The 1st loop copies “Guzdial” into the target The 2nd loop creates 0.1 seconds of silence The 3rd loop copies “is” into the target Then we normalize the sound to make it louder 18 Reversing Sounds We can also modify sounds by reversing them def reverse(source): target = makeEmptySound(getLength(source)) sourceIndex = getLength(source) - 1 # start at end for targetIndex in range(0, getLength(target)): value = getSampleValueAt(source, sourceIndex) setSampleValueAt(target, targetIndex, value) sourceIndex = sourceIndex - 1 # move backwards return target 19 Mirroring We can mirror sounds in exactly the same way we mirrored pictures def mirrorSound(sound): len = getLength(sound) mirrorpoint = len/2 for index in range(0, mirrorpoint): left = getSampleObjectAt(sound, index) right = getSampleObjectAt(sound, len-index-1) value = getSampleValue(left) setSampleValue(right, value) return (sound) 20 Final QUESTIONS??? 21