1. 程式人生 > 其它 >PYNQ開發板上使用USB音效卡+OSS相容層播放音訊

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

,然後將其中的address這一行的IP地址修改為10.42.0.2。

然後禁用再啟用一下電腦的有線網功能並重啟開發板,嘗試能否連進去,連進去以後再嘗試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