mirror of
https://github.com/WinampDesktop/winamp.git
synced 2025-06-18 09:05:47 -04:00
dep/cubeb: Update to dc511c6
This commit is contained in:
@ -8,71 +8,71 @@
|
||||
#define WINVER 0x0501
|
||||
#undef WIN32_LEAN_AND_MEAN
|
||||
|
||||
#include "cubeb-internal.h"
|
||||
#include "cubeb/cubeb.h"
|
||||
#include <malloc.h>
|
||||
#include <windows.h>
|
||||
#include <mmreg.h>
|
||||
#include <mmsystem.h>
|
||||
#include <math.h>
|
||||
#include <process.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include "cubeb/cubeb.h"
|
||||
#include "cubeb-internal.h"
|
||||
#include <windows.h>
|
||||
|
||||
/* clang-format off */
|
||||
/* These need to be included after windows.h */
|
||||
#include <mmreg.h>
|
||||
#include <mmsystem.h>
|
||||
/* clang-format on */
|
||||
|
||||
/* This is missing from the MinGW headers. Use a safe fallback. */
|
||||
#if !defined(MEMORY_ALLOCATION_ALIGNMENT)
|
||||
#define MEMORY_ALLOCATION_ALIGNMENT 16
|
||||
#endif
|
||||
|
||||
/**This is also missing from the MinGW headers. It also appears to be undocumented by Microsoft.*/
|
||||
/**This is also missing from the MinGW headers. It also appears to be
|
||||
* undocumented by Microsoft.*/
|
||||
#ifndef WAVE_FORMAT_48M08
|
||||
#define WAVE_FORMAT_48M08 0x00001000 /* 48 kHz, Mono, 8-bit */
|
||||
#define WAVE_FORMAT_48M08 0x00001000 /* 48 kHz, Mono, 8-bit */
|
||||
#endif
|
||||
#ifndef WAVE_FORMAT_48M16
|
||||
#define WAVE_FORMAT_48M16 0x00002000 /* 48 kHz, Mono, 16-bit */
|
||||
#define WAVE_FORMAT_48M16 0x00002000 /* 48 kHz, Mono, 16-bit */
|
||||
#endif
|
||||
#ifndef WAVE_FORMAT_48S08
|
||||
#define WAVE_FORMAT_48S08 0x00004000 /* 48 kHz, Stereo, 8-bit */
|
||||
#define WAVE_FORMAT_48S08 0x00004000 /* 48 kHz, Stereo, 8-bit */
|
||||
#endif
|
||||
#ifndef WAVE_FORMAT_48S16
|
||||
#define WAVE_FORMAT_48S16 0x00008000 /* 48 kHz, Stereo, 16-bit */
|
||||
#define WAVE_FORMAT_48S16 0x00008000 /* 48 kHz, Stereo, 16-bit */
|
||||
#endif
|
||||
#ifndef WAVE_FORMAT_96M08
|
||||
#define WAVE_FORMAT_96M08 0x00010000 /* 96 kHz, Mono, 8-bit */
|
||||
#define WAVE_FORMAT_96M08 0x00010000 /* 96 kHz, Mono, 8-bit */
|
||||
#endif
|
||||
#ifndef WAVE_FORMAT_96M16
|
||||
#define WAVE_FORMAT_96M16 0x00020000 /* 96 kHz, Mono, 16-bit */
|
||||
#define WAVE_FORMAT_96M16 0x00020000 /* 96 kHz, Mono, 16-bit */
|
||||
#endif
|
||||
#ifndef WAVE_FORMAT_96S08
|
||||
#define WAVE_FORMAT_96S08 0x00040000 /* 96 kHz, Stereo, 8-bit */
|
||||
#define WAVE_FORMAT_96S08 0x00040000 /* 96 kHz, Stereo, 8-bit */
|
||||
#endif
|
||||
#ifndef WAVE_FORMAT_96S16
|
||||
#define WAVE_FORMAT_96S16 0x00080000 /* 96 kHz, Stereo, 16-bit */
|
||||
#define WAVE_FORMAT_96S16 0x00080000 /* 96 kHz, Stereo, 16-bit */
|
||||
#endif
|
||||
|
||||
/**Taken from winbase.h, also not in MinGW.*/
|
||||
#ifndef STACK_SIZE_PARAM_IS_A_RESERVATION
|
||||
#define STACK_SIZE_PARAM_IS_A_RESERVATION 0x00010000 // Threads only
|
||||
#define STACK_SIZE_PARAM_IS_A_RESERVATION 0x00010000 // Threads only
|
||||
#endif
|
||||
|
||||
#ifndef DRVM_MAPPER
|
||||
#define DRVM_MAPPER (0x2000)
|
||||
#define DRVM_MAPPER (0x2000)
|
||||
#endif
|
||||
#ifndef DRVM_MAPPER_PREFERRED_GET
|
||||
#define DRVM_MAPPER_PREFERRED_GET (DRVM_MAPPER+21)
|
||||
#define DRVM_MAPPER_PREFERRED_GET (DRVM_MAPPER + 21)
|
||||
#endif
|
||||
#ifndef DRVM_MAPPER_CONSOLEVOICECOM_GET
|
||||
#define DRVM_MAPPER_CONSOLEVOICECOM_GET (DRVM_MAPPER+23)
|
||||
#define DRVM_MAPPER_CONSOLEVOICECOM_GET (DRVM_MAPPER + 23)
|
||||
#endif
|
||||
|
||||
#define CUBEB_STREAM_MAX 32
|
||||
#define NBUFS 4
|
||||
|
||||
const GUID KSDATAFORMAT_SUBTYPE_PCM =
|
||||
{ 0x00000001, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } };
|
||||
const GUID KSDATAFORMAT_SUBTYPE_IEEE_FLOAT =
|
||||
{ 0x00000003, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } };
|
||||
|
||||
struct cubeb_stream_item {
|
||||
SLIST_ENTRY head;
|
||||
cubeb_stream * stream;
|
||||
@ -110,6 +110,10 @@ struct cubeb_stream {
|
||||
CRITICAL_SECTION lock;
|
||||
uint64_t written;
|
||||
float soft_volume;
|
||||
/* For position wrap-around handling: */
|
||||
size_t frame_size;
|
||||
DWORD prev_pos_lo_dword;
|
||||
DWORD pos_hi_dword;
|
||||
};
|
||||
|
||||
static size_t
|
||||
@ -175,7 +179,7 @@ winmm_refill_stream(cubeb_stream * stm)
|
||||
|
||||
hdr = winmm_get_next_buffer(stm);
|
||||
|
||||
wanted = (DWORD) stm->buffer_size / bytes_per_frame(stm->params);
|
||||
wanted = (DWORD)stm->buffer_size / bytes_per_frame(stm->params);
|
||||
|
||||
/* It is assumed that the caller is holding this lock. It must be dropped
|
||||
during the callback to avoid deadlocks. */
|
||||
@ -199,16 +203,16 @@ winmm_refill_stream(cubeb_stream * stm)
|
||||
|
||||
if (stm->soft_volume != -1.0) {
|
||||
if (stm->params.format == CUBEB_SAMPLE_FLOAT32NE) {
|
||||
float * b = (float *) hdr->lpData;
|
||||
float * b = (float *)hdr->lpData;
|
||||
uint32_t i;
|
||||
for (i = 0; i < got * stm->params.channels; i++) {
|
||||
b[i] *= stm->soft_volume;
|
||||
}
|
||||
} else {
|
||||
short * b = (short *) hdr->lpData;
|
||||
short * b = (short *)hdr->lpData;
|
||||
uint32_t i;
|
||||
for (i = 0; i < got * stm->params.channels; i++) {
|
||||
b[i] = (short) (b[i] * stm->soft_volume);
|
||||
b[i] = (short)(b[i] * stm->soft_volume);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -223,10 +227,9 @@ winmm_refill_stream(cubeb_stream * stm)
|
||||
LeaveCriticalSection(&stm->lock);
|
||||
}
|
||||
|
||||
static unsigned __stdcall
|
||||
winmm_buffer_thread(void * user_ptr)
|
||||
static unsigned __stdcall winmm_buffer_thread(void * user_ptr)
|
||||
{
|
||||
cubeb * ctx = (cubeb *) user_ptr;
|
||||
cubeb * ctx = (cubeb *)user_ptr;
|
||||
XASSERT(ctx);
|
||||
|
||||
for (;;) {
|
||||
@ -242,7 +245,7 @@ winmm_buffer_thread(void * user_ptr)
|
||||
item = InterlockedFlushSList(ctx->work);
|
||||
while (item != NULL) {
|
||||
PSLIST_ENTRY tmp = item;
|
||||
winmm_refill_stream(((struct cubeb_stream_item *) tmp)->stream);
|
||||
winmm_refill_stream(((struct cubeb_stream_item *)tmp)->stream);
|
||||
item = item->Next;
|
||||
_aligned_free(tmp);
|
||||
}
|
||||
@ -256,16 +259,18 @@ winmm_buffer_thread(void * user_ptr)
|
||||
}
|
||||
|
||||
static void CALLBACK
|
||||
winmm_buffer_callback(HWAVEOUT waveout, UINT msg, DWORD_PTR user_ptr, DWORD_PTR p1, DWORD_PTR p2)
|
||||
winmm_buffer_callback(HWAVEOUT waveout, UINT msg, DWORD_PTR user_ptr,
|
||||
DWORD_PTR p1, DWORD_PTR p2)
|
||||
{
|
||||
cubeb_stream * stm = (cubeb_stream *) user_ptr;
|
||||
cubeb_stream * stm = (cubeb_stream *)user_ptr;
|
||||
struct cubeb_stream_item * item;
|
||||
|
||||
if (msg != WOM_DONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
item = _aligned_malloc(sizeof(struct cubeb_stream_item), MEMORY_ALLOCATION_ALIGNMENT);
|
||||
item = _aligned_malloc(sizeof(struct cubeb_stream_item),
|
||||
MEMORY_ALLOCATION_ALIGNMENT);
|
||||
XASSERT(item);
|
||||
item->stream = stm;
|
||||
InterlockedPushEntrySList(stm->context->work, &item->head);
|
||||
@ -284,7 +289,8 @@ calculate_minimum_latency(void)
|
||||
return 500;
|
||||
}
|
||||
|
||||
/* Vista's WinMM implementation underruns when less than 200ms of audio is buffered. */
|
||||
/* Vista's WinMM implementation underruns when less than 200ms of audio is
|
||||
* buffered. */
|
||||
memset(&osvi, 0, sizeof(OSVERSIONINFOEX));
|
||||
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
|
||||
osvi.dwMajorVersion = 6;
|
||||
@ -294,14 +300,16 @@ calculate_minimum_latency(void)
|
||||
VER_SET_CONDITION(mask, VER_MAJORVERSION, VER_EQUAL);
|
||||
VER_SET_CONDITION(mask, VER_MINORVERSION, VER_EQUAL);
|
||||
|
||||
if (VerifyVersionInfo(&osvi, VER_MAJORVERSION | VER_MINORVERSION, mask) != 0) {
|
||||
if (VerifyVersionInfo(&osvi, VER_MAJORVERSION | VER_MINORVERSION, mask) !=
|
||||
0) {
|
||||
return 200;
|
||||
}
|
||||
|
||||
return 100;
|
||||
}
|
||||
|
||||
static void winmm_destroy(cubeb * ctx);
|
||||
static void
|
||||
winmm_destroy(cubeb * ctx);
|
||||
|
||||
/*static*/ int
|
||||
winmm_init(cubeb ** context, char const * context_name)
|
||||
@ -331,7 +339,9 @@ winmm_init(cubeb ** context, char const * context_name)
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
ctx->thread = (HANDLE) _beginthreadex(NULL, 256 * 1024, winmm_buffer_thread, ctx, STACK_SIZE_PARAM_IS_A_RESERVATION, NULL);
|
||||
ctx->thread =
|
||||
(HANDLE)_beginthreadex(NULL, 256 * 1024, winmm_buffer_thread, ctx,
|
||||
STACK_SIZE_PARAM_IS_A_RESERVATION, NULL);
|
||||
if (!ctx->thread) {
|
||||
winmm_destroy(ctx);
|
||||
return CUBEB_ERROR;
|
||||
@ -382,18 +392,18 @@ winmm_destroy(cubeb * ctx)
|
||||
free(ctx);
|
||||
}
|
||||
|
||||
static void winmm_stream_destroy(cubeb_stream * stm);
|
||||
static void
|
||||
winmm_stream_destroy(cubeb_stream * stm);
|
||||
|
||||
static int
|
||||
winmm_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_name,
|
||||
cubeb_devid input_device,
|
||||
winmm_stream_init(cubeb * context, cubeb_stream ** stream,
|
||||
char const * stream_name, cubeb_devid input_device,
|
||||
cubeb_stream_params * input_stream_params,
|
||||
cubeb_devid output_device,
|
||||
cubeb_stream_params * output_stream_params,
|
||||
unsigned int latency_frames,
|
||||
cubeb_data_callback data_callback,
|
||||
cubeb_state_callback state_callback,
|
||||
void * user_ptr)
|
||||
cubeb_state_callback state_callback, void * user_ptr)
|
||||
{
|
||||
MMRESULT r;
|
||||
WAVEFORMATEXTENSIBLE wfx;
|
||||
@ -452,8 +462,10 @@ winmm_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_n
|
||||
return CUBEB_ERROR_INVALID_FORMAT;
|
||||
}
|
||||
|
||||
wfx.Format.nBlockAlign = (wfx.Format.wBitsPerSample * wfx.Format.nChannels) / 8;
|
||||
wfx.Format.nAvgBytesPerSec = wfx.Format.nSamplesPerSec * wfx.Format.nBlockAlign;
|
||||
wfx.Format.nBlockAlign =
|
||||
(wfx.Format.wBitsPerSample * wfx.Format.nChannels) / 8;
|
||||
wfx.Format.nAvgBytesPerSec =
|
||||
wfx.Format.nSamplesPerSec * wfx.Format.nBlockAlign;
|
||||
wfx.Samples.wValidBitsPerSample = wfx.Format.wBitsPerSample;
|
||||
|
||||
EnterCriticalSection(&context->lock);
|
||||
@ -485,9 +497,11 @@ winmm_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_n
|
||||
latency_ms = context->minimum_latency_ms;
|
||||
}
|
||||
|
||||
bufsz = (size_t) (stm->params.rate / 1000.0 * latency_ms * bytes_per_frame(stm->params) / NBUFS);
|
||||
bufsz = (size_t)(stm->params.rate / 1000.0 * latency_ms *
|
||||
bytes_per_frame(stm->params) / NBUFS);
|
||||
if (bufsz % bytes_per_frame(stm->params) != 0) {
|
||||
bufsz += bytes_per_frame(stm->params) - (bufsz % bytes_per_frame(stm->params));
|
||||
bufsz +=
|
||||
bytes_per_frame(stm->params) - (bufsz % bytes_per_frame(stm->params));
|
||||
}
|
||||
XASSERT(bufsz % bytes_per_frame(stm->params) == 0);
|
||||
|
||||
@ -506,7 +520,7 @@ winmm_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_n
|
||||
/* winmm_buffer_callback will be called during waveOutOpen, so all
|
||||
other initialization must be complete before calling it. */
|
||||
r = waveOutOpen(&stm->waveout, WAVE_MAPPER, &wfx.Format,
|
||||
(DWORD_PTR) winmm_buffer_callback, (DWORD_PTR) stm,
|
||||
(DWORD_PTR)winmm_buffer_callback, (DWORD_PTR)stm,
|
||||
CALLBACK_FUNCTION);
|
||||
if (r != MMSYSERR_NOERROR) {
|
||||
winmm_stream_destroy(stm);
|
||||
@ -536,6 +550,10 @@ winmm_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_n
|
||||
winmm_refill_stream(stm);
|
||||
}
|
||||
|
||||
stm->frame_size = bytes_per_frame(stm->params);
|
||||
stm->prev_pos_lo_dword = 0;
|
||||
stm->pos_hi_dword = 0;
|
||||
|
||||
*stream = stm;
|
||||
|
||||
return CUBEB_OK;
|
||||
@ -580,7 +598,8 @@ winmm_stream_destroy(cubeb_stream * stm)
|
||||
|
||||
for (i = 0; i < NBUFS; ++i) {
|
||||
if (stm->buffers[i].dwFlags & WHDR_PREPARED) {
|
||||
waveOutUnprepareHeader(stm->waveout, &stm->buffers[i], sizeof(stm->buffers[i]));
|
||||
waveOutUnprepareHeader(stm->waveout, &stm->buffers[i],
|
||||
sizeof(stm->buffers[i]));
|
||||
}
|
||||
}
|
||||
|
||||
@ -619,7 +638,8 @@ winmm_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
|
||||
}
|
||||
|
||||
static int
|
||||
winmm_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency)
|
||||
winmm_get_min_latency(cubeb * ctx, cubeb_stream_params params,
|
||||
uint32_t * latency)
|
||||
{
|
||||
// 100ms minimum, if we are not in a bizarre configuration.
|
||||
*latency = ctx->minimum_latency_ms * params.rate / 1000;
|
||||
@ -686,6 +706,58 @@ winmm_stream_stop(cubeb_stream * stm)
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
Microsoft wave audio docs say "samples are the preferred time format in which
|
||||
to represent the current position", but relying on this causes problems on
|
||||
Windows XP, the only OS cubeb_winmm is used on.
|
||||
|
||||
While the wdmaud.sys driver internally tracks a 64-bit position and ensures no
|
||||
backward movement, the WinMM API limits the position returned from
|
||||
waveOutGetPosition() to a 32-bit DWORD (this applies equally to XP x64). The
|
||||
higher 32 bits are chopped off, and to an API consumer the position can appear
|
||||
to move backward.
|
||||
|
||||
In theory, even a 32-bit TIME_SAMPLES position should provide plenty of
|
||||
playback time for typical use cases before this pseudo wrap-around, e.g:
|
||||
(2^32 - 1)/48000 = ~24:51:18 for 48.0 kHz stereo;
|
||||
(2^32 - 1)/44100 = ~27:03:12 for 44.1 kHz stereo.
|
||||
In reality, wdmaud.sys doesn't provide a TIME_SAMPLES position at all, only a
|
||||
32-bit TIME_BYTES position, from which wdmaud.drv derives TIME_SAMPLES:
|
||||
SamplePos = (BytePos * 8) / BitsPerFrame,
|
||||
where BitsPerFrame = Channels * BitsPerSample,
|
||||
Per dom\media\AudioSampleFormat.h, desktop builds always use 32-bit FLOAT32
|
||||
samples, so the maximum for TIME_SAMPLES should be:
|
||||
(2^29 - 1)/48000 = ~03:06:25;
|
||||
(2^29 - 1)/44100 = ~03:22:54.
|
||||
This might still be OK for typical browser usage, but there's also a bug in the
|
||||
formula above: BytePos * 8 (BytePos << 3) is done on a 32-bit BytePos, without
|
||||
first casting it to 64 bits, so the highest 3 bits, if set, would get shifted
|
||||
out, and the maximum possible TIME_SAMPLES drops unacceptably low:
|
||||
(2^26 - 1)/48000 = ~00:23:18;
|
||||
(2^26 - 1)/44100 = ~00:25:22.
|
||||
|
||||
To work around these limitations, we just get the position in TIME_BYTES,
|
||||
recover the 64-bit value, and do our own conversion to samples.
|
||||
*/
|
||||
|
||||
/* Convert chopped 32-bit waveOutGetPosition() into 64-bit true position. */
|
||||
static uint64_t
|
||||
update_64bit_position(cubeb_stream * stm, DWORD pos_lo_dword)
|
||||
{
|
||||
/* Caller should be holding stm->lock. */
|
||||
if (pos_lo_dword < stm->prev_pos_lo_dword) {
|
||||
stm->pos_hi_dword++;
|
||||
LOG("waveOutGetPosition() has wrapped around: %#lx -> %#lx",
|
||||
stm->prev_pos_lo_dword, pos_lo_dword);
|
||||
LOG("Wrap-around count = %#lx", stm->pos_hi_dword);
|
||||
LOG("Current 64-bit position = %#llx",
|
||||
(((uint64_t)stm->pos_hi_dword) << 32) | ((uint64_t)pos_lo_dword));
|
||||
}
|
||||
stm->prev_pos_lo_dword = pos_lo_dword;
|
||||
|
||||
return (((uint64_t)stm->pos_hi_dword) << 32) | ((uint64_t)pos_lo_dword);
|
||||
}
|
||||
|
||||
static int
|
||||
winmm_stream_get_position(cubeb_stream * stm, uint64_t * position)
|
||||
{
|
||||
@ -693,15 +765,17 @@ winmm_stream_get_position(cubeb_stream * stm, uint64_t * position)
|
||||
MMTIME time;
|
||||
|
||||
EnterCriticalSection(&stm->lock);
|
||||
time.wType = TIME_SAMPLES;
|
||||
/* See the long comment above for why not just use TIME_SAMPLES here. */
|
||||
time.wType = TIME_BYTES;
|
||||
r = waveOutGetPosition(stm->waveout, &time, sizeof(time));
|
||||
LeaveCriticalSection(&stm->lock);
|
||||
|
||||
if (r != MMSYSERR_NOERROR || time.wType != TIME_SAMPLES) {
|
||||
if (r != MMSYSERR_NOERROR || time.wType != TIME_BYTES) {
|
||||
LeaveCriticalSection(&stm->lock);
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
*position = time.u.sample;
|
||||
*position = update_64bit_position(stm, time.u.cb) / stm->frame_size;
|
||||
LeaveCriticalSection(&stm->lock);
|
||||
|
||||
return CUBEB_OK;
|
||||
}
|
||||
@ -711,20 +785,24 @@ winmm_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
|
||||
{
|
||||
MMRESULT r;
|
||||
MMTIME time;
|
||||
uint64_t written;
|
||||
uint64_t written, position;
|
||||
|
||||
EnterCriticalSection(&stm->lock);
|
||||
time.wType = TIME_SAMPLES;
|
||||
/* See the long comment above for why not just use TIME_SAMPLES here. */
|
||||
time.wType = TIME_BYTES;
|
||||
r = waveOutGetPosition(stm->waveout, &time, sizeof(time));
|
||||
written = stm->written;
|
||||
LeaveCriticalSection(&stm->lock);
|
||||
|
||||
if (r != MMSYSERR_NOERROR || time.wType != TIME_SAMPLES) {
|
||||
if (r != MMSYSERR_NOERROR || time.wType != TIME_BYTES) {
|
||||
LeaveCriticalSection(&stm->lock);
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
XASSERT(written - time.u.sample <= UINT32_MAX);
|
||||
*latency = (uint32_t) (written - time.u.sample);
|
||||
position = update_64bit_position(stm, time.u.cb);
|
||||
written = stm->written;
|
||||
LeaveCriticalSection(&stm->lock);
|
||||
|
||||
XASSERT((written - (position / stm->frame_size)) <= UINT32_MAX);
|
||||
*latency = (uint32_t)(written - (position / stm->frame_size));
|
||||
|
||||
return CUBEB_OK;
|
||||
}
|
||||
@ -738,11 +816,18 @@ winmm_stream_set_volume(cubeb_stream * stm, float volume)
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
#define MM_11025HZ_MASK (WAVE_FORMAT_1M08 | WAVE_FORMAT_1M16 | WAVE_FORMAT_1S08 | WAVE_FORMAT_1S16)
|
||||
#define MM_22050HZ_MASK (WAVE_FORMAT_2M08 | WAVE_FORMAT_2M16 | WAVE_FORMAT_2S08 | WAVE_FORMAT_2S16)
|
||||
#define MM_44100HZ_MASK (WAVE_FORMAT_4M08 | WAVE_FORMAT_4M16 | WAVE_FORMAT_4S08 | WAVE_FORMAT_4S16)
|
||||
#define MM_48000HZ_MASK (WAVE_FORMAT_48M08 | WAVE_FORMAT_48M16 | WAVE_FORMAT_48S08 | WAVE_FORMAT_48S16)
|
||||
#define MM_96000HZ_MASK (WAVE_FORMAT_96M08 | WAVE_FORMAT_96M16 | WAVE_FORMAT_96S08 | WAVE_FORMAT_96S16)
|
||||
#define MM_11025HZ_MASK \
|
||||
(WAVE_FORMAT_1M08 | WAVE_FORMAT_1M16 | WAVE_FORMAT_1S08 | WAVE_FORMAT_1S16)
|
||||
#define MM_22050HZ_MASK \
|
||||
(WAVE_FORMAT_2M08 | WAVE_FORMAT_2M16 | WAVE_FORMAT_2S08 | WAVE_FORMAT_2S16)
|
||||
#define MM_44100HZ_MASK \
|
||||
(WAVE_FORMAT_4M08 | WAVE_FORMAT_4M16 | WAVE_FORMAT_4S08 | WAVE_FORMAT_4S16)
|
||||
#define MM_48000HZ_MASK \
|
||||
(WAVE_FORMAT_48M08 | WAVE_FORMAT_48M16 | WAVE_FORMAT_48S08 | \
|
||||
WAVE_FORMAT_48S16)
|
||||
#define MM_96000HZ_MASK \
|
||||
(WAVE_FORMAT_96M08 | WAVE_FORMAT_96M16 | WAVE_FORMAT_96S08 | \
|
||||
WAVE_FORMAT_96S16)
|
||||
static void
|
||||
winmm_calculate_device_rate(cubeb_device_info * info, DWORD formats)
|
||||
{
|
||||
@ -752,17 +837,20 @@ winmm_calculate_device_rate(cubeb_device_info * info, DWORD formats)
|
||||
info->max_rate = 11025;
|
||||
}
|
||||
if (formats & MM_22050HZ_MASK) {
|
||||
if (info->min_rate == 0) info->min_rate = 22050;
|
||||
if (info->min_rate == 0)
|
||||
info->min_rate = 22050;
|
||||
info->max_rate = 22050;
|
||||
info->default_rate = 22050;
|
||||
}
|
||||
if (formats & MM_44100HZ_MASK) {
|
||||
if (info->min_rate == 0) info->min_rate = 44100;
|
||||
if (info->min_rate == 0)
|
||||
info->min_rate = 44100;
|
||||
info->max_rate = 44100;
|
||||
info->default_rate = 44100;
|
||||
}
|
||||
if (formats & MM_48000HZ_MASK) {
|
||||
if (info->min_rate == 0) info->min_rate = 48000;
|
||||
if (info->min_rate == 0)
|
||||
info->min_rate = 48000;
|
||||
info->max_rate = 48000;
|
||||
info->default_rate = 48000;
|
||||
}
|
||||
@ -775,11 +863,14 @@ winmm_calculate_device_rate(cubeb_device_info * info, DWORD formats)
|
||||
}
|
||||
}
|
||||
|
||||
#define MM_S16_MASK (WAVE_FORMAT_1M16 | WAVE_FORMAT_1S16 | WAVE_FORMAT_2M16 | WAVE_FORMAT_2S16 | WAVE_FORMAT_4M16 | \
|
||||
WAVE_FORMAT_4S16 | WAVE_FORMAT_48M16 | WAVE_FORMAT_48S16 | WAVE_FORMAT_96M16 | WAVE_FORMAT_96S16)
|
||||
#define MM_S16_MASK \
|
||||
(WAVE_FORMAT_1M16 | WAVE_FORMAT_1S16 | WAVE_FORMAT_2M16 | WAVE_FORMAT_2S16 | \
|
||||
WAVE_FORMAT_4M16 | WAVE_FORMAT_4S16 | WAVE_FORMAT_48M16 | \
|
||||
WAVE_FORMAT_48S16 | WAVE_FORMAT_96M16 | WAVE_FORMAT_96S16)
|
||||
static int
|
||||
winmm_query_supported_formats(UINT devid, DWORD formats,
|
||||
cubeb_device_fmt * supfmt, cubeb_device_fmt * deffmt)
|
||||
cubeb_device_fmt * supfmt,
|
||||
cubeb_device_fmt * deffmt)
|
||||
{
|
||||
WAVEFORMATEXTENSIBLE wfx;
|
||||
|
||||
@ -793,13 +884,16 @@ winmm_query_supported_formats(UINT devid, DWORD formats,
|
||||
wfx.Format.nChannels = 2;
|
||||
wfx.Format.nSamplesPerSec = 44100;
|
||||
wfx.Format.wBitsPerSample = 32;
|
||||
wfx.Format.nBlockAlign = (wfx.Format.wBitsPerSample * wfx.Format.nChannels) / 8;
|
||||
wfx.Format.nAvgBytesPerSec = wfx.Format.nSamplesPerSec * wfx.Format.nBlockAlign;
|
||||
wfx.Format.nBlockAlign =
|
||||
(wfx.Format.wBitsPerSample * wfx.Format.nChannels) / 8;
|
||||
wfx.Format.nAvgBytesPerSec =
|
||||
wfx.Format.nSamplesPerSec * wfx.Format.nBlockAlign;
|
||||
wfx.Format.cbSize = 22;
|
||||
wfx.Samples.wValidBitsPerSample = wfx.Format.wBitsPerSample;
|
||||
wfx.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
|
||||
wfx.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
|
||||
if (waveOutOpen(NULL, devid, &wfx.Format, 0, 0, WAVE_FORMAT_QUERY) == MMSYSERR_NOERROR)
|
||||
if (waveOutOpen(NULL, devid, &wfx.Format, 0, 0, WAVE_FORMAT_QUERY) ==
|
||||
MMSYSERR_NOERROR)
|
||||
*supfmt = (cubeb_device_fmt)(*supfmt | CUBEB_DEVICE_FMT_F32LE);
|
||||
|
||||
return (*deffmt != 0) ? CUBEB_OK : CUBEB_ERROR;
|
||||
@ -813,10 +907,10 @@ guid_to_cstr(LPGUID guid)
|
||||
return NULL;
|
||||
}
|
||||
_snprintf_s(ret, 40, _TRUNCATE,
|
||||
"{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}",
|
||||
guid->Data1, guid->Data2, guid->Data3,
|
||||
guid->Data4[0], guid->Data4[1], guid->Data4[2], guid->Data4[3],
|
||||
guid->Data4[4], guid->Data4[5], guid->Data4[6], guid->Data4[7]);
|
||||
"{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}", guid->Data1,
|
||||
guid->Data2, guid->Data3, guid->Data4[0], guid->Data4[1],
|
||||
guid->Data4[2], guid->Data4[3], guid->Data4[4], guid->Data4[5],
|
||||
guid->Data4[6], guid->Data4[7]);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -826,13 +920,15 @@ winmm_query_preferred_out_device(UINT devid)
|
||||
DWORD mmpref = WAVE_MAPPER, compref = WAVE_MAPPER, status;
|
||||
cubeb_device_pref ret = CUBEB_DEVICE_PREF_NONE;
|
||||
|
||||
if (waveOutMessage((HWAVEOUT) WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET,
|
||||
(DWORD_PTR)&mmpref, (DWORD_PTR)&status) == MMSYSERR_NOERROR &&
|
||||
if (waveOutMessage((HWAVEOUT)WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET,
|
||||
(DWORD_PTR)&mmpref,
|
||||
(DWORD_PTR)&status) == MMSYSERR_NOERROR &&
|
||||
devid == mmpref)
|
||||
ret |= CUBEB_DEVICE_PREF_MULTIMEDIA | CUBEB_DEVICE_PREF_NOTIFICATION;
|
||||
|
||||
if (waveOutMessage((HWAVEOUT) WAVE_MAPPER, DRVM_MAPPER_CONSOLEVOICECOM_GET,
|
||||
(DWORD_PTR)&compref, (DWORD_PTR)&status) == MMSYSERR_NOERROR &&
|
||||
if (waveOutMessage((HWAVEOUT)WAVE_MAPPER, DRVM_MAPPER_CONSOLEVOICECOM_GET,
|
||||
(DWORD_PTR)&compref,
|
||||
(DWORD_PTR)&status) == MMSYSERR_NOERROR &&
|
||||
devid == compref)
|
||||
ret |= CUBEB_DEVICE_PREF_VOICE;
|
||||
|
||||
@ -851,10 +947,11 @@ device_id_idx(UINT devid)
|
||||
}
|
||||
|
||||
static void
|
||||
winmm_create_device_from_outcaps2(cubeb_device_info * ret, LPWAVEOUTCAPS2A caps, UINT devid)
|
||||
winmm_create_device_from_outcaps2(cubeb_device_info * ret, LPWAVEOUTCAPS2A caps,
|
||||
UINT devid)
|
||||
{
|
||||
XASSERT(ret);
|
||||
ret->devid = (cubeb_devid) devid;
|
||||
ret->devid = (cubeb_devid)devid;
|
||||
ret->device_id = device_id_idx(devid);
|
||||
ret->friendly_name = _strdup(caps->szPname);
|
||||
ret->group_id = guid_to_cstr(&caps->ProductGuid);
|
||||
@ -866,8 +963,8 @@ winmm_create_device_from_outcaps2(cubeb_device_info * ret, LPWAVEOUTCAPS2A caps,
|
||||
|
||||
ret->max_channels = caps->wChannels;
|
||||
winmm_calculate_device_rate(ret, caps->dwFormats);
|
||||
winmm_query_supported_formats(devid, caps->dwFormats,
|
||||
&ret->format, &ret->default_format);
|
||||
winmm_query_supported_formats(devid, caps->dwFormats, &ret->format,
|
||||
&ret->default_format);
|
||||
|
||||
/* Hardcoded latency estimates... */
|
||||
ret->latency_lo = 100 * ret->default_rate / 1000;
|
||||
@ -875,10 +972,11 @@ winmm_create_device_from_outcaps2(cubeb_device_info * ret, LPWAVEOUTCAPS2A caps,
|
||||
}
|
||||
|
||||
static void
|
||||
winmm_create_device_from_outcaps(cubeb_device_info * ret, LPWAVEOUTCAPSA caps, UINT devid)
|
||||
winmm_create_device_from_outcaps(cubeb_device_info * ret, LPWAVEOUTCAPSA caps,
|
||||
UINT devid)
|
||||
{
|
||||
XASSERT(ret);
|
||||
ret->devid = (cubeb_devid) devid;
|
||||
ret->devid = (cubeb_devid)devid;
|
||||
ret->device_id = device_id_idx(devid);
|
||||
ret->friendly_name = _strdup(caps->szPname);
|
||||
ret->group_id = NULL;
|
||||
@ -890,8 +988,8 @@ winmm_create_device_from_outcaps(cubeb_device_info * ret, LPWAVEOUTCAPSA caps, U
|
||||
|
||||
ret->max_channels = caps->wChannels;
|
||||
winmm_calculate_device_rate(ret, caps->dwFormats);
|
||||
winmm_query_supported_formats(devid, caps->dwFormats,
|
||||
&ret->format, &ret->default_format);
|
||||
winmm_query_supported_formats(devid, caps->dwFormats, &ret->format,
|
||||
&ret->default_format);
|
||||
|
||||
/* Hardcoded latency estimates... */
|
||||
ret->latency_lo = 100 * ret->default_rate / 1000;
|
||||
@ -904,13 +1002,15 @@ winmm_query_preferred_in_device(UINT devid)
|
||||
DWORD mmpref = WAVE_MAPPER, compref = WAVE_MAPPER, status;
|
||||
cubeb_device_pref ret = CUBEB_DEVICE_PREF_NONE;
|
||||
|
||||
if (waveInMessage((HWAVEIN) WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET,
|
||||
(DWORD_PTR)&mmpref, (DWORD_PTR)&status) == MMSYSERR_NOERROR &&
|
||||
if (waveInMessage((HWAVEIN)WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET,
|
||||
(DWORD_PTR)&mmpref,
|
||||
(DWORD_PTR)&status) == MMSYSERR_NOERROR &&
|
||||
devid == mmpref)
|
||||
ret |= CUBEB_DEVICE_PREF_MULTIMEDIA | CUBEB_DEVICE_PREF_NOTIFICATION;
|
||||
|
||||
if (waveInMessage((HWAVEIN) WAVE_MAPPER, DRVM_MAPPER_CONSOLEVOICECOM_GET,
|
||||
(DWORD_PTR)&compref, (DWORD_PTR)&status) == MMSYSERR_NOERROR &&
|
||||
if (waveInMessage((HWAVEIN)WAVE_MAPPER, DRVM_MAPPER_CONSOLEVOICECOM_GET,
|
||||
(DWORD_PTR)&compref,
|
||||
(DWORD_PTR)&status) == MMSYSERR_NOERROR &&
|
||||
devid == compref)
|
||||
ret |= CUBEB_DEVICE_PREF_VOICE;
|
||||
|
||||
@ -918,10 +1018,11 @@ winmm_query_preferred_in_device(UINT devid)
|
||||
}
|
||||
|
||||
static void
|
||||
winmm_create_device_from_incaps2(cubeb_device_info * ret, LPWAVEINCAPS2A caps, UINT devid)
|
||||
winmm_create_device_from_incaps2(cubeb_device_info * ret, LPWAVEINCAPS2A caps,
|
||||
UINT devid)
|
||||
{
|
||||
XASSERT(ret);
|
||||
ret->devid = (cubeb_devid) devid;
|
||||
ret->devid = (cubeb_devid)devid;
|
||||
ret->device_id = device_id_idx(devid);
|
||||
ret->friendly_name = _strdup(caps->szPname);
|
||||
ret->group_id = guid_to_cstr(&caps->ProductGuid);
|
||||
@ -933,8 +1034,8 @@ winmm_create_device_from_incaps2(cubeb_device_info * ret, LPWAVEINCAPS2A caps, U
|
||||
|
||||
ret->max_channels = caps->wChannels;
|
||||
winmm_calculate_device_rate(ret, caps->dwFormats);
|
||||
winmm_query_supported_formats(devid, caps->dwFormats,
|
||||
&ret->format, &ret->default_format);
|
||||
winmm_query_supported_formats(devid, caps->dwFormats, &ret->format,
|
||||
&ret->default_format);
|
||||
|
||||
/* Hardcoded latency estimates... */
|
||||
ret->latency_lo = 100 * ret->default_rate / 1000;
|
||||
@ -942,10 +1043,11 @@ winmm_create_device_from_incaps2(cubeb_device_info * ret, LPWAVEINCAPS2A caps, U
|
||||
}
|
||||
|
||||
static void
|
||||
winmm_create_device_from_incaps(cubeb_device_info * ret, LPWAVEINCAPSA caps, UINT devid)
|
||||
winmm_create_device_from_incaps(cubeb_device_info * ret, LPWAVEINCAPSA caps,
|
||||
UINT devid)
|
||||
{
|
||||
XASSERT(ret);
|
||||
ret->devid = (cubeb_devid) devid;
|
||||
ret->devid = (cubeb_devid)devid;
|
||||
ret->device_id = device_id_idx(devid);
|
||||
ret->friendly_name = _strdup(caps->szPname);
|
||||
ret->group_id = NULL;
|
||||
@ -957,8 +1059,8 @@ winmm_create_device_from_incaps(cubeb_device_info * ret, LPWAVEINCAPSA caps, UIN
|
||||
|
||||
ret->max_channels = caps->wChannels;
|
||||
winmm_calculate_device_rate(ret, caps->dwFormats);
|
||||
winmm_query_supported_formats(devid, caps->dwFormats,
|
||||
&ret->format, &ret->default_format);
|
||||
winmm_query_supported_formats(devid, caps->dwFormats, &ret->format,
|
||||
&ret->default_format);
|
||||
|
||||
/* Hardcoded latency estimates... */
|
||||
ret->latency_lo = 100 * ret->default_rate / 1000;
|
||||
@ -989,7 +1091,8 @@ winmm_enumerate_devices(cubeb * context, cubeb_device_type type,
|
||||
|
||||
for (i = 0; i < outcount; i++) {
|
||||
dev = &devices[collection->count];
|
||||
if (waveOutGetDevCapsA(i, (LPWAVEOUTCAPSA)&woc2, sizeof(woc2)) == MMSYSERR_NOERROR) {
|
||||
if (waveOutGetDevCapsA(i, (LPWAVEOUTCAPSA)&woc2, sizeof(woc2)) ==
|
||||
MMSYSERR_NOERROR) {
|
||||
winmm_create_device_from_outcaps2(dev, &woc2, i);
|
||||
collection->count += 1;
|
||||
} else if (waveOutGetDevCapsA(i, &woc, sizeof(woc)) == MMSYSERR_NOERROR) {
|
||||
@ -1008,7 +1111,8 @@ winmm_enumerate_devices(cubeb * context, cubeb_device_type type,
|
||||
|
||||
for (i = 0; i < incount; i++) {
|
||||
dev = &devices[collection->count];
|
||||
if (waveInGetDevCapsA(i, (LPWAVEINCAPSA)&wic2, sizeof(wic2)) == MMSYSERR_NOERROR) {
|
||||
if (waveInGetDevCapsA(i, (LPWAVEINCAPSA)&wic2, sizeof(wic2)) ==
|
||||
MMSYSERR_NOERROR) {
|
||||
winmm_create_device_from_incaps2(dev, &wic2, i);
|
||||
collection->count += 1;
|
||||
} else if (waveInGetDevCapsA(i, &wic, sizeof(wic)) == MMSYSERR_NOERROR) {
|
||||
@ -1030,13 +1134,13 @@ winmm_device_collection_destroy(cubeb * ctx,
|
||||
uint32_t i;
|
||||
XASSERT(collection);
|
||||
|
||||
(void) ctx;
|
||||
(void)ctx;
|
||||
|
||||
for (i = 0; i < collection->count; i++) {
|
||||
free((void *) collection->device[i].device_id);
|
||||
free((void *) collection->device[i].friendly_name);
|
||||
free((void *) collection->device[i].group_id);
|
||||
free((void *) collection->device[i].vendor_name);
|
||||
free((void *)collection->device[i].device_id);
|
||||
free((void *)collection->device[i].friendly_name);
|
||||
free((void *)collection->device[i].group_id);
|
||||
free((void *)collection->device[i].vendor_name);
|
||||
}
|
||||
|
||||
free(collection->device);
|
||||
@ -1044,26 +1148,24 @@ winmm_device_collection_destroy(cubeb * ctx,
|
||||
}
|
||||
|
||||
static struct cubeb_ops const winmm_ops = {
|
||||
/*.init =*/ winmm_init,
|
||||
/*.get_backend_id =*/ winmm_get_backend_id,
|
||||
/*.get_max_channel_count=*/ winmm_get_max_channel_count,
|
||||
/*.get_min_latency=*/ winmm_get_min_latency,
|
||||
/*.get_preferred_sample_rate =*/ winmm_get_preferred_sample_rate,
|
||||
/*.enumerate_devices =*/ winmm_enumerate_devices,
|
||||
/*.device_collection_destroy =*/ winmm_device_collection_destroy,
|
||||
/*.destroy =*/ winmm_destroy,
|
||||
/*.stream_init =*/ winmm_stream_init,
|
||||
/*.stream_destroy =*/ winmm_stream_destroy,
|
||||
/*.stream_start =*/ winmm_stream_start,
|
||||
/*.stream_stop =*/ winmm_stream_stop,
|
||||
/*.stream_reset_default_device =*/ NULL,
|
||||
/*.stream_get_position =*/ winmm_stream_get_position,
|
||||
/*.stream_get_latency = */ winmm_stream_get_latency,
|
||||
/*.stream_get_input_latency = */ NULL,
|
||||
/*.stream_set_volume =*/ winmm_stream_set_volume,
|
||||
/*.stream_set_name =*/ NULL,
|
||||
/*.stream_get_current_device =*/ NULL,
|
||||
/*.stream_device_destroy =*/ NULL,
|
||||
/*.stream_register_device_changed_callback=*/ NULL,
|
||||
/*.register_device_collection_changed =*/ NULL
|
||||
};
|
||||
/*.init =*/winmm_init,
|
||||
/*.get_backend_id =*/winmm_get_backend_id,
|
||||
/*.get_max_channel_count=*/winmm_get_max_channel_count,
|
||||
/*.get_min_latency=*/winmm_get_min_latency,
|
||||
/*.get_preferred_sample_rate =*/winmm_get_preferred_sample_rate,
|
||||
/*.enumerate_devices =*/winmm_enumerate_devices,
|
||||
/*.device_collection_destroy =*/winmm_device_collection_destroy,
|
||||
/*.destroy =*/winmm_destroy,
|
||||
/*.stream_init =*/winmm_stream_init,
|
||||
/*.stream_destroy =*/winmm_stream_destroy,
|
||||
/*.stream_start =*/winmm_stream_start,
|
||||
/*.stream_stop =*/winmm_stream_stop,
|
||||
/*.stream_get_position =*/winmm_stream_get_position,
|
||||
/*.stream_get_latency = */ winmm_stream_get_latency,
|
||||
/*.stream_get_input_latency = */ NULL,
|
||||
/*.stream_set_volume =*/winmm_stream_set_volume,
|
||||
/*.stream_set_name =*/NULL,
|
||||
/*.stream_get_current_device =*/NULL,
|
||||
/*.stream_device_destroy =*/NULL,
|
||||
/*.stream_register_device_changed_callback=*/NULL,
|
||||
/*.register_device_collection_changed =*/NULL};
|
||||
|
Reference in New Issue
Block a user