1 /*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2021 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20 */
21 #include "../../SDL_internal.h"
22
23 #if SDL_AUDIO_DRIVER_OS2
24
25 /* Allow access to a raw mixing buffer */
26
27 #include "../../core/os2/SDL_os2.h"
28
29 #include "SDL_audio.h"
30 #include "../SDL_audio_c.h"
31 #include "SDL_os2audio.h"
32
33 /*
34 void lockIncr(volatile int *piVal);
35 #pragma aux lockIncr = \
36 "lock add [eax], 1 "\
37 parm [eax];
38
39 void lockDecr(volatile int *piVal);
40 #pragma aux lockDecr = \
41 "lock sub [eax], 1 "\
42 parm [eax];
43 */
44
_getEnvULong(const char * name,ULONG ulMax,ULONG ulDefault)45 static ULONG _getEnvULong(const char *name, ULONG ulMax, ULONG ulDefault)
46 {
47 ULONG ulValue;
48 char* end;
49 char* envval = SDL_getenv(name);
50
51 if (envval == NULL)
52 return ulDefault;
53
54 ulValue = SDL_strtoul(envval, &end, 10);
55 return (end == envval) || (ulValue > ulMax)? ulDefault : ulMax;
56 }
57
_MCIError(const char * func,ULONG ulResult)58 static int _MCIError(const char *func, ULONG ulResult)
59 {
60 CHAR acBuf[128];
61 mciGetErrorString(ulResult, acBuf, sizeof(acBuf));
62 return SDL_SetError("[%s] %s", func, acBuf);
63 }
64
_mixIOError(const char * function,ULONG ulRC)65 static void _mixIOError(const char *function, ULONG ulRC)
66 {
67 debug_os2("%s() - failed, rc = 0x%X (%s)",
68 function, ulRC,
69 (ulRC == MCIERR_INVALID_MODE) ? "Mixer mode does not match request" :
70 (ulRC == MCIERR_INVALID_BUFFER) ? "Caller sent an invalid buffer" : "unknown");
71 }
72
cbAudioWriteEvent(ULONG ulStatus,PMCI_MIX_BUFFER pBuffer,ULONG ulFlags)73 static LONG APIENTRY cbAudioWriteEvent(ULONG ulStatus, PMCI_MIX_BUFFER pBuffer,
74 ULONG ulFlags)
75 {
76 SDL_PrivateAudioData *pAData = (SDL_PrivateAudioData *)pBuffer->ulUserParm;
77 ULONG ulRC;
78
79 if (ulFlags != MIX_WRITE_COMPLETE) {
80 debug_os2("flags = 0x%X", ulFlags);
81 return 0;
82 }
83
84 /*lockDecr((int *)&pAData->ulQueuedBuf);*/
85 ulRC = DosPostEventSem(pAData->hevBuf);
86 if (ulRC != NO_ERROR && ulRC != ERROR_ALREADY_POSTED) {
87 debug_os2("DosPostEventSem(), rc = %u", ulRC);
88 }
89
90 return 1; /* return value doesn't seem to matter. */
91 }
92
cbAudioReadEvent(ULONG ulStatus,PMCI_MIX_BUFFER pBuffer,ULONG ulFlags)93 static LONG APIENTRY cbAudioReadEvent(ULONG ulStatus, PMCI_MIX_BUFFER pBuffer,
94 ULONG ulFlags)
95 {
96 SDL_PrivateAudioData *pAData = (SDL_PrivateAudioData *)pBuffer->ulUserParm;
97 ULONG ulRC;
98
99 if (ulFlags != MIX_READ_COMPLETE) {
100 debug_os2("flags = 0x%X", ulFlags);
101 return 0;
102 }
103
104 pAData->stMCIMixSetup.pmixRead(pAData->stMCIMixSetup.ulMixHandle, pBuffer, 1);
105
106 ulRC = DosPostEventSem(pAData->hevBuf);
107 if (ulRC != NO_ERROR && ulRC != ERROR_ALREADY_POSTED) {
108 debug_os2("DosPostEventSem(), rc = %u", ulRC);
109 }
110
111 return 1;
112 }
113
114
OS2_DetectDevices(void)115 static void OS2_DetectDevices(void)
116 {
117 MCI_SYSINFO_PARMS stMCISysInfo;
118 CHAR acBuf[256];
119 ULONG ulDevicesNum;
120 MCI_SYSINFO_LOGDEVICE stLogDevice;
121 MCI_SYSINFO_PARMS stSysInfoParams;
122 ULONG ulRC;
123 ULONG ulHandle = 0;
124
125 acBuf[0] = '\0';
126 stMCISysInfo.pszReturn = acBuf;
127 stMCISysInfo.ulRetSize = sizeof(acBuf);
128 stMCISysInfo.usDeviceType = MCI_DEVTYPE_AUDIO_AMPMIX;
129 ulRC = mciSendCommand(0, MCI_SYSINFO, MCI_WAIT | MCI_SYSINFO_QUANTITY,
130 &stMCISysInfo, 0);
131 if (ulRC != NO_ERROR) {
132 debug_os2("MCI_SYSINFO, MCI_SYSINFO_QUANTITY - failed, rc = 0x%X", ulRC);
133 return;
134 }
135
136 ulDevicesNum = SDL_strtoul(stMCISysInfo.pszReturn, NULL, 10);
137
138 for (stSysInfoParams.ulNumber = 0; stSysInfoParams.ulNumber < ulDevicesNum;
139 stSysInfoParams.ulNumber++) {
140 /* Get device install name. */
141 stSysInfoParams.pszReturn = acBuf;
142 stSysInfoParams.ulRetSize = sizeof(acBuf);
143 stSysInfoParams.usDeviceType = MCI_DEVTYPE_AUDIO_AMPMIX;
144 ulRC = mciSendCommand(0, MCI_SYSINFO, MCI_WAIT | MCI_SYSINFO_INSTALLNAME,
145 &stSysInfoParams, 0);
146 if (ulRC != NO_ERROR) {
147 debug_os2("MCI_SYSINFO, MCI_SYSINFO_INSTALLNAME - failed, rc = 0x%X", ulRC);
148 continue;
149 }
150
151 /* Get textual product description. */
152 stSysInfoParams.ulItem = MCI_SYSINFO_QUERY_DRIVER;
153 stSysInfoParams.pSysInfoParm = &stLogDevice;
154 SDL_strlcpy(stLogDevice.szInstallName, stSysInfoParams.pszReturn, MAX_DEVICE_NAME);
155 ulRC = mciSendCommand(0, MCI_SYSINFO, MCI_WAIT | MCI_SYSINFO_ITEM,
156 &stSysInfoParams, 0);
157 if (ulRC != NO_ERROR) {
158 debug_os2("MCI_SYSINFO, MCI_SYSINFO_ITEM - failed, rc = 0x%X", ulRC);
159 continue;
160 }
161
162 ulHandle++;
163 SDL_AddAudioDevice(0, stLogDevice.szProductInfo, NULL, (void *)(ulHandle));
164 ulHandle++;
165 SDL_AddAudioDevice(1, stLogDevice.szProductInfo, NULL, (void *)(ulHandle));
166 }
167 }
168
OS2_WaitDevice(_THIS)169 static void OS2_WaitDevice(_THIS)
170 {
171 SDL_PrivateAudioData *pAData = (SDL_PrivateAudioData *)_this->hidden;
172 ULONG ulRC;
173
174 /* Wait for an audio chunk to finish */
175 ulRC = DosWaitEventSem(pAData->hevBuf, 5000);
176 if (ulRC != NO_ERROR) {
177 debug_os2("DosWaitEventSem(), rc = %u", ulRC);
178 }
179 }
180
OS2_GetDeviceBuf(_THIS)181 static Uint8 *OS2_GetDeviceBuf(_THIS)
182 {
183 SDL_PrivateAudioData *pAData = (SDL_PrivateAudioData *)_this->hidden;
184 return (Uint8 *) pAData->aMixBuffers[pAData->ulNextBuf].pBuffer;
185 }
186
OS2_PlayDevice(_THIS)187 static void OS2_PlayDevice(_THIS)
188 {
189 SDL_PrivateAudioData *pAData = (SDL_PrivateAudioData *)_this->hidden;
190 ULONG ulRC;
191 PMCI_MIX_BUFFER pMixBuffer = &pAData->aMixBuffers[pAData->ulNextBuf];
192
193 /* Queue it up */
194 /*lockIncr((int *)&pAData->ulQueuedBuf);*/
195 ulRC = pAData->stMCIMixSetup.pmixWrite(pAData->stMCIMixSetup.ulMixHandle,
196 pMixBuffer, 1);
197 if (ulRC != MCIERR_SUCCESS) {
198 _mixIOError("pmixWrite", ulRC);
199 } else {
200 pAData->ulNextBuf = (pAData->ulNextBuf + 1) % pAData->cMixBuffers;
201 }
202 }
203
OS2_CloseDevice(_THIS)204 static void OS2_CloseDevice(_THIS)
205 {
206 SDL_PrivateAudioData *pAData = (SDL_PrivateAudioData *)_this->hidden;
207 MCI_GENERIC_PARMS sMCIGenericParms;
208 ULONG ulRC;
209
210 if (pAData == NULL)
211 return;
212
213 /* Close up audio */
214 if (pAData->usDeviceId != (USHORT)~0) { /* Device is open. */
215 if (pAData->stMCIMixSetup.ulBitsPerSample != 0) { /* Mixer was initialized. */
216 ulRC = mciSendCommand(pAData->usDeviceId, MCI_MIXSETUP,
217 MCI_WAIT | MCI_MIXSETUP_DEINIT,
218 &pAData->stMCIMixSetup, 0);
219 if (ulRC != MCIERR_SUCCESS) {
220 debug_os2("MCI_MIXSETUP, MCI_MIXSETUP_DEINIT - failed");
221 }
222 }
223
224 if (pAData->cMixBuffers != 0) { /* Buffers was allocated. */
225 MCI_BUFFER_PARMS stMCIBuffer;
226
227 stMCIBuffer.ulBufferSize = pAData->aMixBuffers[0].ulBufferLength;
228 stMCIBuffer.ulNumBuffers = pAData->cMixBuffers;
229 stMCIBuffer.pBufList = pAData->aMixBuffers;
230
231 ulRC = mciSendCommand(pAData->usDeviceId, MCI_BUFFER,
232 MCI_WAIT | MCI_DEALLOCATE_MEMORY, &stMCIBuffer, 0);
233 if (ulRC != MCIERR_SUCCESS) {
234 debug_os2("MCI_BUFFER, MCI_DEALLOCATE_MEMORY - failed");
235 }
236 }
237
238 ulRC = mciSendCommand(pAData->usDeviceId, MCI_CLOSE, MCI_WAIT,
239 &sMCIGenericParms, 0);
240 if (ulRC != MCIERR_SUCCESS) {
241 debug_os2("MCI_CLOSE - failed");
242 }
243 }
244
245 if (pAData->hevBuf != NULLHANDLE)
246 DosCloseEventSem(pAData->hevBuf);
247
248 SDL_free(pAData);
249 }
250
OS2_OpenDevice(_THIS,void * handle,const char * devname,int iscapture)251 static int OS2_OpenDevice(_THIS, void *handle, const char *devname,
252 int iscapture)
253 {
254 SDL_PrivateAudioData *pAData;
255 SDL_AudioFormat SDLAudioFmt;
256 MCI_AMP_OPEN_PARMS stMCIAmpOpen;
257 MCI_BUFFER_PARMS stMCIBuffer;
258 ULONG ulRC;
259 ULONG ulIdx;
260 BOOL new_freq;
261
262 new_freq = FALSE;
263 SDL_zero(stMCIAmpOpen);
264 SDL_zero(stMCIBuffer);
265
266 for (SDLAudioFmt = SDL_FirstAudioFormat(_this->spec.format);
267 SDLAudioFmt != 0; SDLAudioFmt = SDL_NextAudioFormat()) {
268 if (SDLAudioFmt == AUDIO_U8 || SDLAudioFmt == AUDIO_S16)
269 break;
270 }
271 if (SDLAudioFmt == 0) {
272 debug_os2("Unsupported audio format, AUDIO_S16 used");
273 SDLAudioFmt = AUDIO_S16;
274 }
275
276 pAData = (SDL_PrivateAudioData *) SDL_calloc(1, sizeof(struct SDL_PrivateAudioData));
277 if (pAData == NULL)
278 return SDL_OutOfMemory();
279 _this->hidden = pAData;
280
281 ulRC = DosCreateEventSem(NULL, &pAData->hevBuf, DCE_AUTORESET, TRUE);
282 if (ulRC != NO_ERROR) {
283 debug_os2("DosCreateEventSem() failed, rc = %u", ulRC);
284 return -1;
285 }
286
287 /* Open audio device */
288 stMCIAmpOpen.usDeviceID = (handle != NULL) ? ((ULONG)handle - 1) : 0;
289 stMCIAmpOpen.pszDeviceType = (PSZ)MCI_DEVTYPE_AUDIO_AMPMIX;
290 ulRC = mciSendCommand(0, MCI_OPEN,
291 (_getEnvULong("SDL_AUDIO_SHARE", 1, 0) != 0)?
292 MCI_WAIT | MCI_OPEN_TYPE_ID | MCI_OPEN_SHAREABLE :
293 MCI_WAIT | MCI_OPEN_TYPE_ID,
294 &stMCIAmpOpen, 0);
295 if (ulRC != MCIERR_SUCCESS) {
296 stMCIAmpOpen.usDeviceID = (USHORT)~0;
297 return _MCIError("MCI_OPEN", ulRC);
298 }
299 pAData->usDeviceId = stMCIAmpOpen.usDeviceID;
300
301 if (iscapture != 0) {
302 MCI_CONNECTOR_PARMS stMCIConnector;
303 MCI_AMP_SET_PARMS stMCIAmpSet;
304 BOOL fLineIn = _getEnvULong("SDL_AUDIO_LINEIN", 1, 0);
305
306 /* Set particular connector. */
307 SDL_zero(stMCIConnector);
308 stMCIConnector.ulConnectorType = (fLineIn)? MCI_LINE_IN_CONNECTOR :
309 MCI_MICROPHONE_CONNECTOR;
310 mciSendCommand(stMCIAmpOpen.usDeviceID, MCI_CONNECTOR,
311 MCI_WAIT | MCI_ENABLE_CONNECTOR |
312 MCI_CONNECTOR_TYPE, &stMCIConnector, 0);
313
314 /* Disable monitor. */
315 SDL_zero(stMCIAmpSet);
316 stMCIAmpSet.ulItem = MCI_AMP_SET_MONITOR;
317 mciSendCommand(stMCIAmpOpen.usDeviceID, MCI_SET,
318 MCI_WAIT | MCI_SET_OFF | MCI_SET_ITEM,
319 &stMCIAmpSet, 0);
320
321 /* Set record volume. */
322 stMCIAmpSet.ulLevel = _getEnvULong("SDL_AUDIO_RECVOL", 100, 90);
323 stMCIAmpSet.ulItem = MCI_AMP_SET_AUDIO;
324 stMCIAmpSet.ulAudio = MCI_SET_AUDIO_ALL; /* Both cnannels. */
325 stMCIAmpSet.ulValue = (fLineIn) ? MCI_LINE_IN_CONNECTOR :
326 MCI_MICROPHONE_CONNECTOR ;
327
328 mciSendCommand(stMCIAmpOpen.usDeviceID, MCI_SET,
329 MCI_WAIT | MCI_SET_AUDIO | MCI_AMP_SET_GAIN,
330 &stMCIAmpSet, 0);
331 }
332
333 _this->spec.format = SDLAudioFmt;
334 _this->spec.channels = _this->spec.channels > 1 ? 2 : 1;
335 if (_this->spec.freq < 8000) {
336 _this->spec.freq = 8000;
337 new_freq = TRUE;
338 } else if (_this->spec.freq > 48000) {
339 _this->spec.freq = 48000;
340 new_freq = TRUE;
341 }
342
343 /* Setup mixer. */
344 pAData->stMCIMixSetup.ulFormatTag = MCI_WAVE_FORMAT_PCM;
345 pAData->stMCIMixSetup.ulBitsPerSample = SDL_AUDIO_BITSIZE(SDLAudioFmt);
346 pAData->stMCIMixSetup.ulSamplesPerSec = _this->spec.freq;
347 pAData->stMCIMixSetup.ulChannels = _this->spec.channels;
348 pAData->stMCIMixSetup.ulDeviceType = MCI_DEVTYPE_WAVEFORM_AUDIO;
349 if (iscapture == 0) {
350 pAData->stMCIMixSetup.ulFormatMode= MCI_PLAY;
351 pAData->stMCIMixSetup.pmixEvent = cbAudioWriteEvent;
352 } else {
353 pAData->stMCIMixSetup.ulFormatMode= MCI_RECORD;
354 pAData->stMCIMixSetup.pmixEvent = cbAudioReadEvent;
355 }
356
357 ulRC = mciSendCommand(pAData->usDeviceId, MCI_MIXSETUP,
358 MCI_WAIT | MCI_MIXSETUP_INIT, &pAData->stMCIMixSetup, 0);
359 if (ulRC != MCIERR_SUCCESS && _this->spec.freq > 44100) {
360 new_freq = TRUE;
361 pAData->stMCIMixSetup.ulSamplesPerSec = 44100;
362 _this->spec.freq = 44100;
363 ulRC = mciSendCommand(pAData->usDeviceId, MCI_MIXSETUP,
364 MCI_WAIT | MCI_MIXSETUP_INIT, &pAData->stMCIMixSetup, 0);
365 }
366
367 debug_os2("Setup mixer [BPS: %u, Freq.: %u, Channels: %u]: %s",
368 pAData->stMCIMixSetup.ulBitsPerSample,
369 pAData->stMCIMixSetup.ulSamplesPerSec,
370 pAData->stMCIMixSetup.ulChannels,
371 (ulRC == MCIERR_SUCCESS)? "SUCCESS" : "FAIL");
372
373 if (ulRC != MCIERR_SUCCESS) {
374 pAData->stMCIMixSetup.ulBitsPerSample = 0;
375 return _MCIError("MCI_MIXSETUP", ulRC);
376 }
377
378 if (_this->spec.samples == 0 || new_freq == TRUE) {
379 /* also see SDL_audio.c:prepare_audiospec() */
380 /* Pick a default of ~46 ms at desired frequency */
381 Uint32 samples = (_this->spec.freq / 1000) * 46;
382 Uint32 power2 = 1;
383 while (power2 < samples) {
384 power2 <<= 1;
385 }
386 _this->spec.samples = power2;
387 }
388 /* Update the fragment size as size in bytes */
389 SDL_CalculateAudioSpec(&_this->spec);
390
391 /* Allocate memory buffers */
392 stMCIBuffer.ulBufferSize = _this->spec.size;/* (_this->spec.freq / 1000) * 100 */
393 stMCIBuffer.ulNumBuffers = NUM_BUFFERS;
394 stMCIBuffer.pBufList = pAData->aMixBuffers;
395
396 ulRC = mciSendCommand(pAData->usDeviceId, MCI_BUFFER,
397 MCI_WAIT | MCI_ALLOCATE_MEMORY, &stMCIBuffer, 0);
398 if (ulRC != MCIERR_SUCCESS) {
399 return _MCIError("MCI_BUFFER", ulRC);
400 }
401 pAData->cMixBuffers = stMCIBuffer.ulNumBuffers;
402 _this->spec.size = stMCIBuffer.ulBufferSize;
403
404 /* Fill all device buffers with data */
405 for (ulIdx = 0; ulIdx < stMCIBuffer.ulNumBuffers; ulIdx++) {
406 pAData->aMixBuffers[ulIdx].ulFlags = 0;
407 pAData->aMixBuffers[ulIdx].ulBufferLength = stMCIBuffer.ulBufferSize;
408 pAData->aMixBuffers[ulIdx].ulUserParm = (ULONG)pAData;
409
410 SDL_memset(((PMCI_MIX_BUFFER)stMCIBuffer.pBufList)[ulIdx].pBuffer,
411 _this->spec.silence, stMCIBuffer.ulBufferSize);
412 }
413
414 /* Write buffers to kick off the amp mixer */
415 ulRC = pAData->stMCIMixSetup.pmixWrite(pAData->stMCIMixSetup.ulMixHandle,
416 pAData->aMixBuffers, 1);
417 if (ulRC != MCIERR_SUCCESS) {
418 _mixIOError("pmixWrite", ulRC);
419 return -1;
420 }
421
422 return 0;
423 }
424
425
OS2_Init(SDL_AudioDriverImpl * impl)426 static int OS2_Init(SDL_AudioDriverImpl * impl)
427 {
428 /* Set the function pointers */
429 impl->DetectDevices = OS2_DetectDevices;
430 impl->OpenDevice = OS2_OpenDevice;
431 impl->PlayDevice = OS2_PlayDevice;
432 impl->WaitDevice = OS2_WaitDevice;
433 impl->GetDeviceBuf = OS2_GetDeviceBuf;
434 impl->CloseDevice = OS2_CloseDevice;
435
436 /* TODO: IMPLEMENT CAPTURE SUPPORT:
437 impl->CaptureFromDevice = ;
438 impl->FlushCapture = ;
439 impl->HasCaptureSupport = SDL_TRUE;
440 */
441 return 1; /* this audio target is available. */
442 }
443
444
445 AudioBootStrap OS2AUDIO_bootstrap = {
446 "DART", "OS/2 DART", OS2_Init, 0
447 };
448
449 #endif /* SDL_AUDIO_DRIVER_OS2 */
450
451 /* vi: set ts=4 sw=4 expandtab: */
452