(linenum→info "unix/slp.c:2238")

qemu/0.9.1/audio/dsoundaudio.c

    1: /*
    2:  * QEMU DirectSound audio driver
    3:  *
    4:  * Copyright (c) 2005 Vassili Karpov (malc)
    5:  *
    6:  * Permission is hereby granted, free of charge, to any person obtaining a copy
    7:  * of this software and associated documentation files (the "Software"), to deal
    8:  * in the Software without restriction, including without limitation the rights
    9:  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   10:  * copies of the Software, and to permit persons to whom the Software is
   11:  * furnished to do so, subject to the following conditions:
   12:  *
   13:  * The above copyright notice and this permission notice shall be included in
   14:  * all copies or substantial portions of the Software.
   15:  *
   16:  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   17:  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   18:  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
   19:  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   20:  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   21:  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
   22:  * THE SOFTWARE.
   23:  */
   24: 
   25: /*
   26:  * SEAL 1.07 by Carlos 'pel' Hasan was used as documentation
   27:  */
   28: 
   29: #include "qemu-common.h"
   30: #include "audio.h"
   31: 
   32: #define AUDIO_CAP "dsound"
   33: #include "audio_int.h"
   34: 
   35: #define WIN32_LEAN_AND_MEAN
   36: #include <windows.h>
   37: #include <mmsystem.h>
   38: #include <objbase.h>
   39: #include <dsound.h>
   40: 
   41: /* #define DEBUG_DSOUND */
   42: 
   43: static struct {
   44:     int lock_retries;
   45:     int restore_retries;
   46:     int getstatus_retries;
   47:     int set_primary;
   48:     int bufsize_in;
   49:     int bufsize_out;
   50:     audsettings_t settings;
   51:     int latency_millis;
   52: } conf = {
   53:     1,
   54:     1,
   55:     1,
   56:     0,
   57:     16384,
   58:     16384,
   59:     {
   60:         44100,
   61:         2,
   62:         AUD_FMT_S16
   63:     },
   64:     10
   65: };
   66: 
   67: typedef struct {
   68:     LPDIRECTSOUND dsound;
   69:     LPDIRECTSOUNDCAPTURE dsound_capture;
   70:     LPDIRECTSOUNDBUFFER dsound_primary_buffer;
   71:     audsettings_t settings;
   72: } dsound;
   73: 
   74: static dsound glob_dsound;
   75: 
   76: typedef struct {
   77:     HWVoiceOut hw;
   78:     LPDIRECTSOUNDBUFFER dsound_buffer;
   79:     DWORD old_pos;
   80:     int first_time;
   81: #ifdef DEBUG_DSOUND
   82:     DWORD old_ppos;
   83:     DWORD played;
   84:     DWORD mixed;
   85: #endif
   86: } DSoundVoiceOut;
   87: 
   88: typedef struct {
   89:     HWVoiceIn hw;
   90:     int first_time;
   91:     LPDIRECTSOUNDCAPTUREBUFFER dsound_capture_buffer;
   92: } DSoundVoiceIn;
   93: 
   94: static void dsound_log_hresult (HRESULT hr)
   95: {
   96:     const char *str = "BUG";
   97: 
   98:     switch (hr) {
   99:     case DS_OK:
  100:         str = "The method succeeded";
  101:         break;
  102: #ifdef DS_NO_VIRTUALIZATION
  103:     case DS_NO_VIRTUALIZATION:
  104:         str = "The buffer was created, but another 3D algorithm was substituted";
  105:         break;
  106: #endif
  107: #ifdef DS_INCOMPLETE
  108:     case DS_INCOMPLETE:
  109:         str = "The method succeeded, but not all the optional effects were obtained";
  110:         break;
  111: #endif
  112: #ifdef DSERR_ACCESSDENIED
  113:     case DSERR_ACCESSDENIED:
  114:         str = "The request failed because access was denied";
  115:         break;
  116: #endif
  117: #ifdef DSERR_ALLOCATED
  118:     case DSERR_ALLOCATED:
  119:         str = "The request failed because resources, such as a priority level, were already in use by another caller";
  120:         break;
  121: #endif
  122: #ifdef DSERR_ALREADYINITIALIZED
  123:     case DSERR_ALREADYINITIALIZED:
  124:         str = "The object is already initialized";
  125:         break;
  126: #endif
  127: #ifdef DSERR_BADFORMAT
  128:     case DSERR_BADFORMAT:
  129:         str = "The specified wave format is not supported";
  130:         break;
  131: #endif
  132: #ifdef DSERR_BADSENDBUFFERGUID
  133:     case DSERR_BADSENDBUFFERGUID:
  134:         str = "The GUID specified in an audiopath file does not match a valid mix-in buffer";
  135:         break;
  136: #endif
  137: #ifdef DSERR_BUFFERLOST
  138:     case DSERR_BUFFERLOST:
  139:         str = "The buffer memory has been lost and must be restored";
  140:         break;
  141: #endif
  142: #ifdef DSERR_BUFFERTOOSMALL
  143:     case DSERR_BUFFERTOOSMALL:
  144:         str = "The buffer size is not great enough to enable effects processing";
  145:         break;
  146: #endif
  147: #ifdef DSERR_CONTROLUNAVAIL
  148:     case DSERR_CONTROLUNAVAIL:
  149:         str = "The buffer control (volume, pan, and so on) requested by the caller is not available. Controls must be specified when the buffer is created, using the dwFlags member of DSBUFFERDESC";
  150:         break;
  151: #endif
  152: #ifdef DSERR_DS8_REQUIRED
  153:     case DSERR_DS8_REQUIRED:
  154:         str = "A DirectSound object of class CLSID_DirectSound8 or later is required for the requested functionality. For more information, see IDirectSound8 Interface";
  155:         break;
  156: #endif
  157: #ifdef DSERR_FXUNAVAILABLE
  158:     case DSERR_FXUNAVAILABLE:
  159:         str = "The effects requested could not be found on the system, or they are in the wrong order or in the wrong location; for example, an effect expected in hardware was found in software";
  160:         break;
  161: #endif
  162: #ifdef DSERR_GENERIC
  163:     case DSERR_GENERIC :
  164:         str = "An undetermined error occurred inside the DirectSound subsystem";
  165:         break;
  166: #endif
  167: #ifdef DSERR_INVALIDCALL
  168:     case DSERR_INVALIDCALL:
  169:         str = "This function is not valid for the current state of this object";
  170:         break;
  171: #endif
  172: #ifdef DSERR_INVALIDPARAM
  173:     case DSERR_INVALIDPARAM:
  174:         str = "An invalid parameter was passed to the returning function";
  175:         break;
  176: #endif
  177: #ifdef DSERR_NOAGGREGATION
  178:     case DSERR_NOAGGREGATION:
  179:         str = "The object does not support aggregation";
  180:         break;
  181: #endif
  182: #ifdef DSERR_NODRIVER
  183:     case DSERR_NODRIVER:
  184:         str = "No sound driver is available for use, or the given GUID is not a valid DirectSound device ID";
  185:         break;
  186: #endif
  187: #ifdef DSERR_NOINTERFACE
  188:     case DSERR_NOINTERFACE:
  189:         str = "The requested COM interface is not available";
  190:         break;
  191: #endif
  192: #ifdef DSERR_OBJECTNOTFOUND
  193:     case DSERR_OBJECTNOTFOUND:
  194:         str = "The requested object was not found";
  195:         break;
  196: #endif
  197: #ifdef DSERR_OTHERAPPHASPRIO
  198:     case DSERR_OTHERAPPHASPRIO:
  199:         str = "Another application has a higher priority level, preventing this call from succeeding";
  200:         break;
  201: #endif
  202: #ifdef DSERR_OUTOFMEMORY
  203:     case DSERR_OUTOFMEMORY:
  204:         str = "The DirectSound subsystem could not allocate sufficient memory to complete the caller's request";
  205:         break;
  206: #endif
  207: #ifdef DSERR_PRIOLEVELNEEDED
  208:     case DSERR_PRIOLEVELNEEDED:
  209:         str = "A cooperative level of DSSCL_PRIORITY or higher is required";
  210:         break;
  211: #endif
  212: #ifdef DSERR_SENDLOOP
  213:     case DSERR_SENDLOOP:
  214:         str = "A circular loop of send effects was detected";
  215:         break;
  216: #endif
  217: #ifdef DSERR_UNINITIALIZED
  218:     case DSERR_UNINITIALIZED:
  219:         str = "The Initialize method has not been called or has not been called successfully before other methods were called";
  220:         break;
  221: #endif
  222: #ifdef DSERR_UNSUPPORTED
  223:     case DSERR_UNSUPPORTED:
  224:         str = "The function called is not supported at this time";
  225:         break;
  226: #endif
  227:     default:
  228:         AUD_log (AUDIO_CAP, "Reason: Unknown (HRESULT %#lx)\n", hr);
  229:         return;
  230:     }
  231: 
  232:     AUD_log (AUDIO_CAP, "Reason: %s\n", str);
  233: }
  234: 
  235: static void GCC_FMT_ATTR (2, 3) dsound_logerr (
  236:     HRESULT hr,
  237:     const char *fmt,
  238:     ...
  239:     )
  240: {
  241:     va_list ap;
  242: 
  243:     va_start (ap, fmt);
  244:     AUD_vlog (AUDIO_CAP, fmt, ap);
  245:     va_end (ap);
  246: 
  247:     dsound_log_hresult (hr);
  248: }
  249: 
  250: static void GCC_FMT_ATTR (3, 4) dsound_logerr2 (
  251:     HRESULT hr,
  252:     const char *typ,
  253:     const char *fmt,
  254:     ...
  255:     )
  256: {
  257:     va_list ap;
  258: 
  259:     AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
  260:     va_start (ap, fmt);
  261:     AUD_vlog (AUDIO_CAP, fmt, ap);
  262:     va_end (ap);
  263: 
  264:     dsound_log_hresult (hr);
  265: }
  266: 
  267: static DWORD millis_to_bytes (struct audio_pcm_info *info, DWORD millis)
  268: {
  269:     return (millis * info->bytes_per_second) / 1000;
  270: }
  271: 
  272: #ifdef DEBUG_DSOUND
  273: static void print_wave_format (WAVEFORMATEX *wfx)
  274: {
  275:     dolog ("tag             = %d\n", wfx->wFormatTag);
  276:     dolog ("nChannels       = %d\n", wfx->nChannels);
  277:     dolog ("nSamplesPerSec  = %ld\n", wfx->nSamplesPerSec);
  278:     dolog ("nAvgBytesPerSec = %ld\n", wfx->nAvgBytesPerSec);
  279:     dolog ("nBlockAlign     = %d\n", wfx->nBlockAlign);
  280:     dolog ("wBitsPerSample  = %d\n", wfx->wBitsPerSample);
  281:     dolog ("cbSize          = %d\n", wfx->cbSize);
  282: }
  283: #endif
  284: 
  285: static int dsound_restore_out (LPDIRECTSOUNDBUFFER dsb)
  286: {
  287:     HRESULT hr;
  288:     int i;
  289: 
  290:     for (i = 0; i < conf.restore_retries; ++i) {
  291:         hr = IDirectSoundBuffer_Restore (dsb);
  292: 
  293:         switch (hr) {
  294:         case DS_OK:
  295:             return 0;
  296: 
  297:         case DSERR_BUFFERLOST:
  298:             continue;
  299: 
  300:         default:
  301:             dsound_logerr (hr, "Could not restore playback buffer\n");
  302:             return -1;
  303:         }
  304:     }
  305: 
  306:     dolog ("%d attempts to restore playback buffer failed\n", i);
  307:     return -1;
  308: }
  309: 
  310: static int waveformat_from_audio_settings (WAVEFORMATEX *wfx, audsettings_t *as)
  311: {
  312:     memset (wfx, 0, sizeof (*wfx));
  313: 
  314:     wfx->wFormatTag = WAVE_FORMAT_PCM;
  315:     wfx->nChannels = as->nchannels;
  316:     wfx->nSamplesPerSec = as->freq;
  317:     wfx->nAvgBytesPerSec = as->freq << (as->nchannels == 2);
  318:     wfx->nBlockAlign = 1 << (as->nchannels == 2);
  319:     wfx->cbSize = 0;
  320: 
  321:     switch (as->fmt) {
  322:     case AUD_FMT_S8:
  323:         wfx->wBitsPerSample = 8;
  324:         break;
  325: 
  326:     case AUD_FMT_U8:
  327:         wfx->wBitsPerSample = 8;
  328:         break;
  329: 
  330:     case AUD_FMT_S16:
  331:         wfx->wBitsPerSample = 16;
  332:         wfx->nAvgBytesPerSec <<= 1;
  333:         wfx->nBlockAlign <<= 1;
  334:         break;
  335: 
  336:     case AUD_FMT_U16:
  337:         wfx->wBitsPerSample = 16;
  338:         wfx->nAvgBytesPerSec <<= 1;
  339:         wfx->nBlockAlign <<= 1;
  340:         break;
  341: 
  342:     default:
  343:         dolog ("Internal logic error: Bad audio format %d\n", as->freq);
  344:         return -1;
  345:     }
  346: 
  347:     return 0;
  348: }
  349: 
  350: static int waveformat_to_audio_settings (WAVEFORMATEX *wfx, audsettings_t *as)
  351: {
  352:     if (wfx->wFormatTag != WAVE_FORMAT_PCM) {
  353:         dolog ("Invalid wave format, tag is not PCM, but %d\n",
  354:                wfx->wFormatTag);
  355:         return -1;
  356:     }
  357: 
  358:     if (!wfx->nSamplesPerSec) {
  359:         dolog ("Invalid wave format, frequency is zero\n");
  360:         return -1;
  361:     }
  362:     as->freq = wfx->nSamplesPerSec;
  363: 
  364:     switch (wfx->nChannels) {
  365:     case 1:
  366:         as->nchannels = 1;
  367:         break;
  368: 
  369:     case 2:
  370:         as->nchannels = 2;
  371:         break;
  372: 
  373:     default:
  374:         dolog (
  375:             "Invalid wave format, number of channels is not 1 or 2, but %d\n",
  376:             wfx->nChannels
  377:             );
  378:         return -1;
  379:     }
  380: 
  381:     switch (wfx->wBitsPerSample) {
  382:     case 8:
  383:         as->fmt = AUD_FMT_U8;
  384:         break;
  385: 
  386:     case 16:
  387:         as->fmt = AUD_FMT_S16;
  388:         break;
  389: 
  390:     default:
  391:         dolog ("Invalid wave format, bits per sample is not 8 or 16, but %d\n",
  392:                wfx->wBitsPerSample);
  393:         return -1;
  394:     }
  395: 
  396:     return 0;
  397: }
  398: 
  399: #include "dsound_template.h"
  400: #define DSBTYPE_IN
  401: #include "dsound_template.h"
  402: #undef DSBTYPE_IN
  403: 
  404: static int dsound_get_status_out (LPDIRECTSOUNDBUFFER dsb, DWORD *statusp)
  405: {
  406:     HRESULT hr;
  407:     int i;
  408: 
  409:     for (i = 0; i < conf.getstatus_retries; ++i) {
  410:         hr = IDirectSoundBuffer_GetStatus (dsb, statusp);
  411:         if (FAILED (hr)) {
  412:             dsound_logerr (hr, "Could not get playback buffer status\n");
  413:             return -1;
  414:         }
  415: 
  416:         if (*statusp & DSERR_BUFFERLOST) {
  417:             if (dsound_restore_out (dsb)) {
  418:                 return -1;
  419:             }
  420:             continue;
  421:         }
  422:         break;
  423:     }
  424: 
  425:     return 0;
  426: }
  427: 
  428: static int dsound_get_status_in (LPDIRECTSOUNDCAPTUREBUFFER dscb,
  429:                                  DWORD *statusp)
  430: {
  431:     HRESULT hr;
  432: 
  433:     hr = IDirectSoundCaptureBuffer_GetStatus (dscb, statusp);
  434:     if (FAILED (hr)) {
  435:         dsound_logerr (hr, "Could not get capture buffer status\n");
  436:         return -1;
  437:     }
  438: 
  439:     return 0;
  440: }
  441: 
  442: static void dsound_write_sample (HWVoiceOut *hw, uint8_t *dst, int dst_len)
  443: {
  444:     int src_len1 = dst_len;
  445:     int src_len2 = 0;
  446:     int pos = hw->rpos + dst_len;
  447:     st_sample_t *src1 = hw->mix_buf + hw->rpos;
  448:     st_sample_t *src2 = NULL;
  449: 
  450:     if (pos > hw->samples) {
  451:         src_len1 = hw->samples - hw->rpos;
  452:         src2 = hw->mix_buf;
  453:         src_len2 = dst_len - src_len1;
  454:         pos = src_len2;
  455:     }
  456: 
  457:     if (src_len1) {
  458:         hw->clip (dst, src1, src_len1);
  459:     }
  460: 
  461:     if (src_len2) {
  462:         dst = advance (dst, src_len1 << hw->info.shift);
  463:         hw->clip (dst, src2, src_len2);
  464:     }
  465: 
  466:     hw->rpos = pos % hw->samples;
  467: }
  468: 
  469: static void dsound_clear_sample (HWVoiceOut *hw, LPDIRECTSOUNDBUFFER dsb)
  470: {
  471:     int err;
  472:     LPVOID p1, p2;
  473:     DWORD blen1, blen2, len1, len2;
  474: 
  475:     err = dsound_lock_out (
  476:         dsb,
  477:         &hw->info,
  478:         0,
  479:         hw->samples << hw->info.shift,
  480:         &p1, &p2,
  481:         &blen1, &blen2,
  482:         1
  483:         );
  484:     if (err) {
  485:         return;
  486:     }
  487: 
  488:     len1 = blen1 >> hw->info.shift;
  489:     len2 = blen2 >> hw->info.shift;
  490: 
  491: #ifdef DEBUG_DSOUND
  492:     dolog ("clear %p,%ld,%ld %p,%ld,%ld\n",
  493:            p1, blen1, len1,
  494:            p2, blen2, len2);
  495: #endif
  496: 
  497:     if (p1 && len1) {
  498:         audio_pcm_info_clear_buf (&hw->info, p1, len1);
  499:     }
  500: 
  501:     if (p2 && len2) {
  502:         audio_pcm_info_clear_buf (&hw->info, p2, len2);
  503:     }
  504: 
  505:     dsound_unlock_out (dsb, p1, p2, blen1, blen2);
  506: }
  507: 
  508: static void dsound_close (dsound *s)
  509: {
  510:     HRESULT hr;
  511: 
  512:     if (s->dsound_primary_buffer) {
  513:         hr = IDirectSoundBuffer_Release (s->dsound_primary_buffer);
  514:         if (FAILED (hr)) {
  515:             dsound_logerr (hr, "Could not release primary buffer\n");
  516:         }
  517:         s->dsound_primary_buffer = NULL;
  518:     }
  519: }
  520: 
  521: static int dsound_open (dsound *s)
  522: {
  523:     int err;
  524:     HRESULT hr;
  525:     WAVEFORMATEX wfx;
  526:     DSBUFFERDESC dsbd;
  527:     HWND hwnd;
  528: 
  529:     hwnd = GetForegroundWindow ();
  530:     hr = IDirectSound_SetCooperativeLevel (
  531:         s->dsound,
  532:         hwnd,
  533:         DSSCL_PRIORITY
  534:         );
  535: 
  536:     if (FAILED (hr)) {
  537:         dsound_logerr (hr, "Could not set cooperative level for window %p\n",
  538:                        hwnd);
  539:         return -1;
  540:     }
  541: 
  542:     if (!conf.set_primary) {
  543:         return 0;
  544:     }
  545: 
  546:     err = waveformat_from_audio_settings (&wfx, &conf.settings);
  547:     if (err) {
  548:         return -1;
  549:     }
  550: 
  551:     memset (&dsbd, 0, sizeof (dsbd));
  552:     dsbd.dwSize = sizeof (dsbd);
  553:     dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER;
  554:     dsbd.dwBufferBytes = 0;
  555:     dsbd.lpwfxFormat = NULL;
  556: 
  557:     hr = IDirectSound_CreateSoundBuffer (
  558:         s->dsound,
  559:         &dsbd,
  560:         &s->dsound_primary_buffer,
  561:         NULL
  562:         );
  563:     if (FAILED (hr)) {
  564:         dsound_logerr (hr, "Could not create primary playback buffer\n");
  565:         return -1;
  566:     }
  567: 
  568:     hr = IDirectSoundBuffer_SetFormat (s->dsound_primary_buffer, &wfx);
  569:     if (FAILED (hr)) {
  570:         dsound_logerr (hr, "Could not set primary playback buffer format\n");
  571:     }
  572: 
  573:     hr = IDirectSoundBuffer_GetFormat (
  574:         s->dsound_primary_buffer,
  575:         &wfx,
  576:         sizeof (wfx),
  577:         NULL
  578:         );
  579:     if (FAILED (hr)) {
  580:         dsound_logerr (hr, "Could not get primary playback buffer format\n");
  581:         goto fail0;
  582:     }
  583: 
  584: #ifdef DEBUG_DSOUND
  585:     dolog ("Primary\n");
  586:     print_wave_format (&wfx);
  587: #endif
  588: 
  589:     err = waveformat_to_audio_settings (&wfx, &s->settings);
  590:     if (err) {
  591:         goto fail0;
  592:     }
  593: 
  594:     return 0;
  595: 
  596:  fail0:
  597:     dsound_close (s);
  598:     return -1;
  599: }