## Introduction

In this week's lab, you'll explore the world of building audio from scratch.

## Skeleton Files

After you pull from the skeleton repo, you'll have a few key files:

- lab14lib/Generator.java
- lab14lib/GeneratorPlayer.java
- lab14lib/GeneratorDrawer.java
- lab14lib/GeneratorAudioVisualizer.java
- lab14lib/MultiGenerator.java
- SineWaveGenerator.java
- PlayMajorChord.java
- Main.java

Generator is an interface that defines the expected behavior of any Generator class. Each Generator simply needs a method `next`

that returns the next `double`

. `GeneratePlayer`

plays the samples returned by the generator as sound. `GeneratorDrawer`

draws the samples returned by the generator as a graph. `GeneratorAudioVisualizer`

both plays and draws the samples. `MultiGenerator`

provides a convenient way to play multiple waveforms at once. The other files are described below.

## Playing with the Sine Wave Generator

#### Creating and Using a Simple Generator

Open up `Main.java`

, and add the follow lines to main:

```
Generator generator = new SineWaveGenerator(440);
GeneratorPlayer gp = new GeneratorPlayer(generator);
gp.play(1000000);
```

What this does is:

- Creates a SineWaveGenerator that outputs samples corresponding to a 440 Hz sine wave.
- Creates a GeneratorPlayer that will play the SineWaveGenerator.
- Tells the GeneratorPlayer to play the first one million samples from the generator as sound.

Try compiling and running GeneratorPlayer, and you should hear a high pitched beep sound. If working from the command line, you can press Control-C to halt execution.

Try changing the 440 to a 200 Hz, and you should hear a lower sound. Note that if you try even lower frequencies, your laptop speakers are likely too small to generate anything lower than ~60 Hz. However, if you use headphones or real speakers, you'll be able to hear such low frequencies.

#### Using the Generator Visualizer

We can also visualize the output of a generator. Replace the main method of `Main.java`

with the following:

```
Generator generator = new SineWaveGenerator(200);
GeneratorDrawer gd = new GeneratorDrawer(generator);
gd.draw(4096);
```

What this does is:

- Creates a SineWaveGenerator that outputs samples corresponding to a 200 Hz sine wave.
- Creates a GeneratorDrawer that will draw the GeneratorDrawer.
- Tells the GeneratorDrawer to draw the first 4096 samples from the generator as a graph.

Run this and you should see something like:

#### Using the GeneratorAudioVisualizer

In fact, we can do both playing and drawing using `GeneratorAudioVisualizer.java`

. Replace the main method of `Main.java`

with the following:

```
Generator generator = new SineWaveGenerator(200);
GeneratorAudioVisualizer gav = new GeneratorAudioVisualizer(generator);
gav.drawAndPlay(4096, 1000000);
```

Try this out, and you should see the first 4096 samples being drawn and the first 1,000,000 samples being played. The reason that we make these parameters different is that if we draw 1,000,000 samples, we won't be able to see anything useful.

#### Using the GeneratorAudioAnimator

Finally, we have provided a version of the visualizer that produces real-time animations of your generator's output: `GeneratorAudioAnimator.java`

. Try out `SineWaveAnimation`

to see it in action!

#### Using the MultiGenerator

As a last exercise in using the existing Generators, try the following out in `Main.java`

:

```
Generator g1 = new SineWaveGenerator(200);
Generator g2 = new SineWaveGenerator(201);
ArrayList<Generator> generators = new ArrayList<Generator>();
generators.add(g1);
generators.add(g2);
MultiGenerator mg = new MultiGenerator(generators);
GeneratorAudioVisualizer gav = new GeneratorAudioVisualizer(mg);
gav.drawAndPlay(500000, 1000000);
```

You should hear a neat sound -- if you have better speakers, I recommend trying out 60 and 61 hz tones instead. This phenomenon of pulsing volume is known as a beat.

For another example of using the `MultiGenerator`

, try out `PlayMajorChord`

, which will play a C major chord in sine waves.

## Task 1: Generating a SawTooth

Your first major goal is to generate a `SawToothGenerator`

class. Given the method below:

```
Generator generator = new SawToothGenerator(512);
GeneratorAudioVisualizer gav = new GeneratorAudioVisualizer(generator);
gav.drawAndPlay(4096, 1000000);
```

It should draw the waveform below:

Specifically, this waveform should start at -1.0 and linearly increase towards 1.0, before resetting back to -1.0. The argument to SawToothGenerator describes the period of the waveform, i.e. the number of samples before it resets back down to -1.0.

For this task, you should create `SawToothGenerator`

so that it behave as above.

Hints:

- This should be relatively straightforward. Don't overthink it.
- You should use the % operator, with the period as the argument to the right of the %.
- Your
`SawToothGenerator`

should have two instance variables of type int: period and state. - The argument for the
`SawToothGenerator`

should be an integer, not a double. - The argument for the
`SawToothGenerator`

constructor is the period, not the frequency. - There should not be any usage of
`Math.PI`

or`Math.sin`

in your code. - The state of your generator should still be an integer that increments by 1 each time.
- Create a state variable that varies between 0 and period - 1, and write a helper function called
`normalize`

that converts values between 0 and period - 1 to values between -1.0 and 1.0.

For extra fun, use the MultiGenerator to play multiple sawtooth or sine waves in combination with each other.

## Task 2: Generating an AcceleratingSawTooth

Next, we'll generate an `AcceleratingSawToothGenerator`

. Given the method below:

```
Generator generator = new AcceleratingSawToothGenerator(200, 1.1);
GeneratorAudioVisualizer gav = new GeneratorAudioVisualizer(generator);
gav.drawAndPlay(4096, 1000000);
```

This code should draw the waveform below:

Specifically, this waveform should start at -1.0 and linearly increase towards 1.0, before resetting back to -1.0. The first argument to SawToothGenerator describes the period of the waveform, i.e. the number of samples before it resets back down to -1.0. After resetting, the period should change by a factor of the second argument, rounded down. So, in the example above, the period of the second sawtooth should be 220 samples, the 3rd should be 242 samples, the 4th should 266 (which is 266.2 with the 0.2 truncated off).

Experiment with different period factors to see how the sound changes. Anything outside the range 0.9 to 1.1 isn't going to sound particularly interesting since the period will change too quickly.

## Task 3: Generating a Fractal Sound

One feature of Java that we haven't discussed in 61B this semester are bitwise operations. These include `&`

, `|`

, `>>`

, `>>>`

, and `<<`

. These operations take two integers and perform operations on those integers in a bitwise manner.

#### The & Operation

As an example of a bitwise operation, consider the following expression:

` int x = 231 & 62;`

After this expression executes, the integer x will be 38. The reason is that the `&`

operation generates a new integer where the ith bit is 1 if the ith bit of 231 is 1 AND the ith bit of 62 is 1, and 0 otherwise. Or written out:

```
231: 11100111
62: 00111110
x: 00100110
```

Note that x has a 1 only in positions where 231 and 62 have a 1. If we convert `00100110`

from binary into decimal, we get 38, since 32 + 4 + 2 = 38.

#### The >> Operation

As another example, consider the expression:

` int x = 231 >>> 3;`

After this expression executes, the integer x will be 28. The reason is that the `>>>`

operation moves all bits in the number 3 bits to the right, filling in any top digits with zeros. Or written out:

```
231: 11100111
231 >>> 1: 01110011
231 >>> 2: 00111001
231 >>> 3: 00011100
```

If we convert `00011100`

from binary into decimal, we get 16 + 8 + 4 = 28.

#### Generating a Fractal Sound Using Bitwise Operations

Make a copy of your `SawToothGenerator.java`

called `StrangeBitwiseGenerator.java`

. This time, create a temporary variable that is the modulus of the state `&`

a copy of the the state right shifted by 3 places.

For example (your instance variables may be differently named, and the order of your lines may be different):

```
state = state + 1;
int weirdState = state & (state >>> 3) % period;
```

Important: Make sure you aren't assigning the result of your bitwise operations back to state! Try playing/drawing weirdState (but **normalized** so that it fits **in the range -1.0 to 1.0**, as you did in `SawToothGenerator`

), and you should see something like the following:

Now try bitwise-ANDing the current time with a copy of the time right shifted by 3 places AND a copy of the time right shifted by 8 places.

` weirdState = state & (state >> 3) & (state >> 8) % period;`

Try playing a normalized version of `weirdState`

. You should hear something pretty amazing. Try experimenting by adding more shifted versions of the time, other bitwise operations, or even multigenerators. Feel free to post your favorite Generators on Piazza.

Note that the period of this new audio signal is no longer given by the period variable. Instead, the period is somehow much longer. We will not explore the features of these strange fractal sounds, but you're welcome to explore on your own if you'd like.

## Submission

Submit `SawToothGenerator.java`

, `AcceleratingSawToothGenerator.java`

, and `StrangeBitwiseGenerator.java`

to Gradescope. The specifics of your `StrangeBitwiseGenerator`

are not important (its output will not be tested), since its behavior is ill-defined.

## P.S.

P.S. For an Illuminati time, try running:

```
Generator generator = new StrangeBitwiseGenerator(1024);
GeneratorAudioVisualizer gav = new GeneratorAudioVisualizer(generator);
gav.drawAndPlay(128000, 1000000);
```

with

` weirdState = state & (state >> 7) % period;`