Guidance / Manual
Multi-Position Audio
8.1 Concept
What: Multiple speakers playing synchronized audio at different 3D positions.
Real-World Example: Nightclub with 8 speakers playing the same music.
Challenge: Without synchronization, speakers have phasing issues (sounds canceling out).
Solution: DSP-scheduled playback ensures sample-accurate synchronization.
8.2 Architecture
AudioMultiPositionEmitterParent
Component: Manages child emitters
Properties:
int maxEmitters = 16; // Limit (1-16)
bool autoFindChildren = true; // Auto-detect children
bool autoUpdateChildren = true; // Update when hierarchy changes
Methods:
Vector3[] GetActivePositions(); // Positions of active emitters
Transform[] GetActiveTransforms(); // Transforms of active emitters
int ActiveEmitterCount { get; } // Number active
void EnableAllEmitters();
void DisableAllEmitters();
void SetAllVolumes(float volume);
void RefreshChildren();
AudioMultiPositionEmitterChild
Component: Individual emitter point
Properties:
bool isActive = true; // Enable/disable
float volumeMultiplier = 1f; // Per-emitter volume (0-1)
bool useDirectionality = false; // Draws a cone gizmo in the Scene view (no effect on sound)
float coneAngle = 90f; // That gizmo's cone angle (0-360)
Properties (Read-Only):
Vector3 Position { get; } // World position
Quaternion Rotation { get; } // World rotation
Vector3 Forward { get; } // Forward direction
8.3 DSP Synchronization
Problem Without Sync:
Speaker A starts: Frame 1, Sample 0
Speaker B starts: Frame 1, Sample 512 ← Delayed by 512 samples
Speaker C starts: Frame 2, Sample 0 ← Delayed by ~2000 samples
Result: Phasing, echoing, muddy sound
Solution With DSP Scheduling:
double dspTime = AudioSettings.dspTime + 0.1; // Schedule 100ms ahead
foreach (var position in emitterPositions)
{
var voice = GetVoice();
voice.source.clip = clip;
voice.source.transform.position = position;
voice.scheduledStartTime = dspTime;
voice.source.PlayScheduled(dspTime);
}
Result: All speakers start at exact same DSP sample, perfect synchronization.
8.4 Setup Tutorial
Step 1: Create Parent
1. Create empty GameObject: "NightclubSpeakers"
2. Add component: AudioMultiPositionEmitterParent
3. Configure:
- maxEmitters: 8
- autoFindChildren: ✓
Step 2: Create Children (Manual)
1. Create child GameObject: "Speaker_01"
2. Position at (10, 2, 0)
3. Add component: AudioMultiPositionEmitterChild
4. Repeat for 8 speakers in circle pattern
Step 2: Create Children (Auto - Inspector Button)
1. Select parent
2. In Inspector, click "Create 8 Child Emitters (Circle Pattern)"
3. Automatically creates 8 speakers in circle
Step 3: Create Event
1. Create AudioEvent
2. Add action: Play music container
(No multi-position setting is needed on the event: Step 4 posts it with PostMultiPosition.)
Step 4: Script
using UnityEngine;
using AudioSystem;
public class NightclubAudio : MonoBehaviour
{
[SerializeField] private AudioEvent musicEvent;
private AudioMultiHandle musicHandle;
void Start()
{
var parent = GetComponent<AudioMultiPositionEmitterParent>();
musicHandle = musicEvent.PostMultiPosition(parent);
Debug.Log($"Playing on {musicHandle.VoiceCount} speakers");
}
void OnDestroy()
{
musicHandle?.Stop(2f); // Fade out over 2s
}
}
8.5 Per-Emitter Control
Volume:
// Individually
emitterChild.VolumeMultiplier = 0.75f;
// All at once
emitterParent.SetAllVolumes(0.5f);
Active State:
// Individual
emitterChild.IsActive = false; // Disable this speaker
// All
emitterParent.DisableAllEmitters();
emitterParent.EnableAllEmitters();
Directionality (Scene view only):
emitterChild.UseDirectionality = true;
emitterChild.ConeAngle = 90f; // draws a 90° cone along transform.forward
These two only draw a cone gizmo to help you aim an emitter. Playback doesn’t read them: every active emitter is heard the same in every direction. To make one quieter, use its Volume Multiplier.
8.6 Use Cases
Nightclub Speakers:
Setup: 8 speakers in circle
Audio: Looping music
Result: Immersive club environment
Stadium Announcer:
Setup: 12 speakers around stadium
Audio: Announcer voice
Result: Realistic PA system
Vehicle Multi-Speaker:
Setup: 4 speakers (FL, FR, RL, RR)
Audio: Radio/music
Result: Realistic car audio
Ambient Sound Array:
Setup: 6 speakers in outdoor environment
Audio: Wind/nature ambience
Result: Enveloping soundscape
8.7 Performance Considerations
Voice Count:
- 1 multi-position event with 8 emitters = 8 voices
- Monitor total voice count:
AudioManager.Instance.GetStatistics()
Instance Limiting:
Event: Multi-position music
MaxInstances: 1 ← Only one instance of entire array
Occlusion:
- Each emitter can be individually occluded
- Can be expensive with many emitters
- Consider disabling occlusion for some emitters
Distance Culling:
- Virtualization works per-voice
- Under voice pressure, distant emitters are virtualized first (importance falls with distance)
- No special handling needed
From the Pepper.SFX 3.1 guides (USER_GUIDE.md). Something wrong or unclear? Tell me.