1 /*
2  * (C) Gražvydas "notaz" Ignotas, 2009
3  *
4  * This work is licensed under the terms of any of these licenses
5  * (at your option):
6  *  - GNU GPL, version 2 or later.
7  *  - GNU LGPL, version 2.1 or later.
8  *  - MAME license.
9  * See the COPYING file in the top-level directory.
10  */
11 
12 #include <stdlib.h>
13 #define WIN32_LEAN_AND_MEAN
14 #include <windows.h>
15 #include <mmsystem.h>
16 #include <dsound.h>
17 
18 #include "dsnd.h"
19 #include "../lprintf.h"
20 
21 #define NSEGS 4
22 #define RELEASE(x) if (x) x->Release();  x=NULL;
23 
24 static LPDIRECTSOUND DSound;
25 static LPDIRECTSOUNDBUFFER LoopBuffer;
26 static LPDIRECTSOUNDNOTIFY DSoundNotify;
27 static HANDLE seg_played_event;
28 static int LoopLen, LoopWrite, LoopSeg; // bytes
29 
LoopBlank(void)30 static int LoopBlank(void)
31 {
32   void *mema=NULL,*memb=NULL;
33   DWORD sizea=0,sizeb=0;
34 
35   LoopBuffer->Lock(0, LoopLen, &mema,&sizea, &memb,&sizeb, 0);
36 
37   if (mema) memset(mema,0,sizea);
38 
39   LoopBuffer->Unlock(mema,sizea, memb,sizeb);
40 
41   return 0;
42 }
43 
DSoundInit(HWND wnd_coop,int rate,int stereo,int seg_samples)44 int DSoundInit(HWND wnd_coop, int rate, int stereo, int seg_samples)
45 {
46   DSBUFFERDESC dsbd;
47   WAVEFORMATEX wfx;
48   DSBPOSITIONNOTIFY notifies[NSEGS];
49   int i;
50 
51   memset(&dsbd,0,sizeof(dsbd));
52   memset(&wfx,0,sizeof(wfx));
53 
54   // Make wave format:
55   wfx.wFormatTag=WAVE_FORMAT_PCM;
56   wfx.nChannels=stereo ? 2 : 1;
57   wfx.nSamplesPerSec=rate;
58   wfx.wBitsPerSample=16;
59 
60   wfx.nBlockAlign=(WORD)((wfx.nChannels*wfx.wBitsPerSample)>>3);
61   wfx.nAvgBytesPerSec=wfx.nBlockAlign*wfx.nSamplesPerSec;
62 
63   // Create the DirectSound interface:
64   DirectSoundCreate(NULL,&DSound,NULL);
65   if (DSound==NULL) return 1;
66 
67   LoopSeg = seg_samples * 2;
68   if (stereo)
69     LoopSeg *= 2;
70 
71   LoopLen = LoopSeg * NSEGS;
72 
73   DSound->SetCooperativeLevel(wnd_coop, DSSCL_PRIORITY);
74   dsbd.dwFlags=DSBCAPS_GLOBALFOCUS;  // Play in background
75   dsbd.dwFlags|=DSBCAPS_GETCURRENTPOSITION2|DSBCAPS_CTRLPOSITIONNOTIFY;
76 
77   // Create the looping buffer:
78   dsbd.dwSize=sizeof(dsbd);
79   dsbd.dwBufferBytes=LoopLen;
80   dsbd.lpwfxFormat=&wfx;
81 
82   DSound->CreateSoundBuffer(&dsbd,&LoopBuffer,NULL);
83   if (LoopBuffer==NULL) return 1;
84 
85   LoopBuffer->QueryInterface(IID_IDirectSoundNotify, (LPVOID*)&DSoundNotify);
86   if (DSoundNotify == NULL) {
87     lprintf("QueryInterface(IID_IDirectSoundNotify) failed\n");
88     goto out;
89   }
90 
91   seg_played_event = CreateEvent(NULL, 0, 0, NULL);
92   if (seg_played_event == NULL)
93     goto out;
94 
95   for (i = 0; i < NSEGS; i++) {
96     notifies[i].dwOffset = i * LoopSeg;
97     notifies[i].hEventNotify = seg_played_event;
98   }
99   i = DSoundNotify->SetNotificationPositions(NSEGS, notifies);
100   if (i != DS_OK) {
101     lprintf("SetNotificationPositions failed\n");
102     goto out;
103   }
104 
105 out:
106   LoopBlank();
107   LoopBuffer->Play(0, 0, DSBPLAY_LOOPING);
108   return 0;
109 }
110 
DSoundExit(void)111 void DSoundExit(void)
112 {
113   if (LoopBuffer)
114     LoopBuffer->Stop();
115   RELEASE(DSoundNotify);
116   RELEASE(LoopBuffer)
117   RELEASE(DSound)
118   CloseHandle(seg_played_event);
119   seg_played_event = NULL;
120 }
121 
WriteSeg(const void * buff)122 static int WriteSeg(const void *buff)
123 {
124   void *mema=NULL,*memb=NULL;
125   DWORD sizea=0,sizeb=0;
126   int ret;
127 
128   // Lock the segment at 'LoopWrite' and copy the next segment in
129   ret = LoopBuffer->Lock(LoopWrite, LoopSeg, &mema, &sizea, &memb, &sizeb, 0);
130   if (ret != DS_OK)
131     lprintf("LoopBuffer->Lock() failed: %i\n", ret);
132 
133   if (mema) memcpy(mema,buff,sizea);
134 //  if (memb) memcpy(memb,DSoundNext+sizea,sizeb);
135   if (sizeb != 0) lprintf("sizeb is not 0! (%i)\n", sizeb);
136 
137   ret = LoopBuffer->Unlock(mema,sizea, memb, sizeb);
138   if (ret != DS_OK)
139     lprintf("LoopBuffer->Unlock() failed: %i\n", ret);
140 
141   return 0;
142 }
143 
DSoundUpdate(const void * buff,int blocking)144 int DSoundUpdate(const void *buff, int blocking)
145 {
146   DWORD play = 0;
147   int pos;
148 
149   LoopBuffer->GetCurrentPosition(&play, NULL);
150   pos = play;
151 
152   // 'LoopWrite' is the next seg in the loop that we want to write
153   // First check that the sound 'play' pointer has moved out of it:
154   if (blocking) {
155     while (LoopWrite <= pos && pos < LoopWrite + LoopSeg) {
156       WaitForSingleObject(seg_played_event, 5000);
157       LoopBuffer->GetCurrentPosition(&play, NULL);
158       pos = play;
159     }
160   }
161   else {
162     if (LoopWrite <= pos && pos < LoopWrite + LoopSeg)
163       return 1;
164   }
165 
166   WriteSeg(buff);
167 
168   // Advance LoopWrite to next seg:
169   LoopWrite += LoopSeg;
170   if (LoopWrite + LoopSeg > LoopLen)
171     LoopWrite = 0;
172 
173   return 0;
174 }
175 
176