1 // VisualBoyAdvance - Nintendo Gameboy/GameboyAdvance (TM) emulator.
2 // Copyright (C) 1999-2003 Forgotten
3 // Copyright (C) 2004 Forgotten and the VBA development team
4
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 2, or(at your option)
8 // any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software Foundation,
17 // Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
19 #include "stdafx.h"
20 #include "VBA.h"
21 #include "AVIWrite.h"
22 #include "Sound.h"
23 #include "WavWriter.h"
24
25 #include "../System.h"
26 #include "../GBA.h"
27 #include "../Globals.h"
28 #include "../Sound.h"
29
30 #include <mmreg.h>
31 #include <dsound.h>
32
33 class DirectSound : public ISound
34 {
35 private:
36 HINSTANCE dsoundDLL;
37 LPDIRECTSOUND pDirectSound;
38 LPDIRECTSOUNDBUFFER dsbPrimary;
39 LPDIRECTSOUNDBUFFER dsbSecondary;
40 LPDIRECTSOUNDNOTIFY dsbNotify;
41 HANDLE dsbEvent;
42 WAVEFORMATEX wfx;
43
44 public:
45 DirectSound();
46 virtual ~DirectSound();
47
48 bool init();
49 void pause();
50 void reset();
51 void resume();
52 void write();
53 };
54
DirectSound()55 DirectSound::DirectSound()
56 {
57 dsoundDLL = NULL;
58 pDirectSound = NULL;
59 dsbPrimary = NULL;
60 dsbSecondary = NULL;
61 dsbNotify = NULL;
62 dsbEvent = NULL;
63 }
64
~DirectSound()65 DirectSound::~DirectSound()
66 {
67 if(theApp.aviRecorder != NULL) {
68 delete theApp.aviRecorder;
69 theApp.aviRecorder = NULL;
70 theApp.aviFrameNumber = 0;
71 }
72
73 if(theApp.soundRecording) {
74 if(theApp.soundRecorder != NULL) {
75 delete theApp.soundRecorder;
76 theApp.soundRecorder = NULL;
77 }
78 theApp.soundRecording = false;
79 }
80
81 if(dsbNotify != NULL) {
82 dsbNotify->Release();
83 dsbNotify = NULL;
84 }
85
86 if(dsbEvent != NULL) {
87 CloseHandle(dsbEvent);
88 dsbEvent = NULL;
89 }
90
91 if(pDirectSound != NULL) {
92 if(dsbPrimary != NULL) {
93 dsbPrimary->Release();
94 dsbPrimary = NULL;
95 }
96
97 if(dsbSecondary != NULL) {
98 dsbSecondary->Release();
99 dsbSecondary = NULL;
100 }
101
102 pDirectSound->Release();
103 pDirectSound = NULL;
104 }
105
106 if(dsoundDLL != NULL) {
107 FreeLibrary(dsoundDLL);
108 dsoundDLL = NULL;
109 }
110 }
111
init()112 bool DirectSound::init()
113 {
114 HRESULT hr;
115
116 dsoundDLL = LoadLibrary("DSOUND.DLL");
117 HRESULT (WINAPI *DSoundCreate)(LPCGUID,LPDIRECTSOUND *,IUnknown *);
118 if(dsoundDLL != NULL) {
119 DSoundCreate = (HRESULT (WINAPI *)(LPCGUID,LPDIRECTSOUND *,IUnknown *))
120 GetProcAddress(dsoundDLL, "DirectSoundCreate");
121
122 if(DSoundCreate == NULL) {
123 theApp.directXMessage("DirectSoundCreate");
124 return false;
125 }
126 } else {
127 theApp.directXMessage("DSOUND.DLL");
128 return false;
129 }
130
131 if((hr = DSoundCreate(NULL,&pDirectSound,NULL) != DS_OK)) {
132 // errorMessage(myLoadString(IDS_ERROR_SOUND_CREATE), hr);
133 systemMessage(IDS_CANNOT_CREATE_DIRECTSOUND,
134 "Cannot create DirectSound %08x", hr);
135 pDirectSound = NULL;
136 dsbSecondary = NULL;
137 return false;
138 }
139
140 if((hr=pDirectSound->SetCooperativeLevel((HWND)*theApp.m_pMainWnd,
141 DSSCL_EXCLUSIVE)) != DS_OK) {
142 // errorMessage(myLoadString(IDS_ERROR_SOUND_LEVEL), hr);
143 systemMessage(IDS_CANNOT_SETCOOPERATIVELEVEL,
144 "Cannot SetCooperativeLevel %08x", hr);
145 return false;
146 }
147
148 DSBUFFERDESC dsbdesc;
149 ZeroMemory(&dsbdesc,sizeof(DSBUFFERDESC));
150 dsbdesc.dwSize=sizeof(DSBUFFERDESC);
151 dsbdesc.dwFlags = DSBCAPS_PRIMARYBUFFER;
152
153 if((hr=pDirectSound->CreateSoundBuffer(&dsbdesc,
154 &dsbPrimary,
155 NULL) != DS_OK)) {
156 // errorMessage(myLoadString(IDS_ERROR_SOUND_BUFFER),hr);
157 systemMessage(IDS_CANNOT_CREATESOUNDBUFFER,
158 "Cannot CreateSoundBuffer %08x", hr);
159 return false;
160 }
161
162 // Set primary buffer format
163
164 memset(&wfx, 0, sizeof(WAVEFORMATEX));
165 wfx.wFormatTag = WAVE_FORMAT_PCM;
166 wfx.nChannels = 2;
167 switch(soundQuality) {
168 case 2:
169 wfx.nSamplesPerSec = 22050;
170 soundBufferLen = 736*2;
171 soundBufferTotalLen = 7360*2;
172 break;
173 case 4:
174 wfx.nSamplesPerSec = 11025;
175 soundBufferLen = 368*2;
176 soundBufferTotalLen = 3680*2;
177 break;
178 default:
179 soundQuality = 1;
180 wfx.nSamplesPerSec = 44100;
181 soundBufferLen = 1470*2;
182 soundBufferTotalLen = 14700*2;
183 }
184 wfx.wBitsPerSample = 16;
185 wfx.nBlockAlign = (wfx.wBitsPerSample / 8) * wfx.nChannels;
186 wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
187
188 if((hr = dsbPrimary->SetFormat(&wfx)) != DS_OK) {
189 // errorMessage(myLoadString(IDS_ERROR_SOUND_PRIMARY),hr);
190 systemMessage(IDS_CANNOT_SETFORMAT_PRIMARY,
191 "Cannot SetFormat for primary %08x", hr);
192 return false;
193 }
194
195 ZeroMemory(&dsbdesc,sizeof(DSBUFFERDESC));
196 dsbdesc.dwSize = sizeof(DSBUFFERDESC);
197 dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2|DSBCAPS_CTRLPOSITIONNOTIFY;
198 dsbdesc.dwBufferBytes = soundBufferTotalLen;
199 dsbdesc.lpwfxFormat = &wfx;
200
201 if((hr = pDirectSound->CreateSoundBuffer(&dsbdesc, &dsbSecondary, NULL))
202 != DS_OK) {
203 dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2;
204 if((hr = pDirectSound->CreateSoundBuffer(&dsbdesc, &dsbSecondary, NULL))
205 != DS_OK) {
206 systemMessage(IDS_CANNOT_CREATESOUNDBUFFER_SEC,
207 "Cannot CreateSoundBuffer secondary %08x", hr);
208 return false;
209 }
210 }
211
212 dsbSecondary->SetCurrentPosition(0);
213
214 if(!theApp.useOldSync) {
215 hr = dsbSecondary->QueryInterface(IID_IDirectSoundNotify,
216 (void **)&dsbNotify);
217 if(!FAILED(hr)) {
218 dsbEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
219
220 DSBPOSITIONNOTIFY notify[10];
221
222 for(int i = 0; i < 10; i++) {
223 notify[i].dwOffset = i*soundBufferLen;
224 notify[i].hEventNotify = dsbEvent;
225 }
226 if(FAILED(dsbNotify->SetNotificationPositions(10, notify))) {
227 dsbNotify->Release();
228 dsbNotify = NULL;
229 CloseHandle(dsbEvent);
230 dsbEvent = NULL;
231 }
232 }
233 }
234
235 hr = dsbPrimary->Play(0,0,DSBPLAY_LOOPING);
236
237 if(hr != DS_OK) {
238 // errorMessage(myLoadString(IDS_ERROR_SOUND_PLAYPRIM), hr);
239 systemMessage(IDS_CANNOT_PLAY_PRIMARY, "Cannot Play primary %08x", hr);
240 return false;
241 }
242
243 systemSoundOn = true;
244
245 return true;
246 }
247
pause()248 void DirectSound::pause()
249 {
250 if(dsbSecondary != NULL) {
251 DWORD status = 0;
252 dsbSecondary->GetStatus(&status);
253
254 if(status & DSBSTATUS_PLAYING) {
255 dsbSecondary->Stop();
256 }
257 }
258 }
259
reset()260 void DirectSound::reset()
261 {
262 if(dsbSecondary) {
263 dsbSecondary->Stop();
264 dsbSecondary->SetCurrentPosition(0);
265 }
266 }
267
resume()268 void DirectSound::resume()
269 {
270 if(dsbSecondary != NULL) {
271 dsbSecondary->Play(0,0,DSBPLAY_LOOPING);
272 }
273 }
274
write()275 void DirectSound::write()
276 {
277 int len = soundBufferLen;
278 LPVOID lpvPtr1;
279 DWORD dwBytes1;
280 LPVOID lpvPtr2;
281 DWORD dwBytes2;
282
283 if(!pDirectSound)
284 return;
285
286 if(theApp.soundRecording) {
287 if(dsbSecondary) {
288 if(theApp.soundRecorder == NULL) {
289 theApp.soundRecorder = new WavWriter;
290 WAVEFORMATEX format;
291 dsbSecondary->GetFormat(&format, sizeof(format), NULL);
292 if(theApp.soundRecorder->Open(theApp.soundRecordName))
293 theApp.soundRecorder->SetFormat(&format);
294 }
295 }
296
297 if(theApp.soundRecorder) {
298 theApp.soundRecorder->AddSound((u8 *)soundFinalWave, len);
299 }
300 }
301
302 if(theApp.aviRecording) {
303 if(theApp.aviRecorder) {
304 if(dsbSecondary) {
305 if(!theApp.aviRecorder->IsSoundAdded()) {
306 WAVEFORMATEX format;
307 dsbSecondary->GetFormat(&format, sizeof(format), NULL);
308 theApp.aviRecorder->SetSoundFormat(&format);
309 }
310 }
311
312 theApp.aviRecorder->AddSound((const char *)soundFinalWave, len);
313 }
314 }
315
316 HRESULT hr;
317
318 if(!speedup && synchronize && !theApp.throttle) {
319 DWORD status=0;
320 hr = dsbSecondary->GetStatus(&status);
321 if(status && DSBSTATUS_PLAYING) {
322 if(!soundPaused) {
323 DWORD play;
324 while(true) {
325 dsbSecondary->GetCurrentPosition(&play, NULL);
326
327 if(play < soundNextPosition ||
328 play > soundNextPosition+soundBufferLen) {
329 break;
330 }
331
332 if(dsbEvent) {
333 WaitForSingleObject(dsbEvent, 50);
334 }
335 }
336 }
337 } else {
338 soundPaused = 1;
339 }
340 }
341 // Obtain memory address of write block. This will be in two parts
342 // if the block wraps around.
343 hr = dsbSecondary->Lock(soundNextPosition, soundBufferLen, &lpvPtr1,
344 &dwBytes1, &lpvPtr2, &dwBytes2,
345 0);
346
347 // If DSERR_BUFFERLOST is returned, restore and retry lock.
348 if (DSERR_BUFFERLOST == hr) {
349 dsbSecondary->Restore();
350 hr = dsbSecondary->Lock(soundNextPosition, soundBufferLen,&lpvPtr1,
351 &dwBytes1, &lpvPtr2, &dwBytes2,
352 0);
353 }
354
355 soundNextPosition += soundBufferLen;
356 soundNextPosition = soundNextPosition % soundBufferTotalLen;
357
358 if SUCCEEDED(hr) {
359 // Write to pointers.
360 CopyMemory(lpvPtr1, soundFinalWave, dwBytes1);
361 if (NULL != lpvPtr2) {
362 CopyMemory(lpvPtr2, soundFinalWave+dwBytes1, dwBytes2);
363 }
364 // Release the data back to DirectSound.
365 hr = dsbSecondary->Unlock(lpvPtr1, dwBytes1, lpvPtr2,
366 dwBytes2);
367 }
368 }
369
newDirectSound()370 ISound *newDirectSound()
371 {
372 return new DirectSound();
373 }
374