3. Conjunction Function


Download Project | Submit to TigerFile

Goals

  • To provide experience defining and using a collection of (a conjunction of) functions.
  • To deepen your understanding of passing arrays as arguments and returning values.
  • To gain experience producing and manipulating audio.

Getting started

  • This is an individual assignment. Before you begin coding, do the following:

  • Download the project zip file for this assignment from TigerFile , which contains the files you will need for this assignment.

Background

  • Read Sections 2.1-2.2 of the textbook.
  • Make sure you understand the precept exercises. You may also find it instructive to work through some of the other exercises and look at the solutions on the booksite.
  • You should also familiarize yourself with the StdAudio API.

Approach / Design

Create a library to manipulate digital audio and use that library to create an audio collage. As in lecture, we will represent sound as an array of real numbers between –1 and +1, with 44,100 samples per second. You will write a library of functions to produce audio effects by manipulating such arrays.

Write an audio processing library AudioCollage.java by implementing 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 is the reverse of a[].
    public static double[] reverse(double[] a)

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

    // Returns a new array that is the sum of a[] and b[],
    // padding the shorter array with trailing 0s if necessary.
    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 below for the requirements.
    public static void main(String[] args)

}

Assignment Requirements

  • Implement a single class:
    • AudioCollage.java
  • Use only Java features that have already been introduced in the course (e.g., loops, arrays, and functions, but not objects).
  • The functions must not mutate the argument array(s).
  • You may assume that the array arguments to all functions are not null.
  • All samples sent to standard audio must be between –1 and +1. Note that it is possible that the input samples to each method may not be between –1 and +1. The functions should operate as specified. While you should not play samples whose absolute value is greater than 1, it’s fine to manipulate such values along the way. However, to ensure that your final sample compiles with this requirement, you may want to implement one of the private helper methods: clamp() or normalize(), and then process your final sample before sending it to standard audio. (See the section titled Implementing Other Audio Effects for details.)
  • You must not add public methods to the API; however, you may add private methods (which are accessible only in the class in which they are declared).
  • Submit a readme.txt.
  • Complete the acknowledgments.txt file.
  • Read the COS 126 Style Guide to ensure your code follows our conventions. Style is an important component of writing code, and not following guidelines will result in deductions.

amplify()

Creates a new sound that contains the same samples as an existing sound, but with each sample multiplied by a constant \( \alpha \). This increases the volume when \( \alpha > 1 \) and decreases it when \( 0 < \alpha < 1\).

Note: amplify() may produce samples whose absolute value is larger than 1 even if all of the input samples are between –1 and +1. While you should not play samples whose absolute value is greater than 1, it’s fine to produce them as intermediate results.

reverse()

Creates a new sound that contains the same samples as an existing sound, but in reverse order. This can lead to unexpected and entertaining results.

merge()

Creates a new sound that combines two existing sounds by appending the second one after the first. If the two sounds have \(m\) and \(n\) samples, then the resulting sound has \(m + n\) samples. This enables you to play two sounds sequentially.

mix()

Creates a new sound that combines two existing sounds by summing the values of the corresponding samples. This enables you to play two sounds simultaneously. If, when summing, one sound is longer than the other, treat the shorter array as if 0s were appended to the end. For example:

Note: mix() may produce samples whose absolute value is larger than 1 even if all of the input samples are between –1 and +1. While you should not play samples whose absolute value is greater than 1, it is fine to produce them as intermediate results.

changeSpeed()

Creates a new sound that changes the duration of an existing sound via resampling. If the existing sound has \(n\) samples, then the new sound has \(⌊n / \alpha ⌋\) samples for some constant \( \alpha > 0 \), with sample \(i\) of the new sound having the same amplitude as sample \(⌊i \alpha ⌋\) of the existing sound. Assume that \(\alpha > 0\).

The floor function \(⌊x⌋\) returns the largest integer less than or equal to \(x\). You can compute it in Java by either casting to an integer (resulting in an int) or calling Math.floor(x) (resulting in a double).

Note: the changeSpeed() function changes not only the speed of the sound, but also the pitch. Speeding up the sound using resampling raises the pitch and leads to the Chipmunk effect. More sophisticated time stretching techniques are preferred in practice (such as when watching the course videos at 1.5× or 2× speed) because they change the speed but not the pitch.

main()

  • The main() method must create an audio collage and play it using StdAudio.play(). Use the methods you wrote to combine various audio samples into a single array, then play it using StdAudio.

  • The duration must be between ten (10) and sixty (60) seconds (441,000 to 2,646,000 samples).

  • All samples must be valid; i.e., all samples must be between -1.0 and 1.0.

  • Do not use any command-line arguments.

  • The audio collage must use at least five different .wav files. Several .wav files are provided. You may optionally supply or create your own wav files.

  • Use StdAudio.read() to read a .wav file and extract its samples as an array of floating-point numbers between –1.0 and +1.0. For example:

    double[] samples1 = StdAudio.read("cow.wav");
    
  • The audio collage must use all of the audio effects specified in the API (amplify, reverse, merge, mix, and change speed). Feel free to add additional audio effects (see below for some ideas).

  • To convert an audio file into an appropriate format for use with StdAudio:

    • Convert it to a .wav file.
    • Be sure to use 16-bit audio, monaural, and a sampling rate of 44,100 Hz.
    • You may use an online conversion utility, such as Online-Convert.
  • Be creative! We plan to post your wildly creative productions!

Implementing Other Audio Effects

You may experiment with implementing other functions that produce various audio effects. However, these functions must be private. For example:

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

Some ideas include:

  • clamp: replace all samples greater than +1.0 with 1.0; and all samples less than -1.0 with -1.0.
  • normalize: rescale a sound so that all values are between –1.0 and +1.0.
  • cut: extract a contiguous subarray from a given sound.
  • trim: remove leading / trailing sequence of samples that are 0.0 (or nearly 0.0).
  • loop: repeat a given sound a specified number of times.
  • mirror: concatenate a sound with its reverse.
  • hip–hop: increase speed of a sound; mirror it; then loop it.
  • echo, delay, reverb: add a time-delayed version of a sound to itself, attenuated by a given factor.
  • fade-in, fade-out: gradually increase/decrease the volume at the beginning/end of a sound.
  • crossfade: fade-out first sound; fade-in second sound; overlap.
  • tremolo: create a trembling effect by modulating the amplitude up and down.
  • You could also synthesize your own sounds by creating a sine wave, square wave, triangle wave, or sawtooth wave of a given amplitude, frequency, and duration.

The keywords public vs private

You have probably noticed that we have been starting every static method with the keyword public. This keyword identifies the method as available for use by any other program with access to the file. There is an alternative keyword we can use to identify each method: private. A private method cannot be used by another program, it can only be used by other methods in the same file. This is useful when we are implementing an API that requires some internal methods that we want to hide from API users. We will learn more about private methods and also other types of methods later in the class.

Edit the readme.txt

Edit the text file named readme.txt that is a narrative description of your work.

Submission

  • Submit to TigerFile : AudioCollage.java, completed readme.txt and acknowledgments.txt files.
  • Do not submit the .wav files we provided.
  • You may also submit supplementary .wav files if you used any — put them in the same directory as your .java files. Please cite the sources for supplementary .wav files in your readme.txt file.

Note that, as part of this assignment, we may anonymously publish your collage. If you object, please indicate so in your readme.txt when asked. We also reserve the right to remove any collage, at any time, for whatever reason. By submitting your assignment, you implicitly agree with this policy.

Enrichment