実習 6.Raspberry Pi
の音声利用

内 容 (2020,12,16更新)


6. Raspberry Pi の音声利用

第6章ではRaspberry Piを音声利用します.

Raspberry Piには音声出力端子(3.5mmステレオミニジャック)がありますので普通のミニプラグ付きヘッドホンなどからステレオ音声を出力できます.この章では,音楽ファイルをpythonプログラムで出力します.また,Waveファイルのしくみを説明します.そして,音声データを数式で作成して,そのまま音声出力します.


6-1. pythonでmp3ファイル利用

ここでは,MP3ファイルをpythonプログラムで読み込み,そのデータを音声出力します.音声データを再生/停止する基本的なプログラムです.GPIOを利用してボタンから操作します.

(1)pythonプログラムで音声ファイルを再生

以下の手順でpythonプログラムを作成して,音声を再生しましょう.

  1. エディタ(nanoなど)から以下のプログラムを入力しs_test.pyのファイル名で保存
    
    リスト 1. s_test.py
    001 import time, signal, sys
    002 import pygame.mixer
    003
    004 alarm_music = "test1.mp3"
    005 play_volume = 100
    006
    007 pygame.mixer.init()
    008 pygame.mixer.music.load( alarm_music )
    009 pygame.mixer.music.set_volume( play_volume/100 )
    010
    011 pygame.mixer.music.play(loops=0)
    012 while True:
    013     if(pygame.mixer.music.get_busy()!=True):
    014         break
    015     time.sleep( 0.2 )
    016 pygame.mixer.music.stop()
    017
    
  2. wgetコマンドで音声ファイルをget(自分の好みの音楽ファイルをカレンとディレクトリに保存してもよい)
    
    $ wget http://icrus.org/raspi/test0.mp3 ↵
    
    $ wget http://icrus.org/raspi/test1.mp3 ↵
    
  3. 音声出力をHDMIからステレオジャックに切り替えるためのコマンドを実行(必要に応じて随時実行してください.raspberry pi立ち上げ時はHDMI出力になっていることがあります )【現状使用できませんので,ヘッドホンジャックに出力する場合は画面右上のスピーカーアイコンを右クリックして「Analog」を選んでください.(HDIMディスプレイのスピーカーから出力する場合は「HDMI」選択)】
    
    $ amixer cset numid=3 1 ↵
    
    (HDMIに戻すとき)$ amixer cset numid=3 2 ↵
    
  4. プログラムs_test.pyを実行
    
    $ python s_test.py ↵
    
  5. プログラムの簡単な説明

mp3音声ファイルは再生できましたか?自分の好みのmp3ファイルをカレンとディレクトリに置き,s_test.pyの音声ファイル名を変更して再生してみましょう.

(2)GPIOで音声ファイルの再生を制御

次にGPIOを使って,ブレッドボード上のスイッチで音声ファイルの再生を制御してみましょう.

  1. エディタで以下のプログラムを入力し,s_gpio.pyのファイル名で保存
    
    リスト 2. s_gpio.py
    001 import time, signal, sys
    002 import RPi.GPIO as GPIO
    003 import pygame.mixer
    004
    005 alarm_music = "test1.mp3"
    006 play_volume = 100
    007 switch_pin = 4
    008 out_pin = 17
    009
    010 GPIO.cleanup()
    011 GPIO.setmode( GPIO.BCM )
    012 GPIO.setup( switch_pin, GPIO.IN )
    013 GPIO.setup( out_pin, GPIO.OUT )
    014
    015 pygame.mixer.init()
    016 pygame.mixer.music.load( alarm_music )
    017 pygame.mixer.music.set_volume( play_volume/100 )
    018
    019 pygame.mixer.music.play(-1)
    020 b_play=True
    021 while True:
    022     if b_play==True:
    023         GPIO.output( out_pin, GPIO.HIGH )
    024     else:
    025         GPIO.output( out_pin, GPIO.LOW )
    026
    027     if GPIO.input( switch_pin ) == GPIO.HIGH:
    028         if b_play==True:
    029             pygame.mixer.music.pause()
    030             b_play=False
    031         else:
    032             pygame.mixer.music.unpause()
    033             b_play=True
    034         time.sleep(0.2)
    035     time.sleep( 0.1 )
    036
    
  2. 回路図のように回路をブレッドボード上に作成する

    図1 再生制御回路図

  3. プログラムs_gpio.pyを実行
    
    $ python s_gpio.py ↵
    
  4. スイッチを押すと再生停止,次に押すと,再生開始される(繰り返し).再生時にLED点灯
  5. プログラムの簡単な説明

mp3音声ファイルは再生停止はできましたかできましたか?以下に示すURLを参考にすると,いろいろな,命令を実行できます.スイッチを追加して,頭から再生するボタン(など)を作ってみてください.

<参考URL>
http://westplain.sakuraweb.com/translate/pygame/Music.cgi


6-2. Waveファイルの作成

Waveファイル(拡張子wav)には圧縮なしの音データが入っています.mp3ファイル等は1/10~1/20のデータ圧縮が行われており,伸張した場合,特性の劣化が起こりますが,WAVEファイルは録音時そのままのデータを記録します.よって,数式などにより簡単に好みの波形を出力できます.ではWAVEファイルを作成してみましょう.

(1)WAVEファイルの作成

次にpythonプログラムを使って,WAVEファイルを作ってみましょう.

  1. エディタで以下のプログラムを入力し,s_wave.pyのファイル名で保存
    
    リスト 3. s_wave.py
    010 # coding: utf-8
    020 import numpy as np # 数値計算拡張モジュール
    030 import wave        # WAVEファイル処理モジュール
    040 import struct      # 構造体モジュール
    050 
    060 # Cosine wave
    070 def create_wave( A, f0, fs, t ): # A 振幅, f0 周波数, fs サンプリング周波数, t 時間[s]
    080     point = np.arange( 0, fs*t ) # 数列 0~fs*t-1
    090     cos_wave = A * np.cos( 2*np.pi*f0*(point)/fs ) # cos()による数列
    100     cos_wave = [ int(x * 32767.0) for x in cos_wave ] # 符号付き16ビットの整数列にする(WAVEのデータ)
    110     wave_data = struct.pack( "h"*len(cos_wave), *cos_wave )
    120     w = wave.Wave_write( "440Hz.wav" )
    130     p = (1, 2, fs, len(wave_data), 'NONE', 'not compressed')
    140     w.setparams( p )
    150     w.writeframes( wave_data )
    160     w.close()
    170 
    180     print( len(point), len(cos_wave) )
    190 
    200 A       = 1
    210 fs      = 44100
    220 f0      = 440
    230 seconds = 10
    240 create_wave( A, f0, fs, seconds )
    
  2. プログラムs_wave.pyを実行
    
    $ python s_wave.py ↵
    
  3. できあがった440Hz.wavを再生する
    
    $ aplay 440Hz.wave ↵
    

    440Hzの正弦波が再生されます.この音はよく調律に使われるA4の音(「ラ」)です

  4. プログラムの簡単な説明
  5. 440Hz.wavをs_gpio.pyに組み入れてボタンで操作してみてください.

(2)ステレオ仕様のWAVEファイルの作成

pythonプログラムを使って,ステレオ仕様のWAVEファイルを作ってみましょう.

  1. エディタで以下のプログラムを入力し,s_wave_st.pyのファイル名で保存
    
    リスト 4. s_wave_st.py
    010 # coding: utf-8
    020 import numpy as np # 数値計算拡張モジュール
    030 import wave        # WAVEファイル処理モジュール
    040 import struct      # 構造体モジュール
    050 
    060 # Cosine wave
    070 def create_wave( A, f0, fs, t ): # A 振幅, f0 周波数, fs サンプリング周波数, t 時間[s]
    080     point = np.arange( 0, fs*t ) # 数列 0~fs*t-1
    090     cos_wave0 = A * np.cos( 2*np.pi*f0  *(point)/fs ) # cos()による数列
    100     cos_wave1 = A * np.cos( 2*np.pi*f0/2*(point)/fs ) # cos()による数列
    110 
    120     cos_wave = [0]*len(cos_wave0)*2
    130     for i in range(len(cos_wave0)):
    140         cos_wave[i*2]   = cos_wave0[i]
    150         cos_wave[i*2+1] = cos_wave1[i]
    160 
    170     cos_wave = [ int(x * 32767.0) for x in cos_wave ] # 符号付き16ビットの整数列にする(WAVEのデータ)
    180 
    190     wave_data = struct.pack( "h"*len(cos_wave), *cos_wave )
    200     w = wave.Wave_write( "st440Hz.wav" )
    210     p = (2, 2, fs, len(cos_wave), 'NONE', 'not compressed')
    220         #チャンネル数,
    230         #サンプルサイズ(バイト), サンプリング周波数, フレーム数,
    240         #圧縮形式, 圧縮形式を人に判読可能な形 'NONE'に対して'not compressed'
    250     w.setparams( p )
    260     w.writeframes( wave_data )
    270     w.close()
    280 
    290     print( len(point), len(cos_wave) )
    300 
    310 
    320 # main #########################
    330 A       = 1
    340 fs      = 44100
    350 f0      = 440
    360 seconds = 5
    370 create_wave( A, f0, fs, seconds )
    
  2. プログラムs_wave_st.pyを実行
    
    $ python s_wave_st.py ↵
    
  3. できあがったst440Hz.wavを再生する
    
    $ aplay st440Hz.wave ↵
    

    440Hzの正弦波(左)と,220Hzの音(右)が同時に再生されます

  4. プログラムの簡単な説明
  5. st440Hz.wavをs_gpio.pyに組み入れてボタンで操作してみてください.
  6. <応用>s_wave_st.pyを応用して,音像が右→正面→左→正面→右と繰り返し動くWAVEファイルを作ってみましょう.

<参考URL>
https://qiita.com/kinaonao/items/c3f2ef224878fbd232f5


6-3. pythonで音声処理(20201216利用不可)

次にWAVEデータを作成しつつ,オーディオデバイスを直接操り,音声を出力します.

(1)pyaudioテストプログラムの実行

  1. インストールコマンドを実行しpyaudioを準備
    
    $ sudo apt-get install python-pyaudio python3-pyaudio ↵
    
  2. 次のプログラムを入力しファイル名test_pyaudio.pyで保存
    リスト 5 test_pyaudio.py
    001 #coding: utf-8
    002 import wave
    003 import pyaudio
    004
    005 def printWaveInfo(wf):
    006     """WAVEファイルの情報を取得"""
    007     print "チャンネル数:", wf.getnchannels()
    008     print "サンプル幅:", wf.getsampwidth()
    009     print "サンプリング周波数:", wf.getframerate()
    010     print "フレーム数:", wf.getnframes()
    011     print "パラメータ:", wf.getparams()
    012     print "長さ(秒):", float(wf.getnframes()) / wf.getframerate()
    013
    014 if __name__ == '__main__':
    015     wf = wave.open("440Hz.wav", "r")
    016
    017     printWaveInfo(wf)
    018
    019     # ストリームを開く
    020     p = pyaudio.PyAudio()
    021     stream = p.open(format=p.get_format_from_width(wf.getsampwidth()),
    022                     channels=wf.getnchannels(),
    023                     rate=wf.getframerate(),
    024                     output=True)
    025
    026     # チャンク単位でストリームに出力し音声を再生
    027     chunk = 1024
    028     data = wf.readframes(chunk)
    029     while data != '':
    030         stream.write(data)
    031         data = wf.readframes(chunk)
    032     stream.close()
    033     p.terminate()
    
  3. プログラムを実行する
    
    $ python test_pyaudio.py  ↵
    
    結果 音が途切れと途切れになり,現状で対策は見つかっていません.
  4. <参考サイト>http://home.a00.itscom.net/hatada/ssp/pyaudio01.html

    本来は,プログラム中のdataに手を加え音を加工する予定でしたがそこまで行けませんでした.結論,Raspberry PiでpyAudioがうまく動きませんでした.少し時間をかけてトライする必要がありそうです.または,OSバージョンのためかも知れません.pyAudioの項については,パソコン上のLinuxで試してみることをお勧めします.時間があれば,C言語で直接デバイスをたたく方法もあるかも知れませんが,OSやアプリのバージョンアップを待った方が早そうです.それよりも,音声出力を使って,独自にラズパイ的なGPIOなどを使ったアプリケーションを作ったほうがいいかもしれません.時間があれば今日動かなかった,これらについても研究してください.


6-4. C言語で音声処理

音データを作成しつつ,オーディオデバイスを直接操り,音声を出力します.

(1)alsa_test1.cの実行

  1. インストールコマンドを実行しライブラリを準備する
    
    $ sudo apt-get install libasound2-dev libesd0 ↵
    
  2. プログラムを2つダウンロードする
    
    $ wget http://icrus.org/raspi/alsa_test1.c ↵
    $ wget http://icrus.org/raspi/sinewave.c ↵
    
  3. プログラムをコンパイル実行する
    
    $ gcc alsa_test1.c -lasound -lm -o alsa_test1 ↵
    $ ./alsa_test1 ↵
    
  4. プログラムalsa_test1.cの概要説明

(2)alsa_test2.cの実行

  1. プログラムをダウンロードする
    
    $ wget http://icrus.org/raspi/alsa_test2.c ↵
    
  2. プログラムをコンパイル実行する
    
    $ gcc alsa_test2.c -lasound -lm -o alsa_test2 ↵
    $ ./alsa_test2 default off st440Hz.wav ↵
    
  3. プログラムの概要説明
  4. pythonを利用するときも,このCプログラムをラップして呼び出せるようにすると,pythonプログラム中で音データを作成して,デバイスに音出力できるようになります.

    <参考サイト/文献>コンピュータ搭載! Linuxオーディオの作り方 CQ出版社刊 2018年1月 https://shop.cqpub.co.jp/hanbai/books/47/47071.html (ダウロードファイルがサイトに有)(第9章 Linux用超定番サウンドI/Oライブラリ ALSA入門) 

<その他参考サイト>
http://home.a00.itscom.net/hatada/ssp/pyaudio01.html
PyAudio入門

https://qiita.com/Nyanpy/items/cb4ea8dc4dc01fe56918#2pyaudio
pythonで音楽再生する方法

http://takeshid.hatenadiary.jp/entry/2016/01/10/153503
PyAudioの基本メモ2 音声入出力

https://qiita.com/Nyanpy/items/cb4ea8dc4dc01fe56918
Raspberry Piで音楽(wav/mp3)ファイルを再生する方法 python編

IT女子のラズベリーパイ入門奮闘記
http://deviceplus.jp/hobby/raspberrypi_entry_012/

https://ja.stackoverflow.com/questions/23655/raspberry-pi%E3%81%A7pyaudio%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%A6wave%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%8C%E6%AD%A3%E5%B8%B8%E3%81%AB%E9%B3%B4%E3%82%89%E3%81%AA%E3%81%84
Raspberry PiでPyAudioを使ってwaveファイルが正常に鳴らない

足立工科大学情報通信工学科 2017年11月28日(火)

更新2020,12,16(水)