temp10min

cubeb_winmm.c

Jul 26th, 2021
766
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /*
  2.  * Copyright © 2011 Mozilla Foundation
  3.  *
  4.  * This program is made available under an ISC-style license.  See the
  5.  * accompanying file LICENSE for details.
  6.  */
  7. /*
  8.  * https://msfn.org/board/topic/182647-my-browser-builds-part-3/?do=findComment&comment=1202804
  9.  *  media/libcubeb/src/cubeb_winmm.c
  10.  */
  11. #define __MSVCRT_VERSION__ 0x0700
  12. #undef WINVER
  13. #define WINVER 0x0501
  14. #undef WIN32_LEAN_AND_MEAN
  15.  
  16. #include <malloc.h>
  17. #include <windows.h>
  18. #include <mmreg.h>
  19. #include <mmsystem.h>
  20. #include <process.h>
  21. #include <stdio.h>
  22. #include <stdlib.h>
  23. #include <math.h>
  24. #include <IntSafe.h>
  25. #include "cubeb/cubeb.h"
  26. #include "cubeb-internal.h"
  27.  
  28. /* This is missing from the MinGW headers. Use a safe fallback. */
  29. #if !defined(MEMORY_ALLOCATION_ALIGNMENT)
  30. #define MEMORY_ALLOCATION_ALIGNMENT 16
  31. #endif
  32.  
  33. /**This is also missing from the MinGW headers. It  also appears to be undocumented by Microsoft.*/
  34. #ifndef WAVE_FORMAT_48M08
  35. #define WAVE_FORMAT_48M08      0x00001000       /* 48     kHz, Mono, 8-bit */
  36. #endif
  37. #ifndef WAVE_FORMAT_48M16
  38. #define WAVE_FORMAT_48M16      0x00002000       /* 48     kHz, Mono, 16-bit */
  39. #endif
  40. #ifndef WAVE_FORMAT_48S08
  41. #define WAVE_FORMAT_48S08      0x00004000       /* 48     kHz, Stereo, 8-bit */
  42. #endif
  43. #ifndef WAVE_FORMAT_48S16
  44. #define WAVE_FORMAT_48S16      0x00008000       /* 48     kHz, Stereo, 16-bit */
  45. #endif
  46. #ifndef WAVE_FORMAT_96M08
  47. #define WAVE_FORMAT_96M08      0x00010000       /* 96     kHz, Mono, 8-bit */
  48. #endif
  49. #ifndef WAVE_FORMAT_96M16
  50. #define WAVE_FORMAT_96M16      0x00020000       /* 96     kHz, Mono, 16-bit */
  51. #endif
  52. #ifndef WAVE_FORMAT_96S08
  53. #define WAVE_FORMAT_96S08      0x00040000       /* 96     kHz, Stereo, 8-bit */
  54. #endif
  55. #ifndef WAVE_FORMAT_96S16
  56. #define WAVE_FORMAT_96S16      0x00080000       /* 96     kHz, Stereo, 16-bit */
  57. #endif
  58.  
  59. /**Taken from winbase.h, also not in MinGW.*/
  60. #ifndef STACK_SIZE_PARAM_IS_A_RESERVATION
  61. #define STACK_SIZE_PARAM_IS_A_RESERVATION   0x00010000    // Threads only
  62. #endif
  63.  
  64. #ifndef DRVM_MAPPER
  65. #define DRVM_MAPPER             (0x2000)
  66. #endif
  67. #ifndef DRVM_MAPPER_PREFERRED_GET
  68. #define DRVM_MAPPER_PREFERRED_GET                 (DRVM_MAPPER+21)
  69. #endif
  70. #ifndef DRVM_MAPPER_CONSOLEVOICECOM_GET
  71. #define DRVM_MAPPER_CONSOLEVOICECOM_GET           (DRVM_MAPPER+23)
  72. #endif
  73.  
  74. #define CUBEB_STREAM_MAX 32
  75. #define NBUFS 4
  76.  
  77. const GUID KSDATAFORMAT_SUBTYPE_PCM =
  78. { 0x00000001, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } };
  79. const GUID KSDATAFORMAT_SUBTYPE_IEEE_FLOAT =
  80. { 0x00000003, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } };
  81.  
  82. struct cubeb_stream_item {
  83.   SLIST_ENTRY head;
  84.   cubeb_stream * stream;
  85. };
  86.  
  87. static struct cubeb_ops const winmm_ops;
  88.  
  89. struct cubeb {
  90.   struct cubeb_ops const * ops;
  91.   HANDLE event;
  92.   HANDLE thread;
  93.   int shutdown;
  94.   PSLIST_HEADER work;
  95.   CRITICAL_SECTION lock;
  96.   unsigned int active_streams;
  97.   unsigned int minimum_latency_ms;
  98. };
  99.  
  100. struct cubeb_stream {
  101.   cubeb * context;
  102.   cubeb_stream_params params;
  103.   cubeb_data_callback data_callback;
  104.   cubeb_state_callback state_callback;
  105.   void * user_ptr;
  106.   WAVEHDR buffers[NBUFS];
  107.   size_t buffer_size;
  108.   int next_buffer;
  109.   int free_buffers;
  110.   int shutdown;
  111.   int draining;
  112.   HANDLE event;
  113.   HWAVEOUT waveout;
  114.   CRITICAL_SECTION lock;
  115.   uint64_t written;
  116.   float soft_volume;
  117.   /* For position wrap-around handling: */
  118.   size_t frame_size;
  119.   DWORD prev_pos_lo_dword;
  120.   DWORD pos_hi_dword;
  121. };
  122.  
  123. static size_t
  124. bytes_per_frame(cubeb_stream_params params)
  125. {
  126.   size_t bytes;
  127.  
  128.   switch (params.format) {
  129.   case CUBEB_SAMPLE_S16LE:
  130.     bytes = sizeof(signed short);
  131.     break;
  132.   case CUBEB_SAMPLE_FLOAT32LE:
  133.     bytes = sizeof(float);
  134.     break;
  135.   default:
  136.     XASSERT(0);
  137.   }
  138.  
  139.   return bytes * params.channels;
  140. }
  141.  
  142. static WAVEHDR *
  143. winmm_get_next_buffer(cubeb_stream * stm)
  144. {
  145.   WAVEHDR * hdr = NULL;
  146.  
  147.   XASSERT(stm->free_buffers > 0 && stm->free_buffers <= NBUFS);
  148.   hdr = &stm->buffers[stm->next_buffer];
  149.   XASSERT(hdr->dwFlags & WHDR_PREPARED ||
  150.           (hdr->dwFlags & WHDR_DONE && !(hdr->dwFlags & WHDR_INQUEUE)));
  151.   stm->next_buffer = (stm->next_buffer + 1) % NBUFS;
  152.   stm->free_buffers -= 1;
  153.  
  154.   return hdr;
  155. }
  156.  
  157. static void
  158. winmm_refill_stream(cubeb_stream * stm)
  159. {
  160.   WAVEHDR * hdr;
  161.   long got;
  162.   long wanted;
  163.   MMRESULT r;
  164.  
  165.   EnterCriticalSection(&stm->lock);
  166.   stm->free_buffers += 1;
  167.   XASSERT(stm->free_buffers > 0 && stm->free_buffers <= NBUFS);
  168.  
  169.   if (stm->draining) {
  170.     LeaveCriticalSection(&stm->lock);
  171.     if (stm->free_buffers == NBUFS) {
  172.       stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
  173.     }
  174.     SetEvent(stm->event);
  175.     return;
  176.   }
  177.  
  178.   if (stm->shutdown) {
  179.     LeaveCriticalSection(&stm->lock);
  180.     SetEvent(stm->event);
  181.     return;
  182.   }
  183.  
  184.   hdr = winmm_get_next_buffer(stm);
  185.  
  186.   wanted = (DWORD) stm->buffer_size / bytes_per_frame(stm->params);
  187.  
  188.   /* It is assumed that the caller is holding this lock.  It must be dropped
  189.      during the callback to avoid deadlocks. */
  190.   LeaveCriticalSection(&stm->lock);
  191.   got = stm->data_callback(stm, stm->user_ptr, NULL, hdr->lpData, wanted);
  192.   EnterCriticalSection(&stm->lock);
  193.   if (got < 0) {
  194.     LeaveCriticalSection(&stm->lock);
  195.     /* XXX handle this case */
  196.     XASSERT(0);
  197.     return;
  198.   } else if (got < wanted) {
  199.     stm->draining = 1;
  200.   }
  201.   stm->written += got;
  202.  
  203.   XASSERT(hdr->dwFlags & WHDR_PREPARED);
  204.  
  205.   hdr->dwBufferLength = got * bytes_per_frame(stm->params);
  206.   XASSERT(hdr->dwBufferLength <= stm->buffer_size);
  207.  
  208.   if (stm->soft_volume != -1.0) {
  209.     if (stm->params.format == CUBEB_SAMPLE_FLOAT32NE) {
  210.       float * b = (float *) hdr->lpData;
  211.       uint32_t i;
  212.       for (i = 0; i < got * stm->params.channels; i++) {
  213.         b[i] *= stm->soft_volume;
  214.       }
  215.     } else {
  216.       short * b = (short *) hdr->lpData;
  217.       uint32_t i;
  218.       for (i = 0; i < got * stm->params.channels; i++) {
  219.         b[i] = (short) (b[i] * stm->soft_volume);
  220.       }
  221.     }
  222.   }
  223.  
  224.   r = waveOutWrite(stm->waveout, hdr, sizeof(*hdr));
  225.   if (r != MMSYSERR_NOERROR) {
  226.     LeaveCriticalSection(&stm->lock);
  227.     stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
  228.     return;
  229.   }
  230.  
  231.   LeaveCriticalSection(&stm->lock);
  232. }
  233.  
  234. static unsigned __stdcall
  235. winmm_buffer_thread(void * user_ptr)
  236. {
  237.   cubeb * ctx = (cubeb *) user_ptr;
  238.   XASSERT(ctx);
  239.  
  240.   for (;;) {
  241.     DWORD r;
  242.     PSLIST_ENTRY item;
  243.  
  244.     r = WaitForSingleObject(ctx->event, INFINITE);
  245.     XASSERT(r == WAIT_OBJECT_0);
  246.  
  247.     /* Process work items in batches so that a single stream can't
  248.        starve the others by continuously adding new work to the top of
  249.        the work item stack. */
  250.     item = InterlockedFlushSList(ctx->work);
  251.     while (item != NULL) {
  252.       PSLIST_ENTRY tmp = item;
  253.       winmm_refill_stream(((struct cubeb_stream_item *) tmp)->stream);
  254.       item = item->Next;
  255.       _aligned_free(tmp);
  256.     }
  257.  
  258.     if (ctx->shutdown) {
  259.       break;
  260.     }
  261.   }
  262.  
  263.   return 0;
  264. }
  265.  
  266. static void CALLBACK
  267. winmm_buffer_callback(HWAVEOUT waveout, UINT msg, DWORD_PTR user_ptr, DWORD_PTR p1, DWORD_PTR p2)
  268. {
  269.   cubeb_stream * stm = (cubeb_stream *) user_ptr;
  270.   struct cubeb_stream_item * item;
  271.  
  272.   if (msg != WOM_DONE) {
  273.     return;
  274.   }
  275.  
  276.   item = _aligned_malloc(sizeof(struct cubeb_stream_item), MEMORY_ALLOCATION_ALIGNMENT);
  277.   XASSERT(item);
  278.   item->stream = stm;
  279.   InterlockedPushEntrySList(stm->context->work, &item->head);
  280.  
  281.   SetEvent(stm->context->event);
  282. }
  283.  
  284. static unsigned int
  285. calculate_minimum_latency(void)
  286. {
  287.   OSVERSIONINFOEX osvi;
  288.   DWORDLONG mask;
  289.  
  290.   /* Running under Terminal Services results in underruns with low latency. */
  291.   if (GetSystemMetrics(SM_REMOTESESSION) == TRUE) {
  292.     return 500;
  293.   }
  294.  
  295.   /* Vista's WinMM implementation underruns when less than 200ms of audio is buffered. */
  296.   memset(&osvi, 0, sizeof(OSVERSIONINFOEX));
  297.   osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
  298.   osvi.dwMajorVersion = 6;
  299.   osvi.dwMinorVersion = 0;
  300.  
  301.   mask = 0;
  302.   VER_SET_CONDITION(mask, VER_MAJORVERSION, VER_EQUAL);
  303.   VER_SET_CONDITION(mask, VER_MINORVERSION, VER_EQUAL);
  304.  
  305.   if (VerifyVersionInfo(&osvi, VER_MAJORVERSION | VER_MINORVERSION, mask) != 0) {
  306.     return 200;
  307.   }
  308.  
  309.   return 100;
  310. }
  311.  
  312. static void winmm_destroy(cubeb * ctx);
  313.  
  314. /*static*/ int
  315. winmm_init(cubeb ** context, char const * context_name)
  316. {
  317.   cubeb * ctx;
  318.  
  319.   XASSERT(context);
  320.   *context = NULL;
  321.  
  322.   /* Don't initialize a context if there are no devices available. */
  323.   if (waveOutGetNumDevs() == 0) {
  324.     return CUBEB_ERROR;
  325.   }
  326.  
  327.   ctx = calloc(1, sizeof(*ctx));
  328.   XASSERT(ctx);
  329.  
  330.   ctx->ops = &winmm_ops;
  331.  
  332.   ctx->work = _aligned_malloc(sizeof(*ctx->work), MEMORY_ALLOCATION_ALIGNMENT);
  333.   XASSERT(ctx->work);
  334.   InitializeSListHead(ctx->work);
  335.  
  336.   ctx->event = CreateEvent(NULL, FALSE, FALSE, NULL);
  337.   if (!ctx->event) {
  338.     winmm_destroy(ctx);
  339.     return CUBEB_ERROR;
  340.   }
  341.  
  342.   ctx->thread = (HANDLE) _beginthreadex(NULL, 256 * 1024, winmm_buffer_thread, ctx, STACK_SIZE_PARAM_IS_A_RESERVATION, NULL);
  343.   if (!ctx->thread) {
  344.     winmm_destroy(ctx);
  345.     return CUBEB_ERROR;
  346.   }
  347.  
  348.   SetThreadPriority(ctx->thread, THREAD_PRIORITY_TIME_CRITICAL);
  349.  
  350.   InitializeCriticalSection(&ctx->lock);
  351.   ctx->active_streams = 0;
  352.  
  353.   ctx->minimum_latency_ms = calculate_minimum_latency();
  354.  
  355.   *context = ctx;
  356.  
  357.   return CUBEB_OK;
  358. }
  359.  
  360. static char const *
  361. winmm_get_backend_id(cubeb * ctx)
  362. {
  363.   return "winmm";
  364. }
  365.  
  366. static void
  367. winmm_destroy(cubeb * ctx)
  368. {
  369.   DWORD r;
  370.  
  371.   XASSERT(ctx->active_streams == 0);
  372.   XASSERT(!InterlockedPopEntrySList(ctx->work));
  373.  
  374.   DeleteCriticalSection(&ctx->lock);
  375.  
  376.   if (ctx->thread) {
  377.     ctx->shutdown = 1;
  378.     SetEvent(ctx->event);
  379.     r = WaitForSingleObject(ctx->thread, INFINITE);
  380.     XASSERT(r == WAIT_OBJECT_0);
  381.     CloseHandle(ctx->thread);
  382.   }
  383.  
  384.   if (ctx->event) {
  385.     CloseHandle(ctx->event);
  386.   }
  387.  
  388.   _aligned_free(ctx->work);
  389.  
  390.   free(ctx);
  391. }
  392.  
  393. static void winmm_stream_destroy(cubeb_stream * stm);
  394.  
  395. static int
  396. winmm_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_name,
  397.                   cubeb_devid input_device,
  398.                   cubeb_stream_params * input_stream_params,
  399.                   cubeb_devid output_device,
  400.                   cubeb_stream_params * output_stream_params,
  401.                   unsigned int latency_frames,
  402.                   cubeb_data_callback data_callback,
  403.                   cubeb_state_callback state_callback,
  404.                   void * user_ptr)
  405. {
  406.   MMRESULT r;
  407.   WAVEFORMATEXTENSIBLE wfx;
  408.   cubeb_stream * stm;
  409.   int i;
  410.   size_t bufsz;
  411.  
  412.   XASSERT(context);
  413.   XASSERT(stream);
  414.  
  415.   if (input_stream_params) {
  416.     /* Capture support not yet implemented. */
  417.     return CUBEB_ERROR_NOT_SUPPORTED;
  418.   }
  419.  
  420.   if (input_device || output_device) {
  421.     /* Device selection not yet implemented. */
  422.     return CUBEB_ERROR_DEVICE_UNAVAILABLE;
  423.   }
  424.  
  425.   *stream = NULL;
  426.  
  427.   memset(&wfx, 0, sizeof(wfx));
  428.   if (output_stream_params->channels > 2) {
  429.     wfx.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
  430.     wfx.Format.cbSize = sizeof(wfx) - sizeof(wfx.Format);
  431.   } else {
  432.     wfx.Format.wFormatTag = WAVE_FORMAT_PCM;
  433.     if (output_stream_params->format == CUBEB_SAMPLE_FLOAT32LE) {
  434.       wfx.Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
  435.     }
  436.     wfx.Format.cbSize = 0;
  437.   }
  438.   wfx.Format.nChannels = output_stream_params->channels;
  439.   wfx.Format.nSamplesPerSec = output_stream_params->rate;
  440.  
  441.   /* XXX fix channel mappings */
  442.   wfx.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
  443.  
  444.   switch (output_stream_params->format) {
  445.   case CUBEB_SAMPLE_S16LE:
  446.     wfx.Format.wBitsPerSample = 16;
  447.     wfx.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
  448.     break;
  449.   case CUBEB_SAMPLE_FLOAT32LE:
  450.     wfx.Format.wBitsPerSample = 32;
  451.     wfx.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
  452.     break;
  453.   default:
  454.     return CUBEB_ERROR_INVALID_FORMAT;
  455.   }
  456.  
  457.   wfx.Format.nBlockAlign = (wfx.Format.wBitsPerSample * wfx.Format.nChannels) / 8;
  458.   wfx.Format.nAvgBytesPerSec = wfx.Format.nSamplesPerSec * wfx.Format.nBlockAlign;
  459.   wfx.Samples.wValidBitsPerSample = wfx.Format.wBitsPerSample;
  460.  
  461.   EnterCriticalSection(&context->lock);
  462.   /* CUBEB_STREAM_MAX is a horrible hack to avoid a situation where, when
  463.      many streams are active at once, a subset of them will not consume (via
  464.      playback) or release (via waveOutReset) their buffers. */
  465.   if (context->active_streams >= CUBEB_STREAM_MAX) {
  466.     LeaveCriticalSection(&context->lock);
  467.     return CUBEB_ERROR;
  468.   }
  469.   context->active_streams += 1;
  470.   LeaveCriticalSection(&context->lock);
  471.  
  472.   stm = calloc(1, sizeof(*stm));
  473.   XASSERT(stm);
  474.  
  475.   stm->context = context;
  476.  
  477.   stm->params = *output_stream_params;
  478.  
  479.   stm->data_callback = data_callback;
  480.   stm->state_callback = state_callback;
  481.   stm->user_ptr = user_ptr;
  482.   stm->written = 0;
  483.  
  484.   uint32_t latency_ms = latency_frames * 1000 / output_stream_params->rate;
  485.  
  486.   if (latency_ms < context->minimum_latency_ms) {
  487.     latency_ms = context->minimum_latency_ms;
  488.   }
  489.  
  490.   bufsz = (size_t) (stm->params.rate / 1000.0 * latency_ms * bytes_per_frame(stm->params) / NBUFS);
  491.   if (bufsz % bytes_per_frame(stm->params) != 0) {
  492.     bufsz += bytes_per_frame(stm->params) - (bufsz % bytes_per_frame(stm->params));
  493.   }
  494.   XASSERT(bufsz % bytes_per_frame(stm->params) == 0);
  495.  
  496.   stm->buffer_size = bufsz;
  497.  
  498.   InitializeCriticalSection(&stm->lock);
  499.  
  500.   stm->event = CreateEvent(NULL, FALSE, FALSE, NULL);
  501.   if (!stm->event) {
  502.     winmm_stream_destroy(stm);
  503.     return CUBEB_ERROR;
  504.   }
  505.  
  506.   stm->soft_volume = -1.0;
  507.  
  508.   /* winmm_buffer_callback will be called during waveOutOpen, so all
  509.      other initialization must be complete before calling it. */
  510.   r = waveOutOpen(&stm->waveout, WAVE_MAPPER, &wfx.Format,
  511.                   (DWORD_PTR) winmm_buffer_callback, (DWORD_PTR) stm,
  512.                   CALLBACK_FUNCTION);
  513.   if (r != MMSYSERR_NOERROR) {
  514.     winmm_stream_destroy(stm);
  515.     return CUBEB_ERROR;
  516.   }
  517.  
  518.   r = waveOutPause(stm->waveout);
  519.   if (r != MMSYSERR_NOERROR) {
  520.     winmm_stream_destroy(stm);
  521.     return CUBEB_ERROR;
  522.   }
  523.  
  524.  
  525.   for (i = 0; i < NBUFS; ++i) {
  526.     WAVEHDR * hdr = &stm->buffers[i];
  527.  
  528.     hdr->lpData = calloc(1, bufsz);
  529.     XASSERT(hdr->lpData);
  530.     hdr->dwBufferLength = bufsz;
  531.     hdr->dwFlags = 0;
  532.  
  533.     r = waveOutPrepareHeader(stm->waveout, hdr, sizeof(*hdr));
  534.     if (r != MMSYSERR_NOERROR) {
  535.       winmm_stream_destroy(stm);
  536.       return CUBEB_ERROR;
  537.     }
  538.  
  539.     winmm_refill_stream(stm);
  540.   }
  541.  
  542.   stm->frame_size = bytes_per_frame(stm->params);
  543.   stm->prev_pos_lo_dword = 0;
  544.   stm->pos_hi_dword = 0;
  545.  
  546.   *stream = stm;
  547.  
  548.   return CUBEB_OK;
  549. }
  550.  
  551. static void
  552. winmm_stream_destroy(cubeb_stream * stm)
  553. {
  554.   int i;
  555.  
  556.   if (stm->waveout) {
  557.     MMTIME time;
  558.     MMRESULT r;
  559.     int device_valid;
  560.     int enqueued;
  561.  
  562.     EnterCriticalSection(&stm->lock);
  563.     stm->shutdown = 1;
  564.  
  565.     waveOutReset(stm->waveout);
  566.  
  567.     /* Don't need this value, we just want the result to detect invalid
  568.        handle/no device errors than waveOutReset doesn't seem to report. */
  569.     time.wType = TIME_SAMPLES;
  570.     r = waveOutGetPosition(stm->waveout, &time, sizeof(time));
  571.     device_valid = !(r == MMSYSERR_INVALHANDLE || r == MMSYSERR_NODRIVER);
  572.  
  573.     enqueued = NBUFS - stm->free_buffers;
  574.     LeaveCriticalSection(&stm->lock);
  575.  
  576.     /* Wait for all blocks to complete. */
  577.     while (device_valid && enqueued > 0) {
  578.       DWORD rv = WaitForSingleObject(stm->event, INFINITE);
  579.       XASSERT(rv == WAIT_OBJECT_0);
  580.  
  581.       EnterCriticalSection(&stm->lock);
  582.       enqueued = NBUFS - stm->free_buffers;
  583.       LeaveCriticalSection(&stm->lock);
  584.     }
  585.  
  586.     EnterCriticalSection(&stm->lock);
  587.  
  588.     for (i = 0; i < NBUFS; ++i) {
  589.       if (stm->buffers[i].dwFlags & WHDR_PREPARED) {
  590.         waveOutUnprepareHeader(stm->waveout, &stm->buffers[i], sizeof(stm->buffers[i]));
  591.       }
  592.     }
  593.  
  594.     waveOutClose(stm->waveout);
  595.  
  596.     LeaveCriticalSection(&stm->lock);
  597.   }
  598.  
  599.   if (stm->event) {
  600.     CloseHandle(stm->event);
  601.   }
  602.  
  603.   DeleteCriticalSection(&stm->lock);
  604.  
  605.   for (i = 0; i < NBUFS; ++i) {
  606.     free(stm->buffers[i].lpData);
  607.   }
  608.  
  609.   EnterCriticalSection(&stm->context->lock);
  610.   XASSERT(stm->context->active_streams >= 1);
  611.   stm->context->active_streams -= 1;
  612.   LeaveCriticalSection(&stm->context->lock);
  613.  
  614.   free(stm);
  615. }
  616.  
  617. static int
  618. winmm_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
  619. {
  620.   XASSERT(ctx && max_channels);
  621.  
  622.   /* We don't support more than two channels in this backend. */
  623.   *max_channels = 2;
  624.  
  625.   return CUBEB_OK;
  626. }
  627.  
  628. static int
  629. winmm_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency)
  630. {
  631.   // 100ms minimum, if we are not in a bizarre configuration.
  632.   *latency = ctx->minimum_latency_ms * params.rate / 1000;
  633.  
  634.   return CUBEB_OK;
  635. }
  636.  
  637. static int
  638. winmm_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate)
  639. {
  640.   WAVEOUTCAPS woc;
  641.   MMRESULT r;
  642.  
  643.   r = waveOutGetDevCaps(WAVE_MAPPER, &woc, sizeof(WAVEOUTCAPS));
  644.   if (r != MMSYSERR_NOERROR) {
  645.     return CUBEB_ERROR;
  646.   }
  647.  
  648.   /* Check if we support 48kHz, but not 44.1kHz. */
  649.   if (!(woc.dwFormats & WAVE_FORMAT_4S16) &&
  650.       woc.dwFormats & WAVE_FORMAT_48S16) {
  651.     *rate = 48000;
  652.     return CUBEB_OK;
  653.   }
  654.   /* Prefer 44.1kHz between 44.1kHz and 48kHz. */
  655.   *rate = 44100;
  656.  
  657.   return CUBEB_OK;
  658. }
  659.  
  660. static int
  661. winmm_stream_start(cubeb_stream * stm)
  662. {
  663.   MMRESULT r;
  664.  
  665.   EnterCriticalSection(&stm->lock);
  666.   r = waveOutRestart(stm->waveout);
  667.   LeaveCriticalSection(&stm->lock);
  668.  
  669.   if (r != MMSYSERR_NOERROR) {
  670.     return CUBEB_ERROR;
  671.   }
  672.  
  673.   stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STARTED);
  674.  
  675.   return CUBEB_OK;
  676. }
  677.  
  678. static int
  679. winmm_stream_stop(cubeb_stream * stm)
  680. {
  681.   MMRESULT r;
  682.  
  683.   EnterCriticalSection(&stm->lock);
  684.   r = waveOutPause(stm->waveout);
  685.   LeaveCriticalSection(&stm->lock);
  686.  
  687.   if (r != MMSYSERR_NOERROR) {
  688.     return CUBEB_ERROR;
  689.   }
  690.  
  691.   stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STOPPED);
  692.  
  693.   return CUBEB_OK;
  694. }
  695.  
  696. /*
  697. Microsoft wave audio docs say "samples are the preferred time format in which
  698. to represent the current position", but relying on this causes problems on
  699. Windows XP, the only OS cubeb_winmm is used on.
  700.  
  701. While the wdmaud.sys driver internally tracks a 64-bit position and ensures no
  702. backward movement, the WinMM API limits the position returned from
  703. waveOutGetPosition() to a 32-bit DWORD (this applies equally to XP x64). The
  704. higher 32 bits are chopped off, and to an API consumer the position can appear
  705. to move backward.
  706.  
  707. In theory, even a 32-bit TIME_SAMPLES position should provide plenty of
  708. playback time for typical use cases before this pseudo wrap-around, e.g:
  709.     (2^32 - 1)/48000 = ~24:51:18 for 48.0 kHz stereo;
  710.     (2^32 - 1)/44100 = ~27:03:12 for 44.1 kHz stereo.
  711. In reality, wdmaud.sys doesn't provide a TIME_SAMPLES position at all, only a
  712. 32-bit TIME_BYTES position, from which wdmaud.drv derives TIME_SAMPLES:
  713.     SamplePos = (BytePos * 8) / BitsPerFrame,
  714.     where BitsPerFrame = Channels * BitsPerSample,
  715. Per dom\media\AudioSampleFormat.h, desktop builds always use 32-bit FLOAT32
  716. samples, so the maximum for TIME_SAMPLES should be:
  717.     (2^29 - 1)/48000 = ~03:06:25;
  718.     (2^29 - 1)/44100 = ~03:22:54.
  719. This might still be OK for typical browser usage, but there's also a bug in the
  720. formula above: BytePos * 8 (BytePos << 3) is done on a 32-bit BytePos, without
  721. first casting it to 64 bits, so the highest 3 bits, if set, would get shifted
  722. out, and the maximum possible TIME_SAMPLES drops unacceptably low:
  723.     (2^26 - 1)/48000 = ~00:23:18;
  724.     (2^26 - 1)/44100 = ~00:25:22.
  725.  
  726. To work around these limitations, we just get the position in TIME_BYTES,
  727. recover the 64-bit value, and do our own conversion to samples.
  728. */
  729.  
  730. /* Convert chopped 32-bit waveOutGetPosition() into 64-bit true position. */
  731. static uint64_t
  732. update_64bit_position(cubeb_stream * stm, DWORD pos_lo_dword)
  733. {
  734.   /* Caller should be holding stm->lock. */
  735.   if (pos_lo_dword < stm->prev_pos_lo_dword) {
  736.     stm->pos_hi_dword++;
  737.     LOG("waveOutGetPosition() has wrapped around: %#lx -> %#lx",
  738.         stm->prev_pos_lo_dword, pos_lo_dword);
  739.     LOG("Wrap-around count = %#lx", stm->pos_hi_dword);
  740.     LOG("Current 64-bit position = %#llx",
  741.         (((uint64_t) stm->pos_hi_dword)<<32) | ((uint64_t) pos_lo_dword));
  742.   }
  743.   stm->prev_pos_lo_dword = pos_lo_dword;
  744.  
  745.   return (((uint64_t) stm->pos_hi_dword)<<32) | ((uint64_t) pos_lo_dword);
  746. }
  747.  
  748. static int
  749. winmm_stream_get_position(cubeb_stream * stm, uint64_t * position)
  750. {
  751.   MMRESULT r;
  752.   MMTIME time;
  753.  
  754.   EnterCriticalSection(&stm->lock);
  755.   /* See the long comment above for why not just use TIME_SAMPLES here. */
  756.   time.wType = TIME_BYTES;
  757.   r = waveOutGetPosition(stm->waveout, &time, sizeof(time));
  758.  
  759.   if (r != MMSYSERR_NOERROR || time.wType != TIME_BYTES) {
  760.     LeaveCriticalSection(&stm->lock);
  761.     return CUBEB_ERROR;
  762.   }
  763.  
  764.   *position = update_64bit_position(stm, time.u.cb) / stm->frame_size;
  765.   LeaveCriticalSection(&stm->lock);
  766.  
  767.   return CUBEB_OK;
  768. }
  769.  
  770. static int
  771. winmm_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
  772. {
  773.   MMRESULT r;
  774.   MMTIME time;
  775.  
  776.   EnterCriticalSection(&stm->lock);
  777.   /* See the long comment above for why not just use TIME_SAMPLES here. */
  778.   time.wType = TIME_BYTES;
  779.   r = waveOutGetPosition(stm->waveout, &time, sizeof(time));
  780.  
  781.   if (r != MMSYSERR_NOERROR || time.wType != TIME_BYTES) {
  782.     LeaveCriticalSection(&stm->lock);
  783.     return CUBEB_ERROR;
  784.   }
  785.  
  786.   uint64_t position = update_64bit_position(stm, time.u.cb);
  787.   uint64_t written = stm->written;
  788.   LeaveCriticalSection(&stm->lock);
  789.  
  790.   XASSERT((written - (position / stm->frame_size)) <= UINT32_MAX);
  791.   *latency = (uint32_t) (written - (position / stm->frame_size));
  792.  
  793.   return CUBEB_OK;
  794. }
  795.  
  796. static int
  797. winmm_stream_set_volume(cubeb_stream * stm, float volume)
  798. {
  799.   EnterCriticalSection(&stm->lock);
  800.   stm->soft_volume = volume;
  801.   LeaveCriticalSection(&stm->lock);
  802.   return CUBEB_OK;
  803. }
  804.  
  805. #define MM_11025HZ_MASK (WAVE_FORMAT_1M08 | WAVE_FORMAT_1M16 | WAVE_FORMAT_1S08 | WAVE_FORMAT_1S16)
  806. #define MM_22050HZ_MASK (WAVE_FORMAT_2M08 | WAVE_FORMAT_2M16 | WAVE_FORMAT_2S08 | WAVE_FORMAT_2S16)
  807. #define MM_44100HZ_MASK (WAVE_FORMAT_4M08 | WAVE_FORMAT_4M16 | WAVE_FORMAT_4S08 | WAVE_FORMAT_4S16)
  808. #define MM_48000HZ_MASK (WAVE_FORMAT_48M08 | WAVE_FORMAT_48M16 | WAVE_FORMAT_48S08 | WAVE_FORMAT_48S16)
  809. #define MM_96000HZ_MASK (WAVE_FORMAT_96M08 | WAVE_FORMAT_96M16 | WAVE_FORMAT_96S08 | WAVE_FORMAT_96S16)
  810. static void
  811. winmm_calculate_device_rate(cubeb_device_info * info, DWORD formats)
  812. {
  813.   if (formats & MM_11025HZ_MASK) {
  814.     info->min_rate = 11025;
  815.     info->default_rate = 11025;
  816.     info->max_rate = 11025;
  817.   }
  818.   if (formats & MM_22050HZ_MASK) {
  819.     if (info->min_rate == 0) info->min_rate = 22050;
  820.     info->max_rate = 22050;
  821.     info->default_rate = 22050;
  822.   }
  823.   if (formats & MM_44100HZ_MASK) {
  824.     if (info->min_rate == 0) info->min_rate = 44100;
  825.     info->max_rate = 44100;
  826.     info->default_rate = 44100;
  827.   }
  828.   if (formats & MM_48000HZ_MASK) {
  829.     if (info->min_rate == 0) info->min_rate = 48000;
  830.     info->max_rate = 48000;
  831.     info->default_rate = 48000;
  832.   }
  833.   if (formats & MM_96000HZ_MASK) {
  834.     if (info->min_rate == 0) {
  835.       info->min_rate = 96000;
  836.       info->default_rate = 96000;
  837.     }
  838.     info->max_rate = 96000;
  839.   }
  840. }
  841.  
  842.  
  843. #define MM_S16_MASK (WAVE_FORMAT_1M16 | WAVE_FORMAT_1S16 | WAVE_FORMAT_2M16 | WAVE_FORMAT_2S16 | WAVE_FORMAT_4M16 | \
  844.     WAVE_FORMAT_4S16 | WAVE_FORMAT_48M16 | WAVE_FORMAT_48S16 | WAVE_FORMAT_96M16 | WAVE_FORMAT_96S16)
  845. static int
  846. winmm_query_supported_formats(UINT devid, DWORD formats,
  847.     cubeb_device_fmt * supfmt, cubeb_device_fmt * deffmt)
  848. {
  849.   WAVEFORMATEXTENSIBLE wfx;
  850.  
  851.   if (formats & MM_S16_MASK)
  852.     *deffmt = *supfmt = CUBEB_DEVICE_FMT_S16LE;
  853.   else
  854.     *deffmt = *supfmt = 0;
  855.  
  856.   ZeroMemory(&wfx, sizeof(WAVEFORMATEXTENSIBLE));
  857.   wfx.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
  858.   wfx.Format.nChannels = 2;
  859.   wfx.Format.nSamplesPerSec = 44100;
  860.   wfx.Format.wBitsPerSample = 32;
  861.   wfx.Format.nBlockAlign = (wfx.Format.wBitsPerSample * wfx.Format.nChannels) / 8;
  862.   wfx.Format.nAvgBytesPerSec = wfx.Format.nSamplesPerSec * wfx.Format.nBlockAlign;
  863.   wfx.Format.cbSize = 22;
  864.   wfx.Samples.wValidBitsPerSample = wfx.Format.wBitsPerSample;
  865.   wfx.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
  866.   wfx.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
  867.   if (waveOutOpen(NULL, devid, &wfx.Format, 0, 0, WAVE_FORMAT_QUERY) == MMSYSERR_NOERROR)
  868.     *supfmt = (cubeb_device_fmt)(*supfmt | CUBEB_DEVICE_FMT_F32LE);
  869.  
  870.   return (*deffmt != 0) ? CUBEB_OK : CUBEB_ERROR;
  871. }
  872.  
  873. static char *
  874. guid_to_cstr(LPGUID guid)
  875. {
  876.   char * ret = malloc(sizeof(char) * 40);
  877.   if (!ret) {
  878.     return NULL;
  879.   }
  880.   _snprintf_s(ret, sizeof(char) * 40, _TRUNCATE,
  881.       "{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}",
  882.       guid->Data1, guid->Data2, guid->Data3,
  883.       guid->Data4[0], guid->Data4[1], guid->Data4[2], guid->Data4[3],
  884.       guid->Data4[4], guid->Data4[5], guid->Data4[6], guid->Data4[7]);
  885.   return ret;
  886. }
  887.  
  888. static cubeb_device_pref
  889. winmm_query_preferred_out_device(UINT devid)
  890. {
  891.   DWORD mmpref = WAVE_MAPPER, compref = WAVE_MAPPER, status;
  892.   cubeb_device_pref ret = CUBEB_DEVICE_PREF_NONE;
  893.  
  894.   if (waveOutMessage((HWAVEOUT)(size_t)WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET,
  895.         (DWORD_PTR)&mmpref, (DWORD_PTR)&status) == MMSYSERR_NOERROR &&
  896.       devid == mmpref)
  897.     ret |= CUBEB_DEVICE_PREF_MULTIMEDIA | CUBEB_DEVICE_PREF_NOTIFICATION;
  898.  
  899.   if (waveOutMessage((HWAVEOUT)(size_t)WAVE_MAPPER, DRVM_MAPPER_CONSOLEVOICECOM_GET,
  900.         (DWORD_PTR)&compref, (DWORD_PTR)&status) == MMSYSERR_NOERROR &&
  901.       devid == compref)
  902.     ret |= CUBEB_DEVICE_PREF_VOICE;
  903.  
  904.   return ret;
  905. }
  906.  
  907. static char *
  908. device_id_idx(UINT devid)
  909. {
  910.   char * ret = (char *)malloc(sizeof(char)*16);
  911.   if (!ret) {
  912.     return NULL;
  913.   }
  914.   _snprintf_s(ret, 16, _TRUNCATE, "%u", devid);
  915.   return ret;
  916. }
  917.  
  918. static cubeb_device_info *
  919. winmm_create_device_from_outcaps2(LPWAVEOUTCAPS2A caps, UINT devid)
  920. {
  921.   cubeb_device_info * ret;
  922.  
  923.   ret = calloc(1, sizeof(cubeb_device_info));
  924.   if (!ret) {
  925.     return NULL;
  926.   }
  927.   ret->devid = (cubeb_devid)(size_t)devid;
  928.   ret->device_id = device_id_idx(devid);
  929.   ret->friendly_name = _strdup(caps->szPname);
  930.   ret->group_id = guid_to_cstr(&caps->ProductGuid);
  931.   ret->vendor_name = guid_to_cstr(&caps->ManufacturerGuid);
  932.  
  933.   ret->type = CUBEB_DEVICE_TYPE_OUTPUT;
  934.   ret->state = CUBEB_DEVICE_STATE_ENABLED;
  935.   ret->preferred = winmm_query_preferred_out_device(devid);
  936.  
  937.   ret->max_channels = caps->wChannels;
  938.   winmm_calculate_device_rate(ret, caps->dwFormats);
  939.   winmm_query_supported_formats(devid, caps->dwFormats,
  940.       &ret->format, &ret->default_format);
  941.  
  942.   /* Hardcoed latency estimates... */
  943.   ret->latency_lo = 100 * ret->default_rate / 1000;
  944.   ret->latency_hi = 200 * ret->default_rate / 1000;
  945.  
  946.   return ret;
  947. }
  948.  
  949. static cubeb_device_info *
  950. winmm_create_device_from_outcaps(LPWAVEOUTCAPSA caps, UINT devid)
  951. {
  952.   cubeb_device_info * ret;
  953.  
  954.   ret = calloc(1, sizeof(cubeb_device_info));
  955.   if (!ret) {
  956.     return NULL;
  957.   }
  958.   ret->devid = (cubeb_devid)(size_t)devid;
  959.   ret->device_id = device_id_idx(devid);
  960.   ret->friendly_name = _strdup(caps->szPname);
  961.   ret->group_id = NULL;
  962.   ret->vendor_name = NULL;
  963.  
  964.   ret->type = CUBEB_DEVICE_TYPE_OUTPUT;
  965.   ret->state = CUBEB_DEVICE_STATE_ENABLED;
  966.   ret->preferred = winmm_query_preferred_out_device(devid);
  967.  
  968.   ret->max_channels = caps->wChannels;
  969.   winmm_calculate_device_rate(ret, caps->dwFormats);
  970.   winmm_query_supported_formats(devid, caps->dwFormats,
  971.       &ret->format, &ret->default_format);
  972.  
  973.   /* Hardcoed latency estimates... */
  974.   ret->latency_lo = 100 * ret->default_rate / 1000;
  975.   ret->latency_hi = 200 * ret->default_rate / 1000;
  976.  
  977.   return ret;
  978. }
  979.  
  980. static cubeb_device_pref
  981. winmm_query_preferred_in_device(UINT devid)
  982. {
  983.   DWORD mmpref = WAVE_MAPPER, compref = WAVE_MAPPER, status;
  984.   cubeb_device_pref ret = CUBEB_DEVICE_PREF_NONE;
  985.  
  986.   if (waveInMessage((HWAVEIN)(size_t)WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET,
  987.         (DWORD_PTR)&mmpref, (DWORD_PTR)&status) == MMSYSERR_NOERROR &&
  988.       devid == mmpref)
  989.     ret |= CUBEB_DEVICE_PREF_MULTIMEDIA | CUBEB_DEVICE_PREF_NOTIFICATION;
  990.  
  991.   if (waveInMessage((HWAVEIN)(size_t)WAVE_MAPPER, DRVM_MAPPER_CONSOLEVOICECOM_GET,
  992.         (DWORD_PTR)&compref, (DWORD_PTR)&status) == MMSYSERR_NOERROR &&
  993.       devid == compref)
  994.     ret |= CUBEB_DEVICE_PREF_VOICE;
  995.  
  996.   return ret;
  997. }
  998.  
  999. static cubeb_device_info *
  1000. winmm_create_device_from_incaps2(LPWAVEINCAPS2A caps, UINT devid)
  1001. {
  1002.   cubeb_device_info * ret;
  1003.  
  1004.   ret = calloc(1, sizeof(cubeb_device_info));
  1005.   if (!ret) {
  1006.     return NULL;
  1007.   }
  1008.   ret->devid = (cubeb_devid)(size_t)devid;
  1009.   ret->device_id = device_id_idx(devid);
  1010.   ret->friendly_name = _strdup(caps->szPname);
  1011.   ret->group_id = guid_to_cstr(&caps->ProductGuid);
  1012.   ret->vendor_name = guid_to_cstr(&caps->ManufacturerGuid);
  1013.  
  1014.   ret->type = CUBEB_DEVICE_TYPE_INPUT;
  1015.   ret->state = CUBEB_DEVICE_STATE_ENABLED;
  1016.   ret->preferred = winmm_query_preferred_in_device(devid);
  1017.  
  1018.   ret->max_channels = caps->wChannels;
  1019.   winmm_calculate_device_rate(ret, caps->dwFormats);
  1020.   winmm_query_supported_formats(devid, caps->dwFormats,
  1021.       &ret->format, &ret->default_format);
  1022.  
  1023.   /* Hardcoed latency estimates... */
  1024.   ret->latency_lo = 100 * ret->default_rate / 1000;
  1025.   ret->latency_hi = 200 * ret->default_rate / 1000;
  1026.  
  1027.   return ret;
  1028. }
  1029.  
  1030. static cubeb_device_info *
  1031. winmm_create_device_from_incaps(LPWAVEINCAPSA caps, UINT devid)
  1032. {
  1033.   cubeb_device_info * ret;
  1034.  
  1035.   ret = calloc(1, sizeof(cubeb_device_info));
  1036.   if (!ret) {
  1037.     return NULL;
  1038.   }
  1039.   ret->devid = (cubeb_devid)(size_t)devid;
  1040.   ret->device_id = device_id_idx(devid);
  1041.   ret->friendly_name = _strdup(caps->szPname);
  1042.   ret->group_id = NULL;
  1043.   ret->vendor_name = NULL;
  1044.  
  1045.   ret->type = CUBEB_DEVICE_TYPE_INPUT;
  1046.   ret->state = CUBEB_DEVICE_STATE_ENABLED;
  1047.   ret->preferred = winmm_query_preferred_in_device(devid);
  1048.  
  1049.   ret->max_channels = caps->wChannels;
  1050.   winmm_calculate_device_rate(ret, caps->dwFormats);
  1051.   winmm_query_supported_formats(devid, caps->dwFormats,
  1052.       &ret->format, &ret->default_format);
  1053.  
  1054.   /* Hardcoed latency estimates... */
  1055.   ret->latency_lo = 100 * ret->default_rate / 1000;
  1056.   ret->latency_hi = 200 * ret->default_rate / 1000;
  1057.  
  1058.   return ret;
  1059. }
  1060.  
  1061. static int
  1062. winmm_enumerate_devices(cubeb * context, cubeb_device_type type,
  1063.                         cubeb_device_collection ** collection)
  1064. {
  1065.   UINT i, incount, outcount, total;
  1066.   cubeb_device_info * cur;
  1067.  
  1068.   outcount = waveOutGetNumDevs();
  1069.   incount = waveInGetNumDevs();
  1070.   total = outcount + incount;
  1071.   if (total > 0) {
  1072.     total -= 1;
  1073.   }
  1074.   *collection = malloc(sizeof(cubeb_device_collection) +
  1075.       sizeof(cubeb_device_info*) * total);
  1076.   (*collection)->count = 0;
  1077.  
  1078.   if (type & CUBEB_DEVICE_TYPE_OUTPUT) {
  1079.     WAVEOUTCAPSA woc;
  1080.     WAVEOUTCAPS2A woc2;
  1081.  
  1082.     ZeroMemory(&woc, sizeof(woc));
  1083.     ZeroMemory(&woc2, sizeof(woc2));
  1084.  
  1085.     for (i = 0; i < outcount; i++) {
  1086.       if ((waveOutGetDevCapsA(i, (LPWAVEOUTCAPSA)&woc2, sizeof(woc2)) == MMSYSERR_NOERROR &&
  1087.             (cur = winmm_create_device_from_outcaps2(&woc2, i)) != NULL) ||
  1088.           (waveOutGetDevCapsA(i, &woc, sizeof(woc)) == MMSYSERR_NOERROR &&
  1089.             (cur = winmm_create_device_from_outcaps(&woc, i)) != NULL)
  1090.           ) {
  1091.         (*collection)->device[(*collection)->count++] = cur;
  1092.       }
  1093.     }
  1094.   }
  1095.  
  1096.   if (type & CUBEB_DEVICE_TYPE_INPUT) {
  1097.     WAVEINCAPSA wic;
  1098.     WAVEINCAPS2A wic2;
  1099.  
  1100.     ZeroMemory(&wic, sizeof(wic));
  1101.     ZeroMemory(&wic2, sizeof(wic2));
  1102.  
  1103.     for (i = 0; i < incount; i++) {
  1104.       if ((waveInGetDevCapsA(i, (LPWAVEINCAPSA)&wic2, sizeof(wic2)) == MMSYSERR_NOERROR &&
  1105.             (cur = winmm_create_device_from_incaps2(&wic2, i)) != NULL) ||
  1106.           (waveInGetDevCapsA(i, &wic, sizeof(wic)) == MMSYSERR_NOERROR &&
  1107.             (cur = winmm_create_device_from_incaps(&wic, i)) != NULL)
  1108.           ) {
  1109.         (*collection)->device[(*collection)->count++] = cur;
  1110.       }
  1111.     }
  1112.   }
  1113.  
  1114.   return CUBEB_OK;
  1115. }
  1116.  
  1117. static struct cubeb_ops const winmm_ops = {
  1118.   /*.init =*/ winmm_init,
  1119.   /*.get_backend_id =*/ winmm_get_backend_id,
  1120.   /*.get_max_channel_count=*/ winmm_get_max_channel_count,
  1121.   /*.get_min_latency=*/ winmm_get_min_latency,
  1122.   /*.get_preferred_sample_rate =*/ winmm_get_preferred_sample_rate,
  1123.   /*.enumerate_devices =*/ winmm_enumerate_devices,
  1124.   /*.destroy =*/ winmm_destroy,
  1125.   /*.stream_init =*/ winmm_stream_init,
  1126.   /*.stream_destroy =*/ winmm_stream_destroy,
  1127.   /*.stream_start =*/ winmm_stream_start,
  1128.   /*.stream_stop =*/ winmm_stream_stop,
  1129.   /*.stream_get_position =*/ winmm_stream_get_position,
  1130.   /*.stream_get_latency = */ winmm_stream_get_latency,
  1131.   /*.stream_set_volume =*/ winmm_stream_set_volume,
  1132.   /*.stream_set_panning =*/ NULL,
  1133.   /*.stream_get_current_device =*/ NULL,
  1134.   /*.stream_device_destroy =*/ NULL,
  1135.   /*.stream_register_device_changed_callback=*/ NULL,
  1136.   /*.register_device_collection_changed =*/ NULL
  1137. };
  1138.  
RAW Paste Data