日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

STM32MP157-Linux音频应用编程-语音转文字项目

發布時間:2024/3/13 linux 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 STM32MP157-Linux音频应用编程-语音转文字项目 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

  • 前言
  • STM32MP157語音識別
    • alsa-lib簡介:
    • 移植alsa-lib庫:
    • libcurl庫簡介:
    • 安裝CURL
    • 移植CURL
    • API調用
    • 錄音
    • 文件IO
      • 打開音頻文件
      • 打開緩沖文件
    • 文件描述符重定向(已刪除)
    • CURL命令
    • 字符串拼接與解析
    • exec函數族
    • 進程創建
    • 多線程
    • 主循環
    • 實現效果及注意事項
      • 實現效果
      • 注意事項
  • 源代碼(轉載請注明出處)


前言

本篇分享:

Linux應用編程之音頻編程,使用戶可以錄制一段音頻并進行識別(語音轉文字)

環境介紹:

系統:Linux
硬件:正點原子STM32MP157開發板
聲卡:開發板自帶


STM32MP157語音識別

實現目標 :用戶可以錄制一段音頻并進行識別(語音轉文字)
知識點 : C語言、文件IO、文件描述符重定向 、alsa-lib 庫、CURL命令、API調用、字符串拼接與解析、進程創建、exec函數族、多線程。

alsa-lib簡介:

alsa-lib是一套 Linux 應用層的 C 語言函數庫,為音頻應用程序開發提供了一套統一、標準的接口,應用程序只需調用這一套 API 即可完成對底層聲卡設備的操控,譬如播放與錄音。
用戶空間的alsa-lib對應用程序提供了統一的API 接口,這樣可以隱藏驅動層的實現細節,簡化了應用 程序的實現難度、無需應用程序開發人員直接去讀寫音頻設備節點。所以,主要就是學習alsa-lib庫函數的使用、如何基于alsa-lib庫函數開發音頻應用程序。
alsa-lib官方說明文檔:https://www.alsa-project.org/alsa-doc/alsa-lib/

移植alsa-lib庫:

正點STM32MP157開發板出廠已移植(非廣告!),需要請參考其他教程。

要在嵌入式Linux系統上運行使用alsa-lib庫的程序,需要移植alsa-lib庫,可以參考網上移植alsa-lib庫的方法,或自行下載alsa-lib資源包,自行編譯移植。

開源ALSA架構的官網地址:https://www.alsa-project.org/wiki/Main_Page

libcurl庫簡介:

libcurl是一個跨平臺的網絡協議庫,支持http, https, ftp, gopher, telnet, dict, file, 和ldap 協議。libcurl同樣支持HTTPS證書授權,HTTP POST, HTTP PUT, FTP 上傳, HTTP基本表單上傳,代理,cookies,和用戶認證。

官網地址:http://curl.haxx.se/

安裝CURL

在使用curl指令之前,需要先安裝curl軟件包。在大多數Linux發行版中,在ubuntu中可以使用以下命令來安裝curl:

sudo apt-get install curl

移植CURL

正點STM32MP157開發板出廠已移植(非廣告!),需要請參考其他教程。
可以使用curl指令測試是否安裝該軟件包。

curl官方網站:https://curl.se/

API調用

該程序使用的是百度語音識別API

注冊后領取免費額度及創建中文普通話應用(創建前先領取免費額度(180 天免費額度,可調用約 5 萬次左右) )

創建好應用后,可以得到API key和Secret Key(填寫到程序中的相應位置)

調用API相關說明,Demo代碼中有多種語言的調用示例可以參考,使用c語言的話也可以直接在本程序上面再次更改:

錄音

查看Linux應用編程-音頻應用編程-語音轉文字項目中相應的標題。

文件IO

我們需要將錄制的音頻文件保存到本地,就需要用到文件IO相關知識,打開音頻文件以及向音頻文件寫數據。

打開音頻文件

函數:

函數原型: FILE *fopen(const char *filename, const char *mode)參數: filename -- 字符串,表示要打開的文件名稱。 mode -- 字符串,表示文件的訪問模式。作用: 以指定的方式打開文件。

代碼:

/*創建一個保存PCM數據的文件*/ if ((pcm_data_file = fopen(argv[1], "wb")) == NULL) {printf("無法創建%s音頻文件.\n", argv[1]);exit(1); } printf("用于錄制的音頻文件已打開.\n");參數: argv[1]:程序執行時傳遞的參數,./voice record.cpm,則該參數為"record.cpm" "wb":只寫打開或新建一個二進制文件,只允許寫數據。

打開緩沖文件

除了要保存音頻數據的文件,還要有保存CURL執行返回結果的文件作為程序緩沖文件,進而從緩沖文件中提取關鍵信息。

函數:

頭文件: #include <fcntl.h>函數原型: int open(const char *pathname, int flags, mode_t mode);參數: pathname -- 文件路徑名或文件名。 flags -- 打開文件所采用的操作。 mode -- 設置文件訪問權限的初始值。作用: 以指定的方式打開文件。

代碼:

int fd = open(buffer_FileName,O_WRONLY|O_CREAT|O_TRUNC,0777);參數: buffer_FileName -- 文件路徑名或文件名。 O_WRONLY -- 以只讀方式打開。 O_CREAT -- 若不存在則創建該文件。 O_TRUNC -- 若以只讀方式打開,并存在該文件,則清空文件原內容。 0777 -- 打開全部權限。

文件描述符重定向(已刪除)

重定向的作用是使"一個文件描述符"指向"另一文件描述符所指向的文件",這里需要使終端不出現無關輸出,又需要將CURL指令執行返回結果保存(CURL指令執行后會返回結果)。
后續發現curl參數-o可以實現相同功能,代碼已更改,這部分就當復習好了…

例如:

獲取Token:

識別音頻文件:

我們當然不希望這些返回結果直接打印在終端上,而后續程序又需要利用這些返回結果,這里就需要用到文件描述符的重定向。
學習過文件IO相關知識應該知道,終端標準輸出對應的文件描述符是1(標準輸入0,標準錯誤2),所以,我們只需要創建一個數據緩沖文件,得到數據緩沖文件的文件描述符fd,再將標準輸出重定向到fd即可,這樣終端輸出的數據都會存儲到fd指向的文件中。

函數:

函數原型: int dup(int oldfd)參數: oldfd -- 待拷貝的文件描述符。作用: 使用現有的文件描述符,拷貝生成一個新的文件描述符,且函數調用前后這個兩個文件 描述符指向同一文件,即oldfd指向的文件。函數原型: int dup2(int oldfd, int newfd)參數: oldfd -- 待拷貝的文件描述符。 newfd -- 新文件描述符。作用: 使新文件描述符指向待拷貝文件描述符所指向的文件。

代碼:

int OUT_fd = dup(STDOUT_FILENO); int fd = open(buffer_FileName,O_WRONLY|O_CREAT|O_TRUNC,0777); dup2(fd,STDOUT_FILENO);1.首先創建一個OUT_fd保存原標準輸出,為了后續需要終端輸出提示時能重定向回來。 2.打開數據緩沖文件,得到文件描述符fd。 3.重定向標準輸出到fd。

CURL命令

獲取Token:

格式: curl -i -s 'https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=【百度云應用的AK】&client_secret=【百度云應用的SK】' -o 【數據緩沖文件】示例: curl -i -k 'https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=xxxxxxxxx&client_secret=xxxxxxxxx' -o buffer.txt參數: -i -- 顯示傳輸文檔,經常用于測試連接本身,含是否調用成功信息。 -s -- 靜默模式,不輸出任何東西。 -o -- 把輸出寫到該文件中。

音頻文件識別:

格式: curl -i -X POST -H【固定頭部header】"【語音識別請求地址】?dev_pid=【語言參數】&cuid=【用戶唯一標識】&token=【token】" --data-binary "@【音頻文件路徑名】"示例: curl -i -X POST -H "Content-Type: audio/pcm;rate=16000" "http://vop.baidu.com/server_api?dev_pid=1537&cuid=xxxxx&token=1.a6b7dbd428f731035f771b8d********.86400.1292922000-2346678-124328" --data-binary "@/home/test/test.pcm"

字符串拼接與解析

當我們需要執行CURL指令時,需要對多個字符串進行拼接。

函數:

函數原型: int snprintf(char *str, size_t size, const char *format, ...)參數: str -- 拼接的結果保存在該字符串中。 size -- 如果平格式化后的字符串長度 < size,則將此字符串全部復制到str中,并給其后添加一個字符串結束符('\0');如果格式化后的字符串長度 >= size,則只將其中的(size-1)個字符復制到str中,并給其后添加一個字符串結束符('\0')。 format -- 字符串格式。示例: snprintf(buf, 200, "%s,%d", string,num);作用: 格式化字符串。

代碼(構建獲取Token的命令):

void Get_Token(char api_Key[],char secret_Key[]) {char API_TOKEN_URL[] = "https://aip.baidubce.com/oauth/2.0/token";char url_pattern[] = "%s?grant_type=client_credentials&client_id=%s&client_secret=%s";char url_common[200];pid_t pid;/*構建命令參數*/snprintf(url_common, 200, url_pattern, API_TOKEN_URL, api_Key, secret_Key); }

exec函數族

在程序中如果需要調用如"ls、mv、cp"等相關命令,就可以用exec函數族中的execl 函數來實現該功能。這里使用execl函數實現curl命令的調用。

函數:

函數原型: int execl(const char *path, const char *arg, ...);語法: int execl("絕對路徑", "標識符", "需要的參數(需要多少傳入多少)" ,NULL);示例: execl("ls","ls","-l",NULL);參數: 絕對路徑 -- 文件存儲的絕對路徑,使用程序名在 PATH 中搜索。在終端可使用的命令這里直接輸入命令即可。 標識符 -- 命令。 參數 -- 執行命令所需參數。作用: 加載一個進程,通過路徑+程序名來加載。

代碼:

以執行CURL命令獲取Token為例說明:

void Get_Token(char api_Key[],char secret_Key[]) {.../*構建命令參數*/snprintf(url_common, 200, url_pattern, API_TOKEN_URL, api_Key, secret_Key);/*調用curl命令*/execlp("curl","curl","-i","-s","-k",url_common,NULL);... }

進程創建

由于exec函數族的函數一旦調用成功即執行新的程序,不返回。 只有失敗才返回,錯誤值-1。這樣就會導致調用execl函數后整個程序終止,后續代碼無法執行。
所以我們需要創建一個子進程去調用該函數,函數結束,子進程也就結束。

函數:

函數原型: pid_t fork(void)作用: 創建一個子進程。返回值: 失敗返回-1;成功返回:父進程返回子進程的ID(非負);子進程返回 0。注意返回值,不是 fork 函數能返回兩個值,而是 fork 后,fork 函數變為兩個,父子【各自】返回一個。

代碼:

void Get_Token(char api_Key[],char secret_Key[]) {.../*創建子進程執行curl指令 執行完畢子進程結束*/pid = fork();if(pid == 0) execlp("curl","curl","-i","-s",url_common,"-o",buffer_FileName,NULL);/*回收子進程 不回收會存在僵尸進程*/wait(NULL);... }

多線程

函數:

函數原型: int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg)thread -- 傳出參數,保存系統為我們分配好的線程 ID attr -- 通常傳 NULL,表示使用線程默認屬性。若想使用具體屬性也可以修改該參數。 start_routine -- 函數指針,指向線程主函數,該函數運行結束,則線程結束。 arg -- 線程主函數執行期間所使用的參數。作用: 創建一個新線程。函數原型: int int truncate(const char *path,off_t length);參數: path -- 文件路徑名。 length -- 截斷長度,若文件大小>length大小,額外的數據丟失。若文件大小<length大小,那么,這個文件將會被擴展,擴展的部分將補以null,也就是‘\0’。作用: 截斷或擴展文件。

代碼:

/*創建子線程檢測按鍵是否按下*/ pthread_t tid; ret = pthread_create(&tid, NULL, button_tfn, NULL); if (ret != 0) perror("pthread_create failed");void *button_tfn(void *arg) {struct input_event in_ev = {0};int fd;int value = -1;/*打開按鍵事件對應的文件*/if (0 > (fd = open("/dev/input/event0", O_RDONLY))){perror("open error");exit(-1);}while(1){/*循環讀取數據*/if (sizeof(struct input_event) != read(fd, &in_ev, sizeof(struct input_event))){perror("read error");exit(-1);}if (EV_KEY == in_ev.type && in_ev.code == 114)//114為KEY0 { /*按鍵事件*/switch (in_ev.value){/*KEY0松開*/case 0:/*** 1.更新按鍵狀態為松開* 2.延時等待主循環判斷,否則可能出現主循環先判斷標志位為1而出現PCM設備停止還在繼續讀數據* 3.停止PCM設備*/key_flag_now = 0;sleep(1);snd_pcm_drop(capture_handle);break;/*KEY0按下*/case 1:/*** 1.清空文件,使文件從頭開始寫,等于重新錄制音頻* 2.同樣注意順序,先使設備恢復進入準備狀態,避免出現主循環先檢測到標志位為1而讀取聲卡設備* 3.更新按鍵狀態為按下*/truncate(pcm_file_name,1);snd_pcm_prepare(capture_handle);key_flag_now = 1;break;}}else if(EV_KEY == in_ev.type && in_ev.code == 115)//115為KEY1{/*按鍵事件*/switch (in_ev.value){/*KEY1按下*/case 1:/*退出程序*/exit_program();break;}}} }

主循環

主循環內判斷聲卡設備狀態是否改變(按鍵狀態決定),若當前聲卡為運行狀態則進行音頻采集,若當前聲卡為停止狀態則調用API進行識別。

while (1) {/*判斷按鍵狀態是否更新*/if(key_flag_now != key_flag_old){/*視當前狀態為舊狀態*/key_flag_old = key_flag_now;/*若按鍵按下*/if(key_flag_now == 1)printf("開始采集音頻數據...\n");/*若按鍵松開*/else{printf("采集結束!\n");/*識別音頻 成功輸出結果 出錯退出程序*/ret = Speech_Recognition(argv[1],result);if(ret == 0)printf("識別的結果為:%s\n",result);elseexit_program();printf("請長按KEY0按鍵開始采集音頻數據!單擊KEY1退出程序!\n");}}/*若按鍵按下*/if(key_flag_now == 1){/*從聲卡設備讀取一幀音頻數據:2048字節*/ret = snd_pcm_readi(capture_handle, buffer, buffer_frames);if(0 > ret){printf("從音頻接口讀取失敗(%s)\n", snd_strerror(ret));exit(1);}/*寫數據到文件: 音頻的每幀數據樣本大小是16位=2個字節*/fwrite(buffer, (ret * AUDIO_CHANNEL_SET), frame_byte, pcm_data_file);} }

實現效果及注意事項

實現效果

如圖所示長按KEY0按鍵開始音頻錄制,松開即音頻錄制結束,再調用百度語言API進行識別,并向用戶展示識別的結果。之后用戶可自行選擇繼續識別或退出程序。

注意事項

該程序在聲卡不進行錄音時是將聲卡設備給停止工作了的,在停止聲卡設備前需要加入一小段的延時等待,若不添加延時等待,可能會出現子線程使聲卡設備停止的同時主線程在讀取聲卡設備,從而導致下圖中出現的錯誤:


源代碼(轉載請注明出處)

總結

以上是生活随笔為你收集整理的STM32MP157-Linux音频应用编程-语音转文字项目的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。