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