Channels ▼
RSS

JVM Languages

Music Components in Java: Creating Oscillators


Our EG responds to two events: noteOn and noteOff, which occur for example when a keyboard key is depressed and subsequently released. As soon as a noteOn event occurs, the EG's output starts rising from 0.0 to 1.0 at the rate controlled by the attack time setting. As soon as the attack time has expired, the decay time starts and the EG's output falls from 1.0 to the sustain level in the time specified by the decay time setting. The output stays at the sustain level (set by the sustain level setting) until a noteOff event occurs. At that time the EG's output falls from the sustain level to 0.0 in the time set by the release time setting. All of the EG times can range from 1 millisecond to 5 seconds (5000 milliseconds) in duration.

If you were to couple an oscillator, an envelope generator controlling a variable gain element and a SamplePlayer together what you would hear when a noteOn event occurs is the volume of the oscillator going from silence to full volume in the attack time period, the volume then dropping to a specified sustain level within the decay time period and staying there until a noteOff event causes the volume to drop from the sustain level to silence in the release interval. As an aside, if you were to set the attack time to longer than the decay time and the release time really short, the notes would sound as if they were being played backwards.

Accurate timing is an important aspect of the EG's operation. To provide this timing, the EG is implemented as a state machine that runs at the sample rate. This results in accurate timing because, as mentioned earlier, samples must be delivered in a continuous fashion otherwise audible artifacts will be generated. The EG's state machine is shown in Listing Two. The getValue method is called every sample time and returns a value between 0.0 and 1.0. Both events and counter timeouts cause transitions between states in the state machine.

Listing Two: The EG’s State Machine

  public double getValue() {
    
    double value = 0.0;
    
    switch (state) {
      // Process the idle state
      case STATE_IDLE:
        noteOff = false;
        if (noteOn) {
          noteOn = false;
          count = 0;
          state = SM_STATE.STATE_ATTACK;
        }
        break;
        
      // Process the attack state
      case STATE_ATTACK:
        // Did another noteOn event occur?
        if (noteOn) {
          state = SM_STATE.STATE_IDLE;
          break;
        }
        // Calculate the value to return
        value = count * attackSlope;

        // Has attack time elapsed ?
        if (count >= attackCount) {
          count = 0;
          state = SM_STATE.STATE_DECAY;
        }  else  {
          count++;
        }
        break;
        
      // Process the decay state
      case STATE_DECAY:
        // Did another noteOn event occur?
        if (noteOn) {
          state = SM_STATE.STATE_IDLE;
          break;
        }
        // Calculate the value to return
        value = 1.0 - (count * decaySlope);

        // Has decay time elapsed ?
        if (count >= decayCount) {
          state = SM_STATE.STATE_SUSTAIN;
        }  else  {
          count++;
        }
        break;
        
      // Process the sustain state
      case STATE_SUSTAIN:
        // Did another noteOn event occur?
        if (noteOn) {
          state = SM_STATE.STATE_IDLE;
          break;
        }
        // Get value to return
        value = sustainLevel;

        // Did a noteOff event occur ?
        if (noteOff) {
          noteOff = false;
          count = 0;
          state = SM_STATE.STATE_RELEASE;
        }
        break;
        
      // Process the release state
      case STATE_RELEASE:
        // Did another noteOn event occur?
        if (noteOn) {
          state = SM_STATE.STATE_IDLE;
          break;
        }
        // Calculate the value to return
        value = sustainLevel - (count * releaseSlope);
        if (value < 0) {
          value = 0;
        }

        // Has release time elapsed ?
        if (count >= releaseCount) {
          state = SM_STATE.STATE_IDLE;
        }  else  {
          count++;
        }
        break;
    }
    return value;
  }  


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