AgentStack
SKILL verified Apache-2.0 Self-run

Audio Systems

skill-medy-gribkov-arcana-audio-systems · by medy-gribkov

Implement spatial audio, adaptive music, and sound effects with Unity, FMOD, and Wwise. Covers 3D positioning, dynamic mixing, and performance optimization.

No reviews yet
0 installs
5 views
0.0% view→install

Install

$ agentstack add skill-medy-gribkov-arcana-audio-systems

✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

Are you the author of Audio Systems? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Audio Systems

Implementation Workflow

Follow this pattern for professional audio integration:

  1. Architecture - Set up audio manager and pooling
  2. Integration - Connect middleware (FMOD/Wwise) or native engine
  3. Spatial - Configure 3D positioning and attenuation
  4. Mixing - Balance levels and apply dynamic ducking
  5. Optimization - Limit voices, compress assets, stream large files

Unity Native Audio Manager

Step 1: Create Pooled Audio System

// Production-ready audio manager with object pooling
using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class AudioManager : MonoBehaviour
{
    public static AudioManager Instance { get; private set; }

    [System.Serializable]
    public class SoundBank
    {
        public string id;
        public AudioClip[] clips;
        [Range(0f, 1f)] public float volume = 1f;
        [Range(0.1f, 3f)] public float pitchVariation = 0.1f;
        public bool spatial = true;
    }

    [SerializeField] private SoundBank[] _soundBanks;
    [SerializeField] private int _poolSize = 20;
    [SerializeField] private AudioMixerGroup _sfxMixer;

    private Dictionary _bankLookup;
    private Queue _sourcePool;

    void Awake()
    {
        if (Instance != null) { Destroy(gameObject); return; }
        Instance = this;
        DontDestroyOnLoad(gameObject);

        InitializePool();
        BuildLookup();
    }

    public void PlaySound(string id, Vector3 position)
    {
        if (!_bankLookup.TryGetValue(id, out var bank))
        {
            Debug.LogWarning($"Sound '{id}' not found");
            return;
        }

        var source = GetPooledSource();
        source.transform.position = position;
        source.clip = bank.clips[Random.Range(0, bank.clips.Length)];
        source.volume = bank.volume;
        source.pitch = 1f + Random.Range(-bank.pitchVariation, bank.pitchVariation);
        source.spatialBlend = bank.spatial ? 1f : 0f;
        source.outputAudioMixerGroup = _sfxMixer;
        source.Play();

        StartCoroutine(ReturnToPool(source, source.clip.length));
    }

    private void InitializePool()
    {
        _sourcePool = new Queue();
        for (int i = 0; i ();
        foreach (var bank in _soundBanks)
        {
            _bankLookup[bank.id] = bank;
        }
    }

    private AudioSource GetPooledSource()
    {
        return _sourcePool.Count > 0 ? _sourcePool.Dequeue() : CreateNewSource();
    }

    private AudioSource CreateNewSource()
    {
        var go = new GameObject("AudioSource");
        go.transform.SetParent(transform);
        return go.AddComponent();
    }

    private IEnumerator ReturnToPool(AudioSource source, float delay)
    {
        yield return new WaitForSeconds(delay + 0.1f);
        source.Stop();
        _sourcePool.Enqueue(source);
    }
}

Step 2: Use in Gameplay

BAD - Creating new AudioSource every time:

void PlayExplosion()
{
    var source = gameObject.AddComponent();
    source.clip = explosionClip;
    source.Play();
    // Memory leak - never cleaned up!
}

GOOD - Using pooled manager:

void PlayExplosion()
{
    AudioManager.Instance.PlaySound("explosion", transform.position);
}

FMOD Integration

Step 1: Event Player Component

using UnityEngine;
using FMODUnity;
using FMOD.Studio;

public class FMODEventPlayer : MonoBehaviour
{
    [SerializeField] private EventReference _eventRef;
    [SerializeField] private bool _playOnStart = false;

    private EventInstance _instance;
    private bool _isPlaying;

    void Start()
    {
        if (_playOnStart) Play();
    }

    public void Play()
    {
        if (_isPlaying) Stop();

        _instance = RuntimeManager.CreateInstance(_eventRef);
        _instance.set3DAttributes(RuntimeUtils.To3DAttributes(transform));
        _instance.start();
        _isPlaying = true;
    }

    public void SetParameter(string name, float value)
    {
        if (_isPlaying)
            _instance.setParameterByName(name, value);
    }

    public void Stop(bool allowFadeout = true)
    {
        if (!_isPlaying) return;

        _instance.stop(allowFadeout
            ? FMOD.Studio.STOP_MODE.ALLOWFADEOUT
            : FMOD.Studio.STOP_MODE.IMMEDIATE);
        _instance.release();
        _isPlaying = false;
    }

    void OnDestroy() => Stop(false);

    void Update()
    {
        if (_isPlaying)
            _instance.set3DAttributes(RuntimeUtils.To3DAttributes(transform));
    }
}

Step 2: Parameter-Driven Music

// Adaptive music controller
public class MusicController : MonoBehaviour
{
    private EventInstance _musicInstance;

    void Start()
    {
        _musicInstance = RuntimeManager.CreateInstance("event:/Music/Gameplay");
        _musicInstance.start();
    }

    public void SetCombatIntensity(float intensity)
    {
        // 0.0 = exploration, 1.0 = intense combat
        _musicInstance.setParameterByName("Intensity", intensity);
    }

    public void TriggerVictory()
    {
        _musicInstance.setParameterByName("State", 2); // Victory state
    }
}

Wwise Integration

Step 1: Post Events

using UnityEngine;

public class WwisePlayer : MonoBehaviour
{
    [AkEvent] public string playEvent = "Play_Footstep";
    [AkEvent] public string stopEvent = "Stop_Footstep";

    public void PlayFootstep()
    {
        AkSoundEngine.PostEvent(playEvent, gameObject);
    }

    public void SetSurfaceType(string surface)
    {
        // Switch: Surface = Grass/Metal/Wood
        AkSoundEngine.SetSwitch("Surface", surface, gameObject);
    }

    public void SetPlayerSpeed(float speed)
    {
        // RTPC: PlayerSpeed drives footstep rate
        AkSoundEngine.SetRTPCValue("PlayerSpeed", speed, gameObject);
    }
}

Step 2: Dynamic Music with States

public class WwiseMusicManager : MonoBehaviour
{
    void SetGameState(string state)
    {
        // State: GameState = Exploration/Combat/Victory
        AkSoundEngine.SetState("GameState", state);
    }

    void OnPlayerEnterCombat()
    {
        SetGameState("Combat");
    }

    void OnPlayerExitCombat()
    {
        SetGameState("Exploration");
    }
}

Spatial Audio Configuration

Step 1: Set Attenuation Curves

// Configure 3D audio source
AudioSource source = GetComponent();

// Logarithmic rolloff (realistic)
source.rolloffMode = AudioRolloffMode.Logarithmic;
source.minDistance = 1f;   // Full volume
source.maxDistance = 50f;  // Inaudible
source.spatialBlend = 1f;  // Fully 3D

// Doppler effect for vehicles
source.dopplerLevel = 1.5f;

Distance Attenuation:

Volume
  100% ████████████
        1m    5m   ████
                       ████
                          ████
    0%                       ████
       0m    10m   30m   50m   Distance

Min Distance: 1m (full volume)
Max Distance: 50m (silence)
Rolloff: Logarithmic

Step 2: Spatial Blend Based on Sound Type

BAD - All sounds fully 3D:

footsteps.spatialBlend = 1f;  // Correct
music.spatialBlend = 1f;      // Wrong - UI sounds shouldn't be 3D
uiClick.spatialBlend = 1f;    // Wrong

GOOD - Appropriate spatial settings:

footsteps.spatialBlend = 1f;   // 3D gameplay
ambientWind.spatialBlend = 1f; // 3D environment
music.spatialBlend = 0f;       // 2D stereo
uiClick.spatialBlend = 0f;     // 2D UI

Dynamic Mixing and Ducking

Step 1: Mixer Group Setup

// Reduce music volume when dialogue plays
using UnityEngine.Audio;

public class AudioDucker : MonoBehaviour
{
    [SerializeField] private AudioMixer _mixer;

    public void OnDialogueStart()
    {
        _mixer.SetFloat("MusicVolume", -10f); // Duck to -10dB
    }

    public void OnDialogueEnd()
    {
        _mixer.SetFloat("MusicVolume", 0f); // Restore to 0dB
    }
}

Step 2: Smooth Volume Transitions

public IEnumerator FadeAudioSource(AudioSource source, float targetVolume, float duration)
{
    float startVolume = source.volume;
    float elapsed = 0f;

    while (elapsed  [Tension]
      │                │
      ↓                ↓
 [Discovery]      [Combat]
                       │
                       ↓
                [Victory/Defeat]

Transitions: 2-4 bar crossfades on beat boundaries

Voice Limiting and Priority

// Limit simultaneous sounds per category
public class VoiceLimiter : MonoBehaviour
{
    private Dictionary> _activeSources = new();
    private int _maxVoicesPerCategory = 4;

    public void PlayLimitedSound(string category, AudioClip clip, Vector3 position)
    {
        if (!_activeSources.ContainsKey(category))
            _activeSources[category] = new List();

        var sources = _activeSources[category];

        // Remove finished sources
        sources.RemoveAll(s => !s.isPlaying);

        // If at limit, stop oldest
        if (sources.Count >= _maxVoicesPerCategory)
        {
            sources[0].Stop();
            sources.RemoveAt(0);
        }

        // Play new sound
        var newSource = AudioManager.Instance.PlaySound(category, position);
        sources.Add(newSource);
    }
}

Platform-Specific Optimization

Mobile Settings

#if UNITY_ANDROID || UNITY_IOS
    // Reduce simultaneous voices
    AudioSettings.GetConfiguration().numVirtualVoices = 32;
    AudioSettings.GetConfiguration().numRealVoices = 16;

    // Force compression
    audioImporter.compressionFormat = AudioCompressionFormat.Vorbis;
    audioImporter.quality = 0.5f; // 96 kbps
#endif

Console/PC Settings

#if UNITY_STANDALONE || UNITY_PS5 || UNITY_XBOXSERIES
    AudioSettings.GetConfiguration().numVirtualVoices = 256;
    AudioSettings.GetConfiguration().numRealVoices = 128;

    // Higher quality
    audioImporter.compressionFormat = AudioCompressionFormat.Vorbis;
    audioImporter.quality = 0.7f; // 128-192 kbps
#endif

Quick Reference

Mixing Levels:

Master:  -3 dB peak (headroom for mastering)
Music:   -12 to -6 dB (background)
SFX:     -6 to 0 dB (prominent)
Voice:   -6 to -3 dB (always clear)
Ambient: -18 to -12 dB (subtle)

Voice Limits by Platform:

Mobile:   16-32 simultaneous voices
Console:  64-128 simultaneous voices
PC:       128-256 simultaneous voices

Compression Formats:

Music:   Vorbis 128-192 kbps, STREAM
SFX short: ADPCM, DECOMPRESS_ON_LOAD
SFX long:  Vorbis 128 kbps, STREAM
Voice:   Vorbis 96-128 kbps, STREAM

Use this skill: When implementing game audio, designing sound systems, or debugging audio performance issues.

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet — be the first.

Versions

  • v0.1.0 Imported from the upstream source.