Raspberry Pi, Jetson Nano向けオーディオ入出力ボードの作成(動作コード例編)

[前回](https://qiita.com/kmitsu76/items/5d273a5991a937df9882)作成したAK4558ENコーデックボードを実際使うためのボイラープレート的なコードを、まずJetson Nano上でまとめてみたい。

##最終スペック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/387653/42064e63-36ff-a416-b29e-023f6b527df5.png)
CODEC：AK4558EN
対応サンプリングレート：48kHz、96kHz、192kHz（DIPスイッチで手動設定）
電源：低ノイズLDO LT3042搭載
特徴：Raspberry pi,Jetson Nanoからのノイズを分離する、グラウンド・アイソレーター搭載
イヤホン駆動可能なOPA1612によるバッファ回路搭載
バッファ回路にVishay VFCP Series Z-foil無誘導金属箔抵抗を使用

##python　pyaudio
Jetson Nanoでは、[以前](https://qiita.com/kmitsu76/items/dec5701a52f2badd2379)alsaデバイスとして、tegra-snd-t210ref-mobile-rt565x: -(hw:1,0)というデバイス名で認識されることがわかっている。
下記プログラムで、デバイスインデックスを確認する。

``` python
import pyaudio as pa

if __name__ == "__main__":

    p_in = pa.PyAudio()
    print ("device num: {0}".format(p_in.get_device_count()))
    for i in range(p_in.get_device_count()):
        print (p_in.get_device_info_by_index(i))
```

上記を実行すると、

``` 
ALSA lib confmisc.c:1281:(snd_func_refer) Unable to find definition 'cards.tegra-hda.pcm.front.0:CARD=0'
ALSA lib conf.c:4528:(_snd_config_evaluate) function snd_func_refer returned error: No such file or directory
ALSA lib conf.c:5007:(snd_config_expand) Evaluate error: No such file or directory
ALSA lib pcm.c:2495:(snd_pcm_open_noupdate) Unknown PCM front
ALSA lib pcm.c:2495:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.rear
ALSA lib pcm.c:2495:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.center_lfe
ALSA lib pcm.c:2495:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.side
ALSA lib pcm.c:2495:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.surround21
ALSA lib pcm.c:2495:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.surround21
ALSA lib pcm.c:2495:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.surround40
ALSA lib pcm.c:2495:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.surround41
ALSA lib pcm.c:2495:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.surround50
ALSA lib pcm.c:2495:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.surround51
ALSA lib pcm.c:2495:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.surround71
ALSA lib pcm.c:2495:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.iec958
ALSA lib pcm.c:2495:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.iec958
ALSA lib pcm.c:2495:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.iec958
ALSA lib pcm.c:2495:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.modem
ALSA lib pcm.c:2495:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.modem
ALSA lib pcm.c:2495:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.phoneline
ALSA lib pcm.c:2495:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.phoneline
ALSA lib pcm_dmix.c:990:(snd_pcm_dmix_open) The dmix plugin supports only playback stream
ALSA lib pcm_hw.c:1713:(_snd_pcm_hw_open) Invalid value for card
ALSA lib pcm_hw.c:1713:(_snd_pcm_hw_open) Invalid value for card
ALSA lib pcm_hw.c:1713:(_snd_pcm_hw_open) Invalid value for card
ALSA lib pcm_hw.c:1713:(_snd_pcm_hw_open) Invalid value for card
ALSA lib pcm_dmix.c:1052:(snd_pcm_dmix_open) unable to open slave
device num: 18
{'index': 0, 'structVersion': 2, 'name': 'tegra-hda: HDMI 0 (hw:0,3)', 'hostApi': 0, 'maxInputChannels': 0, 'maxOutputChannels': 8, 'defaultLowInputLatency': -1.0, 'defaultLowOutputLatency': 0.005804988662131519, 'defaultHighInputLatency': -1.0, 'defaultHighOutputLatency': 0.034829931972789115, 'defaultSampleRate': 44100.0}
{'index': 1, 'structVersion': 2, 'name': 'tegra-snd-t210ref-mobile-rt565x: - (hw:1,0)', 'hostApi': 0, 'maxInputChannels': 16, 'maxOutputChannels': 16, 'defaultLowInputLatency': 0.008707482993197279, 'defaultLowOutputLatency': 0.008707482993197279, 'defaultHighInputLatency': 0.034829931972789115, 'defaultHighOutputLatency': 0.034829931972789115, 'defaultSampleRate': 44100.0}
{'index': 2, 'structVersion': 2, 'name': 'tegra-snd-t210ref-mobile-rt565x: - (hw:1,1)', 'hostApi': 0, 'maxInputChannels': 16, 'maxOutputChannels': 16, 'defaultLowInputLatency': 0.008707482993197279, 'defaultLowOutputLatency': 0.008707482993197279, 'defaultHighInputLatency': 0.034829931972789115, 'defaultHighOutputLatency': 0.034829931972789115, 'defaultSampleRate': 44100.0}
……
```

長い出力の中に、

```
{'index': 1, 'structVersion': 2, 'name': 'tegra-snd-t210ref-mobile-rt565x: - (hw:1,0)', 'hostApi': 0, 'maxInputChannels': 16, 'maxOutputChannels': 16, 'defaultLowInputLatency': 0.008707482993197279, 'defaultLowOutputLatency': 0.008707482993197279, 'defaultHighInputLatency': 0.034829931972789115, 'defaultHighOutputLatency': 0.034829931972789115, 'defaultSampleRate': 44100.0}
```


上記を見つけることができ、頭に'index': 1を確認できる。これを利用していく。

pyaudio ノンブロッキング音声同時入出力の例
https://people.csail.mit.edu/hubert/pyaudio/

``` python
import time
import numpy as np
import pyaudio

def callback(in_data, frame_count, time_info, status):
    
    d_in = np.frombuffer(in_data, dtype=np.float32)

    d_out = d_in * 2.0

    out_data = d_out.astype(np.float32)

    return  (out_data, pyaudio.paContinue)


def main():

    CHUNK= 48000
    RATE= 48000
    p=pyaudio.PyAudio()

    stream=p.open(	format = pyaudio.paFloat32,
		channels = 1,
		rate = RATE,
		frames_per_buffer = CHUNK,
		input = True,
        input_device_index = 1,
		output = True,
        output_device_index = 1,
        stream_callback=callback
        ) 

    stream.start_stream()

    while stream.is_active():
        time.sleep(1)
	
    stream.stop_stream()
    stream.close()
    p.terminate()


if __name__ == '__main__':
    main() 
```

Pyaudioドキュメントで紹介されているコードを参考。
ポイントは上記で確認したデバイスのインデックス番号１をinput_device_index = 1で指定すること。

この例は、1チャンネルの入出力を同時に開き、CHUNK(48000サンプル)がたまるとcallbackが呼ばれる。
Callback内では、in_dataにたまった音声データがfloat32のバイト列で用意される。
callbackの終わりにreturnでout_dataとして音声データを渡してあげると発声する流れ。
今回、numpyに入力し、信号を2倍（つまり6db音量を上げる）してout_dataに返す、簡単なリアルタイム信号処理の例。
pyaudio.paContinueはループして処理を継続するための返り値。

callbackは48000サンプル毎（1秒）毎に呼ばれる。callback内で1秒で終わらない重い処理をさせるとバッファがあふれてうまくいかない。

##python sounddevice
https://python-sounddevice.readthedocs.io/en/0.3.14/usage.html#simultaneous-playback-and-recording

上記と同等の動きが下記になる。

``` python
import time
import numpy as np
import sounddevice as sd


def callback(indata, outdata, frames, time, status):

    print("Loop start")

    d_in  = indata[: ,0]

    
    d_out = d_in * 2.0

    outdata[: ,0] = d_out

def main():

    CHUNK= 48000
    RATE=48000

    try:
        with sd.Stream(device=1,
                   samplerate=RATE, blocksize=CHUNK,
                   dtype=np.float32,
                   channels=1, callback=callback
                   ):
            print('#' * 80)
            print('press Return to quit')
            print('#' * 80)
            input()
    except KeyboardInterrupt:
        parser.exit('')
    except Exception as e:
        parser.exit(type(e).__name__ + ': ' + str(e))

if __name__ == '__main__':
    main()
```


同じくcallbackで処理するが、若干値の拾い方、戻し方が違う。
音声入力は（データ、チャンネル）のタプルで帰ってくるようなので、1チャンネルの場合は上記のように指定する。


##Raspberry piの場合
Jetson Nanoと同様にデバイスインデックスを確認する

```
Expression 'alsa_snd_pcm_hw_params_set_period_size_near( pcm, hwParams, &alsaPeriodFrames, &dir )' failed in 'src/hostapi/alsa/pa_linux_alsa.c', line: 924
Expression 'alsa_snd_pcm_hw_params_set_period_size_near( pcm, hwParams, &alsaPeriodFrames, &dir )' failed in 'src/hostapi/alsa/pa_linux_alsa.c', line: 924
ALSA lib pcm.c:2565:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.front
ALSA lib pcm.c:2565:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.rear
ALSA lib pcm.c:2565:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.center_lfe
ALSA lib pcm.c:2565:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.side
ALSA lib pcm.c:2565:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.surround21
ALSA lib pcm.c:2565:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.surround21
ALSA lib pcm.c:2565:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.surround40
ALSA lib pcm.c:2565:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.surround41
ALSA lib pcm.c:2565:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.surround50
ALSA lib pcm.c:2565:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.surround51
ALSA lib pcm.c:2565:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.surround71
ALSA lib pcm.c:2565:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.iec958
ALSA lib pcm.c:2565:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.iec958
ALSA lib pcm.c:2565:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.iec958
ALSA lib pcm.c:2565:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.hdmi
ALSA lib pcm.c:2565:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.hdmi
ALSA lib pcm.c:2565:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.modem
ALSA lib pcm.c:2565:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.modem
ALSA lib pcm.c:2565:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.phoneline
ALSA lib pcm.c:2565:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.phoneline
ALSA lib confmisc.c:1281:(snd_func_refer) Unable to find definition 'defaults.bluealsa.device'
ALSA lib conf.c:4568:(_snd_config_evaluate) function snd_func_refer returned error: No such file or directory
ALSA lib conf.c:5036:(snd_config_expand) Args evaluate error: No such file or directory
ALSA lib pcm.c:2565:(snd_pcm_open_noupdate) Unknown PCM bluealsa
ALSA lib confmisc.c:1281:(snd_func_refer) Unable to find definition 'defaults.bluealsa.device'
ALSA lib conf.c:4568:(_snd_config_evaluate) function snd_func_refer returned error: No such file or directory
ALSA lib conf.c:5036:(snd_config_expand) Args evaluate error: No such file or directory
ALSA lib pcm.c:2565:(snd_pcm_open_noupdate) Unknown PCM bluealsa
Expression 'alsa_snd_pcm_hw_params_set_period_size_near( pcm, hwParams, &alsaPeriodFrames, &dir )' failed in 'src/hostapi/alsa/pa_linux_alsa.c', line: 924
Cannot connect to server socket err = No such file or directory
Cannot connect to server request channel
jack server is not running or cannot be started
JackShmReadWritePtr::~JackShmReadWritePtr - Init not done for -1, skipping unlock
JackShmReadWritePtr::~JackShmReadWritePtr - Init not done for -1, skipping unlock
device num: 4
{'index': 0, 'structVersion': 2, 'name': 'bcm2835 ALSA: IEC958/HDMI (hw:0,1)', 'hostApi': 0, 'maxInputChannels': 0, 'maxOutputChannels': 8, 'defaultLowInputLatency': -1.0, 'defaultLowOutputLatency': 0.0016099773242630386, 'defaultHighInputLatency': -1.0, 'defaultHighOutputLatency': 0.034829931972789115, 'defaultSampleRate': 44100.0}
{'index': 1, 'structVersion': 2, 'name': 'bcm2835 ALSA: IEC958/HDMI1 (hw:0,2)', 'hostApi': 0, 'maxInputChannels': 0, 'maxOutputChannels': 8, 'defaultLowInputLatency': -1.0, 'defaultLowOutputLatency': 0.0016099773242630386, 'defaultHighInputLatency': -1.0, 'defaultHighOutputLatency': 0.034829931972789115, 'defaultSampleRate': 44100.0}
{'index': 2, 'structVersion': 2, 'name': 'snd_rpi_simple_card: simple-card_codec_link snd-soc-dummy-dai-0 (hw:1,0)', 'hostApi': 0, 'maxInputChannels': 2, 'maxOutputChannels': 2, 'defaultLowInputLatency': 0.005804988662131519, 'defaultLowOutputLatency': 0.005804988662131519, 'defaultHighInputLatency': 0.034829931972789115, 'defaultHighOutputLatency': 0.034829931972789115, 'defaultSampleRate': 44100.0}
{'index': 3, 'structVersion': 2, 'name': 'dmix', 'hostApi': 0, 'maxInputChannels': 0, 'maxOutputChannels': 2, 'defaultLowInputLatency': -1.0, 'defaultLowOutputLatency': 0.021333333333333333, 'defaultHighInputLatency': -1.0, 'defaultHighOutputLatency': 0.021333333333333333, 'defaultSampleRate': 48000.0}
```

下記の通り、simple-card_codec_link snd-soc-dummy-dai-0 (hw:1,0)が
インデックス2で見つかった。

```
{'index': 2, 'structVersion': 2, 'name': 'snd_rpi_simple_card: simple-card_codec_link snd-soc-dummy-dai-0 (hw:1,0)', 'hostApi': 0, 'maxInputChannels': 2, 'maxOutputChannels': 2, 'defaultLowInputLatency': 0.005804988662131519, 'defaultLowOutputLatency': 0.005804988662131519, 'defaultHighInputLatency': 0.034829931972789115, 'defaultHighOutputLatency': 0.034829931972789115, 'defaultSampleRate': 44100.0}
```

上記コード内を修正して動作させる。