Audio Rendering Parameter Generation on the SPU

Document Sample
Audio Rendering Parameter Generation on the SPU
Description

Paul Martin, Technical Director for Slant Six Games, provides insight into his extensive development work on the Sony PlayStation 3.

Shared by: Zoe Flower
Tags
Stats
views:
24443
posted:
3/19/2009
language:
English
pages:
10
Audio Rendering Parameter

Generation on the SPU

A tale of porting a heavyweight

function.

SoundEngine::Process()

 Fills out all sound rendering parameters that are

passed into the synthesis engine.

– Eg. Volume, azimuth, elevation, reverb send levels,

filter cutoff.

 Rendering parameters are calculated from:

– listener position,

– listener direction, and

– sound positions,

– output from indirect audio.

 Called once per sound, per frame.

 Perfect candidate for parallelization.

SPURS Jobs: A convenient fit

 Appropriate for short bits of code that is executed

many times on independent data.

 SPURS will take care of scheduling and will play

nicely with other SPURS workloads.

 SPURS takes care of running multiple

SoundEngineProcessJob’s which have been queued

up in a job list in parallel!

 SPURS takes care of pipelining asynchronous I/O.

– While the current job is running, the output from the

last job is being DMA’s back to PPU, and/or the input for

the next job is being DMA’d to the SPU.

Determining Inputs

 Listener Position, direction.

 Sound Position.

 Static sound rendering parameters.

– Defined for each individual sound .csv

– Encapsulated in “Spatial Info” struct

 Filtered render state.

– Output data from indirect audio, smoothed over time.

 Reverb preset for sound’s position in space.

 Is the sound visible to the listener?

– Causes direct sound to be filtered.

 Is the sound in the same reverb region as the

listener.

– Causes reverberated sound to be filtered.

Determining Outputs

 SoundParams struct.

– Passed into SCREAM to tell NextSynth how to

render the sound.

– Defines vol, pan, pitch, SCREAM registers,

various filters, send levels, special FX, etc.

 Reverberation accumulation

– There are many sounds, but only 6 reverb

units.

– Reverb units “accumulate” directional gain

from each currently playing sound.

Movin’ stuff around.

 Pointers are no longer valid after data has been

copied to the SPU!

 Classes with virtual functions will not work

without some v-table patching trickery. They are

best avoided.

 SPU’s like data in flat arrays which are aligned to

16-byte boundaries, so they can be easily

copied.

 Simplify complex classes into structs for data

that is copied to/from the SPU.

 Use structures of arrays where appropriate for

vectorization.

A SoundEmitter now keeps track of the sounds that it is emitting in a struct of arrays,

instead of a list. This allows the SoundParams to be DMA’s directly into the array.

/* /*

* SoundsEmitter * SoundsEmitter

*/ */

class SoundEmitter: public SoundEmitterBase class SoundEmitter: public SoundEmitterBase

{ {

public: public:

struct SoundEntry: public

AudioPtrBase::type struct SoundEntryVector

{ {

typedef AudioPtr::type SoundEntryVector(uint size);

Ptr;

~SoundEntryVector();

SoundEntry(); SoundInstancePtr AddSound( … );

~SoundEntry(); void Erase(unsigned int i);

SoundInstancePtr mSound;

PlayParams mParams; SoundInstancePtr* mSoundsArray;

SoundParams* mParamsArray;

float mElapsedTime; float* mElapsedTimesArray;

float mTriggerTime; float* mTriggerTimesArray;

}; SpatialInfo* mSpatialInfoArray;

typedef List const SoundDef** mSoundDefsArray;

SoundEntryList;

unsigned int size;

SoundEntryList mSoundEntries; unsigned int maxSize;

};



SoundEntryVector mSoundEntries;

};

The IIR class gets flattened out, and vectorized. Turns out there is only one type of IIR



class IIR : public AlignedObject class IIRArray : public AlignedObject {

{ public:

public:

IIRArray();

IIR();

virtual ~IIR(); float GetValue(unsigned int idx) const;

float GetValue() const; void GetValuesRange(u32 offs, float* out, u32

void SetValue(float value); n) const;

void SetConstant(float constant); void SetValuesRange(u32 offs, const float*

virtual void AddSample(float newsample); value, u32 n);

void SetHalfLifes(const float* halflife);

protected:

float GetHalfLife(unsigned int idx) const;

float m_curval;

float m_constant;

float m_invConstant; void AddSamples(const float* newsamples, float

}; deltatime);



class TimeDependentIIR : public IIR protected:

{

static const uint ARRAY_SIZE = 24;

public:

TimeDependentIIR(); static const uint NUM_VECS = ARRAY_SIZE/4;

virtual ~TimeDependentIIR();

void SetHalfLife(float halflife); union{ vector float m_curval[NUM_VECS];

float GetHalfLife() const; float m_curval_s[ARRAY_SIZE]; };

virtual void AddSample(float sample, float

dt); union{ vector float m_constant[NUM_VECS];

float m_constant_s[ARRAY_SIZE];};

protected: union{ vector float m_invConstant [NUM_VECS];

float m_halfLife; float m_invConstant_s[ARRAY_SIZE];};

};

union{ vector float m_halfLife[NUM_VECS];

float m_halfLife_s[ARRAY_SIZE];};

};

class FilteredRenderState : public AlignedObject, public NonCopyable class FilteredRenderState : public AlignedObject, public NonCopyable

{

{



Private: public:

SMath::Vector m_unfilteredIndirectDirection; enum

SMath::Point m_unfilteredIndirectPosition; {

DirectDistanceIdx,

float m_unfilteredIndirectDistance;

DirectFocusIdx,

TimeDependentIIR m_filteredIndirectDistance;

There… DirectOcclusionLevelIdx,

float m_unfilteredDirectDistance; DirectObstructionLevelIdx,

TimeDependentIIR m_filteredDirectDistance; IndirectDistanceIdx,

IndirectFocusIdx,

float m_unfilteredDirectFocus;

TimeDependentIIR m_filteredDirectFocus; ..much better! IndirectOcclusionLevelIdx,

IndirectObstructionLevelIdx,

Float m_unfilteredIndirectFocus; IndirectDirection0Idx,

TimeDependentIIR m_filteredIndirectFocus; IndirectDirection1Idx,

IndirectDirection2Idx,

TimeDependentIIR m_filteredIndirectDirectionX;

IndirectDirection3Idx,

TimeDependentIIR m_filteredIndirectDirectionY;

TimeDependentIIR m_filteredIndirectDirectionZ; IndirectPosition0Idx,

IndirectPosition1Idx,

TimeDependentIIR m_filteredIndirectPositionX; IndirectPosition2Idx,

TimeDependentIIR m_filteredIndirectPositionY; IndirectPosition3Idx,

TimeDependentIIR m_filteredIndirectPositionZ;

SourceReverbGain0Idx,

float m_unfilteredDirectOcclusionLevel; SourceReverbGain1Idx,

TimeDependentIIR m_filteredDirectOcclusionLevel; SourceReverbGain2Idx,

SourceReverbGain3Idx,

float m_unfilteredIndirectOcclusionLevel; SourceReverbGain4Idx,

TimeDependentIIR m_filteredIndirectOcclusionLevel;

SourceReverbGain5Idx,

float m_unfilteredIndirectObstructionLevel; SourceReverbGain6Idx,

TimeDependentIIR m_filteredIndirectObstructionLevel; SourceReverbGain7Idx,

NUM_INDICIES

float m_unfilteredDirectObstructionLevel; };

TimeDependentIIR m_filteredDirectObstructionLevel;



float m_unfilteredSourceReverbGains[NUM_REVERB_GAINS];

TimeDependentIIR m_filteredSourceReverbGains[NUM_REVERB_GAINS]; float m_unfilteredValues[NUM_INDICIES];

IIRArray m_filteredValues;

};

Comparison of PPU/SPU methods

• 3-Player game.

• After data structure reorganization.

• SoundEngine::PreUpdate() w/ PPU Process() call

(PreUpdate calls Process directy.)

• Min 1.27 ms

• Max 5.51 ms

• Avg 2.959 ms









•SoundEngine::PreUpdate() w/ SPU Process() call. (PreUpdate queues up

SPU jobs)

•Min 529.2 us

•Max 3.18 ms

•Avg 1.16 ms


Share This Document


Related docs
Other docs by Zoe Flower
FunctionalProgramming_Haskell-Feb262009
Views: 23614  |  Downloads: 3
gplt_presentation
Views: 23240  |  Downloads: 1
Montreal Game Summit 2008
Views: 17  |  Downloads: 1
Montreal Game Summit 2008 Presentation - No Notes
Views: 24445  |  Downloads: 12
Audio Rendering Parameter Generation on the SPU
Views: 24443  |  Downloads: 7
by registering with docstoc.com you agree to our
privacy policy

You are almost ready to download!

You are almost ready to download!