Transcript ppt

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





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
is louder than

Frequency of the wave determines how high or low the
sound is
low-pitched sound
high-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
StdAudio Methods
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;
StdAudio.play(slow);
}
}
//better way
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:
.5
.2
-.1
1
.2
.2
…
Second sound:
.1
-.2
0
.3
-.6
.2
…
Combined sound:
.6
0
-.1
1.3
-.4
.4
…
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:
.5
.2
-.1
1
Second sound:
.1
-.2
0
.3
-.6
.2
…
Combined sound:
.6
0
-.1
1.3
-.6
.2
…
Mix of both sounds
Copy of the longer
sound’s data
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];
}
PlayTwo.java
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);
}
}
}