1+ #include " SoundStreamDS.hpp"
2+ #include " client/app/AppPlatform.hpp"
3+ #include < cassert>
4+ #include < cstring>
5+
6+ SoundStreamDS::SoundStreamDS (LPDIRECTSOUND ds) :
7+ m_directsound(ds),
8+ m_source(nullptr ),
9+ m_bufferSize(0 ),
10+ m_totalBufferSize(0 ),
11+ m_isPlaying(false )
12+ {
13+ _createSource ();
14+ _createBuffers ();
15+ _setVolume (getVolume ());
16+ }
17+
18+ SoundStreamDS::~SoundStreamDS ()
19+ {
20+ _deleteSource ();
21+ _deleteBuffers ();
22+ }
23+
24+ void SoundStreamDS::_deleteSource ()
25+ {
26+ if (m_source)
27+ {
28+ m_source->Stop ();
29+ m_source->Release ();
30+ m_source = nullptr ;
31+ }
32+ }
33+
34+ void SoundStreamDS::_createSource ()
35+ {
36+ if (!m_directsound) return ;
37+
38+ // Create a secondary buffer for streaming
39+ DSBUFFERDESC desc = {};
40+ desc.dwSize = sizeof (DSBUFFERDESC);
41+ desc.dwFlags = DSBCAPS_CTRLVOLUME | DSBCAPS_LOCSOFTWARE | DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_GLOBALFOCUS;
42+ desc.dwBufferBytes = m_totalBufferSize; // Set in _open
43+ desc.lpwfxFormat = &m_waveFormat;
44+
45+ HRESULT hr = m_directsound->CreateSoundBuffer (&desc, &m_source, nullptr );
46+ DS_ERROR_CHECK (hr);
47+ }
48+
49+ void SoundStreamDS::_resetSource ()
50+ {
51+ if (m_source)
52+ {
53+ HRESULT hr = m_source->Stop ();
54+ DS_ERROR_CHECK (hr);
55+ hr = m_source->SetCurrentPosition (0 );
56+ DS_ERROR_CHECK (hr);
57+ }
58+ }
59+
60+ void SoundStreamDS::_deleteBuffers ()
61+ {
62+ for (std::vector<LPDIRECTSOUNDBUFFER>::iterator it = m_buffers.begin (); it != m_buffers.end (); ++it)
63+ {
64+ LPDIRECTSOUNDBUFFER buffer = *it;
65+ if (buffer)
66+ buffer->Release ();
67+ }
68+ m_buffers.clear ();
69+ m_bufferIdMap.clear ();
70+ }
71+
72+ void SoundStreamDS::_createBuffers ()
73+ {
74+ if (!m_directsound || m_bufferSize == 0 ) return ;
75+
76+ assert (m_buffers.empty ());
77+
78+ // Create two streaming buffers
79+ DSBUFFERDESC desc = {};
80+ desc.dwSize = sizeof (DSBUFFERDESC);
81+ desc.dwFlags = DSBCAPS_LOCSOFTWARE;
82+ desc.dwBufferBytes = m_bufferSize;
83+ desc.lpwfxFormat = &m_waveFormat;
84+
85+ for (int i = 0 ; i < 2 ; ++i)
86+ {
87+ LPDIRECTSOUNDBUFFER buffer = nullptr ;
88+ HRESULT hr = m_directsound->CreateSoundBuffer (&desc, &buffer, nullptr );
89+ if (SUCCEEDED (hr))
90+ {
91+ m_buffers.push_back (buffer);
92+ m_bufferIdMap[buffer] = i;
93+ }
94+ else
95+ DS_ERROR_CHECK (hr);
96+ }
97+ }
98+
99+ void SoundStreamDS::_resetBuffers ()
100+ {
101+ _deleteBuffers ();
102+ _createBuffers ();
103+ }
104+
105+ void SoundStreamDS::_setVolume (float vol)
106+ {
107+ if (m_source)
108+ {
109+ // DirectSound volume: 0 to -10000 (hundredths of a decibel)
110+ LONG dsVolume = vol > 0 ? (LONG)(2000 .0f * log10 (vol)) : DSBVOLUME_MIN;
111+ HRESULT hr = m_source->SetVolume (dsVolume);
112+ DS_ERROR_CHECK (hr);
113+ }
114+ }
115+
116+ void SoundStreamDS::_play ()
117+ {
118+ if (m_source)
119+ {
120+ HRESULT hr = m_source->Play (0 , 0 , DSBPLAY_LOOPING);
121+ DS_ERROR_CHECK (hr);
122+ m_isPlaying = SUCCEEDED (hr);
123+ }
124+ }
125+
126+ void SoundStreamDS::_pause ()
127+ {
128+ if (m_source)
129+ {
130+ HRESULT hr = m_source->Stop ();
131+ DS_ERROR_CHECK (hr);
132+ m_isPlaying = false ;
133+ }
134+ }
135+
136+ bool SoundStreamDS::_open (const std::string& fileName)
137+ {
138+ // Set up wave format
139+ m_waveFormat.wFormatTag = WAVE_FORMAT_PCM;
140+ m_waveFormat.nChannels = m_info.channels ;
141+ m_waveFormat.nSamplesPerSec = m_info.sample_rate ;
142+ m_waveFormat.wBitsPerSample = 16 ;
143+ m_waveFormat.nBlockAlign = m_waveFormat.nChannels * m_waveFormat.wBitsPerSample / 8 ;
144+ m_waveFormat.nAvgBytesPerSec = m_waveFormat.nSamplesPerSec * m_waveFormat.nBlockAlign ;
145+
146+ // Align buffer size with m_tempPcmBuffer
147+ m_bufferSize = m_tempPcmBuffer.m_dataSize * sizeof (int16_t );
148+ m_totalBufferSize = m_bufferSize * 2 ; // Two segments
149+
150+ // Recreate source and buffers
151+ _deleteSource ();
152+ _deleteBuffers ();
153+ _createSource ();
154+ _createBuffers ();
155+
156+ // Verify buffers were created
157+ if (m_buffers.size () != 2 || !m_source)
158+ {
159+ LOG_E (" Failed to create DirectSound buffers or source" );
160+ return false ;
161+ }
162+
163+ // Fill initial buffers
164+ for (size_t i = 0 ; i < m_buffers.size (); ++i)
165+ {
166+ if (!_stream (i)) return false ;
167+ }
168+
169+ _play ();
170+ return true ;
171+ }
172+
173+ void SoundStreamDS::_close ()
174+ {
175+ _resetSource ();
176+ m_isPlaying = false ;
177+ }
178+
179+ void SoundStreamDS::_update ()
180+ {
181+ if (!m_source || !m_isPlaying) return ;
182+
183+ // Check if buffer is lost (e.g., when window is minimized)
184+ HRESULT hr = m_source->Restore ();
185+ if (hr == DSERR_BUFFERLOST)
186+ {
187+ LOG_I (" Restoring lost DirectSound buffer" );
188+ hr = m_source->Restore ();
189+ DS_ERROR_CHECK (hr);
190+ if (FAILED (hr)) return ;
191+ // Re-fill buffers after restoration
192+ for (size_t i = 0 ; i < m_buffers.size (); ++i)
193+ {
194+ if (_stream (i)) continue ;
195+
196+ if (shouldLoop ())
197+ {
198+ stb_vorbis_seek_start (m_decoder);
199+ m_totalSamplesLeft = stb_vorbis_stream_length_in_samples (m_decoder) * m_info.channels ;
200+ if (!_stream (i))
201+ {
202+ close ();
203+ return ;
204+ }
205+ }
206+ else
207+ {
208+ close ();
209+ return ;
210+ }
211+ }
212+ _play (); // Restart playback
213+ }
214+
215+ DWORD playCursor, writeCursor;
216+ hr = m_source->GetCurrentPosition (&playCursor, &writeCursor);
217+ DS_ERROR_CHECK (hr);
218+ if (FAILED (hr)) return ;
219+
220+ // Determine which segment to update
221+ static DWORD lastWritePos = 0 ;
222+ DWORD currentSegment = (writeCursor >= m_bufferSize) ? 1 : 0 ;
223+
224+ // Refill the opposite segment if the write cursor has crossed the boundary
225+ if ((lastWritePos < m_bufferSize && writeCursor >= m_bufferSize) ||
226+ (lastWritePos >= m_bufferSize && writeCursor < m_bufferSize))
227+ {
228+ int bufferId = (currentSegment + 1 ) % 2 ;
229+
230+ if (_stream (bufferId))
231+ {
232+ lastWritePos = writeCursor;
233+ return ;
234+ }
235+
236+ if (shouldLoop ())
237+ {
238+ stb_vorbis_seek_start (m_decoder);
239+ m_totalSamplesLeft = stb_vorbis_stream_length_in_samples (m_decoder) * m_info.channels ;
240+ if (!_stream (bufferId))
241+ {
242+ close ();
243+ return ;
244+ }
245+ }
246+ else
247+ {
248+ close ();
249+ return ;
250+ }
251+
252+ lastWritePos = writeCursor;
253+ }
254+ }
255+
256+ void SoundStreamDS::_publishBuffer (unsigned int destBufferId, const SoundBuffer& sourceBuffer)
257+ {
258+ if (destBufferId >= m_buffers.size () || !m_source) return ;
259+
260+ LPDIRECTSOUNDBUFFER buffer = m_buffers[destBufferId];
261+ if (!buffer)
262+ {
263+ LOG_E (" Invalid buffer at index %d" , destBufferId);
264+ return ;
265+ }
266+
267+ // Lock the temporary buffer to copy data
268+ void * ptr1 = nullptr ;
269+ void * ptr2 = nullptr ;
270+ DWORD size1 = 0 , size2 = 0 ;
271+ DWORD dataSize = sourceBuffer.m_dataSize * sizeof (int16_t );
272+
273+ HRESULT hr = buffer->Lock (0 , dataSize, &ptr1, &size1, &ptr2, &size2, 0 );
274+ if (FAILED (hr))
275+ {
276+ DS_ERROR_CHECK (hr);
277+ return ;
278+ }
279+
280+ // Copy audio data
281+ memcpy (ptr1, sourceBuffer.m_pData , size1);
282+ if (ptr2 && size2 > 0 )
283+ memcpy (ptr2, (char *)sourceBuffer.m_pData + size1, size2);
284+
285+ hr = buffer->Unlock (ptr1, size1, ptr2, size2);
286+ DS_ERROR_CHECK (hr);
287+
288+ // Write to the streaming buffer segment
289+ void * streamPtr1 = nullptr ;
290+ void * streamPtr2 = nullptr ;
291+ DWORD streamSize1 = 0 , streamSize2 = 0 ;
292+ DWORD offset = destBufferId * m_bufferSize;
293+
294+ hr = m_source->Lock (offset, dataSize, &streamPtr1, &streamSize1, &streamPtr2, &streamSize2, 0 );
295+ if (SUCCEEDED (hr))
296+ {
297+ memcpy (streamPtr1, sourceBuffer.m_pData , streamSize1);
298+ if (streamPtr2 && streamSize2 > 0 )
299+ memcpy (streamPtr2, (char *)sourceBuffer.m_pData + streamSize1, streamSize2);
300+ m_source->Unlock (streamPtr1, streamSize1, streamPtr2, streamSize2);
301+ }
302+ else
303+ DS_ERROR_CHECK (hr);
304+ }
0 commit comments