【その4】Pythonでメロディーを奏でたい-モジュール化とwavファイル出力
準備編 pythonでメロディーを作りたい - エンジニア戦記
第2回 【その2】pythonでメロディーを作りたい - エンジニア戦記
第3回 【その3】Pythonでメロディーを奏でたい - エンジニア戦記←前回
第4回 ここ
目標
モジュール化をして、作った音楽ファイルをwav形式にして保存したい。
環境
- python3
- windows10
- anaconda
- 第3回まで終わっている。
概要
前回は別ファイルに保存した音データを流してみた。
今回は
- 冗長化したソースを機能ごとに別ファイルに分ける
- 作った音楽をwav形式にして保存させる。
ソース
前回のソース
import wave import struct import numpy as np import pyaudio import sys from pylab import * def openfile(argv): a_file = open (argv,encoding="utf-8") befor_music = a_file.read() a_file.close() return befor_music def conversion(befor_music): befor_list = befor_music.split(',') return befor_list def createSinWave (A, f0, fs, length): data = [] for n in arange(length * fs): s = A * np.sin(2 * np.pi * f0 * n / fs) if s > 1.0: s = 1.0 if s < -1.0: s = -1.0 data.append(s) data = [int(x * 32767.0) for x in data] data = struct.pack("h" * len(data), *data) return data def play(data, fs, bit): p = pyaudio.PyAudio() stream = p.open(format=pyaudio.paInt16, channels=1, rate=int(fs), output=True) chunk = 1024 sp = 0 buffer = data[sp:sp+chunk] while buffer != b'': stream.write(buffer) sp = sp + chunk buffer = data[sp:sp+chunk] stream.close() p.terminate() if __name__ == "__main__" : freqList =conversion(openfile(sys.argv[1])) count = len(freqList) del freqList[count-1] freqList = [int(i) for i in freqList] for f in freqList: data = createSinWave(1.0, f, 8000.0, 2.0) play(data, 8000, 16)
ちょっと長い。見通しも悪い。
オブジェクト指向チックに機能ごとに分けてファイルを分割していく。
Cやjavaと違ってpythonは分割がとても楽。
モジュール
pythonはモジュールという概念がある。
大きな規模のプログラムを作るときは複数のプログラムに分けたくなる。
そのときに役立つのがモジュール(module)という機能。
pythonでは、スクリプトが書かれた1つのファイルを、1つのモジュールとして扱う。
そして、モジュールの中で定義した関数などを、別のファイルの中で利用できるように工夫されている。
そのために使うのがimport文。
よく一番上に書いてあるやつ。
import(モジュール名)
モジュールの例
hoge.py内でfuga()という関数を書いたとする。
- 別のpythonスクリプトから使うには import hoge と書く(拡張子は省略可能)。
- 同じディレクトリ内に2つのソースコードを置く。
- 関数を呼ぶときにはhoge.huga() と呼び出す。
つまりは
hoge.py
def fuga: print ("hello") return 0
をmain.pyから呼び出すには
main.py
import hoge
hoge.fuga()
これだけでいいのだ。
実行結果
$ python main.py hello
これを現在作っているファイルにも利用しよう。
実際に分割してみる。
作るファイルは
- File_open.py
- play_music.py
- test.py
の3つ
File_openには音楽ファイルの入力をまとめておく。
import sys import wave import struct import numpy as np from pylab import * def openfile(argv): a_file = open (argv,encoding="utf-8") befor_music = a_file.read() a_file.close() return befor_music def conversion(befor_music): befor_list = befor_music.split(',') return befor_list
test.pyは正弦波作成と音を鳴らす関数を。
(名前の適当さは許してほしい。)
import wave import struct import numpy as np from pylab import * def createSinWave (A, f0, fs, length): data = [] for n in np.arange(length * fs): s = A * np.sin(2 * np.pi * f0 * n / fs) if s > 1.0: s = 1.0 if s < -1.0: s = -1.0 data.append(s) data = [int(x * 32767.0) for x in data] data = struct.pack("h" * len(data), *data) return data def play(data, fs, bit): import pyaudio p = pyaudio.PyAudio() stream = p.open(format=pyaudio.paInt16, channels=1, rate=int(fs), output=True) chunk = 1024 sp = 0 buffer = data[sp:sp+chunk] while buffer != b'': stream.write(buffer) sp = sp + chunk buffer = data[sp:sp+chunk] stream.close() p.terminate()
play_music.pyはメイン関数として最終的な処理をさせる。
import struct import numpy as np from pylab import * #オリジナルモジュール import File_open import test #ここの2つで定義 if __name__ == "__main__" : freqList = File_open.conversion(File_open.openfile(sys.argv[1])) #File_openのopenfileとconversionを使って代入 count = len(freqList) del freqList[count-1] freqList = [int(i) for i in freqList] for f in freqList: data = test.createSinWave(1.0, f, 8000.0, 2.0) #test内のcreateSinWaveを利用 test.play(data, 8000, 16) #同様に。
そして前回作った音楽のテキストファイルを引数に実行すると
$python play_music.py music.txt
問題なく音楽がなるはず。
作った音楽ファイルをwavにして保存
キレイになったところで、機能の追加を。
いじるのはFile_open.pyとPlay_music.pyの2つ。
wavファイルはマイクロソフト社が開発した音声フォーマットでとても便利。
import waveでwavファイルの多くの機能を導入できる。
まずはopen_Fileに以下の関数を追加。
def save_wave(binwaves): w = wave.Wave_write("output.wav") p = (1, 2, 8000, len(binwaves), 'NONE', 'not compressed') w.setparams(p) w.writeframes(binwaves) w.close()
入力にbinwave(バイナリデータ)を。
Wave_write関数でoutput.wavとして出力させる。
音声フォーマットにはいろいろなステータスを入れる必要があるので、それらを一気に入れて、ファイルクローズ。
ちなみにpython3以降とpython2以前は扱い方が全く違う。
2のときはおそらくこのように書くことになる。
wf = wave.open(filename, "w") wf.setnchannels(1) wf.setsampwidth(bit / 8) wf.setframerate(fs) wf.writeframes(data) wf.close()
次はplay_music.pyを以下のように書き加える
if __name__ == "__main__" : freqList = File_open.conversion(File_open.openfile(sys.argv[1])) datas=b"" #ここと count = len(freqList) del freqList[count-1] freqList = [int(i) for i in freqList] for f in freqList: data = test.createSinWave(1.0, f, 8000.0, 2.0) test.play(data, 8000, 16) datas += data #ここと File_open.save_wave(datas) #ここ
datas=b"" で空のリストを作る。
これもバージョン2とは違って、3は文字列型に厳しくなったため、バイナリデータだと示す必要がある。
リスト名 = b"" のようにbをつけることでバイナリデータ型のリストを作れる。
datas = data で正弦波に変換した音をリストに代入していき
File_open.save_wave(datas) で先程作った保存機能を使っておしまい。
これでoutpot.wavができたはず。
準備編 pythonでメロディーを作りたい - エンジニア戦記
第2回 【その2】pythonでメロディーを作りたい - エンジニア戦記
第3回 【その3】Pythonでメロディーを奏でたい - エンジニア戦記←前回
第4回 ここ