📄 audio.c
字号:
static DWORD wodGetNumDevs(void){ return ALSA_WodNumDevs;}/************************************************************************** * wodMessage (WINEALSA.@) */DWORD WINAPI ALSA_wodMessage(UINT wDevID, UINT wMsg, DWORD dwUser, DWORD dwParam1, DWORD dwParam2){ TRACE("(%u, %04X, %08lX, %08lX, %08lX);\n", wDevID, wMsg, dwUser, dwParam1, dwParam2); switch (wMsg) { case DRVM_INIT: case DRVM_EXIT: case DRVM_ENABLE: case DRVM_DISABLE: /* FIXME: Pretend this is supported */ return 0; case WODM_OPEN: return wodOpen (wDevID, (LPWAVEOPENDESC)dwParam1, dwParam2); case WODM_CLOSE: return wodClose (wDevID); case WODM_GETDEVCAPS: return wodGetDevCaps (wDevID, (LPWAVEOUTCAPSA)dwParam1, dwParam2); case WODM_GETNUMDEVS: return wodGetNumDevs (); case WODM_GETPITCH: return MMSYSERR_NOTSUPPORTED; case WODM_SETPITCH: return MMSYSERR_NOTSUPPORTED; case WODM_GETPLAYBACKRATE: return MMSYSERR_NOTSUPPORTED; case WODM_SETPLAYBACKRATE: return MMSYSERR_NOTSUPPORTED; case WODM_WRITE: return wodWrite (wDevID, (LPWAVEHDR)dwParam1, dwParam2); case WODM_PAUSE: return wodPause (wDevID); case WODM_GETPOS: return wodGetPosition (wDevID, (LPMMTIME)dwParam1, dwParam2); case WODM_BREAKLOOP: return wodBreakLoop (wDevID); case WODM_PREPARE: return wodPrepare (wDevID, (LPWAVEHDR)dwParam1, dwParam2); case WODM_UNPREPARE: return wodUnprepare (wDevID, (LPWAVEHDR)dwParam1, dwParam2); case WODM_GETVOLUME: return wodGetVolume (wDevID, (LPDWORD)dwParam1); case WODM_SETVOLUME: return wodSetVolume (wDevID, dwParam1); case WODM_RESTART: return wodRestart (wDevID); case WODM_RESET: return wodReset (wDevID); case DRV_QUERYDSOUNDIFACE: return wodDsCreate (wDevID, (PIDSDRIVER*)dwParam1); case DRV_QUERYDSOUNDDESC: return wodDsDesc (wDevID, (PDSDRIVERDESC)dwParam1); case DRV_QUERYDSOUNDGUID: return wodDsGuid (wDevID, (LPGUID)dwParam1); default: FIXME("unknown message %d!\n", wMsg); } return MMSYSERR_NOTSUPPORTED;}/*======================================================================* * Low level DSOUND implementation * *======================================================================*/typedef struct IDsDriverImpl IDsDriverImpl;typedef struct IDsDriverBufferImpl IDsDriverBufferImpl;struct IDsDriverImpl{ /* IUnknown fields */ ICOM_VFIELD(IDsDriver); DWORD ref; /* IDsDriverImpl fields */ UINT wDevID; IDsDriverBufferImpl*primary;};struct IDsDriverBufferImpl{ /* IUnknown fields */ ICOM_VFIELD(IDsDriverBuffer); DWORD ref; /* IDsDriverBufferImpl fields */ IDsDriverImpl* drv; CRITICAL_SECTION mmap_crst; LPVOID mmap_buffer; DWORD mmap_buflen_bytes; snd_pcm_uframes_t mmap_buflen_frames; snd_pcm_channel_area_t * mmap_areas; snd_async_handler_t * mmap_async_handler;};static void DSDB_CheckXRUN(IDsDriverBufferImpl* pdbi){ WINE_WAVEOUT * wwo = &(WOutDev[pdbi->drv->wDevID]); snd_pcm_state_t state = snd_pcm_state(wwo->p_handle); if ( state == SND_PCM_STATE_XRUN ) { int err = snd_pcm_prepare(wwo->p_handle); TRACE("xrun occurred\n"); if ( err < 0 ) ERR("recovery from xrun failed, prepare failed: %s\n", snd_strerror(err)); } else if ( state == SND_PCM_STATE_SUSPENDED ) { int err = snd_pcm_resume(wwo->p_handle); TRACE("recovery from suspension occurred\n"); if (err < 0 && err != -EAGAIN){ err = snd_pcm_prepare(wwo->p_handle); if (err < 0) ERR("recovery from suspend failed, prepare failed: %s\n", snd_strerror(err)); } }}static void DSDB_MMAPCopy(IDsDriverBufferImpl* pdbi){ WINE_WAVEOUT * wwo = &(WOutDev[pdbi->drv->wDevID]); int channels; snd_pcm_format_t format; snd_pcm_uframes_t period_size; snd_pcm_sframes_t avail; if ( !pdbi->mmap_buffer || !wwo->hw_params || !wwo->p_handle) return; channels = snd_pcm_hw_params_get_channels(wwo->hw_params); format = snd_pcm_hw_params_get_format(wwo->hw_params); period_size = snd_pcm_hw_params_get_period_size(wwo->hw_params, 0); avail = snd_pcm_avail_update(wwo->p_handle); DSDB_CheckXRUN(pdbi); TRACE("avail=%d format=%s channels=%d\n", (int)avail, snd_pcm_format_name(format), channels ); while (avail >= period_size) { const snd_pcm_channel_area_t *areas; snd_pcm_uframes_t ofs; snd_pcm_uframes_t frames; int err; frames = avail / period_size * period_size; /* round down to a multiple of period_size */ EnterCriticalSection(&pdbi->mmap_crst); snd_pcm_mmap_begin(wwo->p_handle, &areas, &ofs, &frames); snd_pcm_areas_copy(areas, ofs, pdbi->mmap_areas, ofs, channels, frames, format); err = snd_pcm_mmap_commit(wwo->p_handle, ofs, frames); LeaveCriticalSection(&pdbi->mmap_crst); if ( err != (snd_pcm_sframes_t) frames) ERR("mmap partially failed.\n"); avail = snd_pcm_avail_update(wwo->p_handle); } }static void DSDB_PCMCallback(snd_async_handler_t *ahandler){ /* snd_pcm_t * handle = snd_async_handler_get_pcm(ahandler); */ IDsDriverBufferImpl* pdbi = snd_async_handler_get_callback_private(ahandler); TRACE("callback called\n"); DSDB_MMAPCopy(pdbi);}static int DSDB_CreateMMAP(IDsDriverBufferImpl* pdbi) { WINE_WAVEOUT * wwo = &(WOutDev[pdbi->drv->wDevID]); snd_pcm_format_t format = snd_pcm_hw_params_get_format(wwo->hw_params); snd_pcm_uframes_t frames = snd_pcm_hw_params_get_buffer_size(wwo->hw_params); int channels = snd_pcm_hw_params_get_channels(wwo->hw_params); unsigned int bits_per_sample = snd_pcm_format_physical_width(format); unsigned int bits_per_frame = bits_per_sample * channels; snd_pcm_channel_area_t * a; unsigned int c; int err; if (TRACE_ON(wave)) ALSA_TraceParameters(wwo->hw_params, NULL, FALSE); TRACE("format=%s frames=%ld channels=%d bits_per_sample=%d bits_per_frame=%d\n", snd_pcm_format_name(format), frames, channels, bits_per_sample, bits_per_frame); pdbi->mmap_buflen_frames = frames; pdbi->mmap_buflen_bytes = snd_pcm_frames_to_bytes( wwo->p_handle, frames ); pdbi->mmap_buffer = HeapAlloc(GetProcessHeap(),0,pdbi->mmap_buflen_bytes); if (!pdbi->mmap_buffer) return DSERR_OUTOFMEMORY; snd_pcm_format_set_silence(format, pdbi->mmap_buffer, frames ); TRACE("created mmap buffer of %ld frames (%ld bytes) at %p\n", frames, pdbi->mmap_buflen_bytes, pdbi->mmap_buffer); pdbi->mmap_areas = HeapAlloc(GetProcessHeap(),0,channels*sizeof(snd_pcm_channel_area_t)); if (!pdbi->mmap_areas) return DSERR_OUTOFMEMORY; a = pdbi->mmap_areas; for (c = 0; c < channels; c++, a++) { a->addr = pdbi->mmap_buffer; a->first = bits_per_sample * c; a->step = bits_per_frame; TRACE("Area %d: addr=%p first=%d step=%d\n", c, a->addr, a->first, a->step); } InitializeCriticalSection(&pdbi->mmap_crst); err = snd_async_add_pcm_handler(&pdbi->mmap_async_handler, wwo->p_handle, DSDB_PCMCallback, pdbi); if ( err < 0 ) { ERR("add_pcm_handler failed. reason: %s\n", snd_strerror(err)); return DSERR_GENERIC; } return DS_OK; }static void DSDB_DestroyMMAP(IDsDriverBufferImpl* pdbi){ TRACE("mmap buffer %p destroyed\n", pdbi->mmap_buffer); HeapFree(GetProcessHeap(), 0, pdbi->mmap_areas); HeapFree(GetProcessHeap(), 0, pdbi->mmap_buffer); pdbi->mmap_areas = NULL; pdbi->mmap_buffer = NULL; DeleteCriticalSection(&pdbi->mmap_crst);}static HRESULT WINAPI IDsDriverBufferImpl_QueryInterface(PIDSDRIVERBUFFER iface, REFIID riid, LPVOID *ppobj){ /* ICOM_THIS(IDsDriverBufferImpl,iface); */ FIXME("(): stub!\n"); return DSERR_UNSUPPORTED;}static ULONG WINAPI IDsDriverBufferImpl_AddRef(PIDSDRIVERBUFFER iface){ ICOM_THIS(IDsDriverBufferImpl,iface); TRACE("(%p)\n",iface); return ++This->ref;}static ULONG WINAPI IDsDriverBufferImpl_Release(PIDSDRIVERBUFFER iface){ ICOM_THIS(IDsDriverBufferImpl,iface); TRACE("(%p)\n",iface); if (--This->ref) return This->ref; if (This == This->drv->primary) This->drv->primary = NULL; DSDB_DestroyMMAP(This); HeapFree(GetProcessHeap(), 0, This); return 0;}static HRESULT WINAPI IDsDriverBufferImpl_Lock(PIDSDRIVERBUFFER iface, LPVOID*ppvAudio1,LPDWORD pdwLen1, LPVOID*ppvAudio2,LPDWORD pdwLen2, DWORD dwWritePosition,DWORD dwWriteLen, DWORD dwFlags){ /* ICOM_THIS(IDsDriverBufferImpl,iface); */ TRACE("(%p)\n",iface); return DSERR_UNSUPPORTED;}static HRESULT WINAPI IDsDriverBufferImpl_Unlock(PIDSDRIVERBUFFER iface, LPVOID pvAudio1,DWORD dwLen1, LPVOID pvAudio2,DWORD dwLen2){ /* ICOM_THIS(IDsDriverBufferImpl,iface); */ TRACE("(%p)\n",iface); return DSERR_UNSUPPORTED;}static HRESULT WINAPI IDsDriverBufferImpl_SetFormat(PIDSDRIVERBUFFER iface, LPWAVEFORMATEX pwfx){ /* ICOM_THIS(IDsDriverBufferImpl,iface); */ TRACE("(%p,%p)\n",iface,pwfx); return DSERR_BUFFERLOST;}static HRESULT WINAPI IDsDriverBufferImpl_SetFrequency(PIDSDRIVERBUFFER iface, DWORD dwFreq){ /* ICOM_THIS(IDsDriverBufferImpl,iface); */ TRACE("(%p,%ld): stub\n",iface,dwFreq); return DSERR_UNSUPPORTED;}static HRESULT WINAPI IDsDriverBufferImpl_SetVolumePan(PIDSDRIVERBUFFER iface, PDSVOLUMEPAN pVolPan){ /* ICOM_THIS(IDsDriverBufferImpl,iface); */ FIXME("(%p,%p): stub!\n",iface,pVolPan); return DS_OK;}static HRESULT WINAPI IDsDriverBufferImpl_SetPosition(PIDSDRIVERBUFFER iface, DWORD dwNewPos){ /* ICOM_THIS(IDsDriverImpl,iface); */ TRACE("(%p,%ld): stub\n",iface,dwNewPos); return DSERR_UNSUPPORTED;}static HRESULT WINAPI IDsDriverBufferImpl_GetPosition(PIDSDRIVERBUFFER iface, LPDWORD lpdwPlay, LPDWORD lpdwWrite){ ICOM_THIS(IDsDriverBufferImpl,iface); WINE_WAVEOUT * wwo = &(WOutDev[This->drv->wDevID]); snd_pcm_uframes_t hw_ptr; snd_pcm_uframes_t period_size; if (wwo->hw_params == NULL) return DSERR_GENERIC; period_size = snd_pcm_hw_params_get_period_size(wwo->hw_params, 0); if (wwo->p_handle == NULL) return DSERR_GENERIC; /** we need to track down buffer underruns */ DSDB_CheckXRUN(This); EnterCriticalSection(&This->mmap_crst); hw_ptr = _snd_pcm_mmap_hw_ptr(wwo->p_handle); if (lpdwPlay) *lpdwPlay = snd_pcm_frames_to_bytes(wwo->p_handle, hw_ptr/ period_size * period_size) % This->mmap_buflen_bytes; if (lpdwWrite) *lpdwWrite = snd_pcm_frames_to_bytes(wwo->p_handle, (hw_ptr / period_size + 1) * period_size ) % This->mmap_buflen_bytes; LeaveCriticalSection(&This->mmap_crst); TRACE("hw_ptr=0x%08x, playpos=%ld, writepos=%ld\n", (unsigned int)hw_ptr, lpdwPlay?*lpdwPlay:-1, lpdwWrite?*lpdwWrite:-1); return DS_OK;}static HRESULT WINAPI IDsDriverBufferImpl_Play(PIDSDRIVERBUFFER iface, DWORD dwRes1, DWORD dwRes2, DWORD dwFlags){ ICOM_THIS(IDsDriverBufferImpl,iface); WINE_WAVEOUT * wwo = &(WOutDev[This->drv->wDevID]); snd_pcm_state_t state; int err; TRACE("(%p,%lx,%lx,%lx)\n",iface,dwRes1,dwRes2,dwFlags); if (wwo->p_handle == NULL) return DSERR_GENERIC; state = snd_pcm_state(wwo->p_handle); if ( state == SND_PCM_STATE_SETUP ) { err = snd_pcm_prepare(wwo->p_handle); state = snd_pcm_state(wwo->p_handle); } if ( state == SND_PCM_STATE_PREPARED ) { DSDB_MMAPCopy(This); err = snd_pcm_start(wwo->p_handle); } return DS_OK;}static HRESULT WINAPI IDsDriverBufferImpl_Stop(PIDSDRIVERBUFFER iface){ ICOM_THIS(IDsDriverBufferImpl,iface); WINE_WAVEOUT * wwo = &(WOutDev[This->drv->wDevID]); int err; DWORD play; DWORD write; TRACE("(%p)\n",iface); if (wwo->p_handle == NULL) return DSERR_GENERIC; /* ring buffer wrap up detection */ IDsDriverBufferImpl_GetPosition(iface, &play, &write); if ( play > write) { TRACE("writepos wrapper up\n"); return DS_OK; } if ( ( err = snd_pcm_drop(wwo->p_handle)) < 0 ) { ERR("error while stopping pcm: %s\n", snd_strerror(err)); return DSERR_GENERIC; } return DS_OK;}static ICOM_VTABLE(IDsDriverBuffer) dsdbvt ={ ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE IDsDriverBufferImpl_QueryInterface, IDsDriverBufferImpl_AddRef, IDsDriverBufferImpl_Release, IDsDriverBufferImpl_Lock, IDsDriverBufferImpl_Unlock, IDsDriverBufferImpl_SetFormat, IDsDriverBufferImpl_SetFrequency, IDsDriverBufferImpl_SetVolumePan, IDsDriverBufferImpl_SetPosition, IDsDriverBufferImpl_GetPosition, IDsDriverBuffer
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -