PYNQ開發板上使用USB音效卡+OSS相容層播放音訊
PYNQ開發板上使用USB音效卡+OSS相容層播放音訊需要經過聯網裝庫,編譯驅動模組,執行三個步驟。
聯網裝庫
首先需要將PYNQ開發板連上網,才能安裝所需的庫。方法是在電腦上設定共享網路,但要注意大部分作業系統在共享網路的時候無法自己指定本機IP,所以需要修改PYNQ開發板的IP來適應電腦自動設定的IP。設定共享網路的方法網上教程有很多,這裡不再贅述。
設定完共享網路後,注意不要關閉開發板,因為電腦的IP已經改了,如果重啟開發板閘道器不對SSH就連不上了。這時需要檢視電腦的ip(注意是有線區域網下的ip),Windows下使用ipconfig
,Linux下使用ifconfig
。如我的Ubuntu在有線區域網下的ip是10.42.0.1,由於閘道器是255.255.255.0,所以PYNQ開發板的IP必須設定為10.42.0.*。在我的環境下,設定IP的方法是編輯/etc/network/interfaces.d/eth0
然後禁用再啟用一下電腦的有線網功能並重啟開發板,嘗試能否連進去,連進去以後再嘗試ping外網,都可以才算成功。接下來就可以裝庫了,執行:
sudo apt update
sudo apt install alsa-base alsa-utils alsa-oss bc
編譯驅動模組
下載核心原始碼,我PYNQ上的Linux核心版本為5.4.0,可以在xilinx的linux核心倉庫下載,其他版本的核心原始碼也可以在該倉庫的其他分支下載。
下載並傳到開發板上後,執行:
unzip linux-xlnx-xlnx_rebase_v5.4_2020.1.zip
解壓結束後進入該資料夾,然後需要將當前系統的核心配置檔案複製過來,執行:
sudo cp /lib/modules/$(uname -r)/build/.config .
配置需要編譯的驅動模組,執行:
make menuconfig
進入Device Drivers
-> Sound card support
-> Advanced Linux Sound Architecture
-> USB sound devices
,游標移至USB Audio/MIDI driver
,按M鍵選擇編譯此模組,儲存然後一直按Esc鍵退出。執行以下命令:
make prepare make -C . M=sound
編譯驅動模組,生成副檔名為ko的模組檔案,將它們複製到核心資料夾中:
sudo mkdir -p /lib/modules/$(uname -r)/kernel/sound/core
sudo mkdir -p /lib/modules/$(uname -r)/kernel/sound/usb
sudo cp sound/core/*.ko /lib/modules/$(uname -r)/kernel/sound/core
sudo cp sound/usb/*.ko /lib/modules/$(uname -r)/kernel/sound/usb
然後安裝這些模組:
sudo depmod
之後插上USB音效卡,驅動模組應該會自動載入,可以用命令lsmod
檢視模組是否被成功載入,在我的系統上被載入的有以下4個模組:
Module Size Used by
snd_usb_audio 167936 0
snd_hwdep 16384 1 snd_usb_audio
snd_usbmidi_lib 24576 1 snd_usb_audio
snd_rawmidi 24576 1 snd_usbmidi_lib
如果沒有自動載入,可以手動載入:
sudo modprobe snd-usb-audio
此時可以檢視音效卡是否已被識別:
sudo aplay -l
如果輸出中出現card 1,則需要配置一下音效卡編號,因為alsa音訊庫預設使用的音效卡是0號音效卡,而USB音效卡被分配的編號是1,所以需要修改alsa的配置檔案,路徑為/usr/share/alsa/alsa.conf
,在裡面找到defaults.pcm.card 0
這一行,把0改成1。
這時候應該可以播放音樂了:
sudo aplay test.wav
執行
目前我們使用的音訊驅動庫一般是alsa,oss驅動庫已經被淘汰了。但是相比於alsa,oss可能程式設計更簡單一點,因為可以直接以傳統檔案讀寫的方式播放音樂;而且有很多老程式使用的是oss。為此,alsa提供了對oss的相容,主要有兩種方式,一種是載入驅動模組snd-pcm-oss,這種方式可以從核心層面將對oss的操作轉發成對alsa的操作,使用範圍廣;另一種是安裝alsa-oss庫,然後在執行使用oss的程式時使用LD_PRELOAD把對oss的檔案操作替換成這個庫裡對alsa的操作,只能替換固定的幾個檔案操作(像openat就不能替換),但是原理簡單,不用編譯驅動模組。
目前我不知道為什麼編譯出來的snd-pcm-oss模組用不了,執行時總是說找不到函式符號,但是它的依賴模組能編譯出來的我都已經編譯並載入了,不知道是不是連結有什麼問題。所以只能用alsa-oss庫,這裡給出示例程式:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/soundcard.h>
int main() {
// 開啟裝置檔案
int fd = open("/dev/dsp", O_RDWR);
if (fd < 0) {
perror("open error\n"); return 1;
}
// test.wav為16位,雙聲道,取樣率為20000的波形檔案
int bit = 16, rate = 20000, channel = 2;
ioctl(fd, SOUND_PCM_WRITE_BITS, &bit);
ioctl(fd, SOUND_PCM_WRITE_RATE, &rate);
ioctl(fd, SOUND_PCM_WRITE_CHANNELS, &channel);
/*開啟音樂檔案*/
int fp = open("test.wav", O_RDONLY);
if(fp < 0){
perror("open fp error!\n"); return 1;
}
/*求檔案的大小*/
//這裡直接給出了,要計算可以用fstat函式或fseek+ftell函式
int len = 800180;
char *buf = (char *)malloc(len);
memset(buf, 0, len);
// 讀檔案到buf中
int rd = read(fp, buf, len);
if(rd < 0){
perror("read wav error!\n"); return 1;
}
close(fp);
// 把buf寫到裝置檔案中
int wr = write(fd, buf, len);
if(wr < 0){
perror("write dsp error!\n"); return 1;
}
free(buf); buf = NULL;
return 0;
}
可以看到這裡開啟音效卡直接用檔案/dev/dsp
,這個檔案在使用alsa音訊驅動庫時是不存在的,alsa-oss需要你載入libaoss.so這個動態庫並用庫裡的open函式替換掉這個open操作,而它自己的open函式裡會進行檢查,如果嘗試開啟oss的音效卡檔案,就會執行alsa的操作,否則按原來的檔案開啟方式,其他檔案操作函式也是一樣。所以假設上面的測試檔案編譯出來的程式為audio
,則執行時執行:
sudo LD_PRELOAD=libaoss.so ./audio