ubunt14.04下使用多线程操作录音和播放

今天终于完成一个测试程序,开始感觉特别简单,但做的时候发现并不是想象的那么容易

需求:在开发板平台下利用2个线程交替执行,一个负责录音,一个负责播放

            我下面的代码是在PC机测试通过,如果需要在开发板中使用,只需要修改其中的参数

            但结果是可以测试的,但其中有些问题,会导致效率很低,但这是测试程序,只要可以

            用,而且必须加快项目的进度。想到要将主要精力放到项目代码的编写和优化上面。

            我下定决心以后要多写博客,原来也参加了很多的比赛也拿了很多奖品,但发现水平还

           是不高,后来看到一些特别好的博客,上面的方法真的值得我们学习。

#define ALSA_PCM_NEW_HW_PARAMS_API

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <string.h>
#include <alsa/asoundlib.h>

#define THRNUM        2   

static pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

int  flag = 1;//此变量是切换录音和播放的关键

char *buffer;//定义全局的char指针,用来存放录音数据[在这里申请空间],播放的时候打印出数据,且释放char指针

/*录音函数*/

void arecord(void)
{
    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;

    rc=snd_pcm_open(&handle,"default",SND_PCM_STREAM_CAPTURE,0);
    if (rc < 0) {
        fprintf(stderr,"unable to open pcm device\n");
        exit(1);
    }
    snd_pcm_hw_params_alloca(&params);
    snd_pcm_hw_params_any(handle,params);
    snd_pcm_hw_params_set_access(handle,params,SND_PCM_ACCESS_RW_INTERLEAVED);
    snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S16_LE);
    snd_pcm_hw_params_set_channels(handle,params,2);
    val = 44100;
    snd_pcm_hw_params_set_rate_near(handle,params,&val,&dir);
    frames = 32;
    snd_pcm_hw_params_set_period_size_near(handle,params,&frames,&dir);
    rc = snd_pcm_hw_params(handle,params);
    if (rc < 0) {
        fprintf(stderr,"unable to set hw parameters\n");
        exit(1);
    }
    snd_pcm_hw_params_get_period_size(params,&frames,&dir);
    size = frames*4;
    //printf("size = %d frames = %d\n",size,(int)frames);
    buffer = (char *)malloc(size);

    snd_pcm_hw_params_get_period_time(params,&val,&dir);
    loops = 1000000/val;
    while (loops > 0) {
        loops--;
        //录音只需要将声卡中的数据读到内存中就可以了
        rc = snd_pcm_readi(handle,buffer,frames);
        //printf("rc === %d frames == %d\n",rc,(int)frames);
        if (rc == -EPIPE) {//在录音例子中,如果应用程序读取数据不够快,
                           //循环缓存区将会被新的数据覆盖。这种数据的丢失被称为overrun.
            fprintf(stderr,"overrun\n");
            snd_pcm_prepare(handle);
        } else if (rc < 0) {
            fprintf(stderr,"error form read\n");
        } else if (rc != (int)frames) {
            fprintf(stderr,"short read rc = %d frames = %d\n",rc,(int)frames);
        }
    }
    printf("录音数据%s\n",buffer);
    //free(buffer);
    snd_pcm_drain(handle);
    snd_pcm_close(handle);
}
/*播放*/
void aplay(void)
{
    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;
    
    if (buffer != NULL)
    {
        printf("播放数据%s\n",buffer);
    }
    //打开声卡设备
    rc=snd_pcm_open(&handle,"default",SND_PCM_STREAM_PLAYBACK,0);
    if (rc < 0) {
        fprintf(stderr,"unable to open pcm device\n");
        exit(1);
    }
    //在栈上分配snd_pcm_hw_params_t
    snd_pcm_hw_params_alloca(&params);
    //初始化params
    snd_pcm_hw_params_any(handle,params);
    //设置为交错访问[记录完帧1的左声道数据和右声道数据,再记录帧2的左声道数据和右声道数据,依次继续]
    snd_pcm_hw_params_set_access(handle,params,SND_PCM_ACCESS_RW_INTERLEAVED);
    //设置数据格式 主要控制输入的音频数据的类型,无符号,有符号,小端,大端,位数
    snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S16_LE);
    //设置通道数 双通道[立体声]
    snd_pcm_hw_params_set_channels(handle,params,2);
    val = 44100;
    //设置采样率
    snd_pcm_hw_params_set_rate_near(handle,params,&val,&dir);
    frames = 32;
    //
    snd_pcm_hw_params_set_period_size_near(handle,params,&frames,&dir);
    //设置params
    rc = snd_pcm_hw_params(handle,params);
    if (rc < 0) {
        fprintf(stderr,"unable to set hw parameters\n");
        exit(1);
    }
    snd_pcm_hw_params_get_period_size(params,&frames,&dir);
    size = frames*4;

    snd_pcm_hw_params_get_period_time(params,&val,&dir);
    loops = 1000000/val;
    while (loops > 0) {
        loops--;
        rc = snd_pcm_writei(handle,buffer,frames);
        if (rc == -EPIPE) {
            //
            fprintf(stderr,"underrun\n");
            snd_pcm_prepare(handle);
        } else if (rc < 0) {
            fprintf(stderr,"error form read\n");
        } else if (rc != (int)frames) {
            fprintf(stderr,"short read rc = %d frames = %d\n",rc,(int)frames);
        }
    }
    snd_pcm_drain(handle);
    snd_pcm_close(handle);
    free(buffer);
}

void *thr_func1(void *p)
{

    while(1)
    {
        //加锁
        pthread_mutex_lock(&mut);
        while(flag != 1)
            pthread_cond_wait(&cond,&mut);
        //这里来调用录音函数
        printf("录音开始\n");
        arecord();
        printf("录音结束\n");
        printf("##################\n");
        flag = 0;
        pthread_cond_broadcast(&cond);
        pthread_mutex_unlock(&mut);
    }
    pthread_exit(NULL);
}


void *thr_func2(void *p)
{

    while(1)
    {
        pthread_mutex_lock(&mut);
        while(flag != 0)
            pthread_cond_wait(&cond,&mut);
        //这里要调用播放函数
        printf("播放开始\n");
        aplay();
        printf("播放结束\n");
        flag = 1;
        pthread_cond_broadcast(&cond);
        pthread_mutex_unlock(&mut);
    }
    pthread_exit(NULL);
}

int main()
{
    int i,err;
    void *data = NULL;
    pthread_t tid[THRNUM];

    //for(i = 0 ; i < THRNUM ; i++)
    {
        err = pthread_create(tid+0,NULL,thr_func1,data);
        if(err)
        {
            fprintf(stderr,"pthread_create():%s\n",strerror(err));
            exit(1);
        }
        err = pthread_create(tid+1,NULL,thr_func2,data);
    }

    
    alarm(20);

    for(i = 0 ; i < THRNUM ; i++)
        pthread_join(tid[i],NULL);
    

    exit(0);

}


最后说下其实这个程序特别简单,高手千万别吐,希望和大家一起进步,一起学习。

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值