📄 audio.c
字号:
/* -*- tab-width: 8; c-basic-offset: 4 -*- *//* * Sample Wine Driver for Open Sound System (featured in Linux and FreeBSD) * * Copyright 1994 Martin Ayotte * 1999 Eric Pouech (async playing in waveOut/waveIn) * 2000 Eric Pouech (loops in waveOut) * 2002 Eric Pouech (full duplex) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *//* * FIXME: * pause in waveOut does not work correctly in loop mode * Direct Sound Capture driver does not work (not complete yet) *//*#define EMULATE_SB16*//* unless someone makes a wineserver kernel module, Unix pipes are faster than win32 events */#define USE_PIPE_SYNC/* an exact wodGetPosition is usually not worth the extra context switches, * as we're going to have near fragment accuracy anyway *//* #define EXACT_WODPOSITION */#include "config.h"#include "wine/port.h"#include <stdlib.h>#include <stdarg.h>#include <stdio.h>#include <string.h>#ifdef HAVE_UNISTD_H# include <unistd.h>#endif#include <errno.h>#include <fcntl.h>#ifdef HAVE_SYS_IOCTL_H# include <sys/ioctl.h>#endif#ifdef HAVE_SYS_MMAN_H# include <sys/mman.h>#endif#ifdef HAVE_SYS_POLL_H# include <sys/poll.h>#endif#include "windef.h"#include "winbase.h"#include "wingdi.h"#include "winerror.h"#include "wine/winuser16.h"#include "mmddk.h"#include "dsound.h"#include "dsdriver.h"#include "oss.h"#include "wine/debug.h"WINE_DEFAULT_DEBUG_CHANNEL(wave);/* Allow 1% deviation for sample rates (some ES137x cards) */#define NEAR_MATCH(rate1,rate2) (((100*((int)(rate1)-(int)(rate2)))/(rate1))==0)#ifdef HAVE_OSS#define MAX_WAVEDRV (6)/* state diagram for waveOut writing: * * +---------+-------------+---------------+---------------------------------+ * | state | function | event | new state | * +---------+-------------+---------------+---------------------------------+ * | | open() | | STOPPED | * | PAUSED | write() | | PAUSED | * | STOPPED | write() | <thrd create> | PLAYING | * | PLAYING | write() | HEADER | PLAYING | * | (other) | write() | <error> | | * | (any) | pause() | PAUSING | PAUSED | * | PAUSED | restart() | RESTARTING | PLAYING (if no thrd => STOPPED) | * | (any) | reset() | RESETTING | STOPPED | * | (any) | close() | CLOSING | CLOSED | * +---------+-------------+---------------+---------------------------------+ *//* states of the playing device */#define WINE_WS_PLAYING 0#define WINE_WS_PAUSED 1#define WINE_WS_STOPPED 2#define WINE_WS_CLOSED 3/* events to be send to device */enum win_wm_message { WINE_WM_PAUSING = WM_USER + 1, WINE_WM_RESTARTING, WINE_WM_RESETTING, WINE_WM_HEADER, WINE_WM_UPDATE, WINE_WM_BREAKLOOP, WINE_WM_CLOSING, WINE_WM_STARTING, WINE_WM_STOPPING};#ifdef USE_PIPE_SYNC#define SIGNAL_OMR(omr) do { int x = 0; write((omr)->msg_pipe[1], &x, sizeof(x)); } while (0)#define CLEAR_OMR(omr) do { int x = 0; read((omr)->msg_pipe[0], &x, sizeof(x)); } while (0)#define RESET_OMR(omr) do { } while (0)#define WAIT_OMR(omr, sleep) \ do { struct pollfd pfd; pfd.fd = (omr)->msg_pipe[0]; \ pfd.events = POLLIN; poll(&pfd, 1, sleep); } while (0)#else#define SIGNAL_OMR(omr) do { SetEvent((omr)->msg_event); } while (0)#define CLEAR_OMR(omr) do { } while (0)#define RESET_OMR(omr) do { ResetEvent((omr)->msg_event); } while (0)#define WAIT_OMR(omr, sleep) \ do { WaitForSingleObject((omr)->msg_event, sleep); } while (0)#endiftypedef struct { enum win_wm_message msg; /* message identifier */ DWORD param; /* parameter for this message */ HANDLE hEvent; /* if message is synchronous, handle of event for synchro */} OSS_MSG;/* implement an in-process message ring for better performance * (compared to passing thru the server) * this ring will be used by the input (resp output) record (resp playback) routine */#define OSS_RING_BUFFER_INCREMENT 64typedef struct { int ring_buffer_size; OSS_MSG * messages; int msg_tosave; int msg_toget;#ifdef USE_PIPE_SYNC int msg_pipe[2];#else HANDLE msg_event;#endif CRITICAL_SECTION msg_crst;} OSS_MSG_RING;typedef struct tagOSS_DEVICE { char dev_name[32]; char mixer_name[32]; unsigned open_count; WAVEOUTCAPSA out_caps; WAVEINCAPSA in_caps; DWORD in_caps_support; unsigned open_access; int fd; DWORD owner_tid; int sample_rate; int stereo; int format; unsigned audio_fragment; BOOL full_duplex; BOOL bTriggerSupport; BOOL bOutputEnabled; BOOL bInputEnabled; DSDRIVERDESC ds_desc; DSDRIVERCAPS ds_caps; DSCDRIVERCAPS dsc_caps; GUID ds_guid; GUID dsc_guid;} OSS_DEVICE;static OSS_DEVICE OSS_Devices[MAX_WAVEDRV];typedef struct { OSS_DEVICE* ossdev; volatile int state; /* one of the WINE_WS_ manifest constants */ WAVEOPENDESC waveDesc; WORD wFlags; PCMWAVEFORMAT format; /* OSS information */ DWORD dwFragmentSize; /* size of OSS buffer fragment */ DWORD dwBufferSize; /* size of whole OSS buffer in bytes */ LPWAVEHDR lpQueuePtr; /* start of queued WAVEHDRs (waiting to be notified) */ LPWAVEHDR lpPlayPtr; /* start of not yet fully played buffers */ DWORD dwPartialOffset; /* Offset of not yet written bytes in lpPlayPtr */ LPWAVEHDR lpLoopPtr; /* pointer of first buffer in loop, if any */ DWORD dwLoops; /* private copy of loop counter */ DWORD dwPlayedTotal; /* number of bytes actually played since opening */ DWORD dwWrittenTotal; /* number of bytes written to OSS buffer since opening */ BOOL bNeedPost; /* whether audio still needs to be physically started */ /* synchronization stuff */ HANDLE hStartUpEvent; HANDLE hThread; DWORD dwThreadID; OSS_MSG_RING msgRing; /* DirectSound stuff */ LPBYTE mapping; DWORD maplen;} WINE_WAVEOUT;typedef struct { OSS_DEVICE* ossdev; volatile int state; DWORD dwFragmentSize; /* OpenSound '/dev/dsp' give us that size */ WAVEOPENDESC waveDesc; WORD wFlags; PCMWAVEFORMAT format; LPWAVEHDR lpQueuePtr; DWORD dwTotalRecorded; /* synchronization stuff */ HANDLE hThread; DWORD dwThreadID; HANDLE hStartUpEvent; OSS_MSG_RING msgRing; /* DirectSound stuff */ LPBYTE mapping; DWORD maplen;} WINE_WAVEIN;static WINE_WAVEOUT WOutDev [MAX_WAVEDRV];static WINE_WAVEIN WInDev [MAX_WAVEDRV];static unsigned numOutDev;static unsigned numInDev;static DWORD wodDsCreate(UINT wDevID, PIDSDRIVER* drv);static DWORD widDsCreate(UINT wDevID, PIDSCDRIVER* drv);static DWORD wodDsDesc(UINT wDevID, PDSDRIVERDESC desc);static DWORD widDsDesc(UINT wDevID, PDSDRIVERDESC desc);static DWORD wodDsGuid(UINT wDevID, LPGUID pGuid);static DWORD widDsGuid(UINT wDevID, LPGUID pGuid);/* These strings used only for tracing */static const char *wodPlayerCmdString[] = { "WINE_WM_PAUSING", "WINE_WM_RESTARTING", "WINE_WM_RESETTING", "WINE_WM_HEADER", "WINE_WM_UPDATE", "WINE_WM_BREAKLOOP", "WINE_WM_CLOSING", "WINE_WM_STARTING", "WINE_WM_STOPPING",};static int getEnables(OSS_DEVICE *ossdev){ return ( (ossdev->bOutputEnabled ? PCM_ENABLE_OUTPUT : 0) | (ossdev->bInputEnabled ? PCM_ENABLE_INPUT : 0) );}/*======================================================================* * Low level WAVE implementation * *======================================================================*//****************************************************************** * OSS_RawOpenDevice * * Low level device opening (from values stored in ossdev) */static DWORD OSS_RawOpenDevice(OSS_DEVICE* ossdev, int strict_format){ int fd, val, rc; TRACE("(%p,%d)\n",ossdev,strict_format); if ((fd = open(ossdev->dev_name, ossdev->open_access|O_NDELAY, 0)) == -1) { WARN("Couldn't open %s (%s)\n", ossdev->dev_name, strerror(errno)); return (errno == EBUSY) ? MMSYSERR_ALLOCATED : MMSYSERR_ERROR; } fcntl(fd, F_SETFD, 1); /* set close on exec flag */ /* turn full duplex on if it has been requested */ if (ossdev->open_access == O_RDWR && ossdev->full_duplex) { rc = ioctl(fd, SNDCTL_DSP_SETDUPLEX, 0); /* on *BSD, as full duplex is always enabled by default, this ioctl * will fail with EINVAL * so, we don't consider EINVAL an error here */ if (rc != 0 && errno != EINVAL) { ERR("ioctl(%s, SNDCTL_DSP_SETDUPLEX) failed (%s)\n", ossdev->dev_name, strerror(errno)); goto error2; } } if (ossdev->audio_fragment) { rc = ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &ossdev->audio_fragment); if (rc != 0) { ERR("ioctl(%s, SNDCTL_DSP_SETFRAGMENT) failed (%s)\n", ossdev->dev_name, strerror(errno)); goto error2; } } /* First size and stereo then samplerate */ if (ossdev->format>=0) { val = ossdev->format; rc = ioctl(fd, SNDCTL_DSP_SETFMT, &ossdev->format); if (rc != 0 || val != ossdev->format) { TRACE("Can't set format to %d (returned %d)\n", val, ossdev->format); if (strict_format) goto error; } } if (ossdev->stereo>=0) { val = ossdev->stereo; rc = ioctl(fd, SNDCTL_DSP_STEREO, &ossdev->stereo); if (rc != 0 || val != ossdev->stereo) { TRACE("Can't set stereo to %u (returned %d)\n", val, ossdev->stereo); if (strict_format) goto error; } } if (ossdev->sample_rate>=0) { val = ossdev->sample_rate; rc = ioctl(fd, SNDCTL_DSP_SPEED, &ossdev->sample_rate); if (rc != 0 || !NEAR_MATCH(val, ossdev->sample_rate)) { TRACE("Can't set sample_rate to %u (returned %d)\n", val, ossdev->sample_rate); if (strict_format) goto error; } } ossdev->fd = fd; if (ossdev->bTriggerSupport) { int trigger; rc = ioctl(fd, SNDCTL_DSP_GETTRIGGER, &trigger); if (rc != 0) { ERR("ioctl(%s, SNDCTL_DSP_GETTRIGGER) failed (%s)\n", ossdev->dev_name, strerror(errno)); goto error; } ossdev->bOutputEnabled = ((trigger & PCM_ENABLE_OUTPUT) == PCM_ENABLE_OUTPUT); ossdev->bInputEnabled = ((trigger & PCM_ENABLE_INPUT) == PCM_ENABLE_INPUT); } else { ossdev->bOutputEnabled = TRUE; /* OSS enables by default */ ossdev->bInputEnabled = TRUE; /* OSS enables by default */ } return MMSYSERR_NOERROR;error: close(fd); return WAVERR_BADFORMAT; error2: close(fd); return MMSYSERR_ERROR;}/****************************************************************** * OSS_OpenDevice * * since OSS has poor capabilities in full duplex, we try here to let a program * open the device for both waveout and wavein streams... * this is hackish, but it's the way OSS interface is done... */static DWORD OSS_OpenDevice(OSS_DEVICE* ossdev, unsigned req_access, int* frag, int strict_format, int sample_rate, int stereo, int fmt){ DWORD ret; TRACE("(%p,%u,%p,%d,%d,%d,%x)\n",ossdev,req_access,frag,strict_format,sample_rate,stereo,fmt); if (ossdev->full_duplex && (req_access == O_RDONLY || req_access == O_WRONLY)) req_access = O_RDWR; /* FIXME: this should be protected, and it also contains a race with OSS_CloseDevice */ if (ossdev->open_count == 0) { if (access(ossdev->dev_name, 0) != 0) return MMSYSERR_NODRIVER; ossdev->audio_fragment = (frag) ? *frag : 0; ossdev->sample_rate = sample_rate; ossdev->stereo = stereo; ossdev->format = fmt; ossdev->open_access = req_access; ossdev->owner_tid = GetCurrentThreadId();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -