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 + -
显示快捷键?