⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 audio_05.c

📁 Wine-20031016
💻 C
📖 第 1 页 / 共 4 页
字号:
/* -*- tab-width: 8; c-basic-offset: 4 -*- *//* * Sample Wine Driver for Advanced Linux Sound System (ALSA) *      Based on version 0.5 of the ALSA API * * Copyright    2002 Eric Pouech *              2002 David Hammerton * * 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 */#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#include "windef.h"#include "winbase.h"#include "wingdi.h"#include "winerror.h"#include "winuser.h"#include "mmddk.h"#include "dsound.h"#include "dsdriver.h"#include "alsa.h"#include "wine/debug.h"WINE_DEFAULT_DEBUG_CHANNEL(wave);#if defined(HAVE_ALSA) && (SND_LIB_MAJOR == 0) && (SND_LIB_MINOR == 5)#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 */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};typedef 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 */} ALSA_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 */typedef struct {    /* FIXME: this could be made a dynamically growing array (if needed) */#define ALSA_RING_BUFFER_SIZE	30    ALSA_MSG			messages[ALSA_RING_BUFFER_SIZE];    int				msg_tosave;    int				msg_toget;    HANDLE			msg_event;    CRITICAL_SECTION		msg_crst;} ALSA_MSG_RING;typedef struct {    /* Windows information */    volatile int		state;			/* one of the WINE_WS_ manifest constants */    WAVEOPENDESC		waveDesc;    WORD			wFlags;    PCMWAVEFORMAT		format;    WAVEOUTCAPSA		caps;    /* ALSA information */    snd_pcm_t*                  handle;                 /* handle to ALSA device */    DWORD			dwFragmentSize;		/* size of ALSA buffer fragment */    DWORD                       dwBufferSize;           /* size of whole ALSA 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 ALSA buffer since opening */    /* synchronization stuff */    HANDLE			hStartUpEvent;    HANDLE			hThread;    DWORD			dwThreadID;    ALSA_MSG_RING		msgRing;    /* DirectSound stuff */    void*                       mmap_buffer;    snd_pcm_mmap_control_t*     mmap_control;    unsigned                    mmap_block_size;    unsigned                    mmap_block_number;} WINE_WAVEOUT;static WINE_WAVEOUT	WOutDev   [MAX_WAVEOUTDRV];static DWORD            ALSA_WodNumDevs;static DWORD wodDsCreate(UINT wDevID, PIDSDRIVER* drv);/* 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",};/*======================================================================* *                  Low level WAVE implementation			* *======================================================================*//****************************************************************** *		ALSA_WaveInit * * Initialize internal structures from ALSA information */LONG ALSA_WaveInit(void){    snd_pcm_t*                  h;    snd_pcm_info_t              info;    snd_pcm_channel_info_t      chn_info;    TRACE("There are %d cards\n", snd_cards());    ALSA_WodNumDevs = 0;    if (snd_pcm_open(&h, 0, 0, SND_PCM_OPEN_DUPLEX|SND_PCM_OPEN_NONBLOCK))    {        ERR("Error open: %s\n", snd_strerror(errno));        return -1;    }    if (snd_pcm_info(h, &info))    {        ERR("Error info: %s\n", snd_strerror(errno));        return -1;    }    ALSA_WodNumDevs++;    TRACE("type=%u, flags=%s%s%s name=%s #pb=%d cp=%d\n",          info.type, (info.flags & SND_PCM_INFO_PLAYBACK) ? "playback " : "",          (info.flags & SND_PCM_INFO_PLAYBACK) ? "capture " : "",          (info.flags & SND_PCM_INFO_DUPLEX) ? "duplex " : "",          info.name, info.playback, info.capture);    memset(&chn_info, 0, sizeof(chn_info));    if (snd_pcm_channel_info(h, &chn_info))    {        ERR("Error chn info: %s\n", snd_strerror(errno));        return -1;    }#define X(f,s) ((chn_info.flags & (f)) ? #s " " : "")#define Y(f,s) ((chn_info.rates & (f)) ? #s " " : "")    TRACE("subdevice=%d name=%s chn=%d mode=%d\n"          "\tflags=%s%s%s%s%s%s%s%s%s%s%s\n"          "\tfmts=%u rates=%s%s%s%s%s%s%s%s%s%s%s%s%s\n"          "\trates=[%d,%d] voices=[%d,%d] buf_size=%d fg_size=[%d,%d] fg_align=%u\n",          chn_info.subdevice, chn_info.subname, chn_info.channel,          chn_info.mode,          X(SND_PCM_CHNINFO_MMAP,MMAP),          X(SND_PCM_CHNINFO_STREAM,STREAM),          X(SND_PCM_CHNINFO_BLOCK,BLOCK),          X(SND_PCM_CHNINFO_BATCH,BATCH),          X(SND_PCM_CHNINFO_INTERLEAVE,INTERLEAVE),          X(SND_PCM_CHNINFO_NONINTERLEAVE,NONINTERLEAVE),          X(SND_PCM_CHNINFO_BLOCK_TRANSFER,BLOCK_TRANSFER),          X(SND_PCM_CHNINFO_OVERRANGE,OVERRANGE),          X(SND_PCM_CHNINFO_MMAP_VALID,MMAP_VALID),          X(SND_PCM_CHNINFO_PAUSE,PAUSE),          X(SND_PCM_CHNINFO_GLOBAL_PARAMS,GLOBAL_PARAMS),          chn_info.formats,          Y(SND_PCM_RATE_CONTINUOUS,CONTINUOUS),          Y(SND_PCM_RATE_KNOT,KNOT),          Y(SND_PCM_RATE_8000,8000),          Y(SND_PCM_RATE_11025,11025),          Y(SND_PCM_RATE_16000,16000),          Y(SND_PCM_RATE_22050,22050),          Y(SND_PCM_RATE_32000,32000),          Y(SND_PCM_RATE_44100,44100),          Y(SND_PCM_RATE_48000,48000),          Y(SND_PCM_RATE_88200,88200),          Y(SND_PCM_RATE_96000,96000),          Y(SND_PCM_RATE_176400,176400),          Y(SND_PCM_RATE_192000,192000),          chn_info.min_rate, chn_info.max_rate,          chn_info.min_voices, chn_info.max_voices,          chn_info.buffer_size,          chn_info.min_fragment_size, chn_info.max_fragment_size,          chn_info.fragment_align);#undef X#undef Y    /* FIXME: use better values */    WOutDev[0].caps.wMid = 0x0002;    WOutDev[0].caps.wPid = 0x0104;    strcpy(WOutDev[0].caps.szPname, "SB16 Wave Out");    WOutDev[0].caps.vDriverVersion = 0x0100;    WOutDev[0].caps.dwFormats = 0x00000000;    WOutDev[0].caps.dwSupport = WAVECAPS_VOLUME;#define X(r,v) \    if (chn_info.rates & SND_PCM_RATE_##r) \    { \        if (chn_info.formats & SND_PCM_FMT_U8) \        { \            if (chn_info.min_voices <= 1 && 1 <= chn_info.max_voices) \                WOutDev[0].caps.dwFormats |= WAVE_FORMAT_##v##S08; \            if (chn_info.min_voices <= 2 && 2 <= chn_info.max_voices) \                WOutDev[0].caps.dwFormats |= WAVE_FORMAT_##v##S08; \        } \        if (chn_info.formats & SND_PCM_FMT_S16_LE) \        { \            if (chn_info.min_voices <= 1 && 1 <= chn_info.max_voices) \                WOutDev[0].caps.dwFormats |= WAVE_FORMAT_##v##S16; \            if (chn_info.min_voices <= 2 && 2 <= chn_info.max_voices) \                WOutDev[0].caps.dwFormats |= WAVE_FORMAT_##v##S16; \        } \    }    X(11025,1);    X(22050,2);    X(44100,4);#undef X    if (chn_info.min_voices > 1) FIXME("-");    WOutDev[0].caps.wChannels = (chn_info.max_voices >= 2) ? 2 : 1;    if (chn_info.min_voices <= 2 && 2 <= chn_info.max_voices)        WOutDev[0].caps.dwSupport |= WAVECAPS_LRVOLUME;    /* FIXME: always true ? */    WOutDev[0].caps.dwSupport |= WAVECAPS_SAMPLEACCURATE;    /* FIXME: is test sufficient ? */    if (chn_info.flags & SND_PCM_CHNINFO_MMAP)        WOutDev[0].caps.dwSupport |= WAVECAPS_DIRECTSOUND;    TRACE("Configured with dwFmts=%08lx dwSupport=%08lx\n",          WOutDev[0].caps.dwFormats, WOutDev[0].caps.dwSupport);    snd_pcm_close(h);    return 0;}/****************************************************************** *		ALSA_InitRingMessage * * Initialize the ring of messages for passing between driver's caller and playback/record * thread */static int ALSA_InitRingMessage(ALSA_MSG_RING* omr){    omr->msg_toget = 0;    omr->msg_tosave = 0;    omr->msg_event = CreateEventA(NULL, FALSE, FALSE, NULL);    memset(omr->messages, 0, sizeof(ALSA_MSG) * ALSA_RING_BUFFER_SIZE);    InitializeCriticalSection(&omr->msg_crst);    return 0;}/****************************************************************** *		ALSA_DestroyRingMessage * */static int ALSA_DestroyRingMessage(ALSA_MSG_RING* omr){    CloseHandle(omr->msg_event);    DeleteCriticalSection(&omr->msg_crst);    return 0;}/****************************************************************** *		ALSA_AddRingMessage * * Inserts a new message into the ring (should be called from DriverProc derivated routines) */static int ALSA_AddRingMessage(ALSA_MSG_RING* omr, enum win_wm_message msg, DWORD param, BOOL wait){    HANDLE	hEvent = INVALID_HANDLE_VALUE;    EnterCriticalSection(&omr->msg_crst);    if ((omr->msg_toget == ((omr->msg_tosave + 1) % ALSA_RING_BUFFER_SIZE))) /* buffer overflow ? */    {	ERR("buffer overflow !?\n");        LeaveCriticalSection(&omr->msg_crst);	return 0;    }    if (wait)    {        hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);        if (hEvent == INVALID_HANDLE_VALUE)        {            ERR("can't create event !?\n");            LeaveCriticalSection(&omr->msg_crst);            return 0;        }        if (omr->msg_toget != omr->msg_tosave && omr->messages[omr->msg_toget].msg != WINE_WM_HEADER)            FIXME("two fast messages in the queue!!!!\n");        /* fast messages have to be added at the start of the queue */        omr->msg_toget = (omr->msg_toget + ALSA_RING_BUFFER_SIZE - 1) % ALSA_RING_BUFFER_SIZE;        omr->messages[omr->msg_toget].msg = msg;        omr->messages[omr->msg_toget].param = param;        omr->messages[omr->msg_toget].hEvent = hEvent;    }    else    {        omr->messages[omr->msg_tosave].msg = msg;        omr->messages[omr->msg_tosave].param = param;        omr->messages[omr->msg_tosave].hEvent = INVALID_HANDLE_VALUE;        omr->msg_tosave = (omr->msg_tosave + 1) % ALSA_RING_BUFFER_SIZE;    }    LeaveCriticalSection(&omr->msg_crst);    /* signal a new message */    SetEvent(omr->msg_event);    if (wait)    {        /* wait for playback/record thread to have processed the message */        WaitForSingleObject(hEvent, INFINITE);        CloseHandle(hEvent);    }    return 1;}/****************************************************************** *		ALSA_RetrieveRingMessage * * Get a message from the ring. Should be called by the playback/record thread. */static int ALSA_RetrieveRingMessage(ALSA_MSG_RING* omr,                                   enum win_wm_message *msg, DWORD *param, HANDLE *hEvent){    EnterCriticalSection(&omr->msg_crst);    if (omr->msg_toget == omr->msg_tosave) /* buffer empty ? */    {        LeaveCriticalSection(&omr->msg_crst);	return 0;    }    *msg = omr->messages[omr->msg_toget].msg;    omr->messages[omr->msg_toget].msg = 0;    *param = omr->messages[omr->msg_toget].param;    *hEvent = omr->messages[omr->msg_toget].hEvent;    omr->msg_toget = (omr->msg_toget + 1) % ALSA_RING_BUFFER_SIZE;    LeaveCriticalSection(&omr->msg_crst);    return 1;}/*======================================================================* *                  Low level WAVE OUT implementation			* *======================================================================*//************************************************************************** * 			wodNotifyClient			[internal] */static DWORD wodNotifyClient(WINE_WAVEOUT* wwo, WORD wMsg, DWORD dwParam1, DWORD dwParam2){    TRACE("wMsg = 0x%04x dwParm1 = %04lX dwParam2 = %04lX\n", wMsg, dwParam1, dwParam2);    switch (wMsg) {    case WOM_OPEN:    case WOM_CLOSE:    case WOM_DONE:	if (wwo->wFlags != DCB_NULL &&	    !DriverCallback(wwo->waveDesc.dwCallback, wwo->wFlags, wwo->waveDesc.hWave,			    wMsg, wwo->waveDesc.dwInstance, dwParam1, dwParam2)) {	    WARN("can't notify client !\n");	    return MMSYSERR_ERROR;	}	break;    default:	FIXME("Unknown callback message %u\n", wMsg);        return MMSYSERR_INVALPARAM;    }    return MMSYSERR_NOERROR;}/************************************************************************** * 				wodUpdatePlayedTotal	[internal] * */static BOOL wodUpdatePlayedTotal(WINE_WAVEOUT* wwo, snd_pcm_channel_status_t* ps){    snd_pcm_channel_status_t    s;    snd_pcm_channel_status_t*   status = (ps) ? ps : &s;    if (snd_pcm_channel_status(wwo->handle, status))    {        ERR("Can't get channel status: %s\n", snd_strerror(errno));        return FALSE;    }    wwo->dwPlayedTotal = wwo->dwWrittenTotal - (wwo->dwBufferSize - status->count);    if (wwo->dwPlayedTotal != status->scount)    {        FIXME("Ooch: %u played by ALSA, %lu counted by driver\n",              status->scount, wwo->dwPlayedTotal);        if (wwo->dwPlayedTotal & 0x8000000) wwo->dwPlayedTotal = 0;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -