android audio/linux alsa音频-应用层基础
发布日期:2021-09-16 16:46:42 浏览次数:3 分类:技术文章

本文共 16523 字,大约阅读时间需要 55 分钟。

  1. snd_pcm_open

1. 参数和返回值

 

int snd_pcm_open(snd_pcm_t **pcmp, const char *name, snd_pcm_stream_t stream, int mode)

 

pcmp 打开的pcm句柄

name 要打开的pcm设备名字,默认default,或者从asound.conf或者asoundrc里面选择所要打开的设备

stream SND_PCM_STREAM_PLAYBACK 或 SND_PCM_STREAM_CAPTURE,分别表示播放和录音的PCM流

mode 打开pcm句柄时的一些附加参数 SND_PCM_NONBLOCK 非阻塞打开(默认阻塞打开), SND_PCM_ASYNC 异步模式打开

返回值 0 表示打开成功,负数表示失败,对应错误码

2. 调用流程

 

snd_pcm_open 函数位于alsa-lib库中,编译生成的动态链接库为libasound.so,进入alsa-lib中进行分析,snd_pcm_open代码如下:

 

int snd_pcm_open(snd_pcm_t **pcmp, const char *name,

snd_pcm_stream_t stream, int mode)

{

snd_config_t *top;

int err;

err = snd_config_update_ref(&top);

err = snd_pcm_open_noupdate(pcmp, top, name, stream, mode, 0);

snd_config_unref(top);

return err;

}

 

主要调用两个函数:

 

snd_config_update_ref

这个函数作用是检查alsa配置文件是否发生了变化,包括文件名字和各个配置文件的内容是否修改,如果有修改,就重新加载配置文件树,刷新全局变量snd_config并增加一个snd_config的引用计数,调用snd_config_update_ref传入的top就是指向snd_config的指针,这个top当作参数传入snd_pcm_open_noupdate函数中。snd_config_unref接下来会减去snd_config的引用计数。

首先创建顶层配置节点,然后打开/usr/share/alsa/alsa.conf,加载文件内容到顶层配置节点上,然后遍历所有的hooks,调用snd_config_hooks加载所有hooks,并调用相关的hooks函数打开对应的plugin动态库。

下一篇将分析 snd_pcm_open_noupdate 函数

 

  1. 格式说明

下面的程序打印出各种格式:

#include 
#include
int main() { int val; printf("ALSA library version: %s\n", SND_LIB_VERSION_STR); //ALSA版本 printf("\nPCM stream types:\n"); for (val = 0; val <= SND_PCM_STREAM_LAST; val++) printf(" %s\n", snd_pcm_stream_name((snd_pcm_stream_t)val)); printf("\nPCM access types:\n"); for (val = 0; val <= SND_PCM_ACCESS_LAST; val++) { printf(" %s\n", snd_pcm_access_name((snd_pcm_access_t)val)); } printf("\nPCM formats:\n"); for (val = 0; val <= SND_PCM_FORMAT_LAST; val++) { if (snd_pcm_format_name((snd_pcm_format_t)val)!= NULL) { printf(" %s (%s)\n", snd_pcm_format_name((snd_pcm_format_t)val), snd_pcm_format_description( (snd_pcm_format_t)val)); } } printf("\nPCM subformats:\n"); for (val = 0; val <= SND_PCM_SUBFORMAT_LAST;val++) { printf(" %s (%s)\n", snd_pcm_subformat_name(( snd_pcm_subformat_t)val), snd_pcm_subformat_description(( snd_pcm_subformat_t)val)); } printf("\nPCM states:\n"); for (val = 0; val <= SND_PCM_STATE_LAST; val++) printf(" %s\n", snd_pcm_state_name((snd_pcm_state_t)val)); return 0;}

程序运行结果打印:

ALSA library version: 1.1.0PCM stream types: PLAYBACK CAPTUREPCM access types: MMAP_INTERLEAVED MMAP_NONINTERLEAVED MMAP_COMPLEX RW_INTERLEAVED RW_NONINTERLEAVEDPCM formats: S8 (Signed 8 bit) U8 (Unsigned 8 bit) S16_LE (Signed 16 bit Little Endian) S16_BE (Signed 16 bit Big Endian) U16_LE (Unsigned 16 bit Little Endian) U16_BE (Unsigned 16 bit Big Endian) S24_LE (Signed 24 bit Little Endian) S24_BE (Signed 24 bit Big Endian) U24_LE (Unsigned 24 bit Little Endian) U24_BE (Unsigned 24 bit Big Endian) S32_LE (Signed 32 bit Little Endian) S32_BE (Signed 32 bit Big Endian) U32_LE (Unsigned 32 bit Little Endian) U32_BE (Unsigned 32 bit Big Endian) FLOAT_LE (Float 32 bit Little Endian) FLOAT_BE (Float 32 bit Big Endian) FLOAT64_LE (Float 64 bit Little Endian) FLOAT64_BE (Float 64 bit Big Endian) IEC958_SUBFRAME_LE (IEC-958 Little Endian) IEC958_SUBFRAME_BE (IEC-958 Big Endian) MU_LAW (Mu-Law) A_LAW (A-Law) IMA_ADPCM (Ima-ADPCM) MPEG (MPEG) GSM (GSM) SPECIAL (Special) S24_3LE (Signed 24 bit Little Endian in 3bytes) S24_3BE (Signed 24 bit Big Endian in 3bytes) U24_3LE (Unsigned 24 bit Little Endian in 3bytes) U24_3BE (Unsigned 24 bit Big Endian in 3bytes) S20_3LE (Signed 20 bit Little Endian in 3bytes) S20_3BE (Signed 20 bit Big Endian in 3bytes) U20_3LE (Unsigned 20 bit Little Endian in 3bytes) U20_3BE (Unsigned 20 bit Big Endian in 3bytes) S18_3LE (Signed 18 bit Little Endian in 3bytes) S18_3BE (Signed 18 bit Big Endian in 3bytes) U18_3LE (Unsigned 18 bit Little Endian in 3bytes) U18_3BE (Unsigned 18 bit Big Endian in 3bytes) G723_24 (G.723 (ADPCM) 24 kbit/s, 8 samples in 3 bytes) G723_24_1B (G.723 (ADPCM) 24 kbit/s, 1 sample in 1 byte) G723_40 (G.723 (ADPCM) 40 kbit/s, 8 samples in 3 bytes) G723_40_1B (G.723 (ADPCM) 40 kbit/s, 1 sample in 1 byte) DSD_U8 (Direct Stream Digital, 1-byte (x8), oldest bit in MSB) DSD_U16_LE (Direct Stream Digital, 2-byte (x16), little endian, oldest bits in MSB) DSD_U32_LE (Direct Stream Digital, 4-byte (x32), little endian, oldest bits in MSB) DSD_U16_BE (Direct Stream Digital, 2-byte (x16), big endian, oldest bits in MSB) DSD_U32_BE (Direct Stream Digital, 4-byte (x32), big endian, oldest bits in MSB)PCM subformats: STD (Standard)PCM states: OPEN SETUP PREPARED RUNNING XRUN DRAINING PAUSED SUSPENDED DISCONNECTED
  1. 读写操作和XRUN

当调用snd_pcm_writei来发送数据时,可能会发生underrun;

当调用snd_pcm_readi来读取数据时,可能发生overrun。

两者都返回值-EPIPE。返回值为EPIPE表明发生了underrun/overrun,使得PCM音频流进入到XRUN状态并停止处理数据。从该状态中恢复过来的标准方法是调用snd_pcm_prepare()函数,把PCM流置于PREPARED状态,这样下次我们向该PCM流中数据时,它就能重新开始处理数据。

ssize_t CKVM_PCM_Record_Handle::pcm_read(u_char *data, size_t rcount){	ssize_t r;	size_t result = 0;	size_t count = rcount;	if (count != chunk_size) {		count = chunk_size;	}	while (count > 0 && !in_aborting) {		r = readi_func(handle, data, count);				if (r == -EAGAIN || (r >= 0 && (size_t)r < count)) {			 snd_pcm_wait(handle, 100);		} else if (r == -EPIPE) {			xrun();		} else if (r == -ESTRPIPE) {			suspend();		} else if (r < 0) {			error(_("read error: %s"), snd_strerror(r));	 		prg_exit(EXIT_FAILURE);		}				if (r > 0) {			result += r;			count -= r;			data += r * bits_per_frame / 8;		}	}	return rcount;}ssize_t CKVM_PCM_Play_Handle::pcm_write(u_char *data, size_t count){	ssize_t r;	ssize_t result = 0;	if (count < chunk_size) {		snd_pcm_format_set_silence(hwparams.format, data + count * bits_per_frame / 8, (chunk_size - count) * hwparams.channels);		count = chunk_size;	}	//data = remap_data(data, count);	while (count > 0 && !in_aborting) {		r = writei_func(handle, data, count);		if (r == -EAGAIN || (r >= 0 && (size_t)r < count)) {			snd_pcm_wait(handle, 100);		} else if (r == -EPIPE) {			xrun();		} else if (r == -ESTRPIPE) {			suspend();		} else if (r < 0) {			error(_("write error: %s"), snd_strerror(r));			prg_exit(EXIT_FAILURE);		}		if (r > 0) {//			if (vumeter)//				compute_max_peak(data, r * hwparams.channels);			result += r;			count -= r;			data += r * bits_per_frame / 8;		}	}	return result;}/* I/O error handler */void CKVM_PCM_Handle::xrun(void){	snd_pcm_status_t *status;	int res;		snd_pcm_status_alloca(&status);	if ((res = snd_pcm_status(handle, status))<0) {		error(_("status error: %s"), snd_strerror(res));		prg_exit(EXIT_FAILURE);	}	if (snd_pcm_status_get_state(status) == SND_PCM_STATE_XRUN) {		if (fatal_errors) {			error(_("fatal %s: %s"),					stream == SND_PCM_STREAM_PLAYBACK ? _("underrun") : _("overrun"),					snd_strerror(res));			prg_exit(EXIT_FAILURE);		}		if (monotonic) {#ifdef HAVE_CLOCK_GETTIME			struct timespec now, diff, tstamp;			clock_gettime(CLOCK_MONOTONIC, &now);			snd_pcm_status_get_trigger_htstamp(status, &tstamp);			timermsub(&now, &tstamp, &diff);			fprintf(stderr, _("%s!!! (at least %.3f ms long)\n"),				stream == SND_PCM_STREAM_PLAYBACK ? _("underrun") : _("overrun"),				diff.tv_sec * 1000 + diff.tv_nsec / 1000000.0);#else			fprintf(stderr, "%s !!!\n", _("underrun"));#endif		} else {			struct timeval now, diff, tstamp;			gettimeofday(&now, 0);			snd_pcm_status_get_trigger_tstamp(status, &tstamp);			timersub(&now, &tstamp, &diff);			fprintf(stderr, _("%s!!! (at least %.3f ms long)\n"),				stream == SND_PCM_STREAM_PLAYBACK ? _("underrun") : _("overrun"),				diff.tv_sec * 1000 + diff.tv_usec / 1000.0);		}		if (verbose) {			fprintf(stderr, _("Status:\n"));			snd_pcm_status_dump(status, log);		}		if ((res = snd_pcm_prepare(handle))<0) {			error(_("xrun: prepare error: %s"), snd_strerror(res));			prg_exit(EXIT_FAILURE);		}		return;		/* ok, data should be accepted again */	} if (snd_pcm_status_get_state(status) == SND_PCM_STATE_DRAINING) {		if (verbose) {			fprintf(stderr, _("Status(DRAINING):\n"));			snd_pcm_status_dump(status, log);		}		if (stream == SND_PCM_STREAM_CAPTURE) {			fprintf(stderr, _("capture stream format change? attempting recover...\n"));			if ((res = snd_pcm_prepare(handle))<0) {				error(_("xrun(DRAINING): prepare error: %s"), snd_strerror(res));				prg_exit(EXIT_FAILURE);			}			return;		}	}	if (verbose) {		fprintf(stderr, _("Status(R/W):\n"));		snd_pcm_status_dump(status, log);	}	error(_("read/write error, state = %s"), snd_pcm_state_name(snd_pcm_status_get_state(status)));	prg_exit(EXIT_FAILURE);}/* I/O suspend handler */void CKVM_PCM_Handle::suspend(void){	int res;	if (!quiet_mode)		fprintf(stderr, _("Suspended. Trying resume. ")); fflush(stderr);	while ((res = snd_pcm_resume(handle)) == -EAGAIN)		sleep(1);	/* wait until suspend flag is released */	if (res < 0) {		if (!quiet_mode)			fprintf(stderr, _("Failed. Restarting stream. ")); fflush(stderr);		if ((res = snd_pcm_prepare(handle)) < 0) {			error(_("suspend: prepare error: %s"), snd_strerror(res));			prg_exit(EXIT_FAILURE);		}	}	if (!quiet_mode)		fprintf(stderr, _("Done.\n"));}

录音代码示例

/*This example reads from the default PCM deviceand writes to standard output for 5 seconds of data.*//* Use the newer ALSA API */#define ALSA_PCM_NEW_HW_PARAMS_API#include 
int main() {long loops;int rc;int size;snd_pcm_t *handle;snd_pcm_hw_params_t *params;unsigned int val;int dir;snd_pcm_uframes_t frames;char *buffer;/* Open PCM device for recording (capture). */rc = snd_pcm_open(&handle, "default", SND_PCM_STREAM_CAPTURE, 0);if (rc < 0) { fprintf(stderr, "unable to open pcm device: %s\n", snd_strerror(rc)); exit(1);}/* Allocate a hardware parameters object. */snd_pcm_hw_params_alloca(&params);/* Fill it in with default values. */snd_pcm_hw_params_any(handle, params);/* Set the desired hardware parameters. *//* Interleaved mode */snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);/* Signed 16-bit little-endian format */snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE);/* Two channels (stereo) */snd_pcm_hw_params_set_channels(handle, params, 2);/* 44100 bits/second sampling rate (CD quality) */val = 44100;snd_pcm_hw_params_set_rate_near(handle, params, &val, &dir);/* Set period size to 32 frames. */frames = 32;snd_pcm_hw_params_set_period_size_near(handle, params, &frames, &dir);/* Write the parameters to the driver */rc = snd_pcm_hw_params(handle, params);if (rc < 0) { fprintf(stderr, "unable to set hw parameters: %s\n", snd_strerror(rc)); exit(1);}/* Use a buffer large enough to hold one period */snd_pcm_hw_params_get_period_size(params, &frames, &dir);size = frames * 4; /* 2 bytes/sample, 2 channels 每帧大小是4字节*/buffer = (char *) malloc(size);/* We want to loop for 5 seconds val的值即为录制32帧数据需要的时间*/snd_pcm_hw_params_get_period_time(params, &val, &dir); loops = 5000000 / val; //录制5秒while (loops > 0) { loops--; rc = snd_pcm_readi(handle, buffer, frames); if (rc == -EPIPE) { /* EPIPE means overrun */ fprintf(stderr, "overrun occurred\n"); snd_pcm_prepare(handle); } else if (rc < 0) { fprintf(stderr, "error from read: %s\n", snd_strerror(rc)); } else if (rc != (int)frames) { fprintf(stderr, "short read, read %d frames\n", rc); } rc = write(1, buffer, size); if (rc != size) fprintf(stderr, "short write: wrote %d bytes\n", rc);}snd_pcm_drain(handle);snd_pcm_close(handle);free(buffer);return 0;} 

测试:./rec > rec.raw //录音保存到文件rec.raw

播放rec.raw: aplay -f cd rec.raw

 

播放代码示例

/*This example reads standard from input and writesto the default PCM device for 5 seconds of data.*//* Use the newer ALSA API */#define ALSA_PCM_NEW_HW_PARAMS_API#include 
int main() { long loops; int rc; int size; snd_pcm_t *handle; snd_pcm_hw_params_t *params; unsigned int val; int dir; snd_pcm_uframes_t frames; char *buffer; /* Open PCM device for playback. */ rc = snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0); if (rc < 0) { fprintf(stderr,"unable to open pcm device: %s\n",snd_strerror(rc)); exit(1); } /* Allocate a hardware parameters object. */ snd_pcm_hw_params_alloca(&params); /* Fill it in with default values. */ snd_pcm_hw_params_any(handle, params); /* Set the desired hardware parameters. */ /* Interleaved mode */ snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED); /* Signed 16-bit little-endian format */ snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE); /* Two channels (stereo) */ snd_pcm_hw_params_set_channels(handle, params, 2); /* 44100 bits/second sampling rate (CD quality) */ val = 44100; snd_pcm_hw_params_set_rate_near(handle, params, &val, &dir); /* Set period size to 32 frames. */ frames = 32; snd_pcm_hw_params_set_period_size_near(handle, params, &frames, &dir); /* Write the parameters to the driver */ rc = snd_pcm_hw_params(handle, params); if (rc < 0) { fprintf(stderr, "unable to set hw parameters: %s\n", snd_strerror(rc)); exit(1); } /* Use a buffer large enough to hold one period */ snd_pcm_hw_params_get_period_size(params, &frames, &dir); size = frames * 4; /* 2 bytes/sample, 2 channels */ buffer = (char *) malloc(size); /* We want to loop for 5 seconds */ snd_pcm_hw_params_get_period_time(params,&val, &dir); /* 5 seconds in microseconds divided by * period time */ loops = 5000000 / val; while (loops > 0) //循环录音 5 s { loops--; rc = read(0, buffer, size); if (rc == 0) //没有读取到数据 { fprintf(stderr, "end of file on input\n"); break; } else if (rc != size)//实际读取 的数据 小于 要读取的数据 { fprintf(stderr,"short read: read %d bytes\n", rc); } rc = snd_pcm_writei(handle, buffer, frames);//写入声卡 (放音) if (rc == -EPIPE) { /* EPIPE means underrun */ fprintf(stderr, "underrun occurred\n"); snd_pcm_prepare(handle); } else if (rc < 0) { fprintf(stderr,"error from writei: %s\n",snd_strerror(rc)); } else if (rc != (int)frames) { fprintf(stderr,"short write, write %d frames\n", rc); } } snd_pcm_drain(handle); //snd_pcm_drain把所有挂起没有传输完的声音样本传输完全,最后关闭该音频流,释放之前动态分配的缓冲区,退出。 snd_pcm_close(handle); free(buffer); return 0;}
  1. 重定向log

static snd_output_t *log;

int err = snd_output_stdio_attach(&log, stderr, 0); //把stderr的信息重定向到 log中去。
//之后通过 snd_pcm_hw_params_dump(params, log);  snd_pcm_dump(handle, log); snd_pcm_status_dump(status, log); //等把sound的配置和运行信息导出到log中去。
最后关闭 snd_output_close(log);

  1. 获取声卡信息

Alsa/pcm.h中:

/** * \defgroup PCM_Info Stream Information * \ingroup PCM * See the \ref pcm page for more details. * \{ */size_t snd_pcm_info_sizeof(void);/** \hideinitializer * \brief allocate an invalid #snd_pcm_info_t using standard alloca * \param ptr returned pointer */#define snd_pcm_info_alloca(ptr) __snd_alloca(ptr, snd_pcm_info)int snd_pcm_info_malloc(snd_pcm_info_t **ptr);void snd_pcm_info_free(snd_pcm_info_t *obj);void snd_pcm_info_copy(snd_pcm_info_t *dst, const snd_pcm_info_t *src);unsigned int snd_pcm_info_get_device(const snd_pcm_info_t *obj);unsigned int snd_pcm_info_get_subdevice(const snd_pcm_info_t *obj);snd_pcm_stream_t snd_pcm_info_get_stream(const snd_pcm_info_t *obj);int snd_pcm_info_get_card(const snd_pcm_info_t *obj);const char *snd_pcm_info_get_id(const snd_pcm_info_t *obj);const char *snd_pcm_info_get_name(const snd_pcm_info_t *obj);const char *snd_pcm_info_get_subdevice_name(const snd_pcm_info_t *obj);snd_pcm_class_t snd_pcm_info_get_class(const snd_pcm_info_t *obj);snd_pcm_subclass_t snd_pcm_info_get_subclass(const snd_pcm_info_t *obj);unsigned int snd_pcm_info_get_subdevices_count(const snd_pcm_info_t *obj);unsigned int snd_pcm_info_get_subdevices_avail(const snd_pcm_info_t *obj);snd_pcm_sync_id_t snd_pcm_info_get_sync(const snd_pcm_info_t *obj);void snd_pcm_info_set_device(snd_pcm_info_t *obj, unsigned int val);void snd_pcm_info_set_subdevice(snd_pcm_info_t *obj, unsigned int val);void snd_pcm_info_set_stream(snd_pcm_info_t *obj, snd_pcm_stream_t val);

 

 

 

转载地址:https://blog.csdn.net/xgbing/article/details/102812160 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:BCM43438 android6.0移植
下一篇:TI Am335LCD驱动

发表评论

最新留言

哈哈,博客排版真的漂亮呢~
[***.90.31.176]2024年02月29日 00时25分21秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章

matlab 曲线拟合求导,如何对matlab cftool拟合得到的cfit函数求导数 2019-04-21
matlab 50hzquchu,新手求消除50HZ工频干扰陷波滤波器源程序 2019-04-21
laravel没有route.php,Laravel中的RouteCollection.php中的NotFoundHttpException 2019-04-21
php服务端开启socket,php socket服务端能不能在网页端开启?而不是只能用CLI模式开启... 2019-04-21
php不需要也能输出,php 如何只输出最后生成的那个值?? 2019-04-21
php正则过滤sql关键字,使用正则表达式屏蔽关键字的方法 2019-04-21
php取整v,php取整方式分享 2019-04-21
php写模糊搜索api接口,php通过sphinxapi接口实现全文搜索 2019-04-21
oracle安装出现2932,【案例】Oracle报错ORA-19815 fast_recovery_area无剩余空间解决办法... 2019-04-21
rac数据库下oracle打小补丁,Oracle 11g RAC 环境打PSU补丁的详细步骤 2019-04-21
form表单属性名相同java_form表单提交时候有多个相同name 的input如何处理? 2019-04-21
java图片加气泡文字_图片加气泡文字 2019-04-21
java总结i o流_14.java总结I/O流 2019-04-21
java和历转为西历_日期转西暦,和暦 2019-04-21
java 远程 yarn jar_再论Yarn Client和Yarn cluster 2019-04-21
java单元测试断言_单元测试+断言 2019-04-21
java 创建压缩包_用Java创建ZIP压缩文件 2019-04-21
java typedarray_TintTypedArray.java 2019-04-21
java字符字面量_java – 字符串字面量的行为是令人困惑的 2019-04-21
php判断数组的值是否为空,PHP判断数组是否为空的常用方法(五种方法) 2019-04-21