📄 audio.c
字号:
/* -*- tab-width: 8; c-basic-offset: 4 -*- *//* * Wine Driver for NAS Network Audio System * http://radscan.com/nas.html * * Copyright 1994 Martin Ayotte * 1999 Eric Pouech (async playing in waveOut/waveIn) * 2000 Eric Pouech (loops in waveOut) * 2002 Chris Morgan (aRts version of this file) * 2002 Nicolas Escuder (NAS version of this file) * * Copyright 2002 Nicolas Escuder <n.escuder@alineanet.com> * * 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: * with nas we cannot stop the audio that is already in * the servers buffer. * * FIXME: * pause in waveOut does not work correctly in loop mode * */#include "config.h"#include <stdlib.h>#include <stdarg.h>#include <stdio.h>#include <string.h>#ifdef HAVE_UNISTD_H# include <unistd.h>#endif#ifdef HAVE_SYS_TIME_H# include <sys/time.h>#endif#include <fcntl.h>#if 0#define EMULATE_SB16#endif#define FRAG_SIZE 1024#define FRAG_COUNT 10/* avoid type conflicts */#define INT8 X_INT8#define INT16 X_INT16#define INT32 X_INT32#define BOOL X_BOOL#define BYTE X_BYTE#ifdef HAVE_AUDIO_AUDIOLIB_H#include <audio/audiolib.h>#endif#ifdef HAVE_AUDIO_SOUNDLIB_H#include <audio/soundlib.h>#endif#undef INT8#undef INT16#undef INT32#undef BOOL#undef BYTE#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 "nas.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_NASstatic AuServer *AuServ;#define MAX_WAVEOUTDRV (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 */} RING_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 NAS_RING_BUFFER_INCREMENT 64typedef struct { RING_MSG * messages; int ring_buffer_size; int msg_tosave; int msg_toget; HANDLE msg_event; CRITICAL_SECTION msg_crst;} MSG_RING;typedef struct { volatile int state; /* one of the WINE_WS_ manifest constants */ WAVEOPENDESC waveDesc; WORD wFlags; PCMWAVEFORMAT format; WAVEOUTCAPSA caps; int Id; int open; AuServer *AuServ; AuDeviceID AuDev; AuFlowID AuFlow; BOOL FlowStarted; DWORD writeBytes; DWORD freeBytes; DWORD sendBytes; DWORD BufferSize; /* size of whole buffer in bytes */ char* SoundBuffer; long BufferUsed; DWORD volume_left; /* volume control information */ DWORD volume_right; 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 PlayedTotal; /* number of bytes actually played since opening */ DWORD WrittenTotal; /* number of bytes written to the audio device since opening */ /* synchronization stuff */ HANDLE hStartUpEvent; HANDLE hThread; DWORD dwThreadID; MSG_RING msgRing;} WINE_WAVEOUT;static WINE_WAVEOUT WOutDev [MAX_WAVEOUTDRV];static DWORD wodDsCreate(UINT wDevID, PIDSDRIVER* drv);static DWORD wodDsDesc(UINT wDevID, PDSDRIVERDESC desc);static DWORD wodDsGuid(UINT wDevID, LPGUID pGuid);/* NASFUNC */static AuBool event_handler(AuServer* aud, AuEvent* ev, AuEventHandlerRec* hnd);static int nas_init(void);static int nas_end(void);static int nas_finddev(WINE_WAVEOUT* wwo);static int nas_open(WINE_WAVEOUT* wwo);static int nas_free(WINE_WAVEOUT* wwo);static int nas_close(WINE_WAVEOUT* wwo);static void buffer_resize(WINE_WAVEOUT* wwo, int len);static int nas_add_buffer(WINE_WAVEOUT* wwo);static int nas_send_buffer(WINE_WAVEOUT* wwo);/* 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",};static char *nas_elementnotify_kinds[] = { "LowWater", "HighWater", "State", "Unknown"};static char *nas_states[] = { "Stop", "Start", "Pause", "Any"};static char *nas_reasons[] = { "User", "Underrun", "Overrun", "EOF", "Watermark", "Hardware", "Any"};static char* nas_reason(unsigned int reason){ if (reason > 6) reason = 6; return nas_reasons[reason];}static char* nas_elementnotify_kind(unsigned int kind){ if (kind > 2) kind = 3; return nas_elementnotify_kinds[kind];}#if 0static const char* nas_event_type(unsigned int type){ static const char * const nas_event_types[] = { "Undefined", "Undefined", "ElementNotify", "GrabNotify", "MonitorNotify", "BucketNotify", "DeviceNotify" }; if (type > 6) type = 0; return nas_event_types[type];}#endifstatic char* nas_state(unsigned int state){ if (state > 3) state = 3; return nas_states[state];}/*======================================================================* * Low level WAVE implementation * *======================================================================*//* Volume functions derived from Alsaplayer source *//* length is the number of 16 bit samples */void volume_effect16(void *bufin, void* bufout, int length, int left, int right, int nChannels){ short *d_out = (short *)bufout; short *d_in = (short *)bufin; int i, v;/* TRACE("length == %d, nChannels == %d\n", length, nChannels);*/ if (right == -1) right = left; for(i = 0; i < length; i+=(nChannels)) { v = (int) ((*(d_in++) * left) / 100); *(d_out++) = (v>32767) ? 32767 : ((v<-32768) ? -32768 : v); if(nChannels == 2) { v = (int) ((*(d_in++) * right) / 100); *(d_out++) = (v>32767) ? 32767 : ((v<-32768) ? -32768 : v); } }}/* length is the number of 8 bit samples */void volume_effect8(void *bufin, void* bufout, int length, int left, int right, int nChannels){ char *d_out = (char *)bufout; char *d_in = (char *)bufin; int i, v;/* TRACE("length == %d, nChannels == %d\n", length, nChannels);*/ if (right == -1) right = left; for(i = 0; i < length; i+=(nChannels)) { v = (char) ((*(d_in++) * left) / 100); *(d_out++) = (v>255) ? 255 : ((v<0) ? 0 : v); if(nChannels == 2) { v = (char) ((*(d_in++) * right) / 100); *(d_out++) = (v>255) ? 255 : ((v<0) ? 0 : v); } }}/****************************************************************** * NAS_CloseDevice * */void NAS_CloseDevice(WINE_WAVEOUT* wwo){ TRACE("NAS_CloseDevice\n"); nas_close(wwo);}/****************************************************************** * NAS_WaveClose */LONG NAS_WaveClose(void){ nas_end(); /* free up nas server */ return 1;}/****************************************************************** * NAS_WaveInit * * Initialize internal structures from NAS server info */LONG NAS_WaveInit(void){ int i; nas_init(); /* initialize all device handles to -1 */ for (i = 0; i < MAX_WAVEOUTDRV; ++i) { memset(&WOutDev[i].caps, 0, sizeof(WOutDev[i].caps)); /* zero out caps values */ WOutDev[i].AuServ = AuServ; WOutDev[i].AuDev = AuNone; WOutDev[i].Id = i; /* 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[i].caps.wMid = 0x0002; WOutDev[i].caps.wPid = 0x0104; strcpy(WOutDev[i].caps.szPname, "SB16 Wave Out");#else WOutDev[i].caps.wMid = 0x00FF; /* Manufac ID */ WOutDev[i].caps.wPid = 0x0001; /* Product ID */ /* strcpy(WOutDev[i].caps.szPname, "OpenSoundSystem WAVOUT Driver");*/ strcpy(WOutDev[i].caps.szPname, "CS4236/37/38");#endif WOutDev[i].AuFlow = 0; WOutDev[i].caps.vDriverVersion = 0x0100; WOutDev[i].caps.dwFormats = 0x00000000; WOutDev[i].caps.dwSupport = WAVECAPS_VOLUME; WOutDev[i].caps.wChannels = 2; WOutDev[i].caps.dwSupport |= WAVECAPS_LRVOLUME; WOutDev[i].caps.dwFormats |= WAVE_FORMAT_4M08; WOutDev[i].caps.dwFormats |= WAVE_FORMAT_4S08; WOutDev[i].caps.dwFormats |= WAVE_FORMAT_4S16; WOutDev[i].caps.dwFormats |= WAVE_FORMAT_4M16; WOutDev[i].caps.dwFormats |= WAVE_FORMAT_2M08; WOutDev[i].caps.dwFormats |= WAVE_FORMAT_2S08; WOutDev[i].caps.dwFormats |= WAVE_FORMAT_2M16; WOutDev[i].caps.dwFormats |= WAVE_FORMAT_2S16; WOutDev[i].caps.dwFormats |= WAVE_FORMAT_1M08; WOutDev[i].caps.dwFormats |= WAVE_FORMAT_1S08; WOutDev[i].caps.dwFormats |= WAVE_FORMAT_1M16; WOutDev[i].caps.dwFormats |= WAVE_FORMAT_1S16; } return 0;}/****************************************************************** * NAS_InitRingMessage * * Initialize the ring of messages for passing between driver's caller and playback/record * thread */static int NAS_InitRingMessage(MSG_RING* mr){ mr->msg_toget = 0; mr->msg_tosave = 0; mr->msg_event = CreateEventA(NULL, FALSE, FALSE, NULL); mr->ring_buffer_size = NAS_RING_BUFFER_INCREMENT; mr->messages = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,mr->ring_buffer_size * sizeof(RING_MSG)); InitializeCriticalSection(&mr->msg_crst); return 0;}/****************************************************************** * NAS_DestroyRingMessage * */static int NAS_DestroyRingMessage(MSG_RING* mr){ CloseHandle(mr->msg_event); HeapFree(GetProcessHeap(),0,mr->messages); DeleteCriticalSection(&mr->msg_crst); return 0;}/****************************************************************** * NAS_AddRingMessage * * Inserts a new message into the ring (should be called from DriverProc derivated routines) */static int NAS_AddRingMessage(MSG_RING* mr, enum win_wm_message msg, DWORD param, BOOL wait){ HANDLE hEvent = INVALID_HANDLE_VALUE; EnterCriticalSection(&mr->msg_crst); if ((mr->msg_toget == ((mr->msg_tosave + 1) % mr->ring_buffer_size))) { mr->ring_buffer_size += NAS_RING_BUFFER_INCREMENT; TRACE("omr->ring_buffer_size=%d\n",mr->ring_buffer_size); mr->messages = HeapReAlloc(GetProcessHeap(),0,mr->messages, mr->ring_buffer_size * sizeof(RING_MSG)); } if (wait) { hEvent = CreateEventA(NULL, FALSE, FALSE, NULL); if (hEvent == INVALID_HANDLE_VALUE) { ERR("can't create event !?\n"); LeaveCriticalSection(&mr->msg_crst); return 0; } if (mr->msg_toget != mr->msg_tosave && mr->messages[mr->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 */ mr->msg_toget = (mr->msg_toget + mr->ring_buffer_size - 1) % mr->ring_buffer_size; mr->messages[mr->msg_toget].msg = msg; mr->messages[mr->msg_toget].param = param; mr->messages[mr->msg_toget].hEvent = hEvent; } else { mr->messages[mr->msg_tosave].msg = msg; mr->messages[mr->msg_tosave].param = param; mr->messages[mr->msg_tosave].hEvent = INVALID_HANDLE_VALUE; mr->msg_tosave = (mr->msg_tosave + 1) % mr->ring_buffer_size; } LeaveCriticalSection(&mr->msg_crst); SetEvent(mr->msg_event); /* signal a new message */ if (wait) { /* wait for playback/record thread to have processed the message */ WaitForSingleObject(hEvent, INFINITE); CloseHandle(hEvent); } return 1;}/****************************************************************** * NAS_RetrieveRingMessage * * Get a message from the ring. Should be called by the playback/record thread. */static int NAS_RetrieveRingMessage(MSG_RING* mr, enum win_wm_message *msg, DWORD *param, HANDLE *hEvent){ EnterCriticalSection(&mr->msg_crst);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -