Channels ▼
RSS

JVM Languages

Music Components in Java: Creating Oscillators


In the previous article of this series, I discussed terminology and some basic math that must be understood before moving forward. In this article we are going to apply what we learned by building electronic music components entirely in software.

Before we do, however, we have one more important topic that needs discussion. You may recall me mentioning the MiniMoog and PSynth synthesizers in the first article. What these devices have in common is they both utilize subtractive synthesis as their means of sound production. With subtractive synthesis, you start out with a harmonically rich signal source (typically a square or sawtooth wave) and you apply a low-pass filter to selectively remove higher frequency harmonics with the result being a musically useful effect. The harmonic complexity of the signal source and the cut-off frequency and resonance of the filter are controlled in order to simulate the timbre of instruments. Of course, both the MiniMoog and PSynth also provide a sine wave signal source which, while not harmonically rich, can be used for musical purposes.

The Basic Waveforms

Joseph Fourier (1768–1830) proved that complex, harmonically rich, periodic waveforms comprise a series of sine and cos (a cos wave is a sine wave with a phase shift of 90 degrees) terms of increasing frequency. This Wikipedia page has an instructive animation of how, by adding harmonic terms to a base sine wave, a sawtooth wave shape can be produced.

For our use here, we will create an oscillator signal source that can produce three waveforms / wave shapes: sine wave, square wave and sawtooth wave. Each of these wave shapes has characteristics that make it suitable for use in electronic music.

A sine wave is a very pure sound source that has little in the way of harmonic complexity. A sine wave has its fundamental frequency and depending upon harmonic distortion has few (if any), low amplitude harmonics. The sine wave our oscillator will produce looks exactly like the textbook examples of sine waves.

A square wave , unlike a sine wave, contains odd harmonics (which are integer multiples of the fundamental frequency) which gives it more harmonic complexity and the richness required for subtractive synthesis. Square waves are often described as hollow sounding and are useful in electronic music.

As the name implies, sawtooth wave shapes resemble a saw blade. They rises linearly to maximum value then drops straight down to minimum value only to start again. A sawtooth wave shape contains both even and odd integer harmonics of the fundamental frequency and is good for simulating bowed string instruments like violins and cellos.

As we generate complex wave shapes in software, the Nyquist limit discussed in the previous article must be kept in mind because both square and sawtooth wave shapes have higher order harmonics that can exceed the available bandwidth. As you may recall, if we try to reproduce a frequency above the Nyquist limit (greater than or equal to one half the sampling rate), false low frequency signal components to be added into the original signal. This results in distortions that can be detrimental to signal fidelity.

There are two approaches for dealing with this problem. The first, is to generate our complex wave shapes from a Fourier series which by design doesn't have any terms above the Nyquist limit and the second approach is to ignore the problem completely. In our application it is safe to ignore the problem because:

  • Any distortion caused by aliasing only adds to the complexity of our signal sources which can be considered a good thing.
  • The frequencies we typically generate for musical purposes are relatively low which means that many of the harmonics making up the square and sawtooth wave shapes have a low enough amplitude to not be disruptive.

A Basic Oscillator in Java

A basic oscillator is just that; an oscillator without frills and implemented by the BasicOscillator class (Listing One). A BasicOscillator has two fundamental features: the ability to control its frequency and to control the wave shape it produces. The BasicOscillator provide samples, as it is the source of samples for other electronic music components. Being a sample provider means that whenever its getSamples method is called, it must fill the sample buffer with samples of the required type and frequency.

Listing One: A basic oscillator written in Java.

public class BasicOscillator implements SampleProviderIntfc {
    
  /**
   * Waveshape enumeration
   */
  public enum WAVESHAPE {
      SIN, SQU, SAW
  }
  
  /**
   * Basic Oscillator Class Constructor
   * 
   * Default instance has SIN waveshape at 1000 Hz
   */
  public BasicOscillator() {

    // Set defaults
    setOscWaveshape(WAVESHAPE.SIN);
    setFrequency(1000.0);    
  }

  /**
   * Set waveshape of oscillator
   *
   * @param waveshape Determines the waveshape of this oscillator
   */
  public void setOscWaveshape(WAVESHAPE waveshape) {
    
    this.waveshape = waveshape;
  }

  /**
   * Set the frequency of the oscillator in Hz.
   *
   * @param frequency Frequency in Hz for this oscillator
   */
  public void setFrequency(double frequency) {
    
    periodSamples = (long)(SamplePlayer.SAMPLE_RATE / frequency);
  }

  /**
   * Return the next sample of the oscillator's waveform
   *
   * @return Next oscillator sample
   */
  protected double getSample() {
    
    double value;  
    double x = sampleNumber / (double) periodSamples;
    
    switch (waveshape) {

      default:
      case SIN:
        value = Math.sin(2.0 * Math.PI * x);
        break;

      case SQU:
        if (sampleNumber < (periodSamples / 2)) {
          value = 1.0;
        }  else  {
          value = -1.0;
        }
        break;
        
      case SAW:
        value = 2.0 * (x - Math.floor(x + 0.5));
        break;
    }
    sampleNumber = (sampleNumber + 1) % periodSamples;
    return value;
  }
    
  /**
   * Get a buffer of oscillator samples
   *
   * @param buffer Array to fill with samples
   *
   * @return Count of bytes produced.
   */
  public int getSamples(byte [] buffer) {
    int index = 0;
    for (int i = 0; i < SamplePlayer.SAMPLES_PER_BUFFER; i++) {
      double ds = getSample() * Short.MAX_VALUE;
      short ss = (short) Math.round(ds);
      buffer[index++] = (byte)(ss >> 8);
      buffer[index++] = (byte)(ss & 0xFF);      
    }
    return SamplePlayer.BUFFER_SIZE;
  }
  
  // Instance data
  private WAVESHAPE waveshape;
  private long periodSamples;
  private long sampleNumber;
}


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 

Video