DirectSound-based audio pipeline used by the 4920-5960 retail dashboard. Three layers: a static buffer wrapper, a streaming pump base class with a worker thread, and the XAP nodes that the scene graph interacts with.
CAudioBuf : static DSound buffer (sound effects, short clips)
CAudioPump : streaming buffer base, worker thread + segment notify
CFilePump : WAV file streaming
CAudioClip (XAP node) holds a CAudioBuf*: which may actually point to a CFilePump: and dispatches Play/Pause/Stop/SetAttenuation/... through the virtual interface.
Wraps a single IDirectSoundBuffer8 allocated through DSoundManager::DSoundCreateSoundBuffer with DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLFREQUENCY.
| Method | Purpose |
|---|---|
Initialize(WAVEFORMATEX*, int nBufferBytes, void* pvSamples=NULL) |
Create the buffer; if samples provided, copy them in via Lock/Unlock |
Lock()/Unlock(void*) |
Standard DSound locking for direct PCM writes |
Play(bLoop) |
Start playback (looping or one-shot) |
Stop/Pause/IsPaused/IsPlaying |
Transport control |
SetAttenuation(float dB) |
0..100 dB attenuation (mapped to DSound millibels) |
SetPan(float) / SetFrequency(float) |
DSound pan/frequency control |
GetPlaybackTime() / GetPlaybackLength() |
Position in seconds (length returns 0 if unknown) |
Buffer format is captured at Initialize as m_nBytesPerSecond for time conversion.
Streaming buffer for content too large to load in full. Inherits from CAudioBuf and replaces the static buffer with a circular DSound buffer divided into N segments, refilled by a worker thread.
PUMPSTATE_STOPPED -> idle
PUMPSTATE_BUFFERING -> filling initial segments before play
PUMPSTATE_RUNNING -> normal playback, worker refills consumed segments
PUMPSTATE_STOPPING -> draining; worker zeros remaining segments and exits
CAudioPump::Initialize(DWORD dwStackSize, WAVEFORMATEX*, int nBufferBytes, int nSegmentsPerBuffer=4, int nPrebufferSegments=1)
- Allocates
m_nSegmentsPerBufferWin32 events (m_ahNotify[]). - Creates terminate event, run event, and a mutex.
- Spawns the worker thread with
dwStackSizebytes viaCreateThread(StartThread, this). - Creates a DSound buffer of size
nSegmentsPerBuffer * nBufferByteswithDSBCAPS_CTRLPOSITIONNOTIFY | DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLFREQUENCY. - Sets
DSBPOSITIONNOTIFYentries so each segment boundary signals one of them_ahNotifyevents.
ThreadProc() waits on the segment-completion events. When a segment finishes playing, the worker calls FillBuffer(nBuffer):
- Lock the DSound buffer at
nBuffer * m_nBufferBytes. - Call the pure virtual
GetData(BYTE*, int cbBuffer)to fill the segment. - If
GetDatareturned fewer bytes than requested, transition toPUMPSTATE_STOPPINGand zero the remainder. - Unlock and bookkeep
m_nFilledBuffers/m_nCompletedBuffers. - Once
m_nFilledBuffers >= m_nSegmentsPerBufferduring BUFFERING, transition to RUNNING.
Subclasses must implement int GetData(BYTE* pbBuffer, int cbBuffer) to source PCM. Optionally override OnAudioEnd(). The worker thread calls GetData: thread-safety of the source is the subclass's responsibility.
The worker thread stack size is per-pump. Decoder-backed pumps must size for their decoder's scratch usage on top of standard ~4 KB Win32 thread overhead. Pumps that just memcpy from a file handle (CFilePump) work with 8 KB.
Streams a WAV file directly off disk. Holds a HANDLE m_hFile, a start position (data chunk offset), and a buffer-size that is 0x2000 rounded to the format's block alignment.
| Method | Behavior |
|---|---|
Initialize(HANDLE hFile, int nFileBytes, WAVEFORMATEX*) |
Records file/length, calls CAudioPump::Initialize(8192, ...) |
Stop() |
Repositions file pointer back to data chunk start |
GetData(pbBuffer, cbBuffer) |
ReadFile directly into the locked DSound segment |
GetPlaybackLength() |
Returns total file bytes / nAvgBytesPerSec |
The file handle is owned by the pump after Initialize and closed in the destructor.
AudioClip at IMPLEMENT_NODE("AudioClip", CAudioClip, CTimeDepNode). Subclass of CTimeDepNode so the scene graph drives Advance(float seconds) every frame.
| Name | Type |
|---|---|
| volume | number |
| pan | number |
| frequency | number |
| fade | number |
| url | string |
| transportMode | integer (0=stop, 1=play, 2=pause) |
| removeVoice | boolean |
| sendProgress | boolean |
| pause_on_moving | boolean |
| progress | number |
Play, Stop, Pause, PlayOrPause, getMinutes, getSeconds.
CAudioClip::Initialize parses m_url and dispatches:
| Prefix | Source |
|---|---|
cd: |
CD audio playback (CD player UI: DVD/CD playback path) |
st: |
Soundtrack from MusicCollection (song ID after the prefix) |
| (file extension) | Local file: .wav -> OpenWaveFile() |
OpenWaveFile() parses the RIFF/WAVE header (fmt chunk -> m_format, data chunk -> dwDataSize). If the data payload is over 64 KB it instantiates a CFilePump; otherwise a static CAudioBuf and the entire payload is locked/copied/unlocked.
OnSetPropertyflipsm_bDirtywhenurlchanges; the nextAdvancere-runsInitialize.volumeandpansetters with non-zerofadeinterpose aCLerperto ramp the value overfadeseconds.OnIsActiveChangedcallsPlay()/Stop()based onm_isActive.Advancepropagatesm_volume/m_pan/m_frequencyto the underlying buffer, pollsIsPaused/IsPlaying, and writesprogressifsendProgressis set.
m_format is XBOXADPCMWAVEFORMAT (DSound's superset of WAVEFORMATEX to carry XBADPCM coefficients). The format tag is asserted to be WAVE_FORMAT_PCM or WAVE_FORMAT_XBOX_ADPCM.
MusicCollection at IMPLEMENT_NODE("MusicCollection", CMusicCollection, CNode). Used by the music UI to enumerate soundtracks and songs, and to drive copy/edit operations against the system soundtrack store.
| Name | Type |
|---|---|
| copyProgress | number |
| error | integer |
Read side: GetSoundtrackCount, GetSoundtrackID, GetSoundtrackIndexFromID, GetSoundtrackName, FormatSoundtrackTime, GetSoundtrackSongCount, GetSoundtrackSongID, GetSoundtrackSongName, FormatSoundtrackSongTime.
Edit side: AddSoundtrack, DeleteSoundtrack, ClearCopyList, AddSongToCopyList, StartCopy, SetSongName, SetSoundtrackName, MoveSongUp, MoveSongDown, DeleteSong, CreateSoundtrackName, GetUpdateString.
Songs returned by these functions are referenced by ID. The st:<id> URL scheme on CAudioClip resolves the ID back to a playable source through the collection.
OpenWaveFile chooses based on the data chunk size:
- <= 64 KB (
0x10000): staticCAudioBuf. Whole payload memcpy'd into a single DSound buffer at load time. Used for sound effects. - > 64 KB:
CFilePump. Streamed off disk. Used for music and long ambient loops.
The threshold balances DSound buffer allocation cost (static is cheaper but holds RAM) against worker thread overhead (the pump needs an event-driven thread per buffer).
CAudioBuf::SetAttenuation writes attenuation in dB rather than linear. The attenuation is offset against the headroom configured by DSoundManager (1200 millibels by default) to leave room for mixed signal peaks. See DSoundManager.md.