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