audio.c
来自「Wine-20031016」· C语言 代码 · 共 2,103 行 · 第 1/5 页
C
2,103 行
/* -*- tab-width: 8; c-basic-offset: 4 -*- *//* * Wine Driver for Libaudioio * Derived from the Wine OSS Sample Driver * Copyright 1994 Martin Ayotte * 1999 Eric Pouech (async playing in waveOut/waveIn) * 2000 Eric Pouech (loops in waveOut) * 2002 Robert Lunnon (Modifications for libaudioio) * * 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 * * * Note large hacks done to effectively disable DSound altogether * Also Input is not yet working (Lots more work to be done there) * But this does make a reasonable output driver for solaris untill the arts driver comes up to speed *//* * FIXME: * pause in waveOut does not work correctly * full duplex (in/out) is not working (device is opened twice for Out * and In) (OSS is known for its poor duplex capabilities, alsa is * better) *//*#define EMULATE_SB16*/#include "config.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_LIBAUDIOIO_H#include <libaudioio.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 "wine/debug.h"WINE_DEFAULT_DEBUG_CHANNEL(wave);#ifdef HAVE_LIBAUDIOIO/* Allow 1% deviation for sample rates (some ES137x cards) */#define NEAR_MATCH(rate1,rate2) (((100*((int)(rate1)-(int)(rate2)))/(rate1))==0)#define SOUND_DEV "/dev/audio"#define DEFAULT_FRAGMENT_SIZE 4096#define MAX_WAVEOUTDRV (1)#define MAX_WAVEINDRV (1)/* 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 */#define WINE_WM_PAUSING (WM_USER + 1)#define WINE_WM_RESTARTING (WM_USER + 2)#define WINE_WM_RESETTING (WM_USER + 3)#define WINE_WM_CLOSING (WM_USER + 4)#define WINE_WM_HEADER (WM_USER + 5)#define WINE_WM_FIRST WINE_WM_PAUSING#define WINE_WM_LAST WINE_WM_HEADERtypedef struct { int msg; DWORD param;} WWO_MSG;typedef struct { int unixdev; volatile int state; /* one of the WINE_WS_ manifest constants */ DWORD dwFragmentSize; /* size of OSS buffer fragment */ WAVEOPENDESC waveDesc; WORD wFlags; PCMWAVEFORMAT format; LPWAVEHDR lpQueuePtr; /* start of queued WAVEHDRs (waiting to be notified) */ LPWAVEHDR lpPlayPtr; /* start of not yet fully played buffers */ LPWAVEHDR lpLoopPtr; /* pointer of first buffer in loop, if any */ DWORD dwLoops; /* private copy of loop counter */ DWORD dwLastFragDone; /* time in ms, when last played fragment will be actually played */ DWORD dwPlayedTotal; /* number of bytes played since opening */ /* info on current lpQueueHdr->lpWaveHdr */ DWORD dwOffCurrHdr; /* offset in lpPlayPtr->lpData for fragments */ DWORD dwRemain; /* number of bytes to write to end the current fragment */ /* synchronization stuff */ HANDLE hThread; DWORD dwThreadID; HANDLE hEvent;#define WWO_RING_BUFFER_SIZE 30 WWO_MSG messages[WWO_RING_BUFFER_SIZE]; int msg_tosave; int msg_toget; HANDLE msg_event; CRITICAL_SECTION msg_crst; WAVEOUTCAPSA caps; /* DirectSound stuff */ LPBYTE mapping; DWORD maplen;} WINE_WAVEOUT;typedef struct { int unixdev; volatile int state; DWORD dwFragmentSize; /* OpenSound '/dev/dsp' give us that size */ WAVEOPENDESC waveDesc; WORD wFlags; PCMWAVEFORMAT format; LPWAVEHDR lpQueuePtr; DWORD dwTotalRecorded; WAVEINCAPSA caps; BOOL bTriggerSupport; /* synchronization stuff */ HANDLE hThread; DWORD dwThreadID; HANDLE hEvent;} WINE_WAVEIN;static WINE_WAVEOUT WOutDev [MAX_WAVEOUTDRV];static WINE_WAVEIN WInDev [MAX_WAVEINDRV ];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);/*======================================================================* * Low level WAVE implementation * *======================================================================*/SampleSpec_t spec[2];LONG LIBAUDIOIO_WaveInit(void){ int audio; int smplrate; int samplesize = 16; int dsp_stereo = 1; int bytespersmpl; int caps; int mask; int i; int audio_fd; int mode; TRACE("Init ENTERED rate = %d\n",spec[PLAYBACK].rate); spec[RECORD].channels=spec[PLAYBACK].channels=2; spec[RECORD].max_blocks=spec[PLAYBACK].max_blocks=16; spec[RECORD].rate=spec[PLAYBACK].rate=44100; spec[RECORD].encoding=spec[PLAYBACK].encoding= ENCODE_PCM; spec[RECORD].precision=spec[PLAYBACK].precision=16 ; spec[RECORD].endian=spec[PLAYBACK].endian=ENDIAN_INTEL; spec[RECORD].disable_threads=spec[PLAYBACK].disable_threads=1; spec[RECORD].type=spec[PLAYBACK].type=TYPE_SIGNED; /* in 16 bit mode this is what typical PC hardware expects */ mode = O_WRONLY|O_NDELAY; /* start with output device */ /* initialize all device handles to -1 */ for (i = 0; i < MAX_WAVEOUTDRV; ++i) { WOutDev[i].unixdev = -1; } /* FIXME: only one device is supported */ memset(&WOutDev[0].caps, 0, sizeof(WOutDev[0].caps)); /* FIXME: some programs compare this string against the content of the registry * for MM drivers. The names have to match in order for the program to work * (e.g. MS win9x mplayer.exe) */#ifdef EMULATE_SB16 WOutDev[0].caps.wMid = 0x0002; WOutDev[0].caps.wPid = 0x0104; strcpy(WOutDev[0].caps.szPname, "SB16 Wave Out");#else WOutDev[0].caps.wMid = 0x00FF; /* Manufac ID */ WOutDev[0].caps.wPid = 0x0001; /* Product ID */ /* strcpy(WOutDev[0].caps.szPname, "OpenSoundSystem WAVOUT Driver");*/ strcpy(WOutDev[0].caps.szPname, "CS4236/37/38");#endif WOutDev[0].caps.vDriverVersion = 0x0100; WOutDev[0].caps.dwFormats = 0x00000000; WOutDev[0].caps.dwSupport = WAVECAPS_VOLUME; /* * Libaudioio works differently, you tell it what spec audio you want to write and it * Guarantees to match it by converting to the final format on the fly *So we dont need to read back and compare */ bytespersmpl = spec[PLAYBACK].precision/8; WOutDev[0].caps.wChannels = spec[PLAYBACK].channels;/* Fixme: Libaudioio 0.2 doesn't support balance yet (Libaudioio 0.3 does so this must be fixed later)*//* if (WOutDev[0].caps.wChannels > 1) WOutDev[0].caps.dwSupport |= WAVECAPS_LRVOLUME;*/ TRACE("Init sammplerate= %d\n",spec[PLAYBACK].rate); smplrate = spec[PLAYBACK].rate;/* * We have set up the data format to be 16 bit signed in intel format * For Big Endian machines libaudioio will convert the data to bigendian for us */ WOutDev[0].caps.dwFormats |= WAVE_FORMAT_4M16; if (WOutDev[0].caps.wChannels > 1) WOutDev[0].caps.dwFormats |= WAVE_FORMAT_4S16;/* Don't understand this yet, but I dont think this functionality is portable, leave here for future evaluation * if (IOCTL(audio, SNDCTL_DSP_GETCAPS, caps) == 0) { * TRACE("OSS dsp out caps=%08X\n", caps); * if ((caps & DSP_CAP_REALTIME) && !(caps & DSP_CAP_BATCH)) { * WOutDev[0].caps.dwSupport |= WAVECAPS_SAMPLEACCURATE; * } */ /* well, might as well use the DirectSound cap flag for something *//* if ((caps & DSP_CAP_TRIGGER) && (caps & DSP_CAP_MMAP) && * !(caps & DSP_CAP_BATCH)) * WOutDev[0].caps.dwSupport |= WAVECAPS_DIRECTSOUND; * } *//* * Note that libaudioio audio capture is not proven yet, in our open call * set the spec for input audio the same as for output */ TRACE("out dwFormats = %08lX, dwSupport = %08lX\n", WOutDev[0].caps.dwFormats, WOutDev[0].caps.dwSupport); for (i = 0; i < MAX_WAVEINDRV; ++i) { WInDev[i].unixdev = -1; } memset(&WInDev[0].caps, 0, sizeof(WInDev[0].caps));#ifdef EMULATE_SB16 WInDev[0].caps.wMid = 0x0002; WInDev[0].caps.wPid = 0x0004; strcpy(WInDev[0].caps.szPname, "SB16 Wave In");#else WInDev[0].caps.wMid = 0x00FF; /* Manufac ID */ WInDev[0].caps.wPid = 0x0001; /* Product ID */ strcpy(WInDev[0].caps.szPname, "OpenSoundSystem WAVIN Driver");#endif WInDev[0].caps.dwFormats = 0x00000000; WInDev[0].caps.wChannels = spec[RECORD].channels; WInDev[0].bTriggerSupport = TRUE; /* Maybe :-) */ bytespersmpl = spec[RECORD].precision/8; smplrate = spec[RECORD].rate; WInDev[0].caps.dwFormats |= WAVE_FORMAT_4M16; if (WInDev[0].caps.wChannels > 1) WInDev[0].caps.dwFormats |= WAVE_FORMAT_4S16; TRACE("in dwFormats = %08lX\n", WInDev[0].caps.dwFormats); return 0;}/************************************************************************** * LIBAUDIOIO_NotifyClient [internal] */static DWORD LIBAUDIOIO_NotifyClient(UINT wDevID, WORD wMsg, DWORD dwParam1, DWORD dwParam2){ TRACE("wDevID = %04X wMsg = 0x%04x dwParm1 = %04lX dwParam2 = %04lX\n",wDevID, wMsg, dwParam1, dwParam2); switch (wMsg) { case WOM_OPEN: case WOM_CLOSE: case WOM_DONE: if (wDevID >= MAX_WAVEOUTDRV) return MCIERR_INTERNAL; if (WOutDev[wDevID].wFlags != DCB_NULL && !DriverCallback(WOutDev[wDevID].waveDesc.dwCallback, WOutDev[wDevID].wFlags, WOutDev[wDevID].waveDesc.hWave, wMsg, WOutDev[wDevID].waveDesc.dwInstance, dwParam1, dwParam2)) { WARN("can't notify client !\n"); return MMSYSERR_NOERROR; } break; case WIM_OPEN: case WIM_CLOSE: case WIM_DATA: if (wDevID >= MAX_WAVEINDRV) return MCIERR_INTERNAL; if (WInDev[wDevID].wFlags != DCB_NULL && !DriverCallback(WInDev[wDevID].waveDesc.dwCallback, WInDev[wDevID].wFlags, WInDev[wDevID].waveDesc.hWave, wMsg, WInDev[wDevID].waveDesc.dwInstance, dwParam1, dwParam2)) { WARN("can't notify client !\n"); return MMSYSERR_NOERROR; } break; default: FIXME("Unknown CB message %u\n", wMsg); break; } return 0;}/*======================================================================* * Low level WAVE OUT implementation * *======================================================================*//************************************************************************** * wodPlayer_WriteFragments [internal] * * wodPlayer helper. Writes as many fragments as it can to unixdev. * Returns TRUE in case of buffer underrun. */static BOOL wodPlayer_WriteFragments(WINE_WAVEOUT* wwo){ LPWAVEHDR lpWaveHdr; LPBYTE lpData; int count;TRACE("wodPlayer_WriteFragments sammplerate= %d\n",spec[PLAYBACK].rate); for (;;) {/* * Libaudioio doesn't buffer the same way as linux, you can write data is any block size *And libaudioio just tracks the number of blocks in the streams queue to control latency */ if (!AudioIOCheckWriteReady()) return FALSE; /* Returns false if you have execeeded your specified latency (No space left)*/ lpWaveHdr = wwo->lpPlayPtr; if (!lpWaveHdr) { if (wwo->dwRemain > 0 && /* still data to send to complete current fragment */ wwo->dwLastFragDone && /* first fragment has been played */ AudioIOCheckUnderrun()) { /* done with all waveOutWrite()' fragments */ /* FIXME: should do better handling here */ WARN("Oooch, buffer underrun !\n"); return TRUE; /* force resetting of waveOut device */ } return FALSE; /* wait a bit */ }
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?