嵌入式linux音频编程简易录音机简单示例基于ALSA

ALSA是linux的编译框架,linux 2.6 版本发后支持
只要查看/dev/snd这个目录下面存在就可以使用

1、查看当前alsa版本
cat /proc/asound/version

alsa_sound.c

#define ALSA_PCM_NEW_HW_PARAMS_API
#include <stdio.h>
#include <stdlib.h>
#include <alsa/asoundlib.h>


struct fhead
{
    
    
    /****RIFF WAVE CHUNK*/
    unsigned char a[4];//四个字节存放'R','I','F','F'
    long int b;        //整个文件的长度-8;每个Chunk的size字段,都是表示除了本Chunk的ID和SIZE字段外的长度;
    unsigned char c[4];//四个字节存放'W','A','V','E'
    /****RIFF WAVE CHUNK*/
    /****Format CHUNK*/
    unsigned char d[4];//四个字节存放'f','m','t',''
    long int e;       //16后没有附加消息,18后有附加消息;一般为16,其他格式转来的话为18
    short int f;       //编码方式,一般为0x0001;
    short int g;       //声道数目,1单声道,2双声道;
    long int h;        //采样频率;
    long int i;        //每秒所需字节数;
    short int j;       //每个采样需要多少字节,若声道是双,则两个一起考虑;
    short int k;       //即量化位数
    /****Format CHUNK*/
    /***Data Chunk**/
    unsigned char p[4];//四个字节存放'd','a','t','a'
    long int q;        //语音数据部分长度,不包括文件头的任何部分
} wavehead; //定义WAVE文件的文件头结构体

long LENGTH = 3;   //录音时间,秒  
int RATE=9600; //采样频率  
char SIZE=16;   //量化位数  
char CHANNELS=1;   //声道数目  
int RSIZE=8;    //buf的大小,

int alsa_record(char *voicename,long t,int rate, char size,char ch_num,int bufsize)
{
    
    
	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;
    int fd_f;
    int status;
    
    LENGTH = t;   //录音时间,秒  
	RATE = rate; //采样频率  
	SIZE = size;   //量化位数  
	CHANNELS = ch_num;   //声道数目  
	RSIZE = bufsize;    //buf的大小,
	
    /*以下wave 文件头赋值*/
    wavehead.a[0] = 'R';
    wavehead.a[1] = 'I';
    wavehead.a[2] = 'F';
    wavehead.a[3] = 'F';
    wavehead.b = LENGTH * RATE * CHANNELS * SIZE / 8 - 8;
    wavehead.c[0] = 'W';
    wavehead.c[1] = 'A';
    wavehead.c[2] = 'V';
    wavehead.c[3] = 'E';
    wavehead.d[0] = 'f';
    wavehead.d[1] = 'm';
    wavehead.d[2] = 't';
    wavehead.d[3] = ' ';
    wavehead.e = 16;
    wavehead.f = 1;
    wavehead.g = CHANNELS;
    wavehead.h = RATE;
    wavehead.i = RATE * CHANNELS * SIZE / 8;
    wavehead.j = CHANNELS * SIZE / 8;
    wavehead.k = SIZE;
    wavehead.p[0] = 'd';
    wavehead.p[1] = 'a';
    wavehead.p[2] = 't';
    wavehead.p[3] = 'a';
    wavehead.q = LENGTH * RATE * CHANNELS * SIZE / 8;
    /*以上wave 文件头赋值*/


    /* 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, CHANNELS);

    /* 44100 bits/second sampling rate (CD quality) */
    val = RATE;
    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 * 2; /* 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);
    loops = 5000000 / val;




    if(( fd_f = open("voicename", O_CREAT | O_RDWR, 0777)) == -1) //创建一个wave格式语音文件
    {
    
    
        perror("cannot creat the sound file");
    }
    if((status = write(fd_f, &wavehead, sizeof(wavehead))) == -1) //写入wave文件的文件头
    {
    
    
        perror("write to sound'head wrong!!");
    }

    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);
        }

        if(write(fd_f, buffer, size) == -1)
        {
    
    
            perror("write to sound wrong!!");
        }
        if (rc != size)
            fprintf(stderr,
                    "short write: wrote %d bytes\n", rc);
    }

    snd_pcm_drain(handle);
    snd_pcm_close(handle);
    free(buffer);
    close(fd_f);

    return 0;
	return 0;
}

int alsa_play(char *wav_name,unsigned int val,char chs)
{
    
    
	 int i;
    int ret;
    int buf[128];
    int dir=0;
    char *buffer;
    int size;
    snd_pcm_uframes_t frames;
    snd_pcm_uframes_t periodsize;
    snd_pcm_t *playback_handle;//PCM设备句柄pcm.h
    snd_pcm_hw_params_t *hw_params;//硬件信息和PCM流配置
    
 
    FILE *fp = fopen(wav_name, "rb");
    if(fp == NULL)
    	return -1;
    fseek(fp, 100, SEEK_SET);

    //1. 打开PCM,最后一个参数为0意味着标准配置
    ret = snd_pcm_open(&playback_handle, "default", SND_PCM_STREAM_PLAYBACK, 0);
    if (ret < 0) {
    
    
        perror("snd_pcm_open");
        exit(1);
    }

    //2. 分配snd_pcm_hw_params_t结构体
    ret = snd_pcm_hw_params_malloc(&hw_params);
    if (ret < 0) {
    
    
        perror("snd_pcm_hw_params_malloc");
        exit(1);
    }
    //3. 初始化hw_params
    ret = snd_pcm_hw_params_any(playback_handle, hw_params);
    if (ret < 0) {
    
    
        perror("snd_pcm_hw_params_any");
        exit(1);
    }
    //4. 初始化访问权限
    ret = snd_pcm_hw_params_set_access(playback_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED);
    if (ret < 0) {
    
    
        perror("snd_pcm_hw_params_set_access");
        exit(1);
    }
    //5. 初始化采样格式SND_PCM_FORMAT_U8,8位
    ret = snd_pcm_hw_params_set_format(playback_handle, hw_params, SND_PCM_FORMAT_S16_LE);
    if (ret < 0) 
    {
    
    
        perror("snd_pcm_hw_params_set_format");
        exit(1);
    }
    //6. 设置采样率,如果硬件不支持我们设置的采样率,将使用最接近的
    //val =  441000;//,有些录音采样频率固定为8KHz
   //val = 128000;
    ret = snd_pcm_hw_params_set_rate_near(playback_handle, hw_params, &val, &dir);
    if (ret < 0) 
    {
    
    
        perror("snd_pcm_hw_params_set_rate_near");
        exit(1);
    }
    //7. 设置通道数量  
    ret = snd_pcm_hw_params_set_channels(playback_handle, hw_params, chs);
    if (ret < 0) 
    {
    
    
        perror("snd_pcm_hw_params_set_channels");
        exit(1);
    }

    /* Set period size to 32 frames. */
    frames = 32;
    periodsize = frames ;
    ret = snd_pcm_hw_params_set_buffer_size_near(playback_handle, hw_params, &periodsize);
    if (ret < 0)
    {
    
    
         printf("Unable to set buffer size %li : %s\n", frames * 2, snd_strerror(ret));

    }
    periodsize /= 2;

    ret = snd_pcm_hw_params_set_period_size_near(playback_handle, hw_params, &periodsize, 0);
    if (ret < 0)
    {
    
    
        printf("Unable to set period size %li : %s\n", periodsize,  snd_strerror(ret));
    }

    //8. 设置hw_params
    ret = snd_pcm_hw_params(playback_handle, hw_params);
    if (ret < 0) {
    
    
        perror("snd_pcm_hw_params");
        exit(1);
    }

     /* Use a buffer large enough to hold one period */
    snd_pcm_hw_params_get_period_size(hw_params, &frames, &dir);

    size = frames * 2; /* 2 bytes/sample, 2 channels */
    buffer = (char *) malloc(size);
    fprintf(stderr,
            "size = %d\n",
            size);

    while (1)
    {
    
    
        ret = fread(buffer, 1, size, fp);
        if(ret == 0)
        {
    
    
              fprintf(stderr, "end of file on input\n");
              break;
        }
        else if (ret != size)
        {
    
    
        }
        //9. 写音频数据到PCM设备
        while(ret = snd_pcm_writei(playback_handle, buffer, frames)<0)
        {
    
    
            usleep(2000);
            if (ret == -EPIPE)
            {
    
    
                  /* EPIPE means underrun */
                  fprintf(stderr, "underrun occurred\n");
                  //完成硬件参数设置,使设备准备好
                  snd_pcm_prepare(playback_handle);
            }
            else if (ret < 0)
            {
    
    
                  fprintf(stderr,
                      "error from writei: %s\n",
                      snd_strerror(ret));
            }
        }

    }
    //10. 关闭PCM设备句柄
    snd_pcm_close(playback_handle);
	return 0;
}

main.c

#include <stdio.h>
#include <stdlib.h>
#include "alsa_sound.h"

int main()
{
    
    
	printf("开始录音\n");
	alsa_record("./test.wav", 10, 9600, 16, 1, 8);
	printf("播放录音\n");
	alsa_play("test.wad",128000,1);
	return 0;
}

编译 gcc main.c alsa_sound.c -o main -lasound -lm -ldl

猜你喜欢

转载自blog.csdn.net/u010835747/article/details/108641942