Advertisement
Guest User

cubeb_winmm.c with overflow fix

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