音声転送プログラム
■前書き
現代において
インターネット回線を使ってPC同士で会議とか、通話とかする事が出来ます。
今回はその初歩的部分。
pythonを使って一方通行ですが、
音声を送るプログラムを作ってみたので、そのプログラムとセットアップ方法を
記録しておきます。
■構成
マイク-PC1-(LAN)-PC2-アンプスピーカー
■方式
PC1
PyAudioでマイクから音声を入力。44.1Khz モノラル 16Bit
そのままUDPで宛先を指定して送信
PC2
UDPでデータを受け取って
PyAudioでスピーカーに音声を入力。44.1Khz モノラル 16Bit
※この方式の欠点
全く圧縮していないので、ネットワーク帯域を結構使う。
といっても、計算すると
44100×2×8=約71Kbps
ぐらいですので、構内LANで単独使用なら問題ないかなーと思います。
UDPなので、順序保証とか、データの保障が無い。
とはいえ、こちらも多少ノイズとして聞こえるぐらいの
影響しかないかなーと思います。
送信側と受信側で同期を取っていないので、長時間使うと遅延したり、
バッファーがオーバーフローする可能性あり。
送受信の両方でエラー時に自動的に再起動するとか、対処必要になると
思われ。
========================
セットアップ
========================
■ファイル配置
/usr/local/bin/audio-transferに所定のファイルを配置していく
[root@localhost audio-transfer]# pwd
/usr/local/bin/audio-transfer
[root@localhost audio-transfer]# ls -al
合計 12
drwxr-xr-x. 2 root root 57 3月 21 09:42 .
drwxr-xr-x. 3 root root 28 3月 21 09:42 ..
-rw-r--r--. 1 root root 1241 3月 21 09:42 recv.py
-rw-r--r--. 1 root root 1526 3月 21 09:42 send.py
-rw-r--r--. 1 root root 1248 3月 21 09:42 show-audio.py
[root@localhost audio-transfer]#
■python準備
Windowsも手順は同じ。
以下はCentOSの場合を記載しています。
[root@localhost audio-transfer]# python --version
Python 2.7.5
古い。せめて3系にしたいなー
って事で・・・
yum install -y python36
yum install -y python36-devel
pip3 install pyaudio
■テスト
python3.6 show-audio.py
これで、パソコンに入っているオーディオの入出力ポートが一覧出力される。
Device 0: HDA Intel HDMI: HDMI 0 (hw:0,3), Channels: 0
Device 1: HDA Intel HDMI: HDMI 1 (hw:0,7), Channels: 0
Device 2: HDA Intel HDMI: HDMI 2 (hw:0,8), Channels: 0
Device 3: HDA Intel HDMI: HDMI 3 (hw:0,9), Channels: 0
Device 4: HDA Intel HDMI: HDMI 4 (hw:0,10), Channels: 0
Device 5: HDA Intel PCH: ALC269VC Analog (hw:1,0), Channels: 2
Device 6: hdmi, Channels: 0
Device 7: pulse, Channels: 32
Device 8: default, Channels: 32
しかし、このままではどれがマイクからの入力なのかわからん。
なので、0から順番に試していって、
5番がそれである事を突き止めて、
input_device_index = 5 #
に決定。
だけど、一瞬つながった後にエラーになる。
その一瞬に送信側でなにか叫ぶと受信側のスピーカーから音が鳴る。
少し成功。あと一歩です。
Traceback (most recent call last):
File "send.py", line 42, in <module>
data = stream.read(chunk)
File "/usr/local/lib64/python3.6/site-packages/pyaudio/__init__.py", line 571, in read
exception_on_overflow)
OSError: [Errno -9981] Input overflowed
[root@localhost audio-transfer]#
バッファーが足りなくなるとダメっぽいので5倍にする。
「chunk = 50240」に変更
Traceback (most recent call last):
File "send.py", line 45, in <module>
sock.sendto(data, addr)
OSError: [Errno 90] Message too long
[root@localhost audio-transfer]#
今度はUDP送信でメッセージが長いって。
試行錯誤・・・・
結局googleで探して
音声をマイクから読み込む部分に「exception_on_overflow=False」を追加。
data = stream.read(chunk, exception_on_overflow=False)
いちおうこれで落ち無くなった。
いろいろいじってると判るんだけど、
chunkを大きくすると遅延が出ることが判明。
結局chunkは元に戻して1024とする。
「chunk = 1024」
■解決
音はクリアですね。デジタル通信なんだから当たり前といえば当たり前。
しかし、圧縮してないので、理論的に考えて普通のIP電話よりクリアですね。
帯域さえ確保できれば(といいつつも、それほど大きな帯域でもないし)
44.1Khz/16Bitですから、CD音質で音を運ぶのは現代の通信回線において余裕です
■起動
受信側PC(Windows)
python3.6 recv.py
※受信側はOSのFW設定でUDPの5005を開けてください。
送信側PC(CentOS)
python3.6 send.py
■結果のソース。
「送信側」
import pyaudio
import socket
import sys
# ネットワーク設定
UDP_IP = "192.168.10.111" # 送信先のIPアドレス
UDP_PORT = 5005 # 送信先のポート番号
addr = (UDP_IP, UDP_PORT)
# PyAudioの設定
chunk = 1024 # 音声データのチャンクサイズ
#chunk = 10240 # 音声データのチャンクサイズ
format = pyaudio.paInt16 # 音声のフォーマット
channels = 1 # モノラル
rate = 44100 # サンプリングレート
input_device_index = 5 #
# UDPソケットの初期化
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# PyAudioの初期化
p = pyaudio.PyAudio()
# 音声入力ストリームの開始
stream = p.open(format=format,
channels=channels,
rate=rate,
input=True,
frames_per_buffer=chunk,
input_device_index=input_device_index)
# 音声出力ストリームの開始
#output_stream = p.open(format=format,
# channels=channels,
# rate=rate,
# output=True,
# frames_per_buffer=chunk)
print("録音を開始します。CTRL+Cで終了します。")
try:
while True:
data = stream.read(chunk, exception_on_overflow=False)
#output_stream.write(data) # スピーカーから音声出力
sock.sendto(data, addr) # UDPで音声データを送信
except KeyboardInterrupt:
print("録音を終了します。")
# ストリームを閉じる
stream.stop_stream()
stream.close()
#output_stream.stop_stream()
#output_stream.close()
p.terminate()
# ソケットを閉じる
sock.close()
「受信側」
import pyaudio
import socket
import sys
# ネットワーク設定
HOST = "" # 送信先のIPアドレス
PORT = 5005 # 送信先のポート番号
# PyAudioの設定
chunk = 102400 # 音声データのチャンクサイズ
format = pyaudio.paInt16 # 音声のフォーマット
channels = 1 # モノラル
rate = 44100 # サンプリングレート
# UDPソケットの初期化
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind((HOST, PORT))
# PyAudioの初期化
p = pyaudio.PyAudio()
# 音声出力ストリームの開始
output_stream = p.open(format=format,
channels=channels,
rate=rate,
output=True,
frames_per_buffer=chunk)
print("受信を開始します。CTRL+Cで終了します。")
try:
while True:
data, address = sock.recvfrom(chunk*2)
#print(f"address: {address}")
output_stream.write(data) # スピーカーから音声出力
except KeyboardInterrupt:
print("受信を終了します。")
# ストリームを閉じる
output_stream.stop_stream()
output_stream.close()
p.terminate()
# ソケットを閉じる
sock.close()
「オーディオデバイス確認」
import pyaudio
p = pyaudio.PyAudio()
# 使用可能なオーディオデバイスのリストを表示
for i in range(p.get_device_count()):
info = p.get_device_info_by_index(i)
print(f"Device {i}: {info['name']}, Channels: {info['maxInputChannels']}")
# 目的のデバイスのインデックスを選択
input_device_index = 0 # 例えば、リストから選んだ入力デバイスのインデックス
output_device_index = 2 # 例えば、リストから選んだ出力デバイスのインデックス
# 音声入力ストリームの開始(特定のデバイスを使用)
stream = p.open(format=pyaudio.paInt16,
channels=1,
rate=44100,
input=True,
frames_per_buffer=1024,
input_device_index=input_device_index) # 入力デバイスを指定
# 音声出力ストリームの開始(特定のデバイスを使用)
output_stream = p.open(format=pyaudio.paInt16,
channels=1,
rate=44100,
output=True,
frames_per_buffer=1024,
output_device_index=output_device_index) # 出力デバイスを指定