3. Conjunction Function


Download project ZIP | Submit to TigerFile

In this assignment, you will implement functions that manipulates digital audio, then use them to create an audio collage. You will learn how to develop reusable functions, pass and return arrays, and manipulate audio.

Getting Started

Part I: Audio Library

Write a program AudioCollage.java to manipulate digital audio. As in lecture, we represent sound as an array of real numbers between –1 and +1, with 44,100 samples per second. You will implement functions that produce audio effects by transforming these arrays.

Implement the following API:

public class AudioCollage {

    // Returns a new array that rescales a[] by a factor of alpha.
    public static double[] amplify(double[] a, double alpha)

    // Returns a new array that clamps each sample to  the range [-1, +1].
    public static double[] clamp(double[] a)

    // Returns a new array that contains the samples of a[] in reverse order.
    public static double[] reverse(double[] a)

    // Returns a new array that is the concatenation of a[] followed by b[].
    public static double[] merge(double[] a, double[] b)

    // Returns a new array that mixes a[] and b[] by adding corresponding
    // samples. If one array is shorter, treat missing samples as 0.
    public static double[] mix(double[] a, double[] b)

    // Returns a new array that changes the speed of a[] by a factor of alpha.
    public static double[] changeSpeed(double[] a, double alpha)

    // Creates an audio collage and plays it on standard audio.
    // See Part II of the assignment specification for requirements.
    public static void main(String[] args)

}

Here is additional documentation for the API:

  • Each function returns a new array and does not modify its argument array(s).

  • You may assume that all array arguments are non-null.

  • Do not add public methods; you may add private helper methods.

  • amplify(double[] a, alpha)

    Returns a new array with the same length as a[], where each sample is multiplied by a constant $\alpha$. If $|\alpha| > 1$, the result is louder; if $|\alpha| < 1$, it is quieter. For example: Amplifying a single sample array

    Note: amplify() may produce samples whose absolute value exceed 1, even if all input samples are between –1 and +1.

  • clamp(double[] a)

    Returns a new array where each sample in a[] is “clamped” to the range [–1, +1]:

    • values greater than +1 become +1
    • values less than –1 become –1
    • values between –1 and +1 are unchanged

    For example: Clamping a single sample array

  • reverse(double[] a)

    Returns a new array with the same samples as a[], but in reverse order. This can lead to unexpected (and sometimes entertaining) results. For example: Reversing a single sample array

  • merge(double[] a, double[] b)

    Returns a new array that contains the samples of a[] followed by the samples of b[]. If the two sounds have lengths $m$ and $n$, the resulting array has length $m + n$. This enables you to play two sounds sequentially. For example: Merging two audio sample arrays

  • mix(double[] a, double[] b)

    Returns a new array that adds the corresponding samples of a[] and b[] (sample-by-sample). If one array is shorter, treat missing samples as 0. This enables you to play two sounds simultaneously. For example: Mixing two audio sample arrays

    Note: mix() may produce samples whose absolute value exceed 1, even if all input samples are between –1 and +1.

  • changeSpeed(double[] a, double alpha)

    Returns a new array that changes the duration of a[] by resampling, using speed factor $\alpha > 0$. The returned array has length $\lfloor n / \alpha \rfloor$, where $n$ is the length of a[]. Sample $i$ of the returned array has the same value as sample $\lfloor i \alpha \rfloor$ of a[]. Larger $\alpha$ plays the sound faster (shorter duration); smaller $\alpha$ plays it slower (longer duration). For example: Changing the speed of a single audio sample array

    Q.Which does the notation $\lfloor x \rfloor$ mean?
    A.
    It is the floor function: the largest integer less than or equal to $x$.
    Q.How can I compute $\lfloor x \rfloor$ in Java?
    A.
    Cast x to an int, which discards the fractional part (rounds toward 0). If x is nonnegative, this equals $\lfloor x \rfloor$.
    Q.The changeSpeed() function changes not only the speed of the sound, but also its pitch. Why?
    A.
    Resampling changes the playback rate. Speeding up the sound raises the pitch, producing the “chipmunk” effect. More sophisticated time-stretching techniques can change speed without changing pitch. That’s the kind of technique used, for example, when you watch a lecture recording at 2× speed while keeping the speaker’s voice at a natural pitch.

Part II: Audio Collage

Now use your library methods to create an audio collage. In main(), build a single array of samples that represents your collage, then play it using StdAudio. Follow these requirements:

  • At least 5 WAV files. Use at least five different WAV files. Several are provided, and you may also supply your own.
  • Use all audio effects. The collage must use every function from the API: amplify, reverse, merge, mix, change speed, and clamp. You may also add additional effects (see ideas below).
  • Duration. The collage must be between 10 and 60 seconds (441,000 to 2,646,000 samples).
  • No command-line arguments. Do not use command-line arguments.
  • Valid samples. All samples passed to standard audio must be between –1 and +1.
  • Be creative. We plan to post your audio collages.
Q.How do I read a WAV file and extract its samples?
A.

Call StdAudio.read(). It returns a double[] containing samples between –1 and +1.

double[] samples1 = StdAudio.read("cow.wav");
Q.How can I convert an audio file to WAV format?
A.
Use an online conversion utility, such as Online-Convert.
Q.What is a WAV file?
A.
A WAV file (Waveform Audio File Format) is a popular format for storing raw, typically uncompressed, audio data. For details, see the Wikipedia page.
Q.Can intermediate samples be outside the prescribed range?
A.
Yes. Intermediate arrays may contain values outside –1 and +1, but the samples you pass to standard audio must be between –1 and +1. One way to ensure this is to call clamp() on final array.

Implementing Other Audio Effects

You may implement additional functions that produce other audio effects. Any such helper must be private. For example:

private static double[] mirror(double[] samples) {
   ...
}

Some ideas include:

  • normalize: rescale a sound so that all samples are between –1 and +1.
  • cut: extract a contiguous subarray from a sound.
  • trim: remove leading and trailing samples that are 0 (or nearly 0).
  • loop: repeat a sound a specified number of times.
  • mirror: concatenate a sound with its reverse.
  • hip–hop: speed up a sound, mirror it, then loop it.
  • echo / delay / reverb: add a time-delayed copy of a sound to itself, attenuated by a scale factor.
  • fade-in / fade-out: gradually increase or decrease the volume at the beginning or end of a sound.
  • crossfade: fade out the first sound while fading in the second, with an overlap.
  • tremolo: create a trembling effect by modulating the amplitude over time.

You may also synthesize your own sounds by generating a sine, square, triangle, or sawtooth wave with a given amplitude, frequency, and duration.

Submission

Upload all files to TigerFile :

  • AudioCollage.java
  • readme.txt
  • acknowledgments.txt
  • Any supplementary WAV files (do not submit the WAV files we provided).

If you submit supplementary WAV files, cite their source in readme.txt.

We may publish selected collages (anonymously) on the course website. Publishing is optional: if you do not wish your work to be published, indicate this in readme.txt.

Grading Breakdown

File Points
AudioCollage.java 36
readme.txt 4
Total 40

Credits