sdl

FORK: Simple Directmedia Layer
git clone https://git.neptards.moe/neptards/sdl.git
Log | Files | Refs

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: */