ゲームパッドで楽器を作る – PythonによるMIDI信号処理Programming

ゲームパッドをMIDIコントローラ・電子ピアノをMIDI音源として使い、オリジナルの楽器を作ってみました。 A(赤),B(黄),X(青),Y(緑)とSELECTボタンの組み合わせで、C調に属する7つのトライアド(三和音)を鳴らします。

Yボタンはトニック系和音,

Xボタンはドミナント系和音,

Bボタンにはサブドミナント系の和音を割り当てています。

Lボタンはトライアドのルートに対し7度のテンションを付加し、

Rボタンは9度のテンションを付加します。

Aボタンを押すと直前に鳴らした和音を再び鳴らします。

十字ボタンの左右でドミナントモーションを行うことができます。

右ボタンは4度上のトライアドを、左ボタンは5度下のトライアドを鳴らします。

上下ボタンにはオクターブを上下させる機能を持たせました。 例えば、Jazz風なII-V-Iの進行であれば、Bボタン+LRボタンでDm79、Xボタン+LボタンでG7、YボタンとRボタンでC9などとすることができます。

プログラミング環境さえあれば、アイデア次第でいくらでも新しい楽器を作り出せそうですね。 本実験のソースコードは固定コメントをご参照ください。 MIDI制御には弊社開発のMIDI信号処理ライブラリpymidiateを使用しています。

https://github.com/PGkids/pymidiate

Joystickの制御にはpygameを用いています。

## 本実験のソースコードです ##
    import midiate
    import pygame
    
    pygame.init()
    pygame.joystick.init()
    try:
        js = pygame.joystick.Joystick(0)
        js.init()
    except pygame.error:
        raise Exception('Joystick not found')
    
    mid = midiate.Midiator()
    mid.start_process()
    
    outdev= mid.open_output(name='CASIO USB-MIDI')
    mid.send(outdev, 'C000')
    
    JS_STOP  = 7  #終了ボタン
    JS_REPEAT = 0
    JS_T = 3
    JS_D = 2 
    JS_2 = 1
    JS_SD = 1
    JS_3 = 3
    JS_6 = 0 
    JS_7 = 2 
    
    JS_SHIFT = 6
    JS_ADD7 = 4
    JS_ADD9 = 5
    
    MAJOR = (0,2,4,5,7,9,11)
    MINOR = (0,2,3,5,7,8,10)
    TABLE = (JS_T, JS_2, None, None, JS_D)
    TABLE2 = (None,None,JS_3,JS_SD,None,JS_6,JS_7)
    
    def make_scale(xs):
        scale = []
        for octave in range(0,10):
            base = octave*12
            for x in xs:
                scale.append(base+x)
        return scale
    
    scale = make_scale(MAJOR)
    
    current_notes = []
    current_base_index = 7*4
    current_root_index = None
    add_7_needed = False
    add_9_needed = False
    shift = False
    
    def mkmsg(cmd,ch,x,y):
        return b'%d%X%02X%02X'%(cmd,ch,x,y)
    
    def noteoff_if_needed():
        global current_notes
        if current_notes:
            for note in current_notes:
                mid.send(outdev,mkmsg(8,0,note,0))
    
    def detect(notes):
        root_name = ('C','C#','D','D#','E','F','F#','G','G#','A','A#','B')[notes[0]%12]
        tone = '' if notes[1]-notes[0] == 4 else 'm'
        added = ''
        if len(notes) == 5:
            added = ' +7th,9th'
        if len(notes) == 4:
            m = notes[3] - notes[0];
            if m < 12: added = ' +7th'
            else: added = ' +9th' 
        return root_name + tone + added
    
    def exec_chord(notes):
        for note in notes:
            mid.send(outdev, mkmsg(9,0,note,127))
        print('   ',detect(notes))
    
    def noteon(index):
        noteoff_if_needed()
        global current_root_index
        current_root_index = index
        root = scale[index]
        third = scale[index+2]
        fifth = scale[index+4]
        global current_notes
        current_notes = [root,third,fifth]
        if add_7_needed: current_notes.append(scale[index+6])
        if add_9_needed: current_notes.append(scale[index+8])
        exec_chord(current_notes)
                     
    def repeat():
        noteoff_if_needed()
        if current_notes:
            exec_chord(current_notes)
                     
    while True:
        e = pygame.event.wait()
        if e.type==pygame.JOYBUTTONDOWN: #and e.button==JS_QUITBTN:
            btn = e.button
            if btn==JS_STOP:
                if current_notes:
                    noteoff_if_needed()
                    current_notes = None
                else: break
            elif not shift and btn in TABLE:
                index = TABLE.index(btn)
                noteon(current_base_index + index)
            elif shift and btn in TABLE2:
                index = TABLE2.index(btn)
                noteon(current_base_index + index)
            elif btn == JS_REPEAT: repeat()
            elif btn == JS_ADD7: add_7_needed = True
            elif btn == JS_ADD9: add_9_needed = True
            elif btn == JS_SHIFT: shift = True
             
        elif e.type==pygame.JOYBUTTONUP:
            btn = e.button
            if btn == JS_ADD7: add_7_needed = False
            elif btn == JS_ADD9: add_9_needed = False
            elif btn == JS_SHIFT: shift = False
    
        elif e.type==pygame.JOYAXISMOTION and e.axis==0 and current_root_index:
            if e.value > 0.5: 
                noteon(current_root_index + 3)
            elif e.value < -0.5: 
                noteon(current_root_index - 4)
    
        elif e.type==pygame.JOYAXISMOTION and e.axis==1:
            if e.value > 0.5 and current_base_index > 1: 
                current_base_index -= 7
                if current_root_index: current_root_index -= 7
            elif e.value < -0.5 and current_base_index < 50: 
                current_base_index += 7
                if current_root_index: current_root_index += 7
    
    mid.stop_process()

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください