📄 alsa.c
字号:
/*mediastreamer2 library - modular sound and video processing and streamingCopyright (C) 2006 Simon MORLAT (simon.morlat@linphone.org)This program is free software; you can redistribute it and/ormodify it under the terms of the GNU General Public Licenseas published by the Free Software Foundation; either version 2of the License, or (at your option) any later version.This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See theGNU General Public License for more details.You should have received a copy of the GNU General Public Licensealong with this program; if not, write to the Free SoftwareFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.*/#include <alsa/asoundlib.h>#include "mediastreamer2/msfilter.h"#include "mediastreamer2/mssndcard.h"/*uncomment the following line if you have problems with an alsa driverhaving sound quality trouble:*//*#define EPIPE_BUGFIX 1*/static MSSndCard * alsa_card_new(int id);static MSSndCard *alsa_card_duplicate(MSSndCard *obj);static MSFilter * ms_alsa_read_new(const char *dev);static MSFilter * ms_alsa_write_new(const char *dev);struct _AlsaData{ char *pcmdev; char *mixdev;};typedef struct _AlsaData AlsaData;static int alsa_set_params(snd_pcm_t *pcm_handle, int rw, int bits, int stereo, int rate){ snd_pcm_hw_params_t *hwparams=NULL; snd_pcm_sw_params_t *swparams=NULL; int dir; uint exact_uvalue; unsigned long exact_ulvalue; int channels; int periods=8; int periodsize=256; int err; int format; /* Allocate the snd_pcm_hw_params_t structure on the stack. */ snd_pcm_hw_params_alloca(&hwparams); /* Init hwparams with full configuration space */ if (snd_pcm_hw_params_any(pcm_handle, hwparams) < 0) { ms_warning("alsa_set_params: Cannot configure this PCM device.\n"); return -1; } if (snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0) { ms_warning("alsa_set_params: Error setting access.\n"); return -1; } /* Set sample format */ format=SND_PCM_FORMAT_S16; if (snd_pcm_hw_params_set_format(pcm_handle, hwparams, format) < 0) { ms_warning("alsa_set_params: Error setting format.\n"); return -1; } /* Set number of channels */ if (stereo) channels=2; else channels=1; if (snd_pcm_hw_params_set_channels(pcm_handle, hwparams, channels) < 0) { ms_warning("alsa_set_params: Error setting channels.\n"); return -1; } /* Set sample rate. If the exact rate is not supported */ /* by the hardware, use nearest possible rate. */ exact_uvalue=rate; dir=0; if ((err=snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &exact_uvalue, &dir))<0){ ms_warning("alsa_set_params: Error setting rate to %i:%s",rate,snd_strerror(err)); return -1; } if (dir != 0) { ms_warning("alsa_set_params: The rate %d Hz is not supported by your hardware.\n " "==> Using %d Hz instead.\n", rate, exact_uvalue); } /* choose greater period size when rate is high */ periodsize=periodsize*(rate/8000); /* Set buffer size (in frames). The resulting latency is given by */ /* latency = periodsize * periods / (rate * bytes_per_frame) */ /* set period size */ exact_ulvalue=periodsize; dir=0; if (snd_pcm_hw_params_set_period_size_near(pcm_handle, hwparams, &exact_ulvalue, &dir) < 0) { ms_warning("alsa_set_params: Error setting period size.\n"); return -1; } if (dir != 0) { ms_warning("alsa_set_params: The period size %d is not supported by your hardware.\n " "==> Using %d instead.\n", periodsize, (int)exact_ulvalue); } periodsize=exact_ulvalue; /* Set number of periods. Periods used to be called fragments. */ exact_uvalue=periods; dir=0; if (snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &exact_uvalue, &dir) < 0) { ms_warning("alsa_set_params: Error setting periods.\n"); return -1; } if (dir != 0) { ms_warning("alsa_set_params: The number of periods %d is not supported by your hardware.\n " "==> Using %d instead.\n", periods, exact_uvalue); } /* Apply HW parameter settings to */ /* PCM device and prepare device */ if ((err=snd_pcm_hw_params(pcm_handle, hwparams)) < 0) { ms_warning("alsa_set_params: Error setting HW params:%s",snd_strerror(err)); return -1; } /*prepare sw params */ if (rw){ snd_pcm_sw_params_alloca(&swparams); snd_pcm_sw_params_current(pcm_handle, swparams); if ((err=snd_pcm_sw_params_set_start_threshold(pcm_handle, swparams,periodsize*2 ))<0){ ms_warning("alsa_set_params: Error setting start threshold:%s",snd_strerror(err)); return -1; } if ((err=snd_pcm_sw_params(pcm_handle, swparams))<0){ ms_warning("alsa_set_params: Error setting SW params:%s",snd_strerror(err)); return -1; } } return 0; }#ifdef EPIPE_BUGFIXstatic void alsa_fill_w (snd_pcm_t *pcm_handle){ snd_pcm_hw_params_t *hwparams=NULL; int channels; snd_pcm_uframes_t buffer_size; int buffer_size_bytes; void *buffer; /* Allocate the snd_pcm_hw_params_t structure on the stack. */ snd_pcm_hw_params_alloca(&hwparams); snd_pcm_hw_params_current(pcm_handle, hwparams); /* get channels */ snd_pcm_hw_params_get_channels (hwparams, &channels); /* get buffer size */ snd_pcm_hw_params_get_buffer_size (hwparams, &buffer_size); /* fill half */ buffer_size /= 2; /* allocate buffer assuming 2 bytes per sample */ buffer_size_bytes = buffer_size * channels * 2; buffer = alloca (buffer_size_bytes); memset (buffer, 0, buffer_size_bytes); /* write data */ snd_pcm_writei(pcm_handle, buffer, buffer_size);}#endifstatic snd_pcm_t * alsa_open_r(const char *pcmdev,int bits,int stereo,int rate){ snd_pcm_t *pcm_handle; int err; ms_message("alsa_open_r: opening %s at %iHz, bits=%i, stereo=%i",pcmdev,rate,bits,stereo); if (snd_pcm_open(&pcm_handle, pcmdev,SND_PCM_STREAM_CAPTURE,SND_PCM_NONBLOCK) < 0) { ms_warning("alsa_open_r: Error opening PCM device %s\n",pcmdev ); return NULL; } if (alsa_set_params(pcm_handle,0,bits,stereo,rate)<0){ snd_pcm_close(pcm_handle); return NULL; } err=snd_pcm_start(pcm_handle); if (err<0){ ms_warning("snd_pcm_start() failed: %s", snd_strerror(err)); } return pcm_handle;}static snd_pcm_t * alsa_open_w(const char *pcmdev,int bits,int stereo,int rate){ snd_pcm_t *pcm_handle; if (snd_pcm_open(&pcm_handle, pcmdev,SND_PCM_STREAM_PLAYBACK,SND_PCM_NONBLOCK) < 0) { ms_warning("alsa_open_w: Error opening PCM device %s\n",pcmdev ); return NULL; } if (alsa_set_params(pcm_handle,1,bits,stereo,rate)<0){ snd_pcm_close(pcm_handle); return NULL; } return pcm_handle;}static int alsa_read(snd_pcm_t *handle,unsigned char *buf,int nsamples){ int err; err=snd_pcm_readi(handle,buf,nsamples); if (err<0) { if (err==-EPIPE){ snd_pcm_prepare(handle); err=snd_pcm_readi(handle,buf,nsamples); if (err<0) ms_warning("alsa_read: snd_pcm_readi() failed:%s.",snd_strerror(err)); }else if (err!=-EWOULDBLOCK){ ms_warning("alsa_read: snd_pcm_readi() failed:%s.",snd_strerror(err)); } }else if (err==0){ ms_warning("alsa_read: snd_pcm_readi() returned 0"); } return err;}static int alsa_write(snd_pcm_t *handle,unsigned char *buf,int nsamples){ int err; if ((err=snd_pcm_writei(handle,buf,nsamples))<0){ if (err==-EPIPE){ snd_pcm_prepare(handle);#ifdef EPIPE_BUGFIX alsa_fill_w (handle);#endif err=snd_pcm_writei(handle,buf,nsamples); if (err<0) ms_warning("alsa_card_write: Error writing sound buffer (nsamples=%i):%s",nsamples,snd_strerror(err)); }else if (err!=-EWOULDBLOCK){ ms_warning("alsa_card_write: snd_pcm_writei() failed:%s.",snd_strerror(err)); } }else if (err!=nsamples) { ms_debug("Only %i samples written instead of %i",err,nsamples); } return err;}static snd_mixer_t *alsa_mixer_open(const char *mixdev){ snd_mixer_t *mixer=NULL; int err; err=snd_mixer_open(&mixer,0); if (err<0){ ms_warning("Could not open alsa mixer: %s",snd_strerror(err)); return NULL; } if ((err = snd_mixer_attach (mixer, mixdev)) < 0){ ms_warning("Could not attach mixer to card: %s",snd_strerror(err)); snd_mixer_close(mixer); return NULL; } if ((err = snd_mixer_selem_register (mixer, NULL, NULL)) < 0){ ms_warning("snd_mixer_selem_register: %s",snd_strerror(err)); snd_mixer_close(mixer); return NULL; } if ((err = snd_mixer_load (mixer)) < 0){ ms_warning("snd_mixer_load: %s",snd_strerror(err)); snd_mixer_close(mixer); return NULL; } return mixer;}static void alsa_mixer_close(snd_mixer_t *mix){ snd_mixer_close(mix);}typedef enum {CAPTURE, PLAYBACK, CAPTURE_SWITCH, PLAYBACK_SWITCH} MixerAction;static int get_mixer_element(snd_mixer_t *mixer,const char *name, MixerAction action){ long value=0; const char *elemname; snd_mixer_elem_t *elem; int err; long sndMixerPMin=0; long sndMixerPMax=0; long newvol=0; elem=snd_mixer_first_elem(mixer); while (elem!=NULL){ elemname=snd_mixer_selem_get_name(elem); //g_message("Found alsa mixer element %s.",elemname); if (strcmp(elemname,name)==0){ switch (action){ case CAPTURE: if (snd_mixer_selem_has_capture_volume(elem)){ snd_mixer_selem_get_capture_volume_range(elem, &sndMixerPMin, &sndMixerPMax); err=snd_mixer_selem_get_capture_volume(elem,SND_MIXER_SCHN_UNKNOWN,&newvol); newvol-=sndMixerPMin; value=(100*newvol)/(sndMixerPMax-sndMixerPMin); if (err<0) ms_warning("Could not get capture volume for %s:%s",name,snd_strerror(err)); //else ms_message("Succesfully get capture level for %s.",elemname); break; } break; case PLAYBACK: if (snd_mixer_selem_has_playback_volume(elem)){ snd_mixer_selem_get_playback_volume_range(elem, &sndMixerPMin, &sndMixerPMax); err=snd_mixer_selem_get_playback_volume(elem,SND_MIXER_SCHN_FRONT_LEFT,&newvol); newvol-=sndMixerPMin; value=(100*newvol)/(sndMixerPMax-sndMixerPMin); if (err<0) ms_warning("Could not get playback volume for %s:%s",name,snd_strerror(err)); //else g_message("Succesfully get playback level for %s.",elemname); break; } break; case CAPTURE_SWITCH: break; case PLAYBACK_SWITCH: break; } } elem=snd_mixer_elem_next(elem); } return value;}static void set_mixer_element(snd_mixer_t *mixer,const char *name, int level,MixerAction action){ const char *elemname; snd_mixer_elem_t *elem; long sndMixerPMin=0; long sndMixerPMax=0; long newvol=0; elem=snd_mixer_first_elem(mixer); while (elem!=NULL){ elemname=snd_mixer_selem_get_name(elem); //g_message("Found alsa mixer element %s.",elemname); if (strcmp(elemname,name)==0){ switch(action){ case CAPTURE: if (snd_mixer_selem_has_capture_volume(elem)){ snd_mixer_selem_get_capture_volume_range(elem, &sndMixerPMin, &sndMixerPMax); newvol=(((sndMixerPMax-sndMixerPMin)*level)/100)+sndMixerPMin; snd_mixer_selem_set_capture_volume_all(elem,newvol); //g_message("Succesfully set capture level for %s.",elemname); return; } break; case PLAYBACK: if (snd_mixer_selem_has_playback_volume(elem)){ snd_mixer_selem_get_playback_volume_range(elem, &sndMixerPMin, &sndMixerPMax); newvol=(((sndMixerPMax-sndMixerPMin)*level)/100)+sndMixerPMin; snd_mixer_selem_set_playback_volume_all(elem,newvol); //g_message("Succesfully set playback level for %s.",elemname); return; } break; case CAPTURE_SWITCH: if (snd_mixer_selem_has_capture_switch(elem)){ snd_mixer_selem_set_capture_switch_all(elem,level); //g_message("Succesfully set capture switch for %s.",elemname); } break; case PLAYBACK_SWITCH:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -