static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client, BOOL forcewave, WAVEFORMATEX **wfx) { WAVEFORMATEXTENSIBLE *retwfe = NULL; WAVEFORMATEX *w; HRESULT hr; if (!forcewave) { WAVEFORMATEXTENSIBLE *mixwfe; hr = IAudioClient_GetMixFormat(client, (WAVEFORMATEX**)&mixwfe); if (FAILED(hr)) return hr; if (mixwfe->Format.nChannels < device->num_speakers) { device->speaker_config = DSOUND_FindSpeakerConfig(device->mmdevice, mixwfe->Format.nChannels); DSOUND_ParseSpeakerConfig(device); } else if (mixwfe->Format.nChannels > device->num_speakers) { mixwfe->Format.nChannels = device->num_speakers; mixwfe->Format.nBlockAlign = mixwfe->Format.nChannels * mixwfe->Format.wBitsPerSample / 8; mixwfe->Format.nAvgBytesPerSec = mixwfe->Format.nSamplesPerSec * mixwfe->Format.nBlockAlign; mixwfe->dwChannelMask = speaker_config_to_channel_mask(device->speaker_config); } if (!IsEqualGUID(&mixwfe->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) { WAVEFORMATEXTENSIBLE testwfe = *mixwfe; testwfe.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; testwfe.Samples.wValidBitsPerSample = testwfe.Format.wBitsPerSample = 32; testwfe.Format.nBlockAlign = testwfe.Format.nChannels * testwfe.Format.wBitsPerSample / 8; testwfe.Format.nAvgBytesPerSec = testwfe.Format.nSamplesPerSec * testwfe.Format.nBlockAlign; if (FAILED(IAudioClient_IsFormatSupported(client, AUDCLNT_SHAREMODE_SHARED, &testwfe.Format, (WAVEFORMATEX**)&retwfe))) w = DSOUND_CopyFormat(&mixwfe->Format); else if (retwfe) w = DSOUND_CopyFormat(&retwfe->Format); else w = DSOUND_CopyFormat(&testwfe.Format); CoTaskMemFree(retwfe); retwfe = NULL; } else w = DSOUND_CopyFormat(&mixwfe->Format); CoTaskMemFree(mixwfe); } else if (device->primary_pwfx->wFormatTag == WAVE_FORMAT_PCM || device->primary_pwfx->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) { WAVEFORMATEX *wi = device->primary_pwfx; WAVEFORMATEXTENSIBLE *wfe; /* Convert to WAVEFORMATEXTENSIBLE */ w = HeapAlloc(GetProcessHeap(), 0, sizeof(WAVEFORMATEXTENSIBLE)); wfe = (WAVEFORMATEXTENSIBLE*)w; if (!wfe) return DSERR_OUTOFMEMORY; wfe->Format = *wi; w->wFormatTag = WAVE_FORMAT_EXTENSIBLE; w->cbSize = sizeof(*wfe) - sizeof(*w); w->nBlockAlign = w->nChannels * w->wBitsPerSample / 8; w->nAvgBytesPerSec = w->nSamplesPerSec * w->nBlockAlign; wfe->dwChannelMask = 0; if (wi->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) { w->wBitsPerSample = 32; wfe->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; } else wfe->SubFormat = KSDATAFORMAT_SUBTYPE_PCM; wfe->Samples.wValidBitsPerSample = w->wBitsPerSample; } else w = DSOUND_CopyFormat(device->primary_pwfx); if (!w) return DSERR_OUTOFMEMORY; hr = IAudioClient_IsFormatSupported(client, AUDCLNT_SHAREMODE_SHARED, w, (WAVEFORMATEX**)&retwfe); if (retwfe) { memcpy(w, retwfe, sizeof(WAVEFORMATEX) + retwfe->Format.cbSize); CoTaskMemFree(retwfe); } if (FAILED(hr)) { WARN("IsFormatSupported failed: %08x\n", hr); HeapFree(GetProcessHeap(), 0, w); return hr; } *wfx = w; return S_OK; }
HRESULT DSOUND_ReopenDevice(DirectSoundDevice *device, BOOL forcewave) { UINT prebuf_frames; REFERENCE_TIME prebuf_rt; WAVEFORMATEX *wfx = NULL; HRESULT hres; REFERENCE_TIME period; DWORD period_ms; TRACE("(%p, %d)\n", device, forcewave); if(device->client){ IAudioClient_Release(device->client); device->client = NULL; } if(device->render){ IAudioRenderClient_Release(device->render); device->render = NULL; } if(device->clock){ IAudioClock_Release(device->clock); device->clock = NULL; } if(device->volume){ IAudioStreamVolume_Release(device->volume); device->volume = NULL; } hres = IMMDevice_Activate(device->mmdevice, &IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, (void **)&device->client); if(FAILED(hres)) { WARN("Activate failed: %08x\n", hres); return hres; } device->speaker_config = DSOUND_FindSpeakerConfig(device->mmdevice, 0); DSOUND_ParseSpeakerConfig(device); hres = DSOUND_WaveFormat(device, device->client, forcewave, &wfx); if (FAILED(hres)) { IAudioClient_Release(device->client); device->client = NULL; return hres; } HeapFree(GetProcessHeap(), 0, device->pwfx); device->pwfx = wfx; prebuf_frames = device->prebuf * DSOUND_fraglen(device) / device->pwfx->nBlockAlign; prebuf_rt = (10000000 * (UINT64)prebuf_frames) / device->pwfx->nSamplesPerSec; hres = IAudioClient_Initialize(device->client, AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_NOPERSIST | AUDCLNT_STREAMFLAGS_EVENTCALLBACK, prebuf_rt, 0, device->pwfx, NULL); if(FAILED(hres)){ IAudioClient_Release(device->client); device->client = NULL; WARN("Initialize failed: %08x\n", hres); return hres; } IAudioClient_SetEventHandle(device->client, device->sleepev); hres = IAudioClient_GetService(device->client, &IID_IAudioRenderClient, (void**)&device->render); if(FAILED(hres)){ IAudioClient_Release(device->client); device->client = NULL; WARN("GetService failed: %08x\n", hres); return hres; } hres = IAudioClient_GetService(device->client, &IID_IAudioClock, (void**)&device->clock); if(FAILED(hres)){ IAudioClient_Release(device->client); IAudioRenderClient_Release(device->render); device->client = NULL; device->render = NULL; WARN("GetService failed: %08x\n", hres); return hres; } hres = IAudioClient_GetService(device->client, &IID_IAudioStreamVolume, (void**)&device->volume); if(FAILED(hres)){ IAudioClient_Release(device->client); IAudioRenderClient_Release(device->render); IAudioClock_Release(device->clock); device->client = NULL; device->render = NULL; device->clock = NULL; WARN("GetService failed: %08x\n", hres); return hres; } /* Now kick off the timer so the event fires periodically */ hres = IAudioClient_Start(device->client); if (FAILED(hres)) WARN("starting failed with %08x\n", hres); hres = IAudioClient_GetStreamLatency(device->client, &period); if (FAILED(hres)) { WARN("GetStreamLatency failed with %08x\n", hres); period_ms = 10; } else period_ms = (period + 9999) / 10000; TRACE("period %u ms fraglen %u prebuf %u\n", period_ms, device->fraglen, device->prebuf); if (period_ms < 3) device->sleeptime = 5; else device->sleeptime = period_ms * 5 / 2; return S_OK; }