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