Commit 8f03c51e authored by Andrew Eikum's avatar Andrew Eikum Committed by Alexandre Julliard

winmm: Reroute WAVE_MAPPER devices as the default device changes.

parent bc5d953f
...@@ -97,6 +97,7 @@ typedef struct _WINMM_Device { ...@@ -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 { ...@@ -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 { ...@@ -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); ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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{
device->orig_fmt = HeapAlloc(GetProcessHeap(), 0,
sizeof(WAVEFORMATEX) + info->format->cbSize);
memcpy(device->orig_fmt, info->format,
sizeof(WAVEFORMATEX) + info->format->cbSize);
} }
}else
passed_fmt = info->format;
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, ...@@ -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, ...@@ -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, ...@@ -1085,12 +1188,15 @@ static LRESULT WINMM_OpenDevice(WINMM_Device *device, WINMM_OpenInfo *info,
goto error; goto error;
} }
if(info->reset){
device->played_frames = 0; device->played_frames = 0;
device->last_clock_pos = 0;
device->ofs_bytes = 0; device->ofs_bytes = 0;
device->loop_counter = 0; device->loop_counter = 0;
device->stopped = TRUE;
device->first = device->last = device->playing = device->loop_start = NULL; device->first = device->last = device->playing = device->loop_start = NULL;
}
device->stopped = TRUE;
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,13 +1222,26 @@ error: ...@@ -1116,13 +1222,26 @@ 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(info->handle != 0){
device = WINMM_GetDeviceFromHWAVE(info->handle);
if(!device){
WARN("Unexpected! Invalid info->handle given: %p\n", info->handle);
return MMSYSERR_ERROR;
}
EnterCriticalSection(&device->lock);
device->open = TRUE;
}else{
CRITICAL_SECTION *lock;
UINT internal_index;
WINMM_Device **devices;
WINMM_MMDevice *mmdevice;
if(WINMM_IsMapper(info->req_device)){ if(WINMM_IsMapper(info->req_device)){
devices = g_out_mapper_devices; devices = g_out_mapper_devices;
mmdevice = read_map(g_out_map, 0); mmdevice = read_map(g_out_map, 0);
...@@ -1144,13 +1263,15 @@ static LRESULT WOD_Open(WINMM_OpenInfo *info) ...@@ -1144,13 +1263,15 @@ static LRESULT WOD_Open(WINMM_OpenInfo *info)
EnterCriticalSection(lock); EnterCriticalSection(lock);
device = WINMM_FindUnusedDevice(devices, mmdevice, internal_index, TRUE); device = WINMM_FindUnusedDevice(devices, mmdevice,
internal_index, TRUE);
if(!device){ if(!device){
LeaveCriticalSection(lock); LeaveCriticalSection(lock);
return MMSYSERR_ALLOCATED; return MMSYSERR_ALLOCATED;
} }
LeaveCriticalSection(lock); 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)
...@@ -1463,10 +1584,11 @@ static WAVEHDR *WOD_MarkDoneHeaders(WINMM_Device *device) ...@@ -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,6 +1739,9 @@ static void WOD_PushData(WINMM_Device *device) ...@@ -1617,6 +1739,9 @@ static void WOD_PushData(WINMM_Device *device)
goto exit; goto exit;
} }
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; device->played_frames += avail_frames;
exit: exit:
...@@ -1946,8 +2071,8 @@ static LRESULT WINMM_GetPosition(HWAVE hwave, MMTIME *time) ...@@ -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, ...@@ -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, ...@@ -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)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment