Linux下使用alsa-lib库完成音频开发: 实现放音和录音(从声卡获取PCM数据保存、向声卡写PCM数据输出)

简介: Linux下使用alsa-lib库完成音频开发: 实现放音和录音(从声卡获取PCM数据保存、向声卡写PCM数据输出)

一、环境介绍

系统: 虚拟机运行ubuntu18.04 (64位)

声卡: 电脑自带声卡

二、安装alsa-lib库

参考文章: https://blog.csdn.net/xiaolong1126626497/article/details/104916277

三、参考代码:从声卡获取PCM数据,实现录音功能

下面代码在命令行通过gcc编译运行:  读取声卡数据,保存为文件,结束录音可以按下Ctrl+C即可结束。

/*
 进行音频采集,采集pcm数据并直接保存pcm数据
 音频参数: 
   声道数:   1
   采样位数:  16bit、LE格式
   采样频率:  44100Hz
运行示例:
$ gcc linux_pcm_save.c -lasound
$ ./a.out hw:0 123.pcm
*/
#include <stdio.h>
#include <stdlib.h>
#include <alsa/asoundlib.h>
#include <signal.h>
#define AudioFormat SND_PCM_FORMAT_S16_LE  //指定音频的格式,其他常用格式:SND_PCM_FORMAT_U24_LE、SND_PCM_FORMAT_U32_LE
#define AUDIO_CHANNEL_SET   1         //1单声道   2立体声
#define AUDIO_RATE_SET 44100   //音频采样率,常用的采样频率: 44100Hz 、16000HZ、8000HZ、48000HZ、22050HZ
FILE *pcm_data_file=NULL;
int run_flag=0;
void exit_sighandler(int sig)
{
  run_flag=1;
}
int main(int argc, char *argv[])
{
  int i;
  int err;
  char *buffer;
  int buffer_frames = 1024;
  unsigned int rate = AUDIO_RATE_SET;
  snd_pcm_t *capture_handle;// 一个指向PCM设备的句柄
  snd_pcm_hw_params_t *hw_params; //此结构包含有关硬件的信息,可用于指定PCM流的配置
  /*注册信号捕获退出接口*/
  signal(2,exit_sighandler);
  /*PCM的采样格式在pcm.h文件里有定义*/
  snd_pcm_format_t format=AudioFormat; // 采样位数:16bit、LE格式
  /*打开音频采集卡硬件,并判断硬件是否打开成功,若打开失败则打印出错误提示*/
  // SND_PCM_STREAM_PLAYBACK 输出流
  // SND_PCM_STREAM_CAPTURE  输入流
  if ((err = snd_pcm_open (&capture_handle, argv[1],SND_PCM_STREAM_CAPTURE,0))<0) 
  {
    printf("无法打开音频设备: %s (%s)\n",  argv[1],snd_strerror (err));
    exit(1);
  }
  printf("音频接口打开成功.\n");
  /*创建一个保存PCM数据的文件*/
  if((pcm_data_file = fopen(argv[2], "wb")) == NULL)
  {
    printf("无法创建%s音频文件.\n",argv[2]);
    exit(1);
  } 
  printf("用于录制的音频文件已打开.\n");
  /*分配硬件参数结构对象,并判断是否分配成功*/
  if((err = snd_pcm_hw_params_malloc(&hw_params)) < 0) 
  {
    printf("无法分配硬件参数结构 (%s)\n",snd_strerror(err));
    exit(1);
  }
  printf("硬件参数结构已分配成功.\n");
  /*按照默认设置对硬件对象进行设置,并判断是否设置成功*/
  if((err=snd_pcm_hw_params_any(capture_handle,hw_params)) < 0) 
  {
    printf("无法初始化硬件参数结构 (%s)\n", snd_strerror(err));
    exit(1);
  }
  printf("硬件参数结构初始化成功.\n");
  /*
    设置数据为交叉模式,并判断是否设置成功
    interleaved/non interleaved:交叉/非交叉模式。
    表示在多声道数据传输的过程中是采样交叉的模式还是非交叉的模式。
    对多声道数据,如果采样交叉模式,使用一块buffer即可,其中各声道的数据交叉传输;
    如果使用非交叉模式,需要为各声道分别分配一个buffer,各声道数据分别传输。
  */
  if((err = snd_pcm_hw_params_set_access (capture_handle,hw_params,SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) 
  {
    printf("无法设置访问类型(%s)\n",snd_strerror(err));
    exit(1);
  }
  printf("访问类型设置成功.\n");
  /*设置数据编码格式,并判断是否设置成功*/
  if ((err=snd_pcm_hw_params_set_format(capture_handle, hw_params,format)) < 0) 
  {
    printf("无法设置格式 (%s)\n",snd_strerror(err));
    exit(1);
  }
  fprintf(stdout, "PCM数据格式设置成功.\n");
  /*设置采样频率,并判断是否设置成功*/
  if((err=snd_pcm_hw_params_set_rate_near(capture_handle,hw_params,&rate,0))<0) 
  {
    printf("无法设置采样率(%s)\n",snd_strerror(err));
    exit(1);
  }
  printf("采样率设置成功\n");
  /*设置声道,并判断是否设置成功*/
  if((err = snd_pcm_hw_params_set_channels(capture_handle, hw_params,AUDIO_CHANNEL_SET)) < 0) 
  {
    printf("无法设置声道数(%s)\n",snd_strerror(err));
    exit(1);
  }
  printf("声道数设置成功.\n");
  /*将配置写入驱动程序中,并判断是否配置成功*/
  if ((err=snd_pcm_hw_params (capture_handle,hw_params))<0) 
  {
    printf("无法向驱动程序设置参数(%s)\n",snd_strerror(err));
    exit(1);
  }
  printf("参数设置成功.\n");
  /*使采集卡处于空闲状态*/
  snd_pcm_hw_params_free(hw_params);
  /*准备音频接口,并判断是否准备好*/
  if((err=snd_pcm_prepare(capture_handle))<0) 
  {
    printf("无法使用音频接口 (%s)\n",snd_strerror(err));
    exit(1);
  }
  printf("音频接口准备好.\n");
  /*配置一个数据缓冲区用来缓冲数据*/
  //snd_pcm_format_width(format) 获取样本格式对应的大小(单位是:bit)
  int frame_byte=snd_pcm_format_width(format)/8;
  buffer=malloc(buffer_frames*frame_byte*AUDIO_CHANNEL_SET); //2048
  printf("缓冲区分配成功.\n");
  /*开始采集音频pcm数据*/
  printf("开始采集数据...\n");
  while(1) 
  {
    /*从声卡设备读取一帧音频数据:2048字节*/
    if((err=snd_pcm_readi(capture_handle,buffer,buffer_frames))!=buffer_frames) 
    {
        printf("从音频接口读取失败(%s)\n",snd_strerror(err));
        exit(1);
    }
    /*写数据到文件: 音频的每帧数据样本大小是16位=2个字节*/
    fwrite(buffer,(buffer_frames*AUDIO_CHANNEL_SET),frame_byte,pcm_data_file);  
    if(run_flag)
    {
      printf("停止采集.\n");
      break;
    }
  }
  /*释放数据缓冲区*/
  free(buffer);
  /*关闭音频采集卡硬件*/
  snd_pcm_close(capture_handle);
  /*关闭文件流*/
  fclose(pcm_data_file);
  return 0;
}

四、参考代码:从文件读取PCM数据,再写入到声卡设备,实现声音播放功能

下面代码在命令行通过gcc编译运行:  读取文件PCM音频数据,写入到声卡进行播放,结束播放可以按下Ctrl+C即可结束。

/*
 进行音频采集,读取存放pcm数据的文件通过声卡进行播放
 音频参数: 
   声道数:   1
   采样位数:  16bit、LE格式
   采样频率:  44100Hz
运行示例:
$ gcc linux_pcm_save.c -lasound
$ ./a.out hw:0 123.pcm
*/
#include <stdio.h>
#include <stdlib.h>
#include <alsa/asoundlib.h>
#include <signal.h>
#define AudioFormat SND_PCM_FORMAT_S16_LE  //指定音频的格式,其他常用格式:SND_PCM_FORMAT_U24_LE、SND_PCM_FORMAT_U32_LE
#define AUDIO_CHANNEL_SET   1         //1单声道   2立体声
#define AUDIO_RATE_SET 44100   //音频采样率,常用的采样频率: 44100Hz 、16000HZ、8000HZ、48000HZ、22050HZ
FILE *pcm_data_file=NULL;
int run_flag=0;
void exit_sighandler(int sig)
{
  run_flag=1;
}
//
int main(int argc, char *argv[])
{
  int i;
  int err;
  char *buffer;
  int buffer_frames = 1024;
  unsigned int rate = AUDIO_RATE_SET;
  snd_pcm_t *capture_handle;// 一个指向PCM设备的句柄
  snd_pcm_hw_params_t *hw_params; //此结构包含有关硬件的信息,可用于指定PCM流的配置
  /*注册信号捕获退出接口*/
  signal(2,exit_sighandler);
  /*PCM的采样格式在pcm.h文件里有定义*/
  snd_pcm_format_t format=AudioFormat; // 采样位数:16bit、LE格式
  /*打开音频采集卡硬件,并判断硬件是否打开成功,若打开失败则打印出错误提示*/
  // SND_PCM_STREAM_PLAYBACK 输出流
  // SND_PCM_STREAM_CAPTURE  输入流
  if ((err = snd_pcm_open (&capture_handle, argv[1],SND_PCM_STREAM_PLAYBACK,0))<0) 
  {
    printf("无法打开音频设备: %s (%s)\n",  argv[1],snd_strerror (err));
    exit(1);
  }
  printf("音频接口打开成功.\n");
  /*打开存放PCM数据的文件*/
  if((pcm_data_file = fopen(argv[2], "rb")) == NULL)
  {
    printf("无法打开%s音频文件.\n",argv[2]);
    exit(1);
  } 
  printf("用于播放的音频文件已打开.\n");
  /*分配硬件参数结构对象,并判断是否分配成功*/
  if((err = snd_pcm_hw_params_malloc(&hw_params)) < 0) 
  {
    printf("无法分配硬件参数结构 (%s)\n",snd_strerror(err));
    exit(1);
  }
  printf("硬件参数结构已分配成功.\n");
  /*按照默认设置对硬件对象进行设置,并判断是否设置成功*/
  if((err=snd_pcm_hw_params_any(capture_handle,hw_params)) < 0) 
  {
    printf("无法初始化硬件参数结构 (%s)\n", snd_strerror(err));
    exit(1);
  }
  printf("硬件参数结构初始化成功.\n");
  /*
    设置数据为交叉模式,并判断是否设置成功
    interleaved/non interleaved:交叉/非交叉模式。
    表示在多声道数据传输的过程中是采样交叉的模式还是非交叉的模式。
    对多声道数据,如果采样交叉模式,使用一块buffer即可,其中各声道的数据交叉传输;
    如果使用非交叉模式,需要为各声道分别分配一个buffer,各声道数据分别传输。
  */
  if((err = snd_pcm_hw_params_set_access (capture_handle,hw_params,SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) 
  {
    printf("无法设置访问类型(%s)\n",snd_strerror(err));
    exit(1);
  }
  printf("访问类型设置成功.\n");
  /*设置数据编码格式,并判断是否设置成功*/
  if ((err=snd_pcm_hw_params_set_format(capture_handle, hw_params,format)) < 0) 
  {
    printf("无法设置格式 (%s)\n",snd_strerror(err));
    exit(1);
  }
  fprintf(stdout, "PCM数据格式设置成功.\n");
  /*设置采样频率,并判断是否设置成功*/
  if((err=snd_pcm_hw_params_set_rate_near(capture_handle,hw_params,&rate,0))<0) 
  {
    printf("无法设置采样率(%s)\n",snd_strerror(err));
    exit(1);
  }
  printf("采样率设置成功\n");
  /*设置声道,并判断是否设置成功*/
  if((err = snd_pcm_hw_params_set_channels(capture_handle, hw_params,AUDIO_CHANNEL_SET)) < 0) 
  {
    printf("无法设置声道数(%s)\n",snd_strerror(err));
    exit(1);
  }
  printf("声道数设置成功.\n");
  /*将配置写入驱动程序中,并判断是否配置成功*/
  if ((err=snd_pcm_hw_params (capture_handle,hw_params))<0) 
  {
    printf("无法向驱动程序设置参数(%s)\n",snd_strerror(err));
    exit(1);
  }
  printf("参数设置成功.\n");
  /*使采集卡处于空闲状态*/
  snd_pcm_hw_params_free(hw_params);
  /*准备音频接口,并判断是否准备好*/
  if((err=snd_pcm_prepare(capture_handle))<0) 
  {
    printf("无法使用音频接口 (%s)\n",snd_strerror(err));
    exit(1);
  }
  printf("音频接口准备好.\n");
  /*配置一个数据缓冲区用来缓冲数据*/
  //snd_pcm_format_width(format) 获取样本格式对应的大小(单位是:bit)
  int frame_byte=snd_pcm_format_width(format)/8;
  buffer=malloc(buffer_frames*frame_byte*AUDIO_CHANNEL_SET);
  printf("缓冲区分配成功.\n");
  /*开始采集音频pcm数据*/
  printf("开始播放音频数据...\n");
  int read_cnt;
  while(1) 
  {
    /*从文件读取数据: 音频的每帧数据样本大小是16位-2个字节*/
    read_cnt=fread(buffer,1,frame_byte*(buffer_frames*AUDIO_CHANNEL_SET),pcm_data_file);
    if(read_cnt<=0)break;
    /*向声卡设备写一帧音频数据:2048字节*/
    if((err=snd_pcm_writei(capture_handle,buffer,buffer_frames))!=buffer_frames) 
    {
        printf("向音频接口写数据失败(%s)\n",snd_strerror(err));
        exit(1);
    }
    if(run_flag)
    {
      printf("停止播放.\n");
      break;
    }
  }
  printf("播放完成.\n");
  /*释放数据缓冲区*/
  free(buffer);
  /*关闭音频采集卡硬件*/
  snd_pcm_close(capture_handle);
  /*关闭文件流*/
  fclose(pcm_data_file);
  return 0;
}

目录
相关文章
|
5天前
|
存储 弹性计算 缓存
阿里云服务器租赁费用:新版租赁收费标准及活动报价参考
本文更新了2026年阿里云全系列云服务器租赁活动报价,所有特惠资源均可前往阿里云活动中心选购,整体覆盖从个人入门到企业级高性能场景的全梯度需求。其中轻量应用服务器主打极致性价比,2核2G峰值200M带宽配置每日10点、15点限时抢购价仅38元/年,2核4G配置379元/年起;高性价比的经济型e实例、通用算力型u2i实例覆盖2核4G至4核32G全档位,适配开发测试与中小型企业业务;搭载英特尔至强6处理器的第九代c9i企业级实例算力较上代提升20%,支撑高并发生产环境,不同实例规格价差清晰,用户可根据自身业务负载与预算灵活选型。
1576 112
|
12天前
|
云安全 人工智能 运维
阿里云联动百位企业安全专家,共识Agent防御最佳实践
当Agent成为新员工,你的安全边界在哪里?
1942 8
阿里云联动百位企业安全专家,共识Agent防御最佳实践
|
6天前
|
人工智能 程序员 API
Codex 接入 DeepSeek-V4-Flash:还能补上识图,提供两套方案
Codex 接入 DeepSeek-V4-Flash 怎么配?本文覆盖 CLI 与桌面端,再用 qwen3-vl-flash 补识图,两套方案可直接照做
1001 3
|
6天前
|
编解码 人工智能 安全
2核4G/4核8G/8核16G阿里云服务器如何选择实例?经济型e、通用算力型u2i与计算型c9i选哪个?
本文介绍了阿里云2核4G、4核8G、8核16G三档主流配置下经济型e、通用算力型u2i和计算型c9i三种实例的最新活动价格与适用场景。同配置下三者价差显著,以2核4G为例,经济型e低至599.93元/年,计算型c9i则高达1742.08元/年。文章详细解析了各实例的性能定位:经济型e适合轻负载入门场景,u2i兼顾稳定算力与性价比,c9i凭借第9代至强处理器与芯片级安全能力支撑高性能业务。同时提示用户可叠加满减优惠券享受折上折,建议根据业务负载与预算综合决策。
529 112
|
18天前
|
人工智能 前端开发 Linux
Codex 桌面版安装 + CC Switch 接入第三方 API 完整教程(2026 最新)
2026最新教程:手把手教你安装Codex桌面版,通过CC Switch v3.17.0一键接入Fenno等国产API(兼容OpenAI Responses格式),跳过账号登录,完整启用代码审查、多步任务与上下文感知功能。零基础友好,全程图文实操。(239字)
2568 4
|
10天前
|
存储 人工智能 关系型数据库
阿里云AI产品与云产品最新组合套餐:Token Plan、AI coding及云服务器和建站等组合优惠价
阿里云推出全新“算力+模型+应用”一站式云与AI组合套餐活动,覆盖从个人开发者到中大型企业的全场景需求。核心亮点为分三档定价的Token Plan订阅服务,支持Qwen3.8-Max-Preview大模型调用,错峰时段最低可享0.2折优惠。活动同步推出AI Coding、智能体部署、云电脑托管、0代码建站等十余类场景化组合,搭配99元/年的普惠云服务器、88元/年的入门数据库等经典特惠产品,还为企业提供1V1定制化AI转型方案,大幅降低了不同用户群体拥抱AI的技术门槛与采购成本。
724 111
|
20天前
|
人工智能 JSON 安全
Fastjson远程代码执行漏洞,阿里云AI安全为您保驾护航
阿里云AI安全产品联动防御Fastjson攻击
2638 13
Fastjson远程代码执行漏洞,阿里云AI安全为您保驾护航
|
6天前
|
人工智能 JSON Shell
2026AI漫剧本地全开源方案(附各个软件模型链接),8G显卡也能流畅运行
这是一套完全本地化部署的AI漫剧生成技术链路:涵盖LLM剧本分镜生成、FLUX文生图(IP-Adapter人脸锁定)、StoryDiffusion时序连贯控制、LTX-2.3唇形同步视频生成,及ComfyUI全流程调度。零云端费用,仅耗硬件算力,单集2–4小时可产出竖屏短视频,适配抖音/B站分发。
|
7天前
Qoder 一周年 × Qwen3.8-Max 正式上线,多重好礼限时领
8月3日,Qwen3.8-Max 正式上线Qoder,迎来Qoder一周年。新老用户可领800次免费调用,下单再赠2000次;夜间(22:00–08:00)调用5折;邀请好友双方得积分与调用额度。
452 1

热门文章

最新文章