• Home
  • About
  • Site Policies
  • Contact

Designing Sound

Art and technique of sound design

Current Monthly Theme

Tropes and Clichés

  • All Posts
  • Featured
  • News
  • Interviews
  • Reviews
  • Tutorials
  • Resources
    • VR Audio Resources
    • Independent SFX Libraries
    • Events Calendar
  • Series Archives
    • Featured Topics
    • Featured Sound Designers
    • Audio Implementation Greats
    • Exclusive Interviews
    • Behind the Art
    • Webinar/Discussion Group Recordings
    • Sunday Sound Thought
    • The Sound Design Challenge

Making a Music System – Part 4

September 21, 2016 by Charlie Huguenard Leave a Comment

footsteps
By Augustus Binu/ www.dreamsparrow.net/ facebook (Own work) [CC BY-SA 3.0], via Wikimedia Commons
Now that we can tick at a constant interval, we can add rhythm and pitch into the mix. To make interesting melodies, harmonies, and rhythmic parts, let’s make a step sequencer. This lesson will go into the details of creating a reusable step sequencer that we can use to control our samplers.

Lesson 4 – Step Sequencer

Counting Steps

A step sequencer consists of a list of steps that tell a sampler to play a note for some length of time at the specified pitch. It listens to a Metronome for ticks, checks for active steps, and reports to a Sampler (or multiple samplers) with the time and pitch information.

The Step class is a simple container that looks like this:

[Serializable]
public class Step
{
  public bool Active;
  public int MidiNoteNumber;
  public double Duration;
}

The Active field tells us whether that step is active–whether it should be ignored or trigger a sound. The MidiNoteNumber field specifies what note should be played, and is later converted into a pitch value. The Duration field is how long, in seconds, the note should play. This will become the sustain time of the volume envelope. Also, the Serializable attribute tells Unity that fields of this type in other classes can be serialized with the GameObject.

step illustration
Illustration of a sequence of steps.

The StepSequencer class has a familiar event called Ticked. You might remember this from the Metronome class. It performs a similar function, passing listeners the tick time, but also the note number and duration from the appropriate Step. The StepSequencer class listens to the Ticked event from a Metronome, just like the Sampler did in the previous lesson. But instead of just listening for ticks and passing them on to a Sampler, the StepSequencer cycles through its Steps, checks to see if the current step is active, and if so, tells the Sampler to play.

public void HandleTicked(double tickTime)
{
  int numSteps = _steps.Count;

  if (numSteps == 0)
  {
    return;
  }

  Step step = _steps[_currentTick];

  if (step.Active)
  {
    if (Ticked != null)
    {
      Ticked(tickTime, step.MidiNoteNumber, step.Duration);
    }
  }

  _currentTick = (_currentTick + 1) % numSteps;
}

(view full code)

Sampler Modifications

Since the Steps in the StepSequencer will be defining the duration of the note, we can get rid of the sustain time field in the Sampler. And instead of listening for the Ticked event from a Metronome, we’ll listen for the Ticked event from a StepSequencer. We’ll also need to modify the HandleTicked function to add parameters for the note number and duration and pass them on to the SamplerVoice:

private void HandleTicked(double tickTime, int midiNoteNumber, double duration)
{
  float pitch = MusicMathUtils_Lesson4.MidiNoteToPitch(midiNoteNumber, MusicMathUtils_Lesson4.MidiNoteC4);
  _samplerVoices[_nextVoiceIndex].Play(_audioClip, pitch, tickTime, _attackTime, duration, _releaseTime);

  _nextVoiceIndex = (_nextVoiceIndex + 1) % _samplerVoices.Length;
}

(view full code)

Music Math

You might have noticed the reference to MusicMathUtils. For convenience and reuse, we’ll add a helper class to do some basic conversions related to music. The first–and the only one we’ll need for this series–is MidiNoteToPitch. This converts a MIDI note number to a percentage pitch value used by the AudioSource component:

public static class MusicMathUtils_Lesson4
{
  public const int MidiNoteC4 = 60;

  public static float MidiNoteToPitch(int midiNote, int baseNote)
  {
    int semitoneOffset = midiNote - baseNote;
    return Mathf.Pow(2.0f, semitoneOffset / 12.0f);
  }
}

(view full code)

The pitch field of an AudioSource is the playback speed. So a value of 1.0 plays back at normal speed, 2.0 plays back twice as fast (or one octave up in pitch), 0.5 plays half as fast (or one octave down), and a negative value will play the sound backward. MidiNoteToPitch takes the MIDI note we want to play and the “base” note that should correspond to no pitch change (1.0 pitch), and gives us the appropriate pitch. So for example, if our base note is 60 (middle C), and we want the pitch for note 72 (one octave above middle C), MidiNoteToPitch will return 2.0.

For more info on musical note scaling, here’s a great reference.

SamplerVoice Modifications

Finally, we need to update the SamplerVoice class to change its pitch. The only modification needed is to add a pitch parameter to the Play function, and set the pitch of the AudioSource before playing:

public void Play(AudioClip audioClip, float pitch, double startTime, double attackTime, double sustainTime, double releaseTime)
{
  sustainTime = (sustainTime > attackTime) ? (sustainTime - attackTime) : 0.0;
  _envelope.Reset(attackTime, sustainTime, releaseTime, AudioSettings.outputSampleRate);

  double timeUntilTrigger = (startTime > AudioSettings.dspTime) ? (startTime - AudioSettings.dspTime) : 0.0;
  _samplesUntilEnvelopeTrigger = (uint)(timeUntilTrigger * AudioSettings.outputSampleRate);

  _audioSource.clip = audioClip;
  _audioSource.pitch = pitch;
  _audioSource.PlayScheduled(startTime);
}

(view full code)

Try it Out

Similar to last lesson’s setup, we’ll wire up our sequencer and make it make some noise. In addition to the setup you already have, add the StepSequencer component to an empty GameObject. Then assign the Metronome to the metronome field on the StepSequencer, and assign the StepSequencer to the sequencer field on the Sampler. Add some steps in the list on the StepSequencer, press play, and you should hear some sound. Play around with different rhythms, pitches, and durations to create different musical lines. Make a bunch of sequencers and samplers to make a more full piece of music. Enjoy!

Filed Under: featured, tutorials Tagged With: adaptive music, Audio Programming, dynamic music, game audio, game music, interactive audio, interactive music, procedural audio, procedural sound, sequencing, tutorial, tutorials

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Upcoming Themes


May - The Long Run
June -Mentorship
July - Fluidity
August -Character
September -LoFi

Follow the Site

  • Facebook
  • Google+
  • RSS
  • Twitter
  • YouTube
Mailing List

RSS New Questions On Designing Sound Exchange

Posts By Month

Copyright Info

All content on Designing Sound is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.

Copyright © 2018 · Magazine Pro Theme on Genesis Framework · WordPress · Log in