SDL_os2audio.c (15990B)
1 /* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2020 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 45 static ULONG _getEnvULong(PSZ pszName, ULONG ulMax, ULONG ulDefault) 46 { 47 ULONG ulValue; 48 PCHAR pcEnd; 49 PSZ pszEnvVal = SDL_getenv(pszName); 50 51 if (pszEnvVal == NULL) 52 return ulDefault; 53 54 ulValue = SDL_strtoul((const char *)pszEnvVal, &pcEnd, 10); 55 return (pcEnd == pszEnvVal) || (ulValue > ulMax)? ulDefault : ulMax; 56 } 57 58 static int _MCIError(PSZ pszFunc, ULONG ulResult) 59 { 60 CHAR acBuf[128]; 61 mciGetErrorString(ulResult, acBuf, sizeof(acBuf)); 62 return SDL_SetError("[%s] %s", pszFunc, acBuf); 63 } 64 65 static void _mixIOError(PSZ pszFunction, ULONG ulRC) 66 { 67 debug_os2("%s() - failed, rc = 0x%X (%s)", 68 pszFunction, 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 73 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; /* It seems, return value is not matter. */ 91 } 92 93 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 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 = atol(stMCISysInfo.pszReturn); 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 strcpy(stLogDevice.szInstallName, stSysInfoParams.pszReturn); 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, (void *)(ulHandle)); 164 ulHandle++; 165 SDL_AddAudioDevice(1, stLogDevice.szProductInfo, (void *)(ulHandle)); 166 } 167 } 168 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 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 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 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) { 215 /* Device is open. */ 216 if (pAData->stMCIMixSetup.ulBitsPerSample != 0) { 217 /* Mixer was initialized. */ 218 ulRC = mciSendCommand(pAData->usDeviceId, MCI_MIXSETUP, 219 MCI_WAIT | MCI_MIXSETUP_DEINIT, 220 &pAData->stMCIMixSetup, 0); 221 if (ulRC != MCIERR_SUCCESS) { 222 debug_os2("MCI_MIXSETUP, MCI_MIXSETUP_DEINIT - failed"); 223 } 224 } 225 226 if (pAData->cMixBuffers != 0) { 227 /* Buffers was allocated. */ 228 MCI_BUFFER_PARMS stMCIBuffer; 229 230 stMCIBuffer.ulBufferSize = pAData->aMixBuffers[0].ulBufferLength; 231 stMCIBuffer.ulNumBuffers = pAData->cMixBuffers; 232 stMCIBuffer.pBufList = pAData->aMixBuffers; 233 234 ulRC = mciSendCommand(pAData->usDeviceId, MCI_BUFFER, 235 MCI_WAIT | MCI_DEALLOCATE_MEMORY, &stMCIBuffer, 0); 236 if (ulRC != MCIERR_SUCCESS) { 237 debug_os2("MCI_BUFFER, MCI_DEALLOCATE_MEMORY - failed"); 238 } 239 } 240 241 ulRC = mciSendCommand(pAData->usDeviceId, MCI_CLOSE, MCI_WAIT, 242 &sMCIGenericParms, 0); 243 if (ulRC != MCIERR_SUCCESS) { 244 debug_os2("MCI_CLOSE - failed"); 245 } 246 } 247 248 if (pAData->hevBuf != NULLHANDLE) 249 DosCloseEventSem(pAData->hevBuf); 250 251 SDL_free(pAData); 252 } 253 254 static int OS2_OpenDevice(_THIS, void *handle, const char *devname, 255 int iscapture) 256 { 257 SDL_PrivateAudioData *pAData; 258 SDL_AudioFormat SDLAudioFmt; 259 MCI_AMP_OPEN_PARMS stMCIAmpOpen; 260 MCI_BUFFER_PARMS stMCIBuffer; 261 ULONG ulRC; 262 ULONG ulIdx; 263 BOOL new_freq; 264 265 new_freq = FALSE; 266 SDL_zero(stMCIAmpOpen); 267 SDL_zero(stMCIBuffer); 268 269 for (SDLAudioFmt = SDL_FirstAudioFormat(_this->spec.format); 270 SDLAudioFmt != 0; SDLAudioFmt = SDL_NextAudioFormat()) { 271 if (SDLAudioFmt == AUDIO_U8 || SDLAudioFmt == AUDIO_S16) 272 break; 273 } 274 if (SDLAudioFmt == 0) { 275 debug_os2("Unsupported audio format, AUDIO_S16 used"); 276 SDLAudioFmt = AUDIO_S16; 277 } 278 279 pAData = (SDL_PrivateAudioData *) SDL_calloc(1, sizeof(struct SDL_PrivateAudioData)); 280 if (pAData == NULL) 281 return SDL_OutOfMemory(); 282 _this->hidden = pAData; 283 284 ulRC = DosCreateEventSem(NULL, &pAData->hevBuf, DCE_AUTORESET, TRUE); 285 if (ulRC != NO_ERROR) { 286 debug_os2("DosCreateEventSem() failed, rc = %u", ulRC); 287 return -1; 288 } 289 290 /* Open audio device */ 291 stMCIAmpOpen.usDeviceID = (handle != NULL) ? ((ULONG)handle - 1) : 0; 292 stMCIAmpOpen.pszDeviceType = (PSZ)MCI_DEVTYPE_AUDIO_AMPMIX; 293 ulRC = mciSendCommand(0, MCI_OPEN, 294 (_getEnvULong("SDL_AUDIO_SHARE", 1, 0) != 0)? 295 MCI_WAIT | MCI_OPEN_TYPE_ID | MCI_OPEN_SHAREABLE : 296 MCI_WAIT | MCI_OPEN_TYPE_ID, 297 &stMCIAmpOpen, 0); 298 if (ulRC != MCIERR_SUCCESS) { 299 stMCIAmpOpen.usDeviceID = (USHORT)~0; 300 return _MCIError("MCI_OPEN", ulRC); 301 } 302 pAData->usDeviceId = stMCIAmpOpen.usDeviceID; 303 304 if (iscapture != 0) { 305 MCI_CONNECTOR_PARMS stMCIConnector; 306 MCI_AMP_SET_PARMS stMCIAmpSet; 307 BOOL fLineIn = _getEnvULong("SDL_AUDIO_LINEIN", 1, 0); 308 309 /* Set particular connector. */ 310 SDL_zero(stMCIConnector); 311 stMCIConnector.ulConnectorType = (fLineIn)? MCI_LINE_IN_CONNECTOR : 312 MCI_MICROPHONE_CONNECTOR; 313 mciSendCommand(stMCIAmpOpen.usDeviceID, MCI_CONNECTOR, 314 MCI_WAIT | MCI_ENABLE_CONNECTOR | 315 MCI_CONNECTOR_TYPE, &stMCIConnector, 0); 316 317 /* Disable monitor. */ 318 SDL_zero(stMCIAmpSet); 319 stMCIAmpSet.ulItem = MCI_AMP_SET_MONITOR; 320 mciSendCommand(stMCIAmpOpen.usDeviceID, MCI_SET, 321 MCI_WAIT | MCI_SET_OFF | MCI_SET_ITEM, 322 &stMCIAmpSet, 0); 323 324 /* Set record volume. */ 325 stMCIAmpSet.ulLevel = _getEnvULong("SDL_AUDIO_RECVOL", 100, 90); 326 stMCIAmpSet.ulItem = MCI_AMP_SET_AUDIO; 327 stMCIAmpSet.ulAudio = MCI_SET_AUDIO_ALL; /* Both cnannels. */ 328 stMCIAmpSet.ulValue = (fLineIn) ? MCI_LINE_IN_CONNECTOR : 329 MCI_MICROPHONE_CONNECTOR ; 330 331 mciSendCommand(stMCIAmpOpen.usDeviceID, MCI_SET, 332 MCI_WAIT | MCI_SET_AUDIO | MCI_AMP_SET_GAIN, 333 &stMCIAmpSet, 0); 334 } 335 336 _this->spec.format = SDLAudioFmt; 337 _this->spec.channels = _this->spec.channels > 1 ? 2 : 1; 338 if (_this->spec.freq < 8000) { 339 _this->spec.freq = 8000; 340 new_freq = TRUE; 341 } else if (_this->spec.freq > 48000) { 342 _this->spec.freq = 48000; 343 new_freq = TRUE; 344 } 345 346 /* Setup mixer. */ 347 pAData->stMCIMixSetup.ulFormatTag = MCI_WAVE_FORMAT_PCM; 348 pAData->stMCIMixSetup.ulBitsPerSample = SDL_AUDIO_BITSIZE(SDLAudioFmt); 349 pAData->stMCIMixSetup.ulSamplesPerSec = _this->spec.freq; 350 pAData->stMCIMixSetup.ulChannels = _this->spec.channels; 351 pAData->stMCIMixSetup.ulDeviceType = MCI_DEVTYPE_WAVEFORM_AUDIO; 352 if (iscapture == 0) { 353 pAData->stMCIMixSetup.ulFormatMode= MCI_PLAY; 354 pAData->stMCIMixSetup.pmixEvent = cbAudioWriteEvent; 355 } else { 356 pAData->stMCIMixSetup.ulFormatMode= MCI_RECORD; 357 pAData->stMCIMixSetup.pmixEvent = cbAudioReadEvent; 358 } 359 360 ulRC = mciSendCommand(pAData->usDeviceId, MCI_MIXSETUP, 361 MCI_WAIT | MCI_MIXSETUP_INIT, &pAData->stMCIMixSetup, 0); 362 if (ulRC != MCIERR_SUCCESS && _this->spec.freq > 44100) { 363 new_freq = TRUE; 364 pAData->stMCIMixSetup.ulSamplesPerSec = 44100; 365 _this->spec.freq = 44100; 366 ulRC = mciSendCommand(pAData->usDeviceId, MCI_MIXSETUP, 367 MCI_WAIT | MCI_MIXSETUP_INIT, &pAData->stMCIMixSetup, 0); 368 } 369 370 debug_os2("Setup mixer [BPS: %u, Freq.: %u, Channels: %u]: %s", 371 pAData->stMCIMixSetup.ulBitsPerSample, 372 pAData->stMCIMixSetup.ulSamplesPerSec, 373 pAData->stMCIMixSetup.ulChannels, 374 (ulRC == MCIERR_SUCCESS)? "SUCCESS" : "FAIL"); 375 376 if (ulRC != MCIERR_SUCCESS) { 377 pAData->stMCIMixSetup.ulBitsPerSample = 0; 378 return _MCIError("MCI_MIXSETUP", ulRC); 379 } 380 381 if (_this->spec.samples == 0 || new_freq == TRUE) { 382 /* also see SDL_audio.c:prepare_audiospec() */ 383 /* Pick a default of ~46 ms at desired frequency */ 384 Uint32 samples = (_this->spec.freq / 1000) * 46; 385 Uint32 power2 = 1; 386 while (power2 < samples) { 387 power2 <<= 1; 388 } 389 _this->spec.samples = power2; 390 } 391 /* Update the fragment size as size in bytes */ 392 SDL_CalculateAudioSpec(&_this->spec); 393 394 /* Allocate memory buffers */ 395 stMCIBuffer.ulBufferSize = _this->spec.size;/* (_this->spec.freq / 1000) * 100 */ 396 stMCIBuffer.ulNumBuffers = NUM_BUFFERS; 397 stMCIBuffer.pBufList = pAData->aMixBuffers; 398 399 ulRC = mciSendCommand(pAData->usDeviceId, MCI_BUFFER, 400 MCI_WAIT | MCI_ALLOCATE_MEMORY, &stMCIBuffer, 0); 401 if (ulRC != MCIERR_SUCCESS) { 402 return _MCIError("MCI_BUFFER", ulRC); 403 } 404 pAData->cMixBuffers = stMCIBuffer.ulNumBuffers; 405 _this->spec.size = stMCIBuffer.ulBufferSize; 406 407 /* Fill all device buffers with data */ 408 for (ulIdx = 0; ulIdx < stMCIBuffer.ulNumBuffers; ulIdx++) { 409 pAData->aMixBuffers[ulIdx].ulFlags = 0; 410 pAData->aMixBuffers[ulIdx].ulBufferLength = stMCIBuffer.ulBufferSize; 411 pAData->aMixBuffers[ulIdx].ulUserParm = (ULONG)pAData; 412 413 memset(((PMCI_MIX_BUFFER)stMCIBuffer.pBufList)[ulIdx].pBuffer, 414 _this->spec.silence, stMCIBuffer.ulBufferSize); 415 } 416 417 /* Write buffers to kick off the amp mixer */ 418 /*pAData->ulQueuedBuf = 1;//stMCIBuffer.ulNumBuffers */ 419 ulRC = pAData->stMCIMixSetup.pmixWrite(pAData->stMCIMixSetup.ulMixHandle, 420 pAData->aMixBuffers, 421 1 /*stMCIBuffer.ulNumBuffers*/); 422 if (ulRC != MCIERR_SUCCESS) { 423 _mixIOError("pmixWrite", ulRC); 424 return -1; 425 } 426 427 return 0; 428 } 429 430 431 static int OS2_Init(SDL_AudioDriverImpl * impl) 432 { 433 /* Set the function pointers */ 434 impl->DetectDevices = OS2_DetectDevices; 435 impl->OpenDevice = OS2_OpenDevice; 436 impl->PlayDevice = OS2_PlayDevice; 437 impl->WaitDevice = OS2_WaitDevice; 438 impl->GetDeviceBuf = OS2_GetDeviceBuf; 439 impl->CloseDevice = OS2_CloseDevice; 440 441 /* TODO: IMPLEMENT CAPTURE SUPPORT: 442 impl->CaptureFromDevice = ; 443 impl->FlushCapture = ; 444 impl->HasCaptureSupport = SDL_TRUE; 445 */ 446 return 1; /* this audio target is available. */ 447 } 448 449 450 AudioBootStrap OS2AUDIO_bootstrap = { "MMOS2", "OS/2 DART", OS2_Init, 0 }; 451 452 #endif /* SDL_AUDIO_DRIVER_OS2 */ 453 454 /* vi: set ts=4 sw=4 expandtab: */