基於Orangpi Zero和Linux ALSA實現WIFI無線音箱(三)

做品已經完成,先上源碼:php

https://files.cnblogs.com/files/qzrzq1/WIFISpeaker.ziphtml

全文包含三篇,這是第三篇,主要講述接收端程序的原理和過程。python

第一篇:基於Orangpi Zero和Linux ALSA實現WIFI無線音箱(一)linux

第二篇:基於Orangpi Zero和Linux ALSA實現WIFI無線音箱(二)git

 

如下是正文:github

  在進行接收端程序開發前,首先要了解Orangpi Zero的聲音設備。windows

  Orangpi能夠經過ALSA(The Advanced Linux Sound Architecture )訪問系統的聲音設備。數組

1、查找並肯定Orangpi Zero的聲音設備服務器

  要使用ALSA,首先就是要能正確找到聲音設備,做者在使用alsa的時候,嘗試過使用armbian官網(連接地址)上三個不一樣的鏡像,發現有些armbian鏡像有問題,不知道是什麼緣由,分別是:架構

  (1)、基於Ubuntu Xenial的Armbian鏡像,版本號3.4.113(下載地址

  (2)、基於Ubuntu Xenial的Armbian鏡像,版本號4.14.14(下載地址

  (3)、基於Debian Stretch的Armbian鏡像,版本號4.14.14(下載地址

  這三個鏡像中,只有第一個能找到聲卡設備,其餘兩個都提示無聲卡設備。做者只能使用第一個鏡像。

  在armbian中,使用如下命令便可看到聲卡設備。

aplay -l

  如上圖所示,在OrangePi Zero中,共有兩個聲卡設備,一個card0是audiocodec,指的是板載的TV接口,另外一個card1是sndhdmi,指的是HDMI輸出接口,其中card0是默認聲卡設備,由於TV接口在開發板上有直接引出,並且只需3線(左聲道、右聲道、地),本做品直接使用TV接口做爲音頻輸出。硬件電路如圖以下所示。

  若是使用aplay命令顯示出來的card0不是咱們想要的默認聲卡設備,那就要進行更改了,更改方法能夠參考「linux alsa音頻架構的配置與使用」這個文章。

  此外,alsa還有一個虛擬的配置界面,alsamixer,利用它能夠方便的設置聲卡音量、配置聲卡、靜音等功能,相似windows桌面右下角的聲音管理器。要打開alsamixer,直接使用alsamixer命令便可,具體的使用方法,能夠參考「Linux下的音量控制器alsamixer」這篇文章,界面以下圖所示。

alsamixer

  設置以後,利用aplay命令測試一下可否播放音樂,若是TV接口播放音頻正常,接下來就能夠開始接收端的程序開發了。

#播放測試音樂
aplay test.wav

  測試alsa正常後,接下來介紹接收端程序中須要使用到的socketpyalsaaduio模塊。

2、socket模塊

  socket模塊使用比較簡單,首先獲取本機IP,而後初始化socket爲UDP模式,並綁定IP地址和端口號,就能夠開始接受數據包了。主要涉及的函數包括:

#建立socket
socket.socket([family[, type[, proto]]]) #鏈接遠程地址
socket.connect(address) #綁定socket的IP地址和端口號
socket.bind(address) #從socket接收數據包
socket.recvfrom(bufsize[, flags]) #關閉socket
socket.close()

  socket模塊的使用比較簡單,網上有不少範例,這裏再也不詳細說明。

3、pyalsaaudio模塊

  pyalsaaudio(下載地址)是一個用於python中訪問ALSA API的模塊,利用這個模塊,用戶能夠很輕鬆的在程序中訪問Orangpi Zero的PCM和混音器設備,這個模塊的使用說明和範例在這個連接地址裏有。

  一、安裝pyalsaaudio模塊

  依次安裝python對應版本的setuptools、python-dev、libasoud2-dev和pyalsaaudio包便可。其中python-dev包與所使用的python版本有關,可使用python3 -V命令查看python版本,本做品armbian系統預裝了python3.5,因此要安裝python3.5-dev包。依次執行如下命令。

  (1)、安裝python3-setuptools命令:

apt-get install python3-setuptools

  (2)、安裝python3.5-dev命令:

apt-get install python3.5-dev

  (3)、安裝libasoud2-dev命令:

apt-get install libasound2-dev

  (4)最後,使用python的pip3命令安裝pyalsaaudio模塊:

pip3 install pyalsaaudio

  (5)上一步中的pip3命令,是爲了與python2區分的,armbian中預安裝了python2和python3,做者使用的是python3,若是直接使用pip命令,系統就會給python2安裝pyalsaaudio模塊了,因此這裏須要注意。若是提示沒有pip3命令,那就須要使用如下命令安裝pip3。安裝以後就可使用pip3命令操做第4步了。

apt-get install python3-pip

4、接收端程序設計

  接收端比較簡單,在Python環境下直接使用socket和pyalsaaudio模塊便可快速實現數據包的接收和播放,主要使用的pyalsaaudio模塊函數以下。

#默認的構造函數 #系統初始化alsa device,系統默認按如下參數配置:PCM、44.1kHz、雙通道、週期大小32幀 #Sample format: PCM_FORMAT_S16_LE #Rate: 44100 Hz #Channels: 2 #Period size: 32 frames
class alsaaudio.PCM(type=PCM_PLAYBACK, mode=PCM_NORMAL, device='default', cardindex=-1) #設置採樣率,以Hz爲單位。 #典型值是8000(電話)、16000、44100(CD音質)、48000(DVD音質)、96000
PCM.setrate(rate) #設置週期大小,用戶程序每次處理音頻數據的幀數, #即用戶程序每次要寫入(播放)/讀取(錄音)的數據大小 #以幀爲單位,一幀就是一次採樣的字節數
PCM.setperiodsize(period) #寫入待播放的音頻數據。 #data的數據長度必須是幀大小的整數倍, 而且等於週期大小。 #若是小於週期大小,則實際不會播放,直到程序把數據按照週期大小徹底寫入。
PCM.write(data)

  在《基於Orangpi Zero和Linux ALSA實現WIFI無線音箱(二)》中,做者設定了發送來的數據包前40個字節爲識別數據格式的包頭,真正的音頻數據是從第41字節開始。包頭數據的40個字節,實際就是C裏的WAVEFORMATEX結構體,包含採樣率、通道數、位深度信息,在python中,須要對這個結構體(數據包的開始的40字節)的數據進行解析讀取,這樣,才能正確設置pyalsaaudio的PCM類對象。

  要實現上述功能,在C裏,能夠直接把數據包首地址強制轉換成WAVEFORMATEX結構體類型的指針,再訪問各個成員變量便可,但是在python裏,沒有地址和指針的概念,須要使用struct模塊中的pack和unpack函數。

  struct模塊的pcak和unpcak函數是用來處理C結構數據的,經過它們能夠實現對字節數組的解釋。例如WAVEFORMATEX結構體的第2~3字節(以0開始)爲通道數,第4~7字節爲採樣率,unpack函數能夠把這些字節數組按照設定的要求進行轉換。兩個函數的詳細用法,能夠參考「Python中struct.pack()和struct.unpack()用法詳細說明」這篇文章。

  最後,接收端程序設計的流程和源碼以下:

  一、初始化socket

  二、初始化PCM類對象

  三、從socket接受數據(阻塞式)

  四、解釋數據包頭

  五、每隔1s判斷數據包頭指定的格式跟當前格式是否一致,若是不一致,則關閉PCM類對象並從新初始化

  六、播放從第41字節開始的音頻數據

  注意:程序中音頻格式只作了對採樣率的判斷,沒對位深度、通道數等信息的判斷,有興趣的讀者能夠自行添加。

import socket import alsaaudio import struct import time #函數:獲取IP地址
def GetHostIP(): try: s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.connect(('1.1.1.1', 80)) ip = s.getsockname()[0] finally: s.close() return ip #如下是主程序
RecCount = 0 #默認的PCM音頻格式,參考C裏面的WAVEFORMATEX結構體 #格式標識wFormatTag = 0xfe #通道數nChannels = 2 #採樣率nSamplesPerSec = 48000Hz #波特率nAvgBytesPerSec = 192000 #塊對齊nBlockAlign = 4 #位深度wBitsPerSample = 16
list_pwfx = [65534, 2, 48000, 192000, 4, 16] Local_IP=GetHostIP() print('說明') print('1.本機ip:%s:12321'%(Local_IP)) print('2.默認按照48000Hz、雙通道、16位PCM格式播放') print('3.發送端發出的數據包前40個字節爲音頻格式信息,接收端(本程序)每隔1s會解釋一次包頭,讀取並自動修改播放器採樣率信息(如發生變化)') print('4.注意:接收端(本程序)只支持1102五、12000、44100、48000這4種採樣率的自動切換,不支持修改通道數、位深度等其餘信息的切換。') print('5.發送端若是在後臺(如Windows的音頻管理器)修改了採樣率,必須從新點擊‘啓動’按鈕,才能從新發生正確的音頻流') #初始化socket
sss = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) sss.bind((Local_IP, 12321)) #系統初始化alsa device,系統默認按如下參數配置 #Sample format: PCM_FORMAT_S16_LE #Rate: 44100 Hz #Channels: 2 #Period size: 32 frames
device = alsaaudio.PCM() #修改默認採樣率爲48kHz
device.setrate(list_pwfx[2]) #修改緩衝區大小(以幀爲單位,0.1s是4800幀)
device.setperiodsize(list_pwfx[2]//10) Lasttime =time.time() while 1: #申請20k字節緩衝區
    BytesRecv,ServerAddr = sss.recvfrom(20000) #這裏是爲了讓程序自動更改播放音頻的採樣率,若是距離上次設置採樣率的時刻大於1s,
    #則讀取數據包的頭40個字節,判斷服務器傳過來的數據採樣率有無變化,從新設置採樣率,
    #只支持在1102五、12000、44100和48000間切換
    Nowtime = time.time() if (Nowtime-Lasttime) > 1 : Lasttime = Nowtime #解釋包頭(只取前16字節),具體請參考C裏面的WAVEFORMATEX結構體或文件開頭的說明
        #注意struct.unpack返回值是一個元組
        tuple_pwfx_temp = struct.unpack('HHLLHH',BytesRecv[:16]) #print(tuple_pwfx_temp)
        if tuple_pwfx_temp[2] != list_pwfx[2]: print('採樣率發生變化!') if tuple_pwfx_temp[2] in [11025,12000,44100,48000]: #把元組轉換爲列表,再賦值修改採樣率
                list_pwfx[2] =list(tuple_pwfx_temp)[2] #關閉設備並從新初始化設備
 device.close() device = alsaaudio.PCM() device.setrate(list_pwfx[2]) device.setperiodsize(list_pwfx[2]//10) print('採樣率正確,修改採樣率爲%s'%(list_pwfx[2])) else: print('採樣率錯誤!'%(list_pwfx[2])) #將socket接收到的數據送到device播放
    #收到的數據包,第41字節開始纔是音頻數據
    device.write(BytesRecv[40:]) print('RecCount=%s'%(RecCount),end='\r') RecCount+=1 device.close() sss.close()

  同時運行發送端程序和接收端程序,在發送端打開音樂播放器,這個時候,OrangPi接的音箱應該能播放音樂了。

5、設置python腳本開機自啓動

  好了,最後一步就是把這個python腳本設定成開機自啓動,這樣就不用每次登陸OrangPi Zero運行這個腳本,linux下實現python腳本開機自動啓動的方法也簡單,「Linux下Python腳本自啓動與定時任務詳解」這個文章有詳細介紹,修改一下系統配置文件便可。

相關文章
相關標籤/搜索