Skip to content

Commit f00596b

Browse files
authored
DirectSound Streaming (#302)
This means music for DirectSound
1 parent 4ff3c16 commit f00596b

File tree

15 files changed

+462
-10
lines changed

15 files changed

+462
-10
lines changed

platforms/audio/directsound/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ project(reminecraftpe-audio-directsound)
44
# Build
55
add_library(reminecraftpe-audio STATIC
66
SoundSystemDS.cpp
7+
SoundStreamDS.cpp
78
)
89

910
# Libraries

platforms/audio/directsound/CustomSoundSystem.hpp

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include <mmsystem.h>
1919
#include <dsound.h>
2020

21+
#include "SoundStreamDS.hpp"
2122
#include "client/sound/SoundSystem.hpp"
2223
#include "client/sound/SoundData.hpp"
2324

@@ -31,25 +32,32 @@ class SoundSystemDS : public SoundSystem
3132
LPDIRECTSOUNDBUFFER buffer;
3233
LPDIRECTSOUND3DBUFFER object3d;
3334
};
34-
35+
3536
public:
3637
SoundSystemDS();
3738
~SoundSystemDS();
38-
39+
3940
private:
4041
WAVEFORMATEX _getWaveFormat(const PCMSoundHeader& header, float pitch) const;
4142
void _cleanSources();
42-
43+
4344
public:
4445
bool isAvailable() override;
4546
void setListenerPos(const Vec3& pos) override;
4647
void setListenerAngle(const Vec2& rot) override;
4748
void playAt(const SoundDesc& sound, const Vec3& pos, float volume, float pitch) override;
48-
49+
void setMusicVolume(float vol) override;
50+
void update(float) override;
51+
void playMusic(const std::string& soundPath) override;
52+
bool isPlayingMusic() const override;
53+
void stopMusic() override;
54+
void pauseMusic(bool state) override;
55+
4956
private:
5057
bool m_available;
5158
HWND m_hWnd;
5259
IDirectSound* m_directsound;
5360
LPDIRECTSOUND3DLISTENER m_listener;
5461
std::vector<BufferInfo> m_buffers;
55-
};
62+
SoundStreamDS* m_musicStream;
63+
};
Lines changed: 304 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,304 @@
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

Comments
 (0)