190 likes | 369 Views
CS 177 Recitation. Week 9 – Audio Processing. Announcements. Project 3 (final version) is due on Thursday (Oct 29) Any questions about the project?. Questions?. Sound. Like light, sound is a wave We can’t record an (analog) wave directly on a (digital) computer, so we have to do sampling
E N D
CS 177 Recitation Week 9 – Audio Processing
Announcements • Project 3 (final version) is due on Thursday (Oct 29) • Any questions about the project?
Sound • Like light, sound is a wave • We can’t record an (analog) wave directly on a (digital) computer, so we have to do sampling • Sampling converts the analog wave into a digital approximation • Break the wave down into a lot of samples (44,100 samples per second = 44,100 Hz) • Each sample is a double value in the range [-1, 1] • Each sample records the amplitude of the wave at a certain place • In the computer, the sound wave is just an array of doubles
Sound • Amplitude of the wave determines the sound’s volume • Frequency of the wave determines how high or low the sound is is louder than high-pitched sound low-pitched sound
StdAudio • Written by the book’s author, like StdIn and StdDraw • As usual, remember to download the file and put it in the same directory if you want to use the StdAudio methods • Allows you to manipulate sound as a single array of samples • Allows you to read and write WAV files
Simple Audio Example • Reads a sound from file and plays it public class PlaySound { public static void main(String[] args){ System.out.print("Enter filename: "); String file = StdIn.readString(); double[] sound = StdAudio.read(file); StdAudio.play(sound); } }
Slowing Down a Sound • The example in lecture just doubled the length of the sound by using each sample twice • Example: slow[0] = sound[0] slow[1] = sound[0] slow[2] = sound[1] slow[3] = sound[1] • This works, but doesn’t sound so great • Using each sample twice in a row makes the wave kind of blocky instead of
Slow.java • To smooth the slowed sound, use the average of adjacent samples instead of copies public class Slow{ public static void main(String[] args){ double[] sound = StdAudio.read("world.wav"); double[] slow = new double[sound.length*2]; for( int i = 0; i < slow.length-1; i++ ) //slow[i] = sound[i/2]; //old way slow[i] = (sound[i/2] + sound[(i+1)/2]) / 2.0; //better way StdAudio.play(slow); } }
Adding Noise • We can add noise to a sound by adding or subtracting a small random number from each sample • For simplicity, we’ll only add • Remember, the samples can only be in the range -1 to 1 • Whenever you’re changing the samples, be sure that they stay in that range!
AddNoise.java public class AddNoise { public static void main( String [] args ) { String file = “world.wav”; double samples [] = StdAudio.read(file); for ( int i = 0; i < samples.length; i++ ) { double noise = Math.random(); samples[i] += noise; if ( samples[i] > 1 ) samples[i] = 1; } StdAudio.play(samples); } }
Playing Two Sounds Together • Add the samples at each position in the array First sound: Second sound: Combined sound: Don’t forget to check for samples that become >1 or <-1!
Playing Two Sounds Together • If one sound is longer than the other: • mix the two until the end of the shorter sound • copy the rest of the longer sound unchanged onto the end of the mixed sound First sound: Second sound: Combined sound: Copy of the longer sound’s data Mix of both sounds
PlayTwo.java public class PlayTwo { public static void main( String [] args ) { String file1 = ”world.wav"; String file2 = "breakbeato.wav"; double samples1 [] = StdAudio.read(file1); double samples2 [] = StdAudio.read(file2); int longer = samples1.length; int shorter = samples2.length; if ( samples2.length > longer ) { longer = samples2.length; shorter = samples1.length; } double mix [] = new double[longer]; for ( int i = 0; i < shorter; i++ ) { mix[i] = samples1[i] + samples2[i]; } for ( int j = shorter; j < longer; j++ ) { if ( samples1.length > samples2.length ) mix[j] = samples1[j]; else mix[j] = samples2[j]; }
Improving PlayThatTune.java • The book’s PlayThatTune program works fine, but produces vaguely creepy robot music • We want to make it a little less creepily robotic • Add harmonic tones one octave above and below each note to produce a more realistic sound • To run PlayThatTune (has to be done from the command line): java PlayThatTune < elise.txt
Improving PlayThatTune.java • Originally the wave for a note looks like: • With the harmonics, it looks like:
PlayThatTune2.java public class PlayThatTune2 { public static double[] sum(double[]a, double [] b, double awt, double bwt ) { double [] c = new double[a.length]; for ( int i = 0; i < a.length; i++) c[i] = a[i] * awt + b[i] * bwt; return c; } public static double[] tone(double hz, double t) { int SAMPLE_RATE = 44100; int N = (int) (SAMPLE_RATE * t); double[] a = new double[N+1]; for (int i = 0; i <= N; i++) { a[i] = Math.sin(2 * Math.PI * i * hz / SAMPLE_RATE); } return a; }
PlayThatTune2.java con. public static double[]note ( int p, double t) { double hz = 440.0 * Math.pow( 2, p / 12.0); double[] a = tone( hz, t); double[] hi = tone(2 * hz, t); double[]lo = tone(hz/2, t); double[] h = sum(hi, lo, .5, .5); return sum(a,h,.5,.5); } public static void main(String [] args) { while ( !StdIn.isEmpty()) { int pitch = StdIn.readInt(); double duration = StdIn.readDouble(); double[]a = note(pitch, duration); StdAudio.play(a); } } }