📄 audio.c
字号:
/* -*- tab-width: 8; c-basic-offset: 4 -*- *//* * Sample Wine Driver for Advanced Linux Sound System (ALSA) * Based on version <final> of the ALSA API * * Copyright 2002 Eric Pouech * 2002 Marco Pietrobono * * 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 "wine/port.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 <limits.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/library.h"#include "wine/debug.h"WINE_DEFAULT_DEBUG_CHANNEL(wave);#define FAKE_CHARPTR(s) ((char *)(unsigned long)(s))#if defined(HAVE_ALSA) && ((SND_LIB_MAJOR == 0 && SND_LIB_MINOR >= 9) || SND_LIB_MAJOR >= 1)/* internal ALSALIB functions */snd_pcm_uframes_t _snd_pcm_mmap_hw_ptr(snd_pcm_t *pcm);#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 */#define ALSA_RING_BUFFER_INCREMENT 64typedef struct { ALSA_MSG * messages; int 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 (ALSA 0.9/1.x uses two different devices for playback/capture) */ char * device; snd_pcm_t* p_handle; /* handle to ALSA playback device */ snd_pcm_t* c_handle; /* handle to ALSA capture device */ snd_pcm_hw_params_t * hw_params; /* ALSA Hw params */ snd_ctl_t * ctl; /* control handle for the playback volume */ snd_ctl_elem_id_t * playback_eid; /* element id of the playback volume control */ snd_ctl_elem_value_t * playback_evalue; /* element value of the playback volume control */ snd_ctl_elem_info_t * playback_einfo; /* element info of the playback volume control */ snd_pcm_sframes_t (*write)(snd_pcm_t *, const void *, snd_pcm_uframes_t ); struct pollfd *ufds; int count; 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 */ LPWAVEHDR lpLoopPtr; /* pointer of first buffer in loop, if any */ DWORD dwLoops; /* private copy of loop counter */ DWORD dwPlayedTotal; /* synchronization stuff */ HANDLE hStartUpEvent; HANDLE hThread; DWORD dwThreadID; ALSA_MSG_RING msgRing; /* DirectSound stuff */ DSDRIVERDESC ds_desc; GUID ds_guid;} WINE_WAVEOUT;static WINE_WAVEOUT WOutDev [MAX_WAVEOUTDRV];static DWORD ALSA_WodNumDevs;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 */#if 0static const char *wodPlayerCmdString[] = { "WINE_WM_PAUSING", "WINE_WM_RESTARTING", "WINE_WM_RESETTING", "WINE_WM_HEADER", "WINE_WM_UPDATE", "WINE_WM_BREAKLOOP", "WINE_WM_CLOSING",};#endif/*======================================================================* * Low level WAVE implementation * *======================================================================*//************************************************************************** * ALSA_InitializeVolumeCtl [internal] * * used to initialize the PCM Volume Control */static int ALSA_InitializeVolumeCtl(WINE_WAVEOUT * wwo){ snd_ctl_t * ctl = NULL; snd_ctl_card_info_t * cardinfo; snd_ctl_elem_list_t * elemlist; snd_ctl_elem_id_t * e_id; snd_ctl_elem_info_t * einfo; snd_hctl_t * hctl = NULL; snd_hctl_elem_t * elem; int nCtrls; int i; snd_ctl_card_info_alloca(&cardinfo); memset(cardinfo,0,snd_ctl_card_info_sizeof()); snd_ctl_elem_list_alloca(&elemlist); memset(elemlist,0,snd_ctl_elem_list_sizeof()); snd_ctl_elem_id_alloca(&e_id); memset(e_id,0,snd_ctl_elem_id_sizeof()); snd_ctl_elem_info_alloca(&einfo); memset(einfo,0,snd_ctl_elem_info_sizeof());#define EXIT_ON_ERROR(f,txt) do \{ \ int err; \ if ( (err = (f) ) < 0) \ { \ ERR(txt ": %s\n", snd_strerror(err)); \ if (hctl) \ snd_hctl_close(hctl); \ if (ctl) \ snd_ctl_close(ctl); \ return -1; \ } \} while(0) EXIT_ON_ERROR( snd_ctl_open(&ctl,"hw",0) , "ctl open failed" ); EXIT_ON_ERROR( snd_ctl_card_info(ctl, cardinfo), "card info failed"); EXIT_ON_ERROR( snd_ctl_elem_list(ctl, elemlist), "elem list failed"); nCtrls = snd_ctl_elem_list_get_count(elemlist); EXIT_ON_ERROR( snd_hctl_open(&hctl,"hw",0), "hctl open failed"); EXIT_ON_ERROR( snd_hctl_load(hctl), "hctl load failed" ); elem=snd_hctl_first_elem(hctl); for ( i= 0; i<nCtrls; i++) { memset(e_id,0,snd_ctl_elem_id_sizeof()); snd_hctl_elem_get_id(elem,e_id);/* TRACE("ctl: #%d '%s'%d\n", snd_ctl_elem_id_get_numid(e_id), snd_ctl_elem_id_get_name(e_id), snd_ctl_elem_id_get_index(e_id));*/ if ( !strcmp("PCM Playback Volume", snd_ctl_elem_id_get_name(e_id)) ) { EXIT_ON_ERROR( snd_hctl_elem_info(elem,einfo), "hctl elem info failed" ); /* few sanity checks... you'll never know... */ if ( snd_ctl_elem_info_get_type(einfo) != SND_CTL_ELEM_TYPE_INTEGER ) WARN("playback volume control is not an integer\n"); if ( !snd_ctl_elem_info_is_readable(einfo) ) WARN("playback volume control is readable\n"); if ( !snd_ctl_elem_info_is_writable(einfo) ) WARN("playback volume control is readable\n"); TRACE(" ctrl range: min=%ld max=%ld step=%ld\n", snd_ctl_elem_info_get_min(einfo), snd_ctl_elem_info_get_max(einfo), snd_ctl_elem_info_get_step(einfo)); EXIT_ON_ERROR( snd_ctl_elem_id_malloc(&wwo->playback_eid), "elem id malloc failed" ); EXIT_ON_ERROR( snd_ctl_elem_info_malloc(&wwo->playback_einfo), "elem info malloc failed" ); EXIT_ON_ERROR( snd_ctl_elem_value_malloc(&wwo->playback_evalue), "elem value malloc failed" ); /* ok, now we can safely save these objects for later */ snd_ctl_elem_id_copy(wwo->playback_eid, e_id); snd_ctl_elem_info_copy(wwo->playback_einfo, einfo); snd_ctl_elem_value_set_id(wwo->playback_evalue, wwo->playback_eid); wwo->ctl = ctl; } elem=snd_hctl_elem_next(elem); } snd_hctl_close(hctl);#undef EXIT_ON_ERROR return 0;}/************************************************************************** * ALSA_XRUNRecovery [internal] * * used to recovery from XRUN errors (buffer underflow/overflow) */static int ALSA_XRUNRecovery(WINE_WAVEOUT * wwo, int err){ if (err == -EPIPE) { /* under-run */ err = snd_pcm_prepare(wwo->p_handle); if (err < 0) ERR( "underrun recovery failed. prepare failed: %s\n", snd_strerror(err)); return 0; } else if (err == -ESTRPIPE) { while ((err = snd_pcm_resume(wwo->p_handle)) == -EAGAIN) sleep(1); /* wait until the suspend flag is released */ if (err < 0) { err = snd_pcm_prepare(wwo->p_handle); if (err < 0) ERR("recovery from suspend failed, prepare failed: %s\n", snd_strerror(err)); } return 0; } return err;}/************************************************************************** * ALSA_TraceParameters [internal] * * used to trace format changes, hw and sw parameters */static void ALSA_TraceParameters(snd_pcm_hw_params_t * hw_params, snd_pcm_sw_params_t * sw, int full){ snd_pcm_format_t format = snd_pcm_hw_params_get_format(hw_params); snd_pcm_access_t access = snd_pcm_hw_params_get_access(hw_params);#define X(x) ((x)? "true" : "false") if (full) TRACE("FLAGS: sampleres=%s overrng=%s pause=%s resume=%s syncstart=%s batch=%s block=%s double=%s " "halfd=%s joint=%s \n", X(snd_pcm_hw_params_can_mmap_sample_resolution(hw_params)), X(snd_pcm_hw_params_can_overrange(hw_params)), X(snd_pcm_hw_params_can_pause(hw_params)), X(snd_pcm_hw_params_can_resume(hw_params)), X(snd_pcm_hw_params_can_sync_start(hw_params)), X(snd_pcm_hw_params_is_batch(hw_params)), X(snd_pcm_hw_params_is_block_transfer(hw_params)), X(snd_pcm_hw_params_is_double(hw_params)), X(snd_pcm_hw_params_is_half_duplex(hw_params)), X(snd_pcm_hw_params_is_joint_duplex(hw_params)));#undef X if (access >= 0) TRACE("access=%s\n", snd_pcm_access_name(access)); else { snd_pcm_access_mask_t * acmask; snd_pcm_access_mask_alloca(&acmask); snd_pcm_hw_params_get_access_mask(hw_params, acmask); for ( access = SND_PCM_ACCESS_MMAP_INTERLEAVED; access <= SND_PCM_ACCESS_LAST; access++) if (snd_pcm_access_mask_test(acmask, access)) TRACE("access=%s\n", snd_pcm_access_name(access)); } if (format >= 0) { TRACE("format=%s\n", snd_pcm_format_name(format)); } else { snd_pcm_format_mask_t * fmask; snd_pcm_format_mask_alloca(&fmask); snd_pcm_hw_params_get_format_mask(hw_params, fmask); for ( format = SND_PCM_FORMAT_S8; format <= SND_PCM_FORMAT_LAST ; format++) if ( snd_pcm_format_mask_test(fmask, format) ) TRACE("format=%s\n", snd_pcm_format_name(format)); }#define X(x) do { \int n = snd_pcm_hw_params_get_##x(hw_params); \if (n<0) \ TRACE(#x "_min=%ld " #x "_max=%ld\n", \ (long int)snd_pcm_hw_params_get_##x##_min(hw_params), \ (long int)snd_pcm_hw_params_get_##x##_max(hw_params)); \else \ TRACE(#x "=%d\n", n); \} while(0) X(channels); X(buffer_size);#undef X#define X(x) do { \int n = snd_pcm_hw_params_get_##x(hw_params,0); \if (n<0) \ TRACE(#x "_min=%ld " #x "_max=%ld\n", \ (long int)snd_pcm_hw_params_get_##x##_min(hw_params,0), \ (long int)snd_pcm_hw_params_get_##x##_max(hw_params,0)); \else \ TRACE(#x "=%d\n", n); \} while(0) X(rate); X(buffer_time); X(periods); X(period_size); X(period_time); X(tick_time);#undef X if (!sw) return;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -