Linux实现音频录放

一、原理简述

在Linux下,录音——从dsp设备读取数据,放音——向dsp设备写入数据。

开发板采用声卡UDA1341实现音频编解码,完成A/D和D/A转换,芯片UDA1341与CPU的连接图如下:

为了实现全双工,数据传输需要使用两个DMA通道。以音频回放为例,数据传输先由内部总线送到内存, 然后传到DMA控制器通道1,再通过IIS控制器写入IIS总线并传输给音频芯片,通道2用来录音。

二、WAV文件

WAVE是录音时用的标准的Windows文件格式,文件的扩展名为“wav”,数据本身的格式为PCM或压缩型,属于无损音乐格式的一种,符合RIFF(Resource Interchange File Format)规范。所有的WAV都有一个文件头,这个文件头音频流的编码参数。数据块的记录方式是小端(little-endian)字节顺序,标志符并不是字符串而是单独的符号

采样率为8kHz,量化位数为16,单通道的record.wav文件为例,文件前三行信息如下:

第一列表示地址,一行表示16个字节。

0x52,0x49,0x46,0x46   //“RIFF”4个字符对应的ASCII码值

0x57,0x41,0x56,0x45,0x66,0x6D,0x74,0x20  //“WAVEfmt ”各个字符对应的ASCII码值

0x10,0x00,0x00,0x00,0x01,0x00,0x01,0x00//sizeof(PCMWAVEFORMAT)4Byte,格式类别2B,通道数1B(声道)

0x40,0x1F,0x00,0x00,0x80,0x3E,0x00,0x00, //采样频率0X0001F40= 8kHZ(8000Hz)4B,0x00003E80B/s=16kB/s 4B

0x02,0x00,0x10,0x00,0x64,0x61,0x74,0x61};//数据调整数0x0002(1*16/8)2B,即一个采样点所占的字节数,样本数据位数0x10(16位)2B,即一个采样点所表示的位数    “data”4B

地址000014H~000017H的值:2400 01 00即十六进制0x00010024,对应十进制65572(65536+36),表示从0x08开始到文件尾的总字节数;

地址000028H~00002BH的值:0080 0C 00即十六进制0x00010000,对应十进制65536,表示采样数据总数。

录音测试命令:cat  /dev/sound/dsp > audio.wav

使用cat命令生成的audio.wav是一个PCM纯音频文件:

通过添加wav文件头,可以生成一个标准的wav音频文件:

三、Linux下实现录放音

注意驱动程序中的默认参数,应用程序可以通过ioctl()函数设置新的取值。

Open()函数:采用何种模式对声卡进行操作也必须在打开设备时指定,对于不支持全双工的声卡来说,应该使用只读或者只写的方式打开,只有那些支持全双工的声卡,才能以读写的方式打开,这还依赖于驱动程序的具体实现。open_mode有三种选择:O_RDONLY,O_WRONLY和O_RDWR,分别表示只读、只写和读写。OSS建议尽量使用只读或只写,只有在全双工的情况下(即录音和放音同时)才使用读写模式。Linux
允许应用程序多次打开或者关闭与声卡对应的设备文件,从而能够很方便地在放音状态和录音状态之间进行切换。

注意,用户始终要读/写一个完整的采样。例如一个16-bit的立体声模式下,每个采样有4个字节,所以应用程序每次必须读/写4的倍数个字节。

源码如下:

#include<unistd.h>
#include<fcntl.h>
#include<sys/types.h>
#include<sys/ioctl.h>
#include<stdlib.h>
#include<stdio.h>
#include<linux/soundcard.h>

#define LENGTH 3    // 存储秒数
#define RATE 44100  // 采样频率
#define SIZE 16     // 量化位数
#define CHANNELS 2  // 声道数目
/* 用于保存数字音频数据的内存缓冲区*/
unsigned charbuf[LENGTH*RATE*SIZE*CHANNELS/8];
int main()
{
                intfd;     // 声音设备的文件描述符
                intarg;    // 用于ioctl调用的参数
                intstatus; // 系统调用的返回值
                /*打开声音设备 */
                fd= open("/dev/sound/dsp", O_RDONLY);
                if(fd< 0)
                {
                        perror("openof /dev/sound/dsp failed");
                        exit(1);
                }

                /*设置采样时的量化位数 */
                arg= SIZE;
                status= ioctl(fd, SOUND_PCM_WRITE_BITS, &arg);
                if(status== -1)
                        perror("SOUND_PCM_WRITE_BITSioctl failed");
                if(arg!= SIZE)
                        perror("unableto set sample size");
                /*设置采样时的声道数目 */
                arg= CHANNELS;
                status= ioctl(fd, SNDCTL_DSP_STEREO, &arg);
                if(status== -1)
                        perror("SNDCTL_DSP_STEREOioctl failed");
                if(arg!= CHANNELS)
                        perror("unableto set number of channels");
                /*设置采样时的采样频率 */
                arg= RATE;
                status= ioctl(fd, SNDCTL_DSP_SPEED, &arg);
                if(status== -1)
                        perror("SNDCTL_DSP_SPEEDioctl failed");
                if(arg!= RATE)
                        perror("unableto set rate");

                printf("Saysomething:\n");
                status= read(fd, buf, sizeof(buf)); //recording
                if(status!= sizeof(buf))
                        perror("readwrong number of bytes");
                printf("Yousaid:\n");
                close(fd);
                fd= open("/dev/sound/dsp", O_WRONLY);
                if(fd< 0)
                {
                        perror("openof /dev/sound/dsp failed");
                        exit(1);
                } 

                /*设置采样时的量化位数 */
                arg= SIZE;
                status= ioctl(fd, SOUND_PCM_WRITE_BITS, &arg);
                if(status == -1)
                       perror("SOUND_PCM_WRITE_BITS ioctl failed");
                if(arg != SIZE)
                        perror("unable toset sample size");
                /*设置采样时的声道数目 */
                arg = CHANNELS;
                status = ioctl(fd, SNDCTL_DSP_STEREO,&arg);
                if(status == -1)
                       perror("SNDCTL_DSP_STEREO ioctl failed");
                if(arg != CHANNELS)
                        perror("unable toset number of channels");
                /* 设置采样时的采样频率 */
                arg = RATE;
                status = ioctl(fd,SNDCTL_DSP_SPEED, &arg);
                if(status == -1)
                       perror("SNDCTL_DSP_SPEED ioctl failed");
                if(arg != RATE)
                        perror("unable toset rate");

                status= write(fd, buf, sizeof(buf)); //playing
                if(status!= sizeof(buf))
                        perror("wrotewrong number of bytes");
                close(fd);
                return0;
}
 

程序中的注意点是open()函数的参数设置,之前采用参数O_RDWR,结果放音时总是出现错误,具体出错原因可能与驱动程序的设置有关。本设计中,正确的设置是当recording时,选择O_RDONLY,当playing时,选择O_WRONLY。

Linux实现音频录放

时间: 2024-10-09 06:31:54

Linux实现音频录放的相关文章

Linux ALSA 音频系统:物理链路篇

1. Overview 硬件平台及软件版本: Kernel - 3.4.5 SoC - Samsung exynos CODEC - WM8994 Machine - goni_wm8994 Userspace - tinyalsa Linux ALSA 音频系统架构大致如下: +--------+ +--------+ +--------+ |tinyplay| |tinycap | |tinymix | +--------+ +--------+ +--------+ | ^ ^ V | V

基于Linux ALSA音频驱动的wav文件解析及播放程序 2012

本设计思路:先打开一个普通wav音频文件,从定义的文件头前面的44个字节中,取出文件头的定义消息,置于一个文件头的结构体中.然后打开alsa音频驱动,从文件头结构体取出采样精度,声道数,采样频率三个重要参数,利用alsa音频驱动的API设置好参数,最后打开wav文件,定位到数据区,把音频数据依次写到音频驱动中去,开始播放,当写入完成后,退出写入的循环. 注意:本设计需要alsa的libasound-dev的库,编译链接时需要连接 —lasound. #include<stdio.h>#incl

S3C2440 IIS操作 uda134x录放音

IIS(Inter-IC Sound)由飞利浦公司开发,是一种常用的音频设备接口,主要用于CD.MD.MP3等设备. s3c2440一共有5个引脚用于IIS:IISDO.IISDI.IISSCLK.IISLRCK和CDCLK.前两个引脚用于数字音频信号的输出和输入,另外三个引脚都与音频信号的频率有关,可 见要用好IIS,就要把信号频率设置正确,IIS只负责数字音频信号的传输,而要真正实现音频信号的放.录,还需要额外的处理芯片(在这里,我们使用的是UDA1341). IISSCLK为串行时钟,这条

嵌入式驱动开发之---Linux ALSA音频驱动(一)

本文的部分内容参考来自DroidPhone的博客(http://blog.csdn.net/droidphone/article/details/6271122),关于ALSA写得很不错的文章,只是少了实例.本文就是结合实例来分析ALSA音频驱动. 开发环境:ubuntu10.04 目标板:linux-2.6.37 (通过命令uname -r 查看linux内核版信息) 编译器:arm-none-linux-gnueabi- (none 代表编译器的制作者,比如:fsl代表飞思卡尔,内核里面谈E

Linux ALSA 音频系统:逻辑设备篇

6. 声卡和 PCM 设备的建立过程 前面几章分析了 Codec.Platform.Machine 驱动的组成部分及其注册过程,这三者都是物理设备相关的,大家应该对音频物理链路有了一定的认知.接着分析音频驱动的中间层,由于这些并不是真正的物理设备,故我们称之为逻辑设备. PCM 逻辑设备,我们又习惯称之为 PCM 中间层或 pcm native,起着承上启下的作用:往上是与用户态接口的交互,实现音频数据在用户态和内核态之间的拷贝:往下是触发 codec.platform.machine 的操作函

Linux日知录(常用问题笔记)

http://blog.csdn.net/yizhu2000/article/details/70688420)序言 日有一知,当有一录,自09年来,工作所需,接触开源平台,对Linux常有涉猎,其间问题,记录在案,虽为敝帚,不敢自珍,所记条目,并未严格整理,但愿于后来者有所裨益. 1)常用 查看某个命令的路径 which:查看某个命令的路径,该命令在PATH变量配置的路径中寻找命令,并给出第一个查询结果返回 查看用户信息的几种方法 finger id groups 删除目录 果目录为空,可以用

Linux 对音频万能处理的命令——SOX

what's the SOX SoX(即 Sound eXchange)是一个跨平台(Windows,Linux,MacOS 等)的命令行实用程序,可以将各种格式的音频文件转换为需要的其他格式.SoX 还可以对输入的音频文件应用各种效果,也支持在大多数平台上播放和录制音频文件. Windows下载链接:https://sourceforge.net/projects/sox/files/sox/ SoX  可以读取和写入常见格式的音频文件,并在此过程中选择性的加入一些声音效果.可以组合多个输入源

Linux下音频编程-输出音频文件

程序实现了在Linux下播放Ok.wav的功能.程序首先调用fstat函数获得文件相关信息(主要是文件大小信息).通过malloc函数分配指定的内存空间,并将online.wav读入内存:然后,打开声卡设备文件,设置声卡参数:再调用write函数完成文件的播放. 简要的实例,代码如下: #include<unistd.h> #include<fcntl.h> #include<sys/types.h> #include<sys/stat.h> #includ

linux学习2--目录结构

根据FHS(http://www.pathname.com/fhs/)的官方文件指出, 他们的主要目的是希望让使用者可以了解到已安装软件通常放置于那个目录下, 所以他们希望独立的软件开发商.操作系统制作者.以及想解到已安装软件通常放置于那个目录下, 所以他们希望独立的软件开发商.操作系统制作者.以及想要维护系统的用户,都能够遵循FHS 的标准. 事实上,FHS 是根据过去的经验一直再持续的改版的,FHS 依据文件系统使用的频繁与否与是否允许使用者随意更动, 而将目录定义成为四种交互作用的形态,用