1. 程式人生 > >基於ALSA的WAV播放和錄音程式

基於ALSA的WAV播放和錄音程式

這段時間在探索ALSA架構,從ALSA Core到ALSA Lib,再到Android Audio System。在看ALSA Lib時,寫了一個比較典型的基於ALSA的播放錄音程式。程式包包含四個部分:

WAV Parser是對WAV檔案的分析和封裝,這裡只針對Standard WAV File;

SND Common是Playback 和Record共同操作,如SetParams、ReadPCM和WritePCM等;

Playback和Record就分別是播放錄音的主體了。

原理很簡單,以Playback為例:從WAV檔案讀取PCM資料,通過I2S或AC97依次送到Audio Codec。難點在於對snd_pcm_hw_params_t的設定,尤其要確定每次要送到Audio Codec的資料幀大小(peroid_size),這個稍後解釋。

1、從WAV檔案的頭資訊可以分析出:sample_format、channels number、sample_rate、sample_length,這些引數要通過snd_pcm_hw_params_set_XXX()介面設定到snd_pcm_hw_params_t中。

2、接著我們要設定buffer_time 和peroid_time。通過snd_pcm_hw_params_get_buffer_time_max()介面可以獲取該Audio Codec可以支援的最大buffer_time,這裡我們設定buffer_time = (MAX_BUFFER_TIME > 500000) ? 500000 : MAX_BUFFER_TIME; peroid_time = buffer_time/4。

關於peroid的概念有這樣的描述:The “period” is a term that corresponds to a fragment in the OSS world. The period defines the size at which a PCM interrupt is generated. 從底層驅動看來,應該是PCM DMA單次傳送資料幀的大小。其實真正關注底層驅動的話,它並不是關心peroid_time,它關心的是peroid_size,這兩者有轉換關係。具體見struct snd_pcm_hardware結構體。

3、通過snd_pcm_hw_params_get_period_size()取得peroid_size,注意在ALSA中peroid_size是以frame為單位的。The configured buffer and period sizes are stored in “frames” in the runtime. 1 frame = channels * sample_size. 所以要對peroid_size進行轉換:chunk_bytes = peroid_size * sample_length / 8。chunk_bytes就是我們單次從WAV讀PCM資料的大小。

之後的過程就乏善可陳了。唯一要留意的是snd_pcm_writei()和snd_pcm_readi()的第三個引數size也是以frame為單位,不要忘記frames和bytes的轉換。

  1. //File   : wav_parser.h
  2. //Author : Loon <[email protected]>
  3. #ifndef __WAV_PARSER_H
  4. #define __WAV_PARSER_H
  5. typedef unsigned char  uint8_t;  
  6. typedef unsigned short uint16_t;  
  7. typedef unsigned int   uint32_t;  
  8. #if __BYTE_ORDER == __LITTLE_ENDIAN
  9. #define COMPOSE_ID(a,b,c,d) ((a) | ((b)<<8) | ((c)<<16) | ((d)<<24))
  10. #define LE_SHORT(v)           (v)
  11. #define LE_INT(v)               (v)
  12. #define BE_SHORT(v)           bswap_16(v)
  13. #define BE_INT(v)               bswap_32(v)
  14. #elif __BYTE_ORDER == __BIG_ENDIAN
  15. #define COMPOSE_ID(a,b,c,d) ((d) | ((c)<<8) | ((b)<<16) | ((a)<<24))
  16. #define LE_SHORT(v)           bswap_16(v)
  17. #define LE_INT(v)               bswap_32(v)
  18. #define BE_SHORT(v)           (v)
  19. #define BE_INT(v)               (v)
  20. #else
  21. #error "Wrong endian"
  22. #endif
  23. #define WAV_RIFF        COMPOSE_ID('R','I','F','F')
  24. #define WAV_WAVE        COMPOSE_ID('W','A','V','E')
  25. #define WAV_FMT         COMPOSE_ID('f','m','t',' ')
  26. #define WAV_DATA        COMPOSE_ID('d','a','t','a')
  27. /* WAVE fmt block constants from Microsoft mmreg.h header */
  28. #define WAV_FMT_PCM             0x0001
  29. #define WAV_FMT_IEEE_FLOAT      0x0003
  30. #define WAV_FMT_DOLBY_AC3_SPDIF 0x0092
  31. #define WAV_FMT_EXTENSIBLE      0xfffe
  32. /* Used with WAV_FMT_EXTENSIBLE format */
  33. #define WAV_GUID_TAG        "/x00/x00/x00/x00/x10/x00/x80/x00/x00/xAA/x00/x38/x9B/x71"
  34. /* it's in chunks like .voc and AMIGA iff, but my source say there 
  35.    are in only in this combination, so I combined them in one header; 
  36.    it works on all WAVE-file I have 
  37.  */
  38. typedefstruct WAVHeader {  
  39.     uint32_t magic;     /* 'RIFF' */
  40.     uint32_t length;        /* filelen */
  41.     uint32_t type;      /* 'WAVE' */
  42. } WAVHeader_t;  
  43. typedefstruct WAVFmt {  
  44.     uint32_t magic;  /* 'FMT '*/
  45.     uint32_t fmt_size; /* 16 or 18 */
  46.     uint16_t format;        /* see WAV_FMT_* */
  47.     uint16_t channels;  
  48.     uint32_t sample_rate;   /* frequence of sample */
  49.     uint32_t bytes_p_second;  
  50.     uint16_t blocks_align;  /* samplesize; 1 or 2 bytes */
  51.     uint16_t sample_length; /* 8, 12 or 16 bit */
  52. } WAVFmt_t;  
  53. typedefstruct WAVFmtExtensible {  
  54.     WAVFmt_t format;  
  55.     uint16_t ext_size;  
  56.     uint16_t bit_p_spl;  
  57.     uint32_t channel_mask;  
  58.     uint16_t guid_format;   /* WAV_FMT_* */
  59.     uint8_t guid_tag[14];   /* WAV_GUID_TAG */
  60. } WAVFmtExtensible_t;  
  61. typedefstruct WAVChunkHeader {  
  62.     uint32_t type;      /* 'data' */
  63.     uint32_t length;        /* samplecount */
  64. } WAVChunkHeader_t;  
  65. typedefstruct WAVContainer {  
  66.     WAVHeader_t header;  
  67.     WAVFmt_t format;  
  68.     WAVChunkHeader_t chunk;  
  69. } WAVContainer_t;  
  70. int WAV_ReadHeader(int fd, WAVContainer_t *container);  
  71. int WAV_WriteHeader(int fd, WAVContainer_t *container);  
  72. #endif /* #ifndef __WAV_PARSER_H */

  1. //File   : wav_parser.c
  2. //Author : Loon <[email protected]>
  3. #include <assert.h>
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6. #include <unistd.h>
  7. #include <fcntl.h>
  8. #include "wav_parser.h"
  9. #define WAV_PRINT_MSG
  10. char *WAV_P_FmtString(uint16_t fmt)  
  11. {  
  12.     switch (fmt) {  
  13.     case WAV_FMT_PCM:  
  14.         return"PCM";  
  15.         break;  
  16.     case WAV_FMT_IEEE_FLOAT:  
  17.         return"IEEE FLOAT";  
  18.         break;  
  19.     case WAV_FMT_DOLBY_AC3_SPDIF:  
  20.         return"DOLBY AC3 SPDIF";  
  21.         break;  
  22.     case WAV_FMT_EXTENSIBLE:  
  23.         return"EXTENSIBLE";  
  24.         break;  
  25.     default:  
  26.         break;  
  27.     }  
  28.     return"NON Support Fmt";  
  29. }  
  30. void WAV_P_PrintHeader(WAVContainer_t *container)  
  31. {  
  32.     printf("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++/n");  
  33.     printf("/n");  
  34.     printf("File Magic:         [%c%c%c%c]/n",   
  35.         (char)(container->header.magic),   
  36.         (char)(container->header.magic>>8),   
  37.         (char)(container->header.magic>>16),   
  38.         (char

    相關推薦

    基於ALSA的WAV播放錄音程式

    這段時間在探索ALSA架構,從ALSA Core到ALSA Lib,再到Android Audio System。在看ALSA Lib時,寫了一個比較典型的基於ALSA的播放錄音程式。程式包包含四個部分: WAV Parser是對WAV檔案的分析和封裝,

    win10 插入16k采樣的耳機無法播放錄音的問題定位

    解決辦法 疑難問題 問題 采樣率 解決辦法。 聲音 了解 增強 沒有   平時做智能耳機,需要經常在windows上測試不同采樣率的聲音信號。可是,最近在16k雙聲道輸入的情況下, 無論系統都使用該耳機進行播放,該問題思索了好久,一直沒有解決辦法。   今天無意中使用了wi

    MFC 聲音的播放錄音的實現(一)

    程式設計中經常會又到播放聲音的操作,現總結一下在MFC中關於聲音有關的問題。這一篇介紹一下播放聲音的操作。下一篇介紹一下錄音操作的實現。 (一)呼叫Windows API函式實現聲音的播放 VC++ 

    程式列表視訊控制播放設定封面與標題

    程式碼主要實現視訊列表中當播放當前視訊需要點選下一個的時候,上一個視訊關閉,點選視訊播放上一個視訊並顯示封面和標題(回到當初的面貌) 1.wxml檔案 注:這裡我沒有寫for迴圈,列舉三個表達下效果。myVideo1可以代表為傳過來的視訊id {{ index == 1

    微信小程式音訊相關問題:播放錄音等相關

    wx.startRecord(OBJECT) 開始錄音。當主動呼叫wx.stopRecord,或者錄音超過1分鐘時自動結束錄音,返回錄音檔案的臨時檔案路徑。當用戶離開小程式時,此介面無法呼叫。 wx.stopRecord() ​主動呼叫停止錄音。 wx.

    【MFC-8】VS2010更改基於對話方塊的MFC程式標題欄圖示生產的執行檔案的圖示

    一、開發環境 1、VS2010; 2、C++ / MFC; 二、更改步驟 1)建立一個新工程,可以什麼都不加。開啟“資源檢視”, 右鍵點選專案名稱,選擇“新增資源”,匯入“Icon”資原始檔(事先準備好); 2)修改上一步加入的“Icon”資源屬性ID為“IDI_IC

    MFC播放聲音錄音的實現(三)

    上一篇通過Win32控制檯程式簡單地完成了聲音的錄取和回放,但是這個過程都只是是在記憶體中進行的,沒有進行檔案的操作,這樣錄取的聲音也就無法儲存。這一篇介紹一下用MFC實現錄音並生成wave檔案,最後儲存到指定的目錄的方法。 新建一個MFC對話方塊應用程式,命名為Voice

    【OpenCV】基於libvlc SDKOpencv播放rtsp流

    RTST流:使用vlc作為rtsp伺服器 vlc2.1.2 VS2013+OpenCV3.0 C++ 1、編譯時記得匯入vlc 的vlc.dll、vlccore.dll和plugins資料夾,這些可

    【Alsa】播放聲音錄音詳細流程

    不用 數字信號 一個 放音 fcc 根據 dma 時間 int linux中,無論是oss還是alsa體系,錄音和放音的數據流必須分析清楚。先分析alsa驅動層,然後關聯到alsa庫層和應用層。 二,鏈接分析: 1)鏈路一 usr/src/linux-source-3

    ssh2項目整合 struts2.1+hibernate3.3+spring3 基於hibernate註解struts2註解

    模塊 port aware -- ids tle des 項目 @service 項目文件夾結構例如以下: 核心配置文件: web.xml <?xml version="1.0" encoding="UTF-8"

    Nginx基於用戶名密碼的訪問控制

    密碼 nginx 用戶名 1 安裝相關包yum install -y httpd-tools2 創建校驗文件htpasswd -cb /etc/nginx/.htpasswd user1 ‘passwd1‘ chown nginx:nginx /etc/nginx/.htpasswd chmod

    基於正則叠代模式的計算器源碼

    pre 默認參數 utf-8 nco 退出 spl pan ner imp 源碼如下: 1 # encoding:utf-8 2 # Author:"richie" 3 # Date:2017/8/8 4 import re 5 # 待處理數據 6 s =

    基於梯度場Hessian特征值分別獲得圖像的方向場

    htm name tar 我們 值範圍 con tco png mali 一、?我們想要求的方向場的定義為:對於任意一點(x,y),該點的方向可以定義為其所在脊線(或谷線)位置的切線方向與水平軸之間的夾角:將一條直線順時針或逆時針旋轉 180°,直線的方向保持不變。因此,指

    mp4格式-播放斷點續播

    環保 margin ont width center span ces mp4格式 false video 標簽(autopaly自動播放屬性) <video id="videoPlay" class="video" autoplay="autoplay

    基於gin框架jwt-go中間件實現小程序用戶登陸token驗證

    expires 微信 處理 如果 .net local 切入點 小程序 err 本文核心內容是利用jwt-go中間件來開發golang webapi用戶登陸模塊的token下發和驗證,小程序登陸功能只是一個切入點,這套邏輯同樣適用於其他客戶端的登陸處理。 小程序登陸邏輯

    Android視頻播放橫豎屏切換

    相關信息 fonts android systemui video rap profile ase home 最近做了一個項目,裏面用到了視頻播放這一塊,當時想考慮Vitamio,demo也做了出來,但是後來發現它是商業收費的,並且收費相當可觀,所以只能放棄了。然後

    【轉】Red5流服務器搭建(實現在線直播,流媒體視頻播放在線視頻會議)

    htm tps 實現 gho 共享 麥克風 一個 編碼工具 localhost 來自:http://blog.csdn.net/sunroyi666/article/details/52981639 一. 先介紹一下流媒體技術:所謂流媒體技術,是指將連續的影像和聲音信息經過

    基於Orangpi ZeroLinux ALSA實現WIFI無線音箱(二)

    memfree 線程 取數據 edit 這一 緩沖 一起 說了 cpp 作品已經完成,先上源碼: https://files.cnblogs.com/files/qzrzq1/WIFISpeaker.zip 全文包含三篇,這是第二篇,主要講述發送端程序的原理和過程。 第一篇

    基於Orangpi ZeroLinux ALSA實現WIFI無線音箱(三)

    http 和源 cal 快速實現 播放音樂 lis 類型 ive 此外 作品已經完成,先上源碼: https://files.cnblogs.com/files/qzrzq1/WIFISpeaker.zip 全文包含三篇,這是第三篇,主要講述接收端程序的原理和過程。 第一篇

    kvm的Pool創建基於physical disk 一些問題

    CentOS-7.2 最近幾天遇到physical disk創建pool的問題,主要集中在centos7.2 1.一個是基於mbr的分區,因為小於2GB,然後磁盤上的標識是dos。在virt-manager裏,首先是創建pool,遇到問題,具體的忘記了。然後用cli創建,成功後不穩定,然後在創建