前回、MIDI音楽を作成、再生するプログラムをPythonで作りましたが、そのプログラムでは再生中、他の処理がストップしてしまうという問題があります。今回は、スレッド処理を用いることで、他の処理を止めることなく音楽を演奏できるプログラムを作成してみました。
ソースコードはこちら
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 |
import time import pygame.midi import threading class myMIDI(threading.Thread): def __init__(self): super(myMIDI, self).__init__() pygame.midi.init() self.outp = pygame.midi.Output(0) self.outp.set_instrument(0) self.NOTES = dict(ド=60, レ=62, ミ=64, ファ=65, ソ=67, ラ=69, シ=71, 間=-1) self.pace = 1 self.sound_type = 1 self.daemon = True # Allow main to exit even if still running. self.paused = True # Start out paused. self.state = threading.Condition() def __del__(self): pygame.midi.quit() def tone(self, note, tmpo): self.outp.note_on(note, 100) time.sleep(tmpo / self.pace) self.outp.note_off(note, 100) def pause(self): with self.state: self.paused = True # Block self. def resume(self): with self.state: self.paused = False self.state.notify() # Unblock self if waiting. def run(self): self.resume() while True: with self.state: if self.paused: self.state.wait() # Block execution until notified. if self.paused == True: continue self.sound() def play_sound(self, doremi, tempo): for d, t in zip(doremi, tempo): if d == "間": time.sleep(t / self.pace) else: self.tone(self.NOTES[d], t) if self.paused == True: return def set_pace(self, p): try: if 1 <= p and p <= 5: self.pace = p except: pass def set_sound(self, s): try: if 1 <= s and s <= 3: self.sound_type = s except: pass def sound(self): if self.sound_type == 1: # カエルのうた doremi = ["ド", "レ", "ミ", "ファ", "ミ", "レ", "ド", "間", "ミ", "ファ", "ソ", "ラ", "ソ", "ファ", "ミ", "間", "ド", "ド", "ド", "ド", "ド", "ド", "レ", "レ", "ミ", "ミ", "ファ", "ファ", "ミ", "レ", "ド", "間"] tempo = [0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 1, 1, 1, 1, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.5, 0.5, 0.5, 1] elif self.sound_type == 2: doremi = ["ミ", "ファ", "ソ"] tempo = [0.5, 0.5, 0.5] else: doremi = ["ド", "ソ", "ミ"] tempo = [0.5, 0.5, 0.5] self.play_sound(doremi, tempo) if __name__ == "__main__": th = myMIDI(); for i in range(400): print(i) if i == 20: th.start() if i == 200: th.pause() th.set_pace(2) th.resume() if i == 270: th.pause() if i == 300: th.pause() th.set_sound(2) th.resume() if i == 350: th.pause() th.set_pace(3) th.set_sound(3) th.resume() time.sleep(0.1) |
ソースコードの説明
今回もMIDI演奏はpygameを用いています。インストールしていない人は以下のコマンドでインストールしてください。
|
1 |
pip install pygame |
|
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
class myMIDI(threading.Thread): def __init__(self): super(myMIDI, self).__init__() pygame.midi.init() self.outp = pygame.midi.Output(0) self.outp.set_instrument(0) self.NOTES = dict(ド=60, レ=62, ミ=64, ファ=65, ソ=67, ラ=69, シ=71, 間=-1) self.pace = 1 self.sound_type = 1 self.daemon = True # Allow main to exit even if still running. self.paused = True # Start out paused. self.state = threading.Condition() def __del__(self): pygame.midi.quit() def tone(self, note, tmpo): self.outp.note_on(note, 100) time.sleep(tmpo / self.pace) self.outp.note_off(note, 100) def pause(self): with self.state: self.paused = True # Block self. def resume(self): with self.state: self.paused = False self.state.notify() # Unblock self if waiting. def run(self): self.resume() while True: with self.state: if self.paused: self.state.wait() # Block execution until notified. if self.paused == True: continue self.sound() def play_sound(self, doremi, tempo): for d, t in zip(doremi, tempo): if d == "間": time.sleep(t / self.pace) else: self.tone(self.NOTES[d], t) if self.paused == True: return def set_pace(self, p): try: if 1 <= p and p <= 5: self.pace = p except: pass def set_sound(self, s): try: if 1 <= s and s <= 3: self.sound_type = s except: pass def sound(self): if self.sound_type == 1: # カエルのうた doremi = ["ド", "レ", "ミ", "ファ", "ミ", "レ", "ド", "間", "ミ", "ファ", "ソ", "ラ", "ソ", "ファ", "ミ", "間", "ド", "ド", "ド", "ド", "ド", "ド", "レ", "レ", "ミ", "ミ", "ファ", "ファ", "ミ", "レ", "ド", "間"] tempo = [0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 1, 1, 1, 1, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.5, 0.5, 0.5, 1] elif self.sound_type == 2: doremi = ["ミ", "ファ", "ソ"] tempo = [0.5, 0.5, 0.5] else: doremi = ["ド", "ソ", "ミ"] tempo = [0.5, 0.5, 0.5] self.play_sound(doremi, tempo) |
まずmyMIDIというクラスを作成しています。このクラスはthreading.Threadを継承しており、MIDIの再生、停止などを行います。threading.Threadのstartメソッドを実行してスレッドを開始します。startメソッドを複数回実行するとエラーになるので注意してください。スレッド開始後、音楽の停止はpauseメソッド、再開(再生)はresumeメソッドを使います。
また、このクラスにあるsoundメソッドで3つの音楽(?)を作成しています。1つ目は前回同様「カエルの歌」、2つ目と3つ目は適当な3音「ミファソ」と「ドソミ」です。set_soundメソッドで1~3を切り替えられるようにしています。さらにset_paceメソッドで音の速さを5段階で調節できるようになっています。set_paceメソッドの引数は1~5の整数を取ります。
|
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 |
if __name__ == "__main__": th = myMIDI(); for i in range(400): print(i) if i == 20: th.start() if i == 200: th.pause() th.set_pace(2) th.resume() if i == 270: th.pause() if i == 300: th.pause() th.set_sound(2) th.resume() if i == 350: th.pause() th.set_pace(3) th.set_sound(3) th.resume() time.sleep(0.1) |
このスクリプトを実行すると、カウントが20のときに「カエルの歌」の演奏が始まり、カウントが200のときにペースが速くなります。そしてカウント270でいったん演奏を止めたあと、300から別の音(ミファソ)、さらに350から別の音(ドソミ)に切り替わります。
実際に実行した様子はこちら
音が鳴っているときも数字がカウントアップされており、処理が止まっていないことが分かります。
いかがだったでしょうか。今回は最初から再生できる音を3つだけにしていましたが、次回は音楽データ(音符と長さ)を書いたファイルを読み込んでMIDI演奏するGUIプログラムを紹介したいと思います。
- 投稿タグ
- プログラミング