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