1 /* MikMod sound library
2 (c) 1998, 1999, 2000 Miodrag Vallat and others - see file AUTHORS for
3 complete list.
4
5 This library is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Library General Public License as
7 published by the Free Software Foundation; either version 2 of
8 the License, or (at your option) 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 Library General Public License for more details.
14
15 You should have received a copy of the GNU Library General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
18 02111-1307, USA.
19 */
20
21 /*==============================================================================
22
23 $Id$
24
25 Driver for output on win32 platforms using the multimedia API
26
27 ==============================================================================*/
28
29 /*
30
31 Written by Bjornar Henden <bhenden@online.no>
32
33 */
34
35 #ifdef HAVE_CONFIG_H
36 #include "config.h"
37 #endif
38
39 #include "mikmod_internals.h"
40
41 #ifdef DRV_WIN
42
43 #include <windows.h>
44
45 #if defined(_MSC_VER)
46 #pragma comment(lib,"winmm.lib")
47 #if (_MSC_VER < 1300)
48 typedef DWORD DWORD_PTR;
49 #endif
50 #endif
51
52 /* PF_XMMI64_INSTRUCTIONS_AVAILABLE not in all SDKs. */
53 #ifndef PF_XMMI64_INSTRUCTIONS_AVAILABLE
54 #define PF_XMMI64_INSTRUCTIONS_AVAILABLE 10
55 #endif
56
57 #ifndef WAVE_FORMAT_IEEE_FLOAT
58 #define WAVE_FORMAT_IEEE_FLOAT 0x0003
59 #endif
60
61 #define NUMBUFFERS 6 /* number of buffers */
62 #define BUFFERSIZE 120 /* buffer size in milliseconds */
63
64 static HWAVEOUT hwaveout;
65 static WAVEHDR header[NUMBUFFERS];
66 static HPSTR buffer[NUMBUFFERS]; /* pointers to buffers */
67 static WORD buffersout; /* number of buffers playing/about to be played */
68 static WORD nextbuffer; /* next buffer to be mixed */
69 static ULONG buffersize; /* buffer size in bytes */
70
71 /* converts Windows error to libmikmod error */
WIN_GetError(MMRESULT mmr)72 static int WIN_GetError(MMRESULT mmr)
73 {
74 switch (mmr) {
75 case MMSYSERR_INVALHANDLE:
76 return MMERR_WINMM_HANDLE;
77 case MMSYSERR_NODRIVER:
78 return MMERR_OPENING_AUDIO;
79 case MMSYSERR_NOMEM:
80 return MMERR_OUT_OF_MEMORY;
81 case MMSYSERR_ALLOCATED:
82 return MMERR_WINMM_ALLOCATED;
83 case MMSYSERR_BADDEVICEID:
84 return MMERR_WINMM_DEVICEID;
85 case WAVERR_BADFORMAT:
86 return MMERR_WINMM_FORMAT;
87 default:
88 return MMERR_WINMM_UNKNOWN; /* should hopefully not happen */
89 }
90 }
91
WIN_IsThere(void)92 static BOOL WIN_IsThere(void)
93 {
94 return waveOutGetNumDevs()>0?1:0;
95 }
96
WIN_CallBack(HWAVEOUT hwo,UINT uMsg,DWORD_PTR dwInstance,DWORD_PTR dwParam1,DWORD_PTR dwParam2)97 static void CALLBACK WIN_CallBack(HWAVEOUT hwo,UINT uMsg,DWORD_PTR dwInstance,DWORD_PTR dwParam1,DWORD_PTR dwParam2)
98 {
99 if (uMsg==WOM_DONE) --buffersout;
100 }
101
WIN_Init(void)102 static int WIN_Init(void)
103 {
104 WAVEFORMATEX wfe;
105 WORD samplesize;
106 MMRESULT mmr;
107 int n;
108
109 samplesize=1;
110 if (md_mode&DMODE_STEREO) samplesize<<=1;
111 if (md_mode&DMODE_FLOAT) samplesize<<=2;
112 else if (md_mode&DMODE_16BITS) samplesize<<=1;
113
114 wfe.wFormatTag=(md_mode&DMODE_FLOAT)? WAVE_FORMAT_IEEE_FLOAT : WAVE_FORMAT_PCM;
115 wfe.nChannels=md_mode&DMODE_STEREO?2:1;
116 wfe.nSamplesPerSec=md_mixfreq;
117 wfe.nAvgBytesPerSec=md_mixfreq*samplesize;
118 wfe.nBlockAlign=samplesize;
119 wfe.wBitsPerSample=(md_mode&DMODE_FLOAT)?32:(md_mode&DMODE_16BITS)?16:8;
120 wfe.cbSize=sizeof(wfe);
121
122 mmr=waveOutOpen(&hwaveout,WAVE_MAPPER,&wfe,(DWORD_PTR)WIN_CallBack,0,CALLBACK_FUNCTION);
123 if (mmr!=MMSYSERR_NOERROR) {
124 _mm_errno=WIN_GetError(mmr);
125 return 1;
126 }
127
128 buffersize=md_mixfreq*samplesize*BUFFERSIZE/1000;
129
130 for (n=0;n<NUMBUFFERS;n++) {
131 buffer[n]=(HPSTR)MikMod_malloc(buffersize);
132 header[n].lpData=buffer[n];
133 header[n].dwBufferLength=buffersize;
134 mmr=waveOutPrepareHeader(hwaveout,&header[n],sizeof(WAVEHDR));
135 if (!buffer[n]||mmr!=MMSYSERR_NOERROR) {
136 if (!buffer[n])
137 _mm_errno=MMERR_OUT_OF_MEMORY;
138 else
139 _mm_errno=WIN_GetError(mmr);
140 return 1;
141 }
142 }
143
144 md_mode|=DMODE_SOFT_MUSIC|DMODE_SOFT_SNDFX;
145 #if defined HAVE_SSE2
146 /* This test only works on Windows XP or later */
147 if (IsProcessorFeaturePresent(PF_XMMI64_INSTRUCTIONS_AVAILABLE)) {
148 md_mode|=DMODE_SIMDMIXER;
149 }
150 #endif
151 buffersout=nextbuffer=0;
152 return VC_Init();
153 }
154
WIN_Exit(void)155 static void WIN_Exit(void)
156 {
157 int n;
158
159 VC_Exit();
160 if (hwaveout) {
161 for (n=0;n<NUMBUFFERS;n++) {
162 if (header[n].dwFlags&WHDR_PREPARED)
163 waveOutUnprepareHeader(hwaveout,&header[n],sizeof(WAVEHDR));
164 MikMod_free(buffer[n]);
165 buffer[n] = NULL;
166 }
167 while (waveOutClose(hwaveout)==WAVERR_STILLPLAYING) Sleep(10);
168 hwaveout=NULL;
169 }
170 }
171
WIN_Update(void)172 static void WIN_Update(void)
173 {
174 ULONG done;
175
176 while (buffersout<NUMBUFFERS) {
177 done=VC_WriteBytes((SBYTE*)buffer[nextbuffer],buffersize);
178 if (!done) break;
179 header[nextbuffer].dwBufferLength=done;
180 waveOutWrite(hwaveout,&header[nextbuffer],sizeof(WAVEHDR));
181 if (++nextbuffer>=NUMBUFFERS) nextbuffer%=NUMBUFFERS;
182 ++buffersout;
183 }
184 }
185
WIN_PlayStop(void)186 static void WIN_PlayStop(void)
187 {
188 if (hwaveout) waveOutReset(hwaveout);
189 VC_PlayStop();
190 }
191
192 MIKMODAPI MDRIVER drv_win={
193 NULL,
194 "Windows waveform-audio",
195 "Windows waveform-audio driver v0.2",
196 0,255,
197 "winmm",
198 NULL,
199 NULL,
200 WIN_IsThere,
201 VC_SampleLoad,
202 VC_SampleUnload,
203 VC_SampleSpace,
204 VC_SampleLength,
205 WIN_Init,
206 WIN_Exit,
207 NULL,
208 VC_SetNumVoices,
209 VC_PlayStart,
210 WIN_PlayStop,
211 WIN_Update,
212 NULL,
213 VC_VoiceSetVolume,
214 VC_VoiceGetVolume,
215 VC_VoiceSetFrequency,
216 VC_VoiceGetFrequency,
217 VC_VoiceSetPanning,
218 VC_VoiceGetPanning,
219 VC_VoicePlay,
220 VC_VoiceStop,
221 VC_VoiceStopped,
222 VC_VoiceGetPosition,
223 VC_VoiceRealVolume
224 };
225
226 #else
227
228 MISSING(drv_win);
229
230 #endif
231
232 /* ex:set ts=4: */
233