📄 audio.c
字号:
/* -*- tab-width: 8; c-basic-offset: 4 -*- *//* * Wine Driver for aRts Sound Server * http://www.arts-project.org * * 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) * * 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 arts we cannot stop the audio that is already in * the servers buffer, so to reduce delays during starting * and stoppping of audio streams adjust the * audio buffer size in the kde control center or in the * artsd startup script * * FIXME: * pause in waveOut does not work correctly in loop mode * * TODO: * implement wave-in support with artsc *//*#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 <fcntl.h>#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 "arts.h"#include "wine/debug.h"WINE_DEFAULT_DEBUG_CHANNEL(wave);#ifdef HAVE_ARTS#include <artsc.h>#define BUFFER_SIZE 16 * 1024#define SPACE_THRESHOLD 5 * 1024#define MAX_WAVEOUTDRV (10)/* 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 ARTS_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;} ARTS_MSG_RING;typedef struct { volatile int state; /* one of the WINE_WS_ manifest constants */ WAVEOPENDESC waveDesc; WORD wFlags; PCMWAVEFORMAT format; WAVEOUTCAPSA caps; /* arts information */ arts_stream_t play_stream; /* the stream structure we get from arts when opening a stream for playing */ DWORD dwBufferSize; /* size of whole buffer in bytes */ char* sound_buffer; long buffer_size; 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 */ 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 the audio device since opening */ /* synchronization stuff */ HANDLE hStartUpEvent; HANDLE hThread; DWORD dwThreadID; ARTS_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);/* 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 * *======================================================================*//* 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){ BYTE *d_out = (BYTE *)bufout; BYTE *d_in = (BYTE *)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 = (BYTE) ((*(d_in++) * left) / 100); *(d_out++) = (v>255) ? 255 : ((v<0) ? 0 : v); if(nChannels == 2) { v = (BYTE) ((*(d_in++) * right) / 100); *(d_out++) = (v>255) ? 255 : ((v<0) ? 0 : v); } }}/****************************************************************** * ARTS_CloseDevice * */void ARTS_CloseDevice(WINE_WAVEOUT* wwo){ arts_close_stream(wwo->play_stream); /* close the arts stream */ wwo->play_stream = (arts_stream_t*)-1; /* free up the buffer we use for volume and reset the size */ if(wwo->sound_buffer) HeapFree(GetProcessHeap(), 0, wwo->sound_buffer); wwo->buffer_size = 0;}/****************************************************************** * ARTS_Init */static int ARTS_Init(void){ return arts_init(); /* initialize arts and return errorcode */}/****************************************************************** * ARTS_WaveClose */LONG ARTS_WaveClose(void){ int iDevice; /* close all open devices */ for(iDevice = 0; iDevice < MAX_WAVEOUTDRV; iDevice++) { if(WOutDev[iDevice].play_stream != (arts_stream_t*)-1) { ARTS_CloseDevice(&WOutDev[iDevice]); } } arts_free(); /* free up arts */ return 1;}/****************************************************************** * ARTS_WaveInit * * Initialize internal structures from ARTS server info */LONG ARTS_WaveInit(void){ int i; int errorcode; TRACE("called\n"); if ((errorcode = ARTS_Init()) < 0) { ERR("arts_init() failed (%d)\n", errorcode); return -1; } /* initialize all device handles to -1 */ for (i = 0; i < MAX_WAVEOUTDRV; ++i) { WOutDev[i].play_stream = (arts_stream_t*)-1; memset(&WOutDev[i].caps, 0, sizeof(WOutDev[i].caps)); /* zero out caps values */ /* 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].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;}/****************************************************************** * ARTS_InitRingMessage * * Initialize the ring of messages for passing between driver's caller and playback/record * thread */static int ARTS_InitRingMessage(ARTS_MSG_RING* mr){ mr->msg_toget = 0; mr->msg_tosave = 0; mr->msg_event = CreateEventA(NULL, FALSE, FALSE, NULL); mr->ring_buffer_size = ARTS_RING_BUFFER_INCREMENT; mr->messages = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,mr->ring_buffer_size * sizeof(RING_MSG)); InitializeCriticalSection(&mr->msg_crst); return 0;}/****************************************************************** * ARTS_DestroyRingMessage * */static int ARTS_DestroyRingMessage(ARTS_MSG_RING* mr){ CloseHandle(mr->msg_event); HeapFree(GetProcessHeap(),0,mr->messages); DeleteCriticalSection(&mr->msg_crst); return 0;}/****************************************************************** * ARTS_AddRingMessage * * Inserts a new message into the ring (should be called from DriverProc derivated routines) */static int ARTS_AddRingMessage(ARTS_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 += ARTS_RING_BUFFER_INCREMENT; TRACE("mr->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;}/****************************************************************** * ARTS_RetrieveRingMessage * * Get a message from the ring. Should be called by the playback/record thread. */static int ARTS_RetrieveRingMessage(ARTS_MSG_RING* mr, enum win_wm_message *msg, DWORD *param, HANDLE *hEvent){ EnterCriticalSection(&mr->msg_crst); if (mr->msg_toget == mr->msg_tosave) /* buffer empty ? */ { LeaveCriticalSection(&mr->msg_crst); return 0; } *msg = mr->messages[mr->msg_toget].msg; mr->messages[mr->msg_toget].msg = 0; *param = mr->messages[mr->msg_toget].param; *hEvent = mr->messages[mr->msg_toget].hEvent; mr->msg_toget = (mr->msg_toget + 1) % mr->ring_buffer_size; LeaveCriticalSection(&mr->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, (HDRVR)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] *
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -