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. ...
SPURS Jobs: A convenient fit Appropriate for short bits of code that is executed  many times on independent data. SPURS ...
Determining Inputs   Listener Position, direction.   Sound Position.   Static sound rendering parameters.    – Defined ...
Determining Outputs   SoundParams struct.    – Passed into SCREAM to tell NextSynth how to      render the sound.    – De...
Movin’ stuff around.   Pointers are no longer valid after data has been    copied to the SPU!   Classes with virtual fun...
A SoundEmitter now keeps track of the sounds that it is emitting in a struct of arrays, instead of a list. This allows the...
The IIR class gets flattened out, and vectorized. Turns out there is only one type of IIRclass IIR : public AlignedObject ...
class FilteredRenderState : public AlignedObject, public NonCopyable      class FilteredRenderState : public AlignedObject...
Comparison of PPU/SPU methods• 3-Player game.• After data structure reorganization.• SoundEngine::PreUpdate() w/ PPU Proce...
Upcoming SlideShare
Loading in …5
×

Audio SPU Presentation

736 views

Published on

An example of moving C++ code to PS3 SPUs

Published in: Technology, Business
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
736
On SlideShare
0
From Embeds
0
Number of Embeds
1
Actions
Shares
0
Downloads
3
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

Audio SPU Presentation

  1. 1. Audio Rendering Parameter Generation on the SPU A tale of porting a heavyweight function.
  2. 2. 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.
  3. 3. 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.
  4. 4. Determining Inputs Listener Position, direction. Sound Position. Static sound rendering parameters. – Defined for each individual sound <sound_bank>.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.
  5. 5. 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.
  6. 6. 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.
  7. 7. 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<SoundEntry>::type struct SoundEntryVector { { typedef AudioPtr<SoundEntry>::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<SoundEntry::Ptr> const SoundDef** mSoundDefsArray; SoundEntryList; unsigned int size; SoundEntryList mSoundEntries; unsigned int maxSize; }; … SoundEntryVector mSoundEntries;};
  8. 8. The IIR class gets flattened out, and vectorized. Turns out there is only one type of IIRclass 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];}; };
  9. 9. class FilteredRenderState : public AlignedObject, public NonCopyable class FilteredRenderState : public AlignedObject, public NonCopyable{ {…Private: public:SMath::Vector m_unfilteredIndirectDirection; enumSMath::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_INDICIESfloat 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;};
  10. 10. 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

×