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);
}
}
}