AudioGenerator

class in Clatter.Core

An AudioGenerator can generate audio within a dynamic physics simulation.

AudioGenerator is not required for audio generation but is usually your best option, for two reasons. First, AudioGenerator automatically converts CollisionEvent data into audio data, meaning that it's suitable for physics simulation. Second, AudioGenerator is multi-threaded, meaning that concurrent audio generation is very fast.

AudioGenerator is structured like a UnityEngine MonoBehaviour object but it isn't a subclass of MonoBehaviour and won't update like one; Update() needs to be called manually.

Regarding randomness: AudioGenerator has an rng parameter that is a System.Random object. In Clatter, audio is generated from both fixed values and random values; see the constructor for Modes and Modes.AdjustPowers(). In most cases, you'll want the audio to be truly random. If you want to replicate the exact same audio every time you run your program, set a random seed: Random rng = new Random(0).

Static Fields

Name Type Description Default Value
maxNumAudioEvents int The maximum number of impacts, scrapes, and rolls that can occur on one frame. After this many events, any new audio events are ignored. 200

Fields

Name Type Description Default Value
onImpact AudioGenerationAction Invoked when impact audio is generated.
onScrapeStart AudioGenerationAction Invoked when audio is generated by a new scrape event.
onScrapeOngoing AudioGenerationAction Invoked when audio is generated by an ongoing scrape event.
onScrapeEnd Action< int > Invoked when a scrape ends.

Delegates

AudioGenerationAction

public delegate void AudioGenerationAction(CollisionEvent collisionEvent, Samples samples, Vector3d position, int audioSourceId)

Delegate for actions that are invoked during audio generation.

Name Type Description
collisionEvent CollisionEvent The collision event that generated the audio.
samples Samples The audio samples.
position Vector3d The position of the audio source.
audioSourceId int The audio source ID.

Methods

AudioGenerator

public AudioGenerator(IEnumerable< ClatterObjectData > clatterObjects, int? seed=null)

Name Type Description
clatterObjects ClatterObjectData The objects that will generate audio.
seed int? The random seed. If null, the seed is random.

Update

public void Update()

Call this once per frame to process collision events and generate audio.

AddCollision

public void AddCollision(CollisionEvent collisionEvent)

Register a new collision audio event.

Name Type Description
collisionEvent CollisionEvent The collision event.

End

public void End()

Call this to announce to kill lingering threads.

Code Examples

This is a minimal example of how to process multiple concurrent collisions with an AudioGenerator and, using WavWriter, write .wav files. Note that we're making a few implausible assumptions:

  • All of the objects are randomly generated. In a real simulation, you'll probably want more control over the objects' audio values.

  • All of the collisions have a centroid of (0, 0, 0). In a real simulation, the collisions should probably be spatialized.

  • All of the collision events are impacts. In a real simulation, we could add a ScrapeMaterial to a "floor" object to start generating scrape audio.

using System;
using System.IO;
using Clatter.Core;

public static class AudioGeneratorImpact
{
    private static AudioGenerator generator;
    private static Queue audioData = new Queue();
    
    public static void Main(string[] args)
    {
        Random rng = new Random();
        // Add some objects.
        ClatterObjectData[] objects = new ClatterObjectData[64];
        uint[] objectIds = new uint[64];
        for (uint i = 0; i < objects.Length; i++)
        {
            // Generate a random object.
            objects[i] = new ClatterObjectData(i, ImpactMaterial.glass_1, rng.NextDouble(), rng.NextDouble(), rng.NextDouble() * 5);
            objectIds[i] = i;
        }
        // Create the audio generator.
        generator = new AudioGenerator(objects);
        // Listen for impact events.
        generator.onImpact += OnImpact;
        // Get the output directory.
        string outputDirectory = Path.GetFullPath("output");
        // Iterate for 15 frames.
        for (int i = 0; i < 15; i++)
        {
            // Get a random number of collisions.
            int numCollisions = rng.Next(15, 30);
            // Randomize the object IDs.
            objectIds = objectIds.OrderBy(x => rng.NextDouble()).ToArray();
            int objectIndex = 0;
            // Generate collisions.
            for (int j = 0; j < numCollisions; j++)
            {
                // Get random primary and secondary objects.
                ClatterObjectData primary = objects[objectIds[objectIndex]];
                ClatterObjectData secondary = objects[objectIds[objectIndex + 1]];
                // Generate a collision.
                CollisionEvent collisionEvent = new CollisionEvent(primary, secondary, AudioEventType.impact, rng.NextDouble() * 1.75, Vector3d.Zero);
                // Add the collision.
                generator.AddCollision(collisionEvent);
                // Increment the object index for the next collision.
                objectIndex += 2;
                if (objectIndex >= objects.Length)
                {
                    objectIndex = 0;
                }
            }
            // Update.
            generator.Update();
            // Write the audio wav data.
            int audioIndex = 0;
            while (audioData.Count > 0)
            {
                WavWriter writer = new WavWriter(Path.Combine(outputDirectory, audioIndex + ".wav"));
                writer.Write(audioData.Dequeue());
                writer.End();
                audioIndex++;
            }
        }
    }


    private static void OnImpact(CollisionEvent collisionEvent, Samples samples, Vector3d centroid, int audioSourceId)
    {
        audioData.Enqueue(samples.ToInt16Bytes());
    }
}