diff --git a/dlls/winmm/waveform.c b/dlls/winmm/waveform.c index cbaabc1c938..207b6ca4458 100644 --- a/dlls/winmm/waveform.c +++ b/dlls/winmm/waveform.c @@ -97,6 +97,7 @@ typedef struct _WINMM_Device { IAudioClock *clock; IAudioStreamVolume *volume; + WAVEFORMATEX *orig_fmt; HACMSTREAM acm_handle; ACMSTREAMHEADER acm_hdr; UINT32 acm_offs; @@ -106,6 +107,7 @@ typedef struct _WINMM_Device { BOOL stopped; DWORD loop_counter; 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 */ UINT64 last_clock_pos; @@ -172,6 +174,7 @@ typedef struct _WINMM_OpenInfo { DWORD_PTR callback; DWORD_PTR cb_user; DWORD flags; + BOOL reset; } WINMM_OpenInfo; typedef struct _WINMM_ControlDetails { @@ -191,6 +194,7 @@ static LRESULT WOD_Open(WINMM_OpenInfo *info); static LRESULT WOD_Close(HWAVEOUT hwave); static LRESULT WID_Open(WINMM_OpenInfo *info); static LRESULT WID_Close(HWAVEIN hwave); +static MMRESULT WINMM_BeginPlaying(WINMM_Device *device); void WINMM_DeleteWaveform(void) { @@ -273,14 +277,10 @@ static inline void WINMM_DecomposeHWAVE(HWAVE hwave, UINT *mmdevice_index, *junk = l >> 15; } -static void WINMM_InitDevice(WINMM_Device *device, - WINMM_MMDevice *parent, HWAVE hwave) +static void WINMM_InitDevice(WINMM_Device *device) { InitializeCriticalSection(&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) @@ -312,14 +312,16 @@ static WINMM_Device *WINMM_FindUnusedDevice(WINMM_Device **devices, if(!device) return NULL; - WINMM_InitDevice(device, parent, - WINMM_MakeHWAVE(internal_index, is_out, i)); + WINMM_InitDevice(device); EnterCriticalSection(&device->lock); }else EnterCriticalSection(&device->lock); if(!device->open){ + device->handle = WINMM_MakeHWAVE(internal_index, is_out, i); + device->parent = parent; device->open = TRUE; + return device; } @@ -678,38 +680,20 @@ static HRESULT WINAPI notif_OnDeviceRemoved(IMMNotificationClient *iface, return S_OK; } -static HRESULT WINAPI notif_OnDefaultDeviceChanged(IMMNotificationClient *iface, - EDataFlow flow, ERole role, const WCHAR *device_id) +static HRESULT update_mapping(WINMM_MMDevice ***map, UINT count, + const WCHAR *default_id) { - WINMM_MMDevice ***map; WINMM_MMDevice *prev; - UINT count, 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; - } + UINT i; prev = (*map)[0]; for(i = 0; i < count; ++i){ WINMM_MMDevice *tmp; - if(!lstrcmpW((*map)[i]->dev_id, device_id)){ + if(!lstrcmpW((*map)[i]->dev_id, default_id)){ (*map)[0] = (*map)[i]; (*map)[i] = prev; - LeaveCriticalSection(&g_devthread_lock); - 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"); (*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); return S_OK; @@ -849,118 +948,119 @@ static MMRESULT WINMM_TryDeviceMapping(WINMM_Device *device, WAVEFORMATEX *fmt, return MMSYSERR_NOERROR; } -static MMRESULT WINMM_MapDevice(WINMM_Device *device, WAVEFORMATEX *fmt, - BOOL is_out) +static MMRESULT WINMM_MapDevice(WINMM_Device *device, BOOL is_out) { 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 */ - if(fmt->wFormatTag != WAVE_FORMAT_PCM && - !(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE && + if(device->orig_fmt->wFormatTag != WAVE_FORMAT_PCM && + !(device->orig_fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE && IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))){ /* convert to PCM format if it's not already */ - mr = WINMM_TryDeviceMapping(device, fmt, fmt->nChannels, - fmt->nSamplesPerSec, 16, is_out); + mr = WINMM_TryDeviceMapping(device, device->orig_fmt, + device->orig_fmt->nChannels, device->orig_fmt->nSamplesPerSec, + 16, is_out); if(mr == MMSYSERR_NOERROR) return mr; - mr = WINMM_TryDeviceMapping(device, fmt, fmt->nChannels, - fmt->nSamplesPerSec, 8, is_out); + mr = WINMM_TryDeviceMapping(device, device->orig_fmt, + device->orig_fmt->nChannels, device->orig_fmt->nSamplesPerSec, + 8, is_out); if(mr == MMSYSERR_NOERROR) return mr; }else{ WORD channels; /* first try just changing bit depth and channels */ - channels = fmt->nChannels; - mr = WINMM_TryDeviceMapping(device, fmt, channels, - fmt->nSamplesPerSec, 16, is_out); + channels = device->orig_fmt->nChannels; + mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels, + device->orig_fmt->nSamplesPerSec, 16, is_out); if(mr == MMSYSERR_NOERROR) return mr; - mr = WINMM_TryDeviceMapping(device, fmt, channels, - fmt->nSamplesPerSec, 8, is_out); + mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels, + device->orig_fmt->nSamplesPerSec, 8, is_out); if(mr == MMSYSERR_NOERROR) return mr; channels = (channels == 2) ? 1 : 2; - mr = WINMM_TryDeviceMapping(device, fmt, channels, - fmt->nSamplesPerSec, 16, is_out); + mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels, + device->orig_fmt->nSamplesPerSec, 16, is_out); if(mr == MMSYSERR_NOERROR) return mr; - mr = WINMM_TryDeviceMapping(device, fmt, channels, - fmt->nSamplesPerSec, 8, is_out); + mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels, + device->orig_fmt->nSamplesPerSec, 8, is_out); if(mr == MMSYSERR_NOERROR) return mr; /* that didn't work, so now try different sample rates */ - channels = fmt->nChannels; - mr = WINMM_TryDeviceMapping(device, fmt, channels, 96000, 16, is_out); + channels = device->orig_fmt->nChannels; + mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels, 96000, 16, is_out); if(mr == MMSYSERR_NOERROR) 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) 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) 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) 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) return mr; 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) 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) 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) 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) 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) return mr; - channels = fmt->nChannels; - mr = WINMM_TryDeviceMapping(device, fmt, channels, 96000, 8, is_out); + channels = device->orig_fmt->nChannels; + mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels, 96000, 8, is_out); if(mr == MMSYSERR_NOERROR) 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) 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) 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) 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) return mr; 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) 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) 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) 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) 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) 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, BOOL is_out) { - WAVEFORMATEX fmt, *passed_fmt; LRESULT ret = MMSYSERR_NOMEM; HRESULT hr; @@ -1000,30 +1099,34 @@ static LRESULT WINMM_OpenDevice(WINMM_Device *device, WINMM_OpenInfo *info, if(info->format->wFormatTag == WAVE_FORMAT_PCM){ /* we aren't guaranteed that the struct in lpFormat is a full * WAVEFORMATEX struct, which IAC::IsFormatSupported requires */ - passed_fmt = &fmt; - memcpy(passed_fmt, info->format, sizeof(PCMWAVEFORMAT)); - passed_fmt->cbSize = 0; - if(fmt.wBitsPerSample % 8 != 0){ - WARN("Fixing bad wBitsPerSample (%u)\n", fmt.wBitsPerSample); - fmt.wBitsPerSample = (fmt.wBitsPerSample + 7) & ~7; + device->orig_fmt = HeapAlloc(GetProcessHeap(), 0, sizeof(WAVEFORMATEX)); + memcpy(device->orig_fmt, info->format, sizeof(PCMWAVEFORMAT)); + device->orig_fmt->cbSize = 0; + if(device->orig_fmt->wBitsPerSample % 8 != 0){ + WARN("Fixing bad wBitsPerSample (%u)\n", device->orig_fmt->wBitsPerSample); + device->orig_fmt->wBitsPerSample = (device->orig_fmt->wBitsPerSample + 7) & ~7; } /* winmm ignores broken blockalign and avgbytes */ - if(fmt.nBlockAlign != fmt.nChannels * fmt.wBitsPerSample/8){ - WARN("Fixing bad nBlockAlign (%u)\n", fmt.nBlockAlign); - 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", device->orig_fmt->nBlockAlign); + device->orig_fmt->nBlockAlign = device->orig_fmt->nChannels * device->orig_fmt->wBitsPerSample/8; } - if (fmt.nAvgBytesPerSec != fmt.nSamplesPerSec * fmt.nBlockAlign) { - WARN("Fixing bad nAvgBytesPerSec (%u)\n", fmt.nAvgBytesPerSec); - 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", device->orig_fmt->nAvgBytesPerSec); + device->orig_fmt->nAvgBytesPerSec = device->orig_fmt->nSamplesPerSec * device->orig_fmt->nBlockAlign; } - }else - passed_fmt = info->format; + }else{ + 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){ WAVEFORMATEX *closer_fmt = NULL; hr = IAudioClient_IsFormatSupported(device->client, - AUDCLNT_SHAREMODE_SHARED, passed_fmt, &closer_fmt); + AUDCLNT_SHAREMODE_SHARED, device->orig_fmt, &closer_fmt); if(closer_fmt) CoTaskMemFree(closer_fmt); 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, 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(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) goto error; }else{ @@ -1044,8 +1147,8 @@ static LRESULT WINMM_OpenDevice(WINMM_Device *device, WINMM_OpenInfo *info, goto error; } }else{ - device->bytes_per_frame = passed_fmt->nBlockAlign; - device->samples_per_sec = passed_fmt->nSamplesPerSec; + device->bytes_per_frame = device->orig_fmt->nBlockAlign; + device->samples_per_sec = device->orig_fmt->nSamplesPerSec; } hr = IAudioClient_GetService(device->client, &IID_IAudioClock, @@ -1085,12 +1188,15 @@ static LRESULT WINMM_OpenDevice(WINMM_Device *device, WINMM_OpenInfo *info, goto error; } - device->played_frames = 0; - device->last_clock_pos = 0; - device->ofs_bytes = 0; - device->loop_counter = 0; + if(info->reset){ + device->played_frames = 0; + device->ofs_bytes = 0; + device->loop_counter = 0; + device->first = device->last = device->playing = device->loop_start = NULL; + } + 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.callback = info->callback; @@ -1116,42 +1222,57 @@ error: static LRESULT WOD_Open(WINMM_OpenInfo *info) { - WINMM_Device *device, **devices; - WINMM_MMDevice *mmdevice; - CRITICAL_SECTION *lock; - UINT internal_index; + WINMM_Device *device; LRESULT ret = MMSYSERR_ERROR; HRESULT hr; - 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; + 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{ - if(info->req_device >= g_outmmdevices_count) - return MMSYSERR_BADDEVICEID; + CRITICAL_SECTION *lock; + 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]) - return MMSYSERR_NOTENABLED; + mmdevice = read_map(g_out_map, info->req_device); - devices = mmdevice->devices; - lock = &mmdevice->lock; - internal_index = mmdevice->index; - } + if(!mmdevice->out_caps.szPname[0]) + return MMSYSERR_NOTENABLED; - 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); - return MMSYSERR_ALLOCATED; } - LeaveCriticalSection(lock); - ret = WINMM_OpenDevice(device, info, TRUE); if((info->flags & WAVE_FORMAT_QUERY) || ret != MMSYSERR_NOERROR) goto error; @@ -1463,10 +1584,11 @@ static WAVEHDR *WOD_MarkDoneHeaders(WINMM_Device *device) nloops = device->loop_counter; while(queue && (queue_frames += WINMM_HeaderLenFrames(device, queue)) <= - clock_frames - device->last_clock_pos){ + clock_frames - device->last_clock_pos + device->remainder_frames){ if(!nloops){ last = queue; device->last_clock_pos += queue_frames; + device->remainder_frames = 0; queue_frames = 0; queue = device->first = queue->lpNext; }else{ @@ -1617,7 +1739,10 @@ static void WOD_PushData(WINMM_Device *device) 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: cb_info = device->cb_info; @@ -1946,8 +2071,8 @@ static LRESULT WINMM_GetPosition(HWAVE hwave, MMTIME *time) return MMSYSERR_INVALHANDLE; played_frames = device->played_frames; - sample_rate = device->samples_per_sec; - bytes_per_frame = device->bytes_per_frame; + sample_rate = device->orig_fmt->nSamplesPerSec; + bytes_per_frame = device->orig_fmt->nBlockAlign; LeaveCriticalSection(&device->lock); @@ -2591,11 +2716,13 @@ MMRESULT WINAPI waveOutOpen(LPHWAVEOUT lphWaveOut, UINT uDeviceID, if(res != MMSYSERR_NOERROR) return res; + info.handle = 0; info.format = (WAVEFORMATEX*)lpFormat; info.callback = dwCallback; info.cb_user = dwInstance; info.req_device = uDeviceID; info.flags = dwFlags; + info.reset = TRUE; res = SendMessageW(g_devices_hwnd, WODM_OPEN, (DWORD_PTR)&info, 0); if(res != MMSYSERR_NOERROR) @@ -3219,11 +3346,13 @@ MMRESULT WINAPI waveInOpen(HWAVEIN* lphWaveIn, UINT uDeviceID, if(res != MMSYSERR_NOERROR) return res; + info.handle = 0; info.format = (WAVEFORMATEX*)lpFormat; info.callback = dwCallback; info.cb_user = dwInstance; info.req_device = uDeviceID; info.flags = dwFlags; + info.reset = TRUE; res = SendMessageW(g_devices_hwnd, WIDM_OPEN, (DWORD_PTR)&info, 0); if(res != MMSYSERR_NOERROR)