winmm: Reroute WAVE_MAPPER devices as the default device changes.
This commit is contained in:
parent
bc5d953f7e
commit
8f03c51e55
@ -97,6 +97,7 @@ typedef struct _WINMM_Device {
|
|||||||
IAudioClock *clock;
|
IAudioClock *clock;
|
||||||
IAudioStreamVolume *volume;
|
IAudioStreamVolume *volume;
|
||||||
|
|
||||||
|
WAVEFORMATEX *orig_fmt;
|
||||||
HACMSTREAM acm_handle;
|
HACMSTREAM acm_handle;
|
||||||
ACMSTREAMHEADER acm_hdr;
|
ACMSTREAMHEADER acm_hdr;
|
||||||
UINT32 acm_offs;
|
UINT32 acm_offs;
|
||||||
@ -106,6 +107,7 @@ typedef struct _WINMM_Device {
|
|||||||
BOOL stopped;
|
BOOL stopped;
|
||||||
DWORD loop_counter;
|
DWORD loop_counter;
|
||||||
UINT32 bytes_per_frame, samples_per_sec, ofs_bytes, played_frames;
|
UINT32 bytes_per_frame, samples_per_sec, ofs_bytes, played_frames;
|
||||||
|
UINT32 remainder_frames; /* header chunk frames already played when a device switch occured */
|
||||||
|
|
||||||
/* stored in frames of sample rate, *not* AC::GetFrequency */
|
/* stored in frames of sample rate, *not* AC::GetFrequency */
|
||||||
UINT64 last_clock_pos;
|
UINT64 last_clock_pos;
|
||||||
@ -172,6 +174,7 @@ typedef struct _WINMM_OpenInfo {
|
|||||||
DWORD_PTR callback;
|
DWORD_PTR callback;
|
||||||
DWORD_PTR cb_user;
|
DWORD_PTR cb_user;
|
||||||
DWORD flags;
|
DWORD flags;
|
||||||
|
BOOL reset;
|
||||||
} WINMM_OpenInfo;
|
} WINMM_OpenInfo;
|
||||||
|
|
||||||
typedef struct _WINMM_ControlDetails {
|
typedef struct _WINMM_ControlDetails {
|
||||||
@ -191,6 +194,7 @@ static LRESULT WOD_Open(WINMM_OpenInfo *info);
|
|||||||
static LRESULT WOD_Close(HWAVEOUT hwave);
|
static LRESULT WOD_Close(HWAVEOUT hwave);
|
||||||
static LRESULT WID_Open(WINMM_OpenInfo *info);
|
static LRESULT WID_Open(WINMM_OpenInfo *info);
|
||||||
static LRESULT WID_Close(HWAVEIN hwave);
|
static LRESULT WID_Close(HWAVEIN hwave);
|
||||||
|
static MMRESULT WINMM_BeginPlaying(WINMM_Device *device);
|
||||||
|
|
||||||
void WINMM_DeleteWaveform(void)
|
void WINMM_DeleteWaveform(void)
|
||||||
{
|
{
|
||||||
@ -273,14 +277,10 @@ static inline void WINMM_DecomposeHWAVE(HWAVE hwave, UINT *mmdevice_index,
|
|||||||
*junk = l >> 15;
|
*junk = l >> 15;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void WINMM_InitDevice(WINMM_Device *device,
|
static void WINMM_InitDevice(WINMM_Device *device)
|
||||||
WINMM_MMDevice *parent, HWAVE hwave)
|
|
||||||
{
|
{
|
||||||
InitializeCriticalSection(&device->lock);
|
InitializeCriticalSection(&device->lock);
|
||||||
device->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": WINMM_Device.lock");
|
device->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": WINMM_Device.lock");
|
||||||
|
|
||||||
device->handle = hwave;
|
|
||||||
device->parent = parent;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline WINMM_MMDevice *read_map(WINMM_MMDevice **map, UINT index)
|
static inline WINMM_MMDevice *read_map(WINMM_MMDevice **map, UINT index)
|
||||||
@ -312,14 +312,16 @@ static WINMM_Device *WINMM_FindUnusedDevice(WINMM_Device **devices,
|
|||||||
if(!device)
|
if(!device)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
WINMM_InitDevice(device, parent,
|
WINMM_InitDevice(device);
|
||||||
WINMM_MakeHWAVE(internal_index, is_out, i));
|
|
||||||
EnterCriticalSection(&device->lock);
|
EnterCriticalSection(&device->lock);
|
||||||
}else
|
}else
|
||||||
EnterCriticalSection(&device->lock);
|
EnterCriticalSection(&device->lock);
|
||||||
|
|
||||||
if(!device->open){
|
if(!device->open){
|
||||||
|
device->handle = WINMM_MakeHWAVE(internal_index, is_out, i);
|
||||||
|
device->parent = parent;
|
||||||
device->open = TRUE;
|
device->open = TRUE;
|
||||||
|
|
||||||
return device;
|
return device;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -678,38 +680,20 @@ static HRESULT WINAPI notif_OnDeviceRemoved(IMMNotificationClient *iface,
|
|||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static HRESULT WINAPI notif_OnDefaultDeviceChanged(IMMNotificationClient *iface,
|
static HRESULT update_mapping(WINMM_MMDevice ***map, UINT count,
|
||||||
EDataFlow flow, ERole role, const WCHAR *device_id)
|
const WCHAR *default_id)
|
||||||
{
|
{
|
||||||
WINMM_MMDevice ***map;
|
|
||||||
WINMM_MMDevice *prev;
|
WINMM_MMDevice *prev;
|
||||||
UINT count, i;
|
UINT i;
|
||||||
|
|
||||||
TRACE("%u %u %s\n", flow, role, wine_dbgstr_w(device_id));
|
|
||||||
|
|
||||||
if(role != eConsole)
|
|
||||||
return S_OK;
|
|
||||||
|
|
||||||
EnterCriticalSection(&g_devthread_lock);
|
|
||||||
|
|
||||||
if(flow == eRender){
|
|
||||||
map = &g_out_map;
|
|
||||||
count = g_outmmdevices_count;
|
|
||||||
}else{
|
|
||||||
map = &g_in_map;
|
|
||||||
count = g_inmmdevices_count;
|
|
||||||
}
|
|
||||||
|
|
||||||
prev = (*map)[0];
|
prev = (*map)[0];
|
||||||
for(i = 0; i < count; ++i){
|
for(i = 0; i < count; ++i){
|
||||||
WINMM_MMDevice *tmp;
|
WINMM_MMDevice *tmp;
|
||||||
|
|
||||||
if(!lstrcmpW((*map)[i]->dev_id, device_id)){
|
if(!lstrcmpW((*map)[i]->dev_id, default_id)){
|
||||||
(*map)[0] = (*map)[i];
|
(*map)[0] = (*map)[i];
|
||||||
(*map)[i] = prev;
|
(*map)[i] = prev;
|
||||||
|
|
||||||
LeaveCriticalSection(&g_devthread_lock);
|
|
||||||
|
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -721,6 +705,121 @@ static HRESULT WINAPI notif_OnDefaultDeviceChanged(IMMNotificationClient *iface,
|
|||||||
WARN("Couldn't find new default device! Rearranged map for no reason.\n");
|
WARN("Couldn't find new default device! Rearranged map for no reason.\n");
|
||||||
(*map)[0] = prev;
|
(*map)[0] = prev;
|
||||||
|
|
||||||
|
return S_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static HRESULT reroute_mapper_device(WINMM_Device *device, BOOL is_out)
|
||||||
|
{
|
||||||
|
WINMM_OpenInfo info;
|
||||||
|
BOOL stopped;
|
||||||
|
MMRESULT mr;
|
||||||
|
HRESULT hr;
|
||||||
|
UINT64 clock_freq, clock_pos;
|
||||||
|
|
||||||
|
TRACE("rerouting device %p\n", device->handle);
|
||||||
|
|
||||||
|
EnterCriticalSection(&device->lock);
|
||||||
|
|
||||||
|
if(!device->open || device->acm_handle){
|
||||||
|
/* Windows 7 doesn't re-route ACM devices, so we don't either.
|
||||||
|
* Seems to be because of the data waveXxxPrepareHeader allocates. */
|
||||||
|
LeaveCriticalSection(&device->lock);
|
||||||
|
return S_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
stopped = device->stopped;
|
||||||
|
|
||||||
|
info.handle = 0;
|
||||||
|
info.req_device = WAVE_MAPPER;
|
||||||
|
info.format = device->orig_fmt;
|
||||||
|
info.callback = device->cb_info.callback;
|
||||||
|
info.cb_user = device->cb_info.user;
|
||||||
|
/* We have to use direct here so that we don't suddenly introduce ACM
|
||||||
|
* into a playing stream that hasn't been Prepared for it */
|
||||||
|
info.flags = (device->cb_info.flags << 16) | WAVE_FORMAT_DIRECT_QUERY;
|
||||||
|
info.reset = FALSE;
|
||||||
|
|
||||||
|
if(is_out)
|
||||||
|
mr = WOD_Open(&info);
|
||||||
|
else
|
||||||
|
mr = WID_Open(&info);
|
||||||
|
|
||||||
|
if(mr != MMSYSERR_NOERROR){
|
||||||
|
TRACE("New default device doesn't support this stream: %p\n", device->handle);
|
||||||
|
LeaveCriticalSection(&device->lock);
|
||||||
|
return S_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr = IAudioClient_Stop(device->client);
|
||||||
|
if(FAILED(hr))
|
||||||
|
WARN("Stop failed: %08x\n", hr);
|
||||||
|
|
||||||
|
hr = IAudioClock_GetFrequency(device->clock, &clock_freq);
|
||||||
|
if(FAILED(hr)){
|
||||||
|
WARN("GetFrequency failed: %08x\n", hr);
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr = IAudioClock_GetPosition(device->clock, &clock_pos, NULL);
|
||||||
|
if(FAILED(hr)){
|
||||||
|
WARN("GetPosition failed: %08x\n", hr);
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
device->remainder_frames = MulDiv(clock_pos, device->samples_per_sec, clock_freq) - device->last_clock_pos;
|
||||||
|
|
||||||
|
info.handle = device->handle;
|
||||||
|
info.flags = (device->cb_info.flags << 16) | WAVE_FORMAT_DIRECT;
|
||||||
|
|
||||||
|
if(is_out){
|
||||||
|
WOD_Close((HWAVEOUT)device->handle);
|
||||||
|
device->parent = read_map(g_out_map, 0);
|
||||||
|
mr = WOD_Open(&info);
|
||||||
|
}else{
|
||||||
|
WID_Close((HWAVEIN)device->handle);
|
||||||
|
device->parent = read_map(g_in_map, 0);
|
||||||
|
mr = WID_Open(&info);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(mr != MMSYSERR_NOERROR){
|
||||||
|
ERR("Opening new default device failed! %u\n", mr);
|
||||||
|
LeaveCriticalSection(&device->lock);
|
||||||
|
return E_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
HeapFree(GetProcessHeap(), 0, info.format);
|
||||||
|
|
||||||
|
if(!stopped)
|
||||||
|
WINMM_BeginPlaying(device);
|
||||||
|
|
||||||
|
LeaveCriticalSection(&device->lock);
|
||||||
|
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static HRESULT WINAPI notif_OnDefaultDeviceChanged(IMMNotificationClient *iface,
|
||||||
|
EDataFlow flow, ERole role, const WCHAR *device_id)
|
||||||
|
{
|
||||||
|
UINT i;
|
||||||
|
|
||||||
|
TRACE("%u %u %s\n", flow, role, wine_dbgstr_w(device_id));
|
||||||
|
|
||||||
|
if(role != eConsole)
|
||||||
|
return S_OK;
|
||||||
|
|
||||||
|
EnterCriticalSection(&g_devthread_lock);
|
||||||
|
|
||||||
|
if(flow == eRender)
|
||||||
|
update_mapping(&g_out_map, g_outmmdevices_count, device_id);
|
||||||
|
else
|
||||||
|
update_mapping(&g_in_map, g_inmmdevices_count, device_id);
|
||||||
|
|
||||||
|
for(i = 0; i < MAX_DEVICES && g_out_mapper_devices[i]; ++i)
|
||||||
|
reroute_mapper_device(g_out_mapper_devices[i], TRUE);
|
||||||
|
|
||||||
|
for(i = 0; i < MAX_DEVICES && g_in_mapper_devices[i]; ++i)
|
||||||
|
reroute_mapper_device(g_in_mapper_devices[i], FALSE);
|
||||||
|
|
||||||
LeaveCriticalSection(&g_devthread_lock);
|
LeaveCriticalSection(&g_devthread_lock);
|
||||||
|
|
||||||
return S_OK;
|
return S_OK;
|
||||||
@ -849,118 +948,119 @@ static MMRESULT WINMM_TryDeviceMapping(WINMM_Device *device, WAVEFORMATEX *fmt,
|
|||||||
return MMSYSERR_NOERROR;
|
return MMSYSERR_NOERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
static MMRESULT WINMM_MapDevice(WINMM_Device *device, WAVEFORMATEX *fmt,
|
static MMRESULT WINMM_MapDevice(WINMM_Device *device, BOOL is_out)
|
||||||
BOOL is_out)
|
|
||||||
{
|
{
|
||||||
MMRESULT mr;
|
MMRESULT mr;
|
||||||
WAVEFORMATEXTENSIBLE *fmtex = (WAVEFORMATEXTENSIBLE*)fmt;
|
WAVEFORMATEXTENSIBLE *fmtex = (WAVEFORMATEXTENSIBLE*)device->orig_fmt;
|
||||||
|
|
||||||
TRACE("(%p, %p, %u)\n", device, fmt, is_out);
|
TRACE("(%p, %u)\n", device, is_out);
|
||||||
|
|
||||||
/* set up the ACM stream */
|
/* set up the ACM stream */
|
||||||
if(fmt->wFormatTag != WAVE_FORMAT_PCM &&
|
if(device->orig_fmt->wFormatTag != WAVE_FORMAT_PCM &&
|
||||||
!(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
|
!(device->orig_fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
|
||||||
IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))){
|
IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))){
|
||||||
/* convert to PCM format if it's not already */
|
/* convert to PCM format if it's not already */
|
||||||
mr = WINMM_TryDeviceMapping(device, fmt, fmt->nChannels,
|
mr = WINMM_TryDeviceMapping(device, device->orig_fmt,
|
||||||
fmt->nSamplesPerSec, 16, is_out);
|
device->orig_fmt->nChannels, device->orig_fmt->nSamplesPerSec,
|
||||||
|
16, is_out);
|
||||||
if(mr == MMSYSERR_NOERROR)
|
if(mr == MMSYSERR_NOERROR)
|
||||||
return mr;
|
return mr;
|
||||||
|
|
||||||
mr = WINMM_TryDeviceMapping(device, fmt, fmt->nChannels,
|
mr = WINMM_TryDeviceMapping(device, device->orig_fmt,
|
||||||
fmt->nSamplesPerSec, 8, is_out);
|
device->orig_fmt->nChannels, device->orig_fmt->nSamplesPerSec,
|
||||||
|
8, is_out);
|
||||||
if(mr == MMSYSERR_NOERROR)
|
if(mr == MMSYSERR_NOERROR)
|
||||||
return mr;
|
return mr;
|
||||||
}else{
|
}else{
|
||||||
WORD channels;
|
WORD channels;
|
||||||
|
|
||||||
/* first try just changing bit depth and channels */
|
/* first try just changing bit depth and channels */
|
||||||
channels = fmt->nChannels;
|
channels = device->orig_fmt->nChannels;
|
||||||
mr = WINMM_TryDeviceMapping(device, fmt, channels,
|
mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels,
|
||||||
fmt->nSamplesPerSec, 16, is_out);
|
device->orig_fmt->nSamplesPerSec, 16, is_out);
|
||||||
if(mr == MMSYSERR_NOERROR)
|
if(mr == MMSYSERR_NOERROR)
|
||||||
return mr;
|
return mr;
|
||||||
mr = WINMM_TryDeviceMapping(device, fmt, channels,
|
mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels,
|
||||||
fmt->nSamplesPerSec, 8, is_out);
|
device->orig_fmt->nSamplesPerSec, 8, is_out);
|
||||||
if(mr == MMSYSERR_NOERROR)
|
if(mr == MMSYSERR_NOERROR)
|
||||||
return mr;
|
return mr;
|
||||||
|
|
||||||
channels = (channels == 2) ? 1 : 2;
|
channels = (channels == 2) ? 1 : 2;
|
||||||
mr = WINMM_TryDeviceMapping(device, fmt, channels,
|
mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels,
|
||||||
fmt->nSamplesPerSec, 16, is_out);
|
device->orig_fmt->nSamplesPerSec, 16, is_out);
|
||||||
if(mr == MMSYSERR_NOERROR)
|
if(mr == MMSYSERR_NOERROR)
|
||||||
return mr;
|
return mr;
|
||||||
mr = WINMM_TryDeviceMapping(device, fmt, channels,
|
mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels,
|
||||||
fmt->nSamplesPerSec, 8, is_out);
|
device->orig_fmt->nSamplesPerSec, 8, is_out);
|
||||||
if(mr == MMSYSERR_NOERROR)
|
if(mr == MMSYSERR_NOERROR)
|
||||||
return mr;
|
return mr;
|
||||||
|
|
||||||
/* that didn't work, so now try different sample rates */
|
/* that didn't work, so now try different sample rates */
|
||||||
channels = fmt->nChannels;
|
channels = device->orig_fmt->nChannels;
|
||||||
mr = WINMM_TryDeviceMapping(device, fmt, channels, 96000, 16, is_out);
|
mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels, 96000, 16, is_out);
|
||||||
if(mr == MMSYSERR_NOERROR)
|
if(mr == MMSYSERR_NOERROR)
|
||||||
return mr;
|
return mr;
|
||||||
mr = WINMM_TryDeviceMapping(device, fmt, channels, 48000, 16, is_out);
|
mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels, 48000, 16, is_out);
|
||||||
if(mr == MMSYSERR_NOERROR)
|
if(mr == MMSYSERR_NOERROR)
|
||||||
return mr;
|
return mr;
|
||||||
mr = WINMM_TryDeviceMapping(device, fmt, channels, 44100, 16, is_out);
|
mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels, 44100, 16, is_out);
|
||||||
if(mr == MMSYSERR_NOERROR)
|
if(mr == MMSYSERR_NOERROR)
|
||||||
return mr;
|
return mr;
|
||||||
mr = WINMM_TryDeviceMapping(device, fmt, channels, 22050, 16, is_out);
|
mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels, 22050, 16, is_out);
|
||||||
if(mr == MMSYSERR_NOERROR)
|
if(mr == MMSYSERR_NOERROR)
|
||||||
return mr;
|
return mr;
|
||||||
mr = WINMM_TryDeviceMapping(device, fmt, channels, 11025, 16, is_out);
|
mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels, 11025, 16, is_out);
|
||||||
if(mr == MMSYSERR_NOERROR)
|
if(mr == MMSYSERR_NOERROR)
|
||||||
return mr;
|
return mr;
|
||||||
|
|
||||||
channels = (channels == 2) ? 1 : 2;
|
channels = (channels == 2) ? 1 : 2;
|
||||||
mr = WINMM_TryDeviceMapping(device, fmt, channels, 96000, 16, is_out);
|
mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels, 96000, 16, is_out);
|
||||||
if(mr == MMSYSERR_NOERROR)
|
if(mr == MMSYSERR_NOERROR)
|
||||||
return mr;
|
return mr;
|
||||||
mr = WINMM_TryDeviceMapping(device, fmt, channels, 48000, 16, is_out);
|
mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels, 48000, 16, is_out);
|
||||||
if(mr == MMSYSERR_NOERROR)
|
if(mr == MMSYSERR_NOERROR)
|
||||||
return mr;
|
return mr;
|
||||||
mr = WINMM_TryDeviceMapping(device, fmt, channels, 44100, 16, is_out);
|
mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels, 44100, 16, is_out);
|
||||||
if(mr == MMSYSERR_NOERROR)
|
if(mr == MMSYSERR_NOERROR)
|
||||||
return mr;
|
return mr;
|
||||||
mr = WINMM_TryDeviceMapping(device, fmt, channels, 22050, 16, is_out);
|
mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels, 22050, 16, is_out);
|
||||||
if(mr == MMSYSERR_NOERROR)
|
if(mr == MMSYSERR_NOERROR)
|
||||||
return mr;
|
return mr;
|
||||||
mr = WINMM_TryDeviceMapping(device, fmt, channels, 11025, 16, is_out);
|
mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels, 11025, 16, is_out);
|
||||||
if(mr == MMSYSERR_NOERROR)
|
if(mr == MMSYSERR_NOERROR)
|
||||||
return mr;
|
return mr;
|
||||||
|
|
||||||
channels = fmt->nChannels;
|
channels = device->orig_fmt->nChannels;
|
||||||
mr = WINMM_TryDeviceMapping(device, fmt, channels, 96000, 8, is_out);
|
mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels, 96000, 8, is_out);
|
||||||
if(mr == MMSYSERR_NOERROR)
|
if(mr == MMSYSERR_NOERROR)
|
||||||
return mr;
|
return mr;
|
||||||
mr = WINMM_TryDeviceMapping(device, fmt, channels, 48000, 8, is_out);
|
mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels, 48000, 8, is_out);
|
||||||
if(mr == MMSYSERR_NOERROR)
|
if(mr == MMSYSERR_NOERROR)
|
||||||
return mr;
|
return mr;
|
||||||
mr = WINMM_TryDeviceMapping(device, fmt, channels, 44100, 8, is_out);
|
mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels, 44100, 8, is_out);
|
||||||
if(mr == MMSYSERR_NOERROR)
|
if(mr == MMSYSERR_NOERROR)
|
||||||
return mr;
|
return mr;
|
||||||
mr = WINMM_TryDeviceMapping(device, fmt, channels, 22050, 8, is_out);
|
mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels, 22050, 8, is_out);
|
||||||
if(mr == MMSYSERR_NOERROR)
|
if(mr == MMSYSERR_NOERROR)
|
||||||
return mr;
|
return mr;
|
||||||
mr = WINMM_TryDeviceMapping(device, fmt, channels, 11025, 8, is_out);
|
mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels, 11025, 8, is_out);
|
||||||
if(mr == MMSYSERR_NOERROR)
|
if(mr == MMSYSERR_NOERROR)
|
||||||
return mr;
|
return mr;
|
||||||
|
|
||||||
channels = (channels == 2) ? 1 : 2;
|
channels = (channels == 2) ? 1 : 2;
|
||||||
mr = WINMM_TryDeviceMapping(device, fmt, channels, 96000, 8, is_out);
|
mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels, 96000, 8, is_out);
|
||||||
if(mr == MMSYSERR_NOERROR)
|
if(mr == MMSYSERR_NOERROR)
|
||||||
return mr;
|
return mr;
|
||||||
mr = WINMM_TryDeviceMapping(device, fmt, channels, 48000, 8, is_out);
|
mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels, 48000, 8, is_out);
|
||||||
if(mr == MMSYSERR_NOERROR)
|
if(mr == MMSYSERR_NOERROR)
|
||||||
return mr;
|
return mr;
|
||||||
mr = WINMM_TryDeviceMapping(device, fmt, channels, 44100, 8, is_out);
|
mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels, 44100, 8, is_out);
|
||||||
if(mr == MMSYSERR_NOERROR)
|
if(mr == MMSYSERR_NOERROR)
|
||||||
return mr;
|
return mr;
|
||||||
mr = WINMM_TryDeviceMapping(device, fmt, channels, 22050, 8, is_out);
|
mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels, 22050, 8, is_out);
|
||||||
if(mr == MMSYSERR_NOERROR)
|
if(mr == MMSYSERR_NOERROR)
|
||||||
return mr;
|
return mr;
|
||||||
mr = WINMM_TryDeviceMapping(device, fmt, channels, 11025, 8, is_out);
|
mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels, 11025, 8, is_out);
|
||||||
if(mr == MMSYSERR_NOERROR)
|
if(mr == MMSYSERR_NOERROR)
|
||||||
return mr;
|
return mr;
|
||||||
}
|
}
|
||||||
@ -972,7 +1072,6 @@ static MMRESULT WINMM_MapDevice(WINMM_Device *device, WAVEFORMATEX *fmt,
|
|||||||
static LRESULT WINMM_OpenDevice(WINMM_Device *device, WINMM_OpenInfo *info,
|
static LRESULT WINMM_OpenDevice(WINMM_Device *device, WINMM_OpenInfo *info,
|
||||||
BOOL is_out)
|
BOOL is_out)
|
||||||
{
|
{
|
||||||
WAVEFORMATEX fmt, *passed_fmt;
|
|
||||||
LRESULT ret = MMSYSERR_NOMEM;
|
LRESULT ret = MMSYSERR_NOMEM;
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
|
|
||||||
@ -1000,30 +1099,34 @@ static LRESULT WINMM_OpenDevice(WINMM_Device *device, WINMM_OpenInfo *info,
|
|||||||
if(info->format->wFormatTag == WAVE_FORMAT_PCM){
|
if(info->format->wFormatTag == WAVE_FORMAT_PCM){
|
||||||
/* we aren't guaranteed that the struct in lpFormat is a full
|
/* we aren't guaranteed that the struct in lpFormat is a full
|
||||||
* WAVEFORMATEX struct, which IAC::IsFormatSupported requires */
|
* WAVEFORMATEX struct, which IAC::IsFormatSupported requires */
|
||||||
passed_fmt = &fmt;
|
device->orig_fmt = HeapAlloc(GetProcessHeap(), 0, sizeof(WAVEFORMATEX));
|
||||||
memcpy(passed_fmt, info->format, sizeof(PCMWAVEFORMAT));
|
memcpy(device->orig_fmt, info->format, sizeof(PCMWAVEFORMAT));
|
||||||
passed_fmt->cbSize = 0;
|
device->orig_fmt->cbSize = 0;
|
||||||
if(fmt.wBitsPerSample % 8 != 0){
|
if(device->orig_fmt->wBitsPerSample % 8 != 0){
|
||||||
WARN("Fixing bad wBitsPerSample (%u)\n", fmt.wBitsPerSample);
|
WARN("Fixing bad wBitsPerSample (%u)\n", device->orig_fmt->wBitsPerSample);
|
||||||
fmt.wBitsPerSample = (fmt.wBitsPerSample + 7) & ~7;
|
device->orig_fmt->wBitsPerSample = (device->orig_fmt->wBitsPerSample + 7) & ~7;
|
||||||
}
|
}
|
||||||
/* winmm ignores broken blockalign and avgbytes */
|
/* winmm ignores broken blockalign and avgbytes */
|
||||||
if(fmt.nBlockAlign != fmt.nChannels * fmt.wBitsPerSample/8){
|
if(device->orig_fmt->nBlockAlign != device->orig_fmt->nChannels * device->orig_fmt->wBitsPerSample/8){
|
||||||
WARN("Fixing bad nBlockAlign (%u)\n", fmt.nBlockAlign);
|
WARN("Fixing bad nBlockAlign (%u)\n", device->orig_fmt->nBlockAlign);
|
||||||
fmt.nBlockAlign = fmt.nChannels * fmt.wBitsPerSample/8;
|
device->orig_fmt->nBlockAlign = device->orig_fmt->nChannels * device->orig_fmt->wBitsPerSample/8;
|
||||||
}
|
}
|
||||||
if (fmt.nAvgBytesPerSec != fmt.nSamplesPerSec * fmt.nBlockAlign) {
|
if (device->orig_fmt->nAvgBytesPerSec != device->orig_fmt->nSamplesPerSec * device->orig_fmt->nBlockAlign) {
|
||||||
WARN("Fixing bad nAvgBytesPerSec (%u)\n", fmt.nAvgBytesPerSec);
|
WARN("Fixing bad nAvgBytesPerSec (%u)\n", device->orig_fmt->nAvgBytesPerSec);
|
||||||
fmt.nAvgBytesPerSec = fmt.nSamplesPerSec * fmt.nBlockAlign;
|
device->orig_fmt->nAvgBytesPerSec = device->orig_fmt->nSamplesPerSec * device->orig_fmt->nBlockAlign;
|
||||||
}
|
}
|
||||||
}else
|
}else{
|
||||||
passed_fmt = info->format;
|
device->orig_fmt = HeapAlloc(GetProcessHeap(), 0,
|
||||||
|
sizeof(WAVEFORMATEX) + info->format->cbSize);
|
||||||
|
memcpy(device->orig_fmt, info->format,
|
||||||
|
sizeof(WAVEFORMATEX) + info->format->cbSize);
|
||||||
|
}
|
||||||
|
|
||||||
if(info->flags & WAVE_FORMAT_QUERY){
|
if(info->flags & WAVE_FORMAT_QUERY){
|
||||||
WAVEFORMATEX *closer_fmt = NULL;
|
WAVEFORMATEX *closer_fmt = NULL;
|
||||||
|
|
||||||
hr = IAudioClient_IsFormatSupported(device->client,
|
hr = IAudioClient_IsFormatSupported(device->client,
|
||||||
AUDCLNT_SHAREMODE_SHARED, passed_fmt, &closer_fmt);
|
AUDCLNT_SHAREMODE_SHARED, device->orig_fmt, &closer_fmt);
|
||||||
if(closer_fmt)
|
if(closer_fmt)
|
||||||
CoTaskMemFree(closer_fmt);
|
CoTaskMemFree(closer_fmt);
|
||||||
ret = hr == S_FALSE ? WAVERR_BADFORMAT : hr2mmr(hr);
|
ret = hr == S_FALSE ? WAVERR_BADFORMAT : hr2mmr(hr);
|
||||||
@ -1032,10 +1135,10 @@ static LRESULT WINMM_OpenDevice(WINMM_Device *device, WINMM_OpenInfo *info,
|
|||||||
|
|
||||||
hr = IAudioClient_Initialize(device->client, AUDCLNT_SHAREMODE_SHARED,
|
hr = IAudioClient_Initialize(device->client, AUDCLNT_SHAREMODE_SHARED,
|
||||||
AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST,
|
AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST,
|
||||||
AC_BUFLEN, 0, passed_fmt, &device->parent->session);
|
AC_BUFLEN, 0, device->orig_fmt, &device->parent->session);
|
||||||
if(FAILED(hr)){
|
if(FAILED(hr)){
|
||||||
if(hr == AUDCLNT_E_UNSUPPORTED_FORMAT){
|
if(hr == AUDCLNT_E_UNSUPPORTED_FORMAT){
|
||||||
ret = WINMM_MapDevice(device, passed_fmt, is_out);
|
ret = WINMM_MapDevice(device, is_out);
|
||||||
if(ret != MMSYSERR_NOERROR || info->flags & WAVE_FORMAT_QUERY)
|
if(ret != MMSYSERR_NOERROR || info->flags & WAVE_FORMAT_QUERY)
|
||||||
goto error;
|
goto error;
|
||||||
}else{
|
}else{
|
||||||
@ -1044,8 +1147,8 @@ static LRESULT WINMM_OpenDevice(WINMM_Device *device, WINMM_OpenInfo *info,
|
|||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
device->bytes_per_frame = passed_fmt->nBlockAlign;
|
device->bytes_per_frame = device->orig_fmt->nBlockAlign;
|
||||||
device->samples_per_sec = passed_fmt->nSamplesPerSec;
|
device->samples_per_sec = device->orig_fmt->nSamplesPerSec;
|
||||||
}
|
}
|
||||||
|
|
||||||
hr = IAudioClient_GetService(device->client, &IID_IAudioClock,
|
hr = IAudioClient_GetService(device->client, &IID_IAudioClock,
|
||||||
@ -1085,12 +1188,15 @@ static LRESULT WINMM_OpenDevice(WINMM_Device *device, WINMM_OpenInfo *info,
|
|||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
device->played_frames = 0;
|
if(info->reset){
|
||||||
device->last_clock_pos = 0;
|
device->played_frames = 0;
|
||||||
device->ofs_bytes = 0;
|
device->ofs_bytes = 0;
|
||||||
device->loop_counter = 0;
|
device->loop_counter = 0;
|
||||||
|
device->first = device->last = device->playing = device->loop_start = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
device->stopped = TRUE;
|
device->stopped = TRUE;
|
||||||
device->first = device->last = device->playing = device->loop_start = NULL;
|
device->last_clock_pos = 0;
|
||||||
|
|
||||||
device->cb_info.flags = HIWORD(info->flags & CALLBACK_TYPEMASK);
|
device->cb_info.flags = HIWORD(info->flags & CALLBACK_TYPEMASK);
|
||||||
device->cb_info.callback = info->callback;
|
device->cb_info.callback = info->callback;
|
||||||
@ -1116,42 +1222,57 @@ error:
|
|||||||
|
|
||||||
static LRESULT WOD_Open(WINMM_OpenInfo *info)
|
static LRESULT WOD_Open(WINMM_OpenInfo *info)
|
||||||
{
|
{
|
||||||
WINMM_Device *device, **devices;
|
WINMM_Device *device;
|
||||||
WINMM_MMDevice *mmdevice;
|
|
||||||
CRITICAL_SECTION *lock;
|
|
||||||
UINT internal_index;
|
|
||||||
LRESULT ret = MMSYSERR_ERROR;
|
LRESULT ret = MMSYSERR_ERROR;
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
|
|
||||||
if(WINMM_IsMapper(info->req_device)){
|
if(info->handle != 0){
|
||||||
devices = g_out_mapper_devices;
|
device = WINMM_GetDeviceFromHWAVE(info->handle);
|
||||||
mmdevice = read_map(g_out_map, 0);
|
if(!device){
|
||||||
lock = &g_devthread_lock;
|
WARN("Unexpected! Invalid info->handle given: %p\n", info->handle);
|
||||||
internal_index = MAPPER_INDEX;
|
return MMSYSERR_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
EnterCriticalSection(&device->lock);
|
||||||
|
|
||||||
|
device->open = TRUE;
|
||||||
}else{
|
}else{
|
||||||
if(info->req_device >= g_outmmdevices_count)
|
CRITICAL_SECTION *lock;
|
||||||
return MMSYSERR_BADDEVICEID;
|
UINT internal_index;
|
||||||
|
WINMM_Device **devices;
|
||||||
|
WINMM_MMDevice *mmdevice;
|
||||||
|
|
||||||
mmdevice = read_map(g_out_map, info->req_device);
|
if(WINMM_IsMapper(info->req_device)){
|
||||||
|
devices = g_out_mapper_devices;
|
||||||
|
mmdevice = read_map(g_out_map, 0);
|
||||||
|
lock = &g_devthread_lock;
|
||||||
|
internal_index = MAPPER_INDEX;
|
||||||
|
}else{
|
||||||
|
if(info->req_device >= g_outmmdevices_count)
|
||||||
|
return MMSYSERR_BADDEVICEID;
|
||||||
|
|
||||||
if(!mmdevice->out_caps.szPname[0])
|
mmdevice = read_map(g_out_map, info->req_device);
|
||||||
return MMSYSERR_NOTENABLED;
|
|
||||||
|
|
||||||
devices = mmdevice->devices;
|
if(!mmdevice->out_caps.szPname[0])
|
||||||
lock = &mmdevice->lock;
|
return MMSYSERR_NOTENABLED;
|
||||||
internal_index = mmdevice->index;
|
|
||||||
}
|
|
||||||
|
|
||||||
EnterCriticalSection(lock);
|
devices = mmdevice->devices;
|
||||||
|
lock = &mmdevice->lock;
|
||||||
|
internal_index = mmdevice->index;
|
||||||
|
}
|
||||||
|
|
||||||
|
EnterCriticalSection(lock);
|
||||||
|
|
||||||
|
device = WINMM_FindUnusedDevice(devices, mmdevice,
|
||||||
|
internal_index, TRUE);
|
||||||
|
if(!device){
|
||||||
|
LeaveCriticalSection(lock);
|
||||||
|
return MMSYSERR_ALLOCATED;
|
||||||
|
}
|
||||||
|
|
||||||
device = WINMM_FindUnusedDevice(devices, mmdevice, internal_index, TRUE);
|
|
||||||
if(!device){
|
|
||||||
LeaveCriticalSection(lock);
|
LeaveCriticalSection(lock);
|
||||||
return MMSYSERR_ALLOCATED;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LeaveCriticalSection(lock);
|
|
||||||
|
|
||||||
ret = WINMM_OpenDevice(device, info, TRUE);
|
ret = WINMM_OpenDevice(device, info, TRUE);
|
||||||
if((info->flags & WAVE_FORMAT_QUERY) || ret != MMSYSERR_NOERROR)
|
if((info->flags & WAVE_FORMAT_QUERY) || ret != MMSYSERR_NOERROR)
|
||||||
goto error;
|
goto error;
|
||||||
@ -1463,10 +1584,11 @@ static WAVEHDR *WOD_MarkDoneHeaders(WINMM_Device *device)
|
|||||||
nloops = device->loop_counter;
|
nloops = device->loop_counter;
|
||||||
while(queue &&
|
while(queue &&
|
||||||
(queue_frames += WINMM_HeaderLenFrames(device, queue)) <=
|
(queue_frames += WINMM_HeaderLenFrames(device, queue)) <=
|
||||||
clock_frames - device->last_clock_pos){
|
clock_frames - device->last_clock_pos + device->remainder_frames){
|
||||||
if(!nloops){
|
if(!nloops){
|
||||||
last = queue;
|
last = queue;
|
||||||
device->last_clock_pos += queue_frames;
|
device->last_clock_pos += queue_frames;
|
||||||
|
device->remainder_frames = 0;
|
||||||
queue_frames = 0;
|
queue_frames = 0;
|
||||||
queue = device->first = queue->lpNext;
|
queue = device->first = queue->lpNext;
|
||||||
}else{
|
}else{
|
||||||
@ -1617,7 +1739,10 @@ static void WOD_PushData(WINMM_Device *device)
|
|||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
device->played_frames += avail_frames;
|
if(device->orig_fmt->nSamplesPerSec != device->samples_per_sec)
|
||||||
|
device->played_frames += MulDiv(avail_frames, device->orig_fmt->nSamplesPerSec, device->samples_per_sec);
|
||||||
|
else
|
||||||
|
device->played_frames += avail_frames;
|
||||||
|
|
||||||
exit:
|
exit:
|
||||||
cb_info = device->cb_info;
|
cb_info = device->cb_info;
|
||||||
@ -1946,8 +2071,8 @@ static LRESULT WINMM_GetPosition(HWAVE hwave, MMTIME *time)
|
|||||||
return MMSYSERR_INVALHANDLE;
|
return MMSYSERR_INVALHANDLE;
|
||||||
|
|
||||||
played_frames = device->played_frames;
|
played_frames = device->played_frames;
|
||||||
sample_rate = device->samples_per_sec;
|
sample_rate = device->orig_fmt->nSamplesPerSec;
|
||||||
bytes_per_frame = device->bytes_per_frame;
|
bytes_per_frame = device->orig_fmt->nBlockAlign;
|
||||||
|
|
||||||
LeaveCriticalSection(&device->lock);
|
LeaveCriticalSection(&device->lock);
|
||||||
|
|
||||||
@ -2591,11 +2716,13 @@ MMRESULT WINAPI waveOutOpen(LPHWAVEOUT lphWaveOut, UINT uDeviceID,
|
|||||||
if(res != MMSYSERR_NOERROR)
|
if(res != MMSYSERR_NOERROR)
|
||||||
return res;
|
return res;
|
||||||
|
|
||||||
|
info.handle = 0;
|
||||||
info.format = (WAVEFORMATEX*)lpFormat;
|
info.format = (WAVEFORMATEX*)lpFormat;
|
||||||
info.callback = dwCallback;
|
info.callback = dwCallback;
|
||||||
info.cb_user = dwInstance;
|
info.cb_user = dwInstance;
|
||||||
info.req_device = uDeviceID;
|
info.req_device = uDeviceID;
|
||||||
info.flags = dwFlags;
|
info.flags = dwFlags;
|
||||||
|
info.reset = TRUE;
|
||||||
|
|
||||||
res = SendMessageW(g_devices_hwnd, WODM_OPEN, (DWORD_PTR)&info, 0);
|
res = SendMessageW(g_devices_hwnd, WODM_OPEN, (DWORD_PTR)&info, 0);
|
||||||
if(res != MMSYSERR_NOERROR)
|
if(res != MMSYSERR_NOERROR)
|
||||||
@ -3219,11 +3346,13 @@ MMRESULT WINAPI waveInOpen(HWAVEIN* lphWaveIn, UINT uDeviceID,
|
|||||||
if(res != MMSYSERR_NOERROR)
|
if(res != MMSYSERR_NOERROR)
|
||||||
return res;
|
return res;
|
||||||
|
|
||||||
|
info.handle = 0;
|
||||||
info.format = (WAVEFORMATEX*)lpFormat;
|
info.format = (WAVEFORMATEX*)lpFormat;
|
||||||
info.callback = dwCallback;
|
info.callback = dwCallback;
|
||||||
info.cb_user = dwInstance;
|
info.cb_user = dwInstance;
|
||||||
info.req_device = uDeviceID;
|
info.req_device = uDeviceID;
|
||||||
info.flags = dwFlags;
|
info.flags = dwFlags;
|
||||||
|
info.reset = TRUE;
|
||||||
|
|
||||||
res = SendMessageW(g_devices_hwnd, WIDM_OPEN, (DWORD_PTR)&info, 0);
|
res = SendMessageW(g_devices_hwnd, WIDM_OPEN, (DWORD_PTR)&info, 0);
|
||||||
if(res != MMSYSERR_NOERROR)
|
if(res != MMSYSERR_NOERROR)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user