Skip to content

Latest commit

 

History

History
176 lines (120 loc) · 6.82 KB

File metadata and controls

176 lines (120 loc) · 6.82 KB

Hands-Free Sound Jam: the minimum viable product

This document will serve to describe all the whisper functions needed to run the MSHFSJ minimum viable product (MVP), and what order to call them in. Rather than provide an API reference page, the format of this document will be more of an introductory tutorial on how the Whisper library works in general, and things to think about when using it from C# via the compiled DLL.

Initialization

The initial minimum viable product has a UI layer written in C using OpenGL via glfw, a cross platform handler for OpenGL calls, and NanoVG, a small library on top of OpenGL that can draw antialiased vector graphics. The whisper engine provides the audio engine, and has callbacks which hook into button events generated by the UI later. The OpenGl UI layer is completely decoupled from the audio layer. (This is by design, as the intention is to migrate the UI components to a managed code layer like C#/XAML).

This document will only focus on the whisper audio engine. Discussions related to UI elements will be done in a platform-neutral conceptual way (buttons, etc).

A number of things need to happen when the app starts, and it is important that they happen in the correct order.

Before any sound can start, the whisper library needs to be explicitely set to eyejam mode with the function:

whisper_set_eyejam();

After this function is called, the audio engine can start up. This function also allocates memory, so it is important to call this function before any other functions below, otherwise you'll get a corrupted memory error.

whisper_eyejam_start_audio();

The whisper library works an audio engine for other applications, such as the Battle Of Midway demo. This library sets up the proper internal callbacks for initialization, runtime, and shutdown functions. Each application has a set of top-level callbacks which are implemented here. This approach may not scale well if several applications are planned on being made, but having a monolithic DLL file makes life a little bit more convenient.

In order for the MSHFSJ demo to work, it needs to be able to play clips! Clips are short musical phrases (rhythmic, melodic, percussive) that can be launched inside a track. Only one clip can be launched at a time per track.

By default, eyejam provides a blank canvas with empty clips. Demo clips need to be explicitely populated with the command:

whisper_eyejam_demo_clips();

This will load clips that are stored internally inside the whisper library.

When a clip has finished playing, it will turn itself off by default. To repeat a melody, one must cue the clip up while it is currently playing. For things like drum loops, this would get very annoying! Clips inside a track can be set to be looped. In the MSHFSJ MVP, the drums, bass, and chord tracks all are set to be loopable. The tracks for these are 1, 2, and 3, respectively:

whisper_tracks_loopmode(1, 1);
whisper_tracks_loopmode(2, 1);
whisper_tracks_loopmode(3, 1);

The first argument of this function is the track number, while the second argument is the state. A value of 1 is loopmode, while a value of 0 is the default single-shot mode.

The melody channel, residing on track 0, does not use loop mode. This can be optionally set with:

whisper_tracks_loopmode(0, 0);

Since this is the default behavior of the track, this function does not actually need to be called, unless you wish to change the looping behavior during runtime.

The overall mix of the track can be adjusted by changing the gain (volume) of each track. The units here are in dB, where a value of 0 is set to be full volume. The following is the mix configuration for the eyejam demo:

whisper_tracks_gain(0, -8.f);
whisper_tracks_gain(1, -4.f);
whisper_tracks_gain(2, -1.f);
whisper_tracks_gain(3, 2.f);

These functions are safe to call during runtime.

The overall gain of the project can also be set. For now, it is set to be the full (normal) volume by default:

whisper_eyejam_gain(0.f);

Note that these functions do not touch overall system volume. Only volumes relative to the app.

And now you are up and running!

Shutdown

When the program quits, Eyejam must be cleanly shutdown. This is done with the following function:

whisper_eyejam_destroy();

Clip Launching

Eyejam, on startup, will be silent. A clip must be launched in order to make sound. In order to launch a clip, it must be scheduled for launch. A scheduled clip will wait for the next downbeat for it to play.

In order to schedule a particular clip, you must know which track to run, as well as which clip number it is on that track. For instance, to schedule the first clip (0) on the first track, run:

whisper_schedule(0, 0);

The order of arguments is track, clip.

Tracks and Instruments

Each track in Eyejam is bound to a particular instrument. In the MVP, they are as follows:

  • track 0 is a melody track using an instance of the Trinity synthesizer
  • track 1 is a drum track using an instance of the DrumKit synthesizer
  • track 2 is a bass track using an instance of the Trinity synthesizer
  • track 3 is a harmony track using an instance of the Surgeon synthesizer

Since bass and harmony are so coupled together, the demo has merged these two tracks together. This simply means that clips on each of those tracks are launched at the same time. For instance, to launch clip 2 on both tracks:

/* launch bass track clip #2 */
whisper_schedule(2, 2);
/* launch harmony track clip #2 */
whisper_schedule(3, 2);

Button Behavior

Buttons in the MSHFSJ OpenGL MVP are color coded based on state. These states are obtained based on if the clip is currently playing, or about to be played.

To get the current clip being played on a track:

    int clip = whisper_tracks_current_clip(track);

To get the next clip that will be played on a track:

    int next = whisper_tracks_next_clip(track);

If no clip is playing, the function will return negative values. Otherwise, it will return a value between 0-4 (zero indexed 5 clips).

This function gets called inside the draw loop for every frame.

Eyejam takes advantage of the OpenGL draw loop to incorporate animations that pulse to the beat of the internal clock. Buttons that are about to be launched have their dimensions scaled using the following function:

double scale = whisper_eyejam_pulse();

This value returns a snapshot of an audio-rate signal. This signal has a rise and fall pattern synchronized to the pulse of a clock. It is a value between 0 and 1 which can be scaled to map to visual elements.

Metronome

Optionally, a metronome counting measure and beats can be turned on or off with the following function. This can be useful for the testing phase.

whisper_eyejam_metro(int state);

When state is 0, the metronome is off. When state is 1, the metronome is on.