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

📄 alsa.c

📁 mediastreamer2是开源的网络传输媒体流的库
💻 C
📖 第 1 页 / 共 2 页
字号:
/*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"//#define THREADED_VERSION/*in case of troubles with a particular driver, try incrementing ALSA_PERIOD_SIZEto 512, 1024, 2048, 4096...then try incrementing the number of periods*/#define ALSA_PERIODS 8#define ALSA_PERIOD_SIZE 256/*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=ALSA_PERIODS;	int periodsize=ALSA_PERIOD_SIZE;	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.");		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.");		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.");		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.");		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.", 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.");		return -1;	}	if (dir != 0) {		ms_warning("alsa_set_params: The period size %d is not supported by your hardware.\n "		"==> Using %d instead.", periodsize, (int)exact_ulvalue);	}	ms_warning("alsa_set_params: periodsize:%d Using %d", 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.");		return -1;	}	ms_warning("alsa_set_params: period:%d Using %d", periods, exact_uvalue);	if (dir != 0) {		ms_warning("alsa_set_params: The number of periods %d is not supported by your hardware.\n "		"==> Using %d instead.", 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));		}		if ((err=snd_pcm_sw_params_set_stop_threshold(pcm_handle, swparams,periodsize*periods ))<0){			ms_warning("alsa_set_params: Error setting stop threshold:%s",snd_strerror(err));		}		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);#ifndef THREADED_VERSION	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",pcmdev );		return NULL;	}#else	/* want blocking mode for threaded version */	if (snd_pcm_open(&pcm_handle, pcmdev,SND_PCM_STREAM_CAPTURE,0) < 0) {		ms_warning("alsa_open_r: Error opening PCM device %s",pcmdev );		return NULL;	}#endif	{	struct timeval tv1;	struct timeval tv2;	struct timezone tz;	int diff = 0;	err = gettimeofday(&tv1, &tz);	while (1) { 		if (!(alsa_set_params(pcm_handle,0,bits,stereo,rate)<0)){			ms_message("alsa_open_r: Audio params set");			break;		}		if (!gettimeofday(&tv2, &tz) && !err) {			diff = ((tv2.tv_sec - tv1.tv_sec) * 1000000) + (tv2.tv_usec - tv1.tv_usec);		} else {			diff = -1;		}		if ((diff < 0) || (diff > 3000000)) { /* 3 secondes */			ms_error("alsa_open_r: Error setting params for more than 3 seconds");			snd_pcm_close(pcm_handle);			return NULL;		}		ms_warning("alsa_open_r: Error setting params (for %d micros)", diff);		usleep(200000);	}	}	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",pcmdev );		return NULL;	}		{	struct timeval tv1;	struct timeval tv2;	struct timezone tz;	int diff = 0;	int err;	err = gettimeofday(&tv1, &tz);	while (1) { 		if (!(alsa_set_params(pcm_handle,1,bits,stereo,rate)<0)){			ms_message("alsa_open_w: Audio params set");			break;		}		if (!gettimeofday(&tv2, &tz) && !err) {			diff = ((tv2.tv_sec - tv1.tv_sec) * 1000000) + (tv2.tv_usec - tv1.tv_usec);		} else {			diff = -1;		}		if ((diff < 0) || (diff > 3000000)) { /* 3 secondes */			ms_error("alsa_open_w: Error setting params for more than 3 seconds");			snd_pcm_close(pcm_handle);			return NULL;		}		ms_warning("alsa_open_w: Error setting params (for %d micros)", diff);		usleep(200000);	}	}	return pcm_handle;}static int alsa_can_read(snd_pcm_t *dev){	snd_pcm_sframes_t avail;	int err;	avail = snd_pcm_avail_update(dev);	if (avail < 0) {		ms_error("snd_pcm_avail_update: %s", snd_strerror(avail));	// most probably -EPIPE		/* overrun occured, snd_pcm_state() would return SND_PCM_STATE_XRUN		 FIXME: handle other error conditions*/		ms_error("*** alsa_can_read fixup, trying to recover");		snd_pcm_drain(dev); /* Ignore possible error, at least -EAGAIN.*/		err = snd_pcm_recover(dev, avail, 0);		if (err){ 			ms_error("snd_pcm_recover() failed with err %d: %s", err, snd_strerror(err));			return -1;		}		err = snd_pcm_start(dev);		if (err){ 			ms_error("snd_pcm_start() failed with err %d: %s", err, snd_strerror(err)); 			return -1; 		}		ms_message("Recovery done");	}	return avail;}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) {		ms_warning("alsa_read: snd_pcm_readi() returned %i",err);		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);		//ms_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("Successfully 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 ms_message("Successfully 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);		//ms_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);					//ms_message("Successfully 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);					//ms_message("Successfully 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);					//ms_message("Successfully set capture switch for %s.",elemname);				}				break;				case PLAYBACK_SWITCH:				if (snd_mixer_selem_has_playback_switch(elem)){					snd_mixer_selem_set_playback_switch_all(elem,level);					//ms_message("Successfully set capture switch for %s.",elemname);				}				break;			}		}		elem=snd_mixer_elem_next(elem);	}	return ;}static void alsa_card_set_level(MSSndCard *obj,MSSndCardMixerElem e,int a){		snd_mixer_t *mixer;	AlsaData *ad=(AlsaData*)obj->data;	mixer=alsa_mixer_open(ad->mixdev);	if (mixer==NULL) return ;	switch(e){		case MS_SND_CARD_MASTER:			set_mixer_element(mixer,"Master",a,PLAYBACK);		break;		case MS_SND_CARD_CAPTURE:			set_mixer_element(mixer,"Capture",a,CAPTURE);		break;		case MS_SND_CARD_PLAYBACK:			set_mixer_element(mixer,"PCM",a,PLAYBACK);		break;		default:			ms_warning("alsa_card_set_level: unsupported command.");

⌨️ 快捷键说明

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