inst39.py - ポケット・ミクで楽器音を鳴らす。 #大人の科学ポケミク
u'''inst39.py - ポケット・ミクを楽器に ミクさんの代わりに楽器が鳴るよ。 「A I U E O」ボタンで楽器変更。 「SHIFT」で音を伸ばす。 「VIBRATO」でビブラート。 ''' import pygame import pygame.midi from pygame.locals import * WIDTH = 600 # 画面の大きさ HEIGHT = WIDTH // 2 COLOR = 0, 255, 200 # 文字色 BG_COLOR = 100, 0, 50 # 背景色 FONT_SIZE = HEIGHT // 5 FPS = 60 MODURATION = 64 # モジュレーションの値 # ポケミクのボタン BUTTON_A = 0x01 BUTTON_I = 0x02 BUTTON_U = 0x04 BUTTON_E = 0x08 BUTTON_O = 0x10 BUTTON_VIBRATO = 0x20 BUTTOU_SHIFT = 0x40 POKEMIKU_NOTE = 78 # ポケミクから送出されるMIDIノートナンバー GM = { # ノートNo.:(名前, 音域下限, 音域上限) 1:('Acoustic Grand Piano', 21, 108), 2:('Bright Acoustic Piano', 21, 108), 3:('Electric Grand Piano', 21, 108), 4:('Honky-tonk Piano', 21, 108), 5:('Electric Piano1', 28, 103), 6:('Electric Piano 2', 28, 103), 7:('Harpsichord', 41, 89), 8:('Clavi', 36, 96), 9:('Celesta', 60, 108), 10:('Glockenspiel', 72, 108), 11:('Music Box', 60, 84), 12:('Vibraphone', 53, 89), 13:('Marimba', 48, 84), 14:('Xylophone', 65, 96), 15:('Tubular Bells', 60, 77), 16:('Dulcimer', 60, 84), 17:('Drawbar Organ', 36, 96), 18:('Percussive Organ', 36, 96), 19:('Rock Organ', 36, 96), 20:('Church Organ', 21, 108), 21:('Reed Organ', 36, 96), 22:('Accordion', 53, 89), 23:('Harmonica', 60, 84), 24:('Tango Accordion', 53, 89), 25:('Acoustic Guitar (nylon)', 40, 84), 26:('Acoustic Guitar (steel)', 40, 84), 27:('Electric Guitar(jazz)', 40, 86), 28:('Electric Guitar (clean)', 40, 86), 29:('Electric Guitar (muted)', 40, 86), 30:('Overdriven Guitar', 40, 86), 31:('Distortion Guitar', 40, 86), 32:('Guitar Harmonics', 40, 86), 33:('Acoustic Bass', 28, 55), 34:('Electric Bass(finger)', 28, 55), 35:('Electric Bass(pick)', 28, 55), 36:('Fretless Bass', 28, 55), 37:('Slap Bass 1', 28, 55), 38:('Slap Bass 2', 28, 55), 39:('Synth Bass 1', 28, 55), 40:('Synth Bass 2', 28, 55), 41:('Violin', 55, 96), 42:('Viola', 48, 84), 43:('Cello', 36, 72), 44:('Contrabass', 28, 55), 45:('Tremolo Strings', 28, 96), 46:('Pizzicato Strings', 28, 96), 47:('Orchestral Harp', 23, 103), 48:('Timpani', 36, 57), 49:('String Ensembles 1', 28, 96), 50:('String Ensembles 2', 28, 96), 51:('SynthStrings 1', 36, 96), 52:('SynthStrings 2', 36, 96), 53:('Choir Aahs', 48, 79), 54:('Voice Oohs', 48, 79), 55:('Synth Voice', 48, 84), 56:('Orchestra Hit', 48, 72), 57:('Trumpet', 58, 94), 58:('Trombone', 34, 75), 59:('Tuba', 29, 55), 60:('Muted trumpet', 58, 82), 61:('French Horn', 41, 77), 62:('Brass Section', 36, 96), 63:('Synth Brass 1', 36, 96), 64:('Synth Brass 2', 36, 96), 65:('Soprano Sax', 54, 87), 66:('Alto Sax', 49, 80), 67:('Tenor Sax', 42, 75), 68:('Baritone Sax', 37, 68), 69:('Oboe', 58, 91), 70:('English Horn', 52, 81), 71:('Bassoon', 34, 72), 72:('Clarinet', 50, 91), 73:('Piccolo', 74, 108), 74:('Flute', 60, 96), 75:('Recorder', 60, 96), 76:('Pan Flute', 60, 96), 77:('Blown Bottle', 60, 96), 78:('Shakuhachi', 55, 84), 79:('Whistle', 60, 96), 80:('Ocarina', 60, 84), 81:('Lead 1(square)', 21, 108), 82:('Lead 2(sawtooth)', 21, 108), 83:('Lead 3(calliope)', 36, 96), 84:('Lead 4(chiff)', 36, 96), 85:('Lead 5(charang)', 36, 96), 86:('Lead 6(voice)', 36, 96), 87:('Lead 7(fifths)', 36, 96), 88:('Lead 8(bass + lead)', 21, 108), 89:('Pad 1(new age)', 36, 96), 90:('Pad 2(warm)', 36, 96), 91:('Pad 3(polysynth)', 36, 96), 92:('Pad 4(choir)', 36, 96), 93:('Pad 5(bowed)', 36, 96), 94:('Pad 6(metallic)', 36, 96), 95:('Pad 7(halo)', 36, 96), 96:('Pad 8(sweep)', 36, 96), 97:('FX 1(rain)', 36, 96), 98:('FX 2(soundtrack)', 36, 96), 99:('FX 3(crystal)', 36, 96), 100:('FX 4(atmosphere)', 36, 96), 101:('FX 5(brightness)', 36, 96), 102:('FX 6(goblins)', 36, 96), 103:('FX 7(echoes)', 36, 96), 104:('FX 8(sci-fi)', 36, 96), 105:('Sitar', 48, 77), 106:('Banjo', 48, 84), 107:('Shamisen', 50, 79), 108:('Koto', 55, 84), 109:('Kalimba', 48, 79), 110:('Bag pipe', 36, 77), 111:('Fiddle', 55, 96), 112:('Shanai', 48, 72), 113:('Tinkle Bell', 72, 84), 114:('Agogo', 60, 72), 115:('Steel Drums', 52, 76), 116:('Woodblock', 0, 127), 117:('Taiko Drum', 0, 127), 118:('Melodic Tom', 0, 127), 119:('Synth Drum', 0, 127), 120:('Reverse Cymbal', 0, 127), 121:('Guitar Fret Noise', 0, 127), 122:('Breath Noise', 0, 127), 123:('Seashore', 0, 127), 124:('Bird Tweet', 0, 127), 125:('Telephone Ring', 0, 127), 126:('Helicopter', 0, 127), 127:('Applause', 0, 127), 128:('Gunshot', 0, 127) } GM_CATEGORY = ''' Piano Chromatic Percussion Organ Guitar Bass Strings Ensemble Brass Reed Pipe Synth Lead Synth Pad Synth Effects Ethnic Percussive Sound effects '''.strip().split('\n') def set_lyric(output, lyric): output.write_sys_ex( 0, [0xF0, 0x43, 0x79, 0x09, 0x11, 0x0A, 0x00, lyric, 0xF7]) def set_pitch_bend_sensitivity(output, ch, value): output.write_short(0xB0 | ch, 101, 0) output.write_short(0xB0 | ch, 100, 0) output.write_short(0xB0 | ch, 6, 16) def put_text(surface, font, text, dest, color=COLOR): x, y = dest for t in text.split('\n'): s = font.render(t, True, color) surface.blit(s, (x, y)) y += font.get_linesize() def set_inst(output, inst, ch=1): output.set_instrument(inst, ch) name, lower, upper = GM[inst + 1] average = (lower + upper) / 2.0 note_shift = int((average - POKEMIKU_NOTE) / 12) * 12 # ポケミクとの音程差 return note_shift def main(): pygame.init() pygame.midi.init() screen = pygame.display.set_mode((WIDTH, HEIGHT)) pygame.display.set_caption('inst39.py') font = pygame.font.SysFont(pygame.font.get_default_font(), FONT_SIZE) for i in range(pygame.midi.get_count()): interf, name, input, output, opened = pygame.midi.get_device_info(i) if output and name == 'NSX-39 ': midiout = pygame.midi.Output(i) if input and name == 'NSX-39 ': midiin = pygame.midi.Input(i) clock = pygame.time.Clock() clock.tick(FPS) inst = 0 # プログラム番号初期値(-1されている) note_shift = set_inst(midiout, inst) set_pitch_bend_sensitivity(midiout, 1, 16) midiout.write_short(0xB0, 7, 0) # ミクさんの音量を0に last_note = None # 最後に送出したノート番号 moduration = 0 # モジュレーション ON/OFF sustain = False # 次のノートオンまで音を続けるか while True: sysex = False for e in pygame.event.get(): if (e.type is QUIT) or (e.type is KEYDOWN and e.key is K_ESCAPE): if last_note is not None: midiout.note_off(last_note, 64, 1) midiout.write_short(0xB0, 7, 64) # ミクさんの音量を64に return if midiin.poll(): for e in midiin.read(1000): (status,data1,data2,data3), timestamp = e if sysex and status == 0x11 and data1 == 0x20: if data3 & BUTTON_A: inst = (inst - 8) & 0x7f note_shift = set_inst(midiout, inst) if data3 & BUTTON_I: inst = (inst - 1) & 0x7f note_shift = set_inst(midiout, inst) if data3 & BUTTON_U: inst = (inst + 1) & 0x7f note_shift = set_inst(midiout, inst) if data3 & BUTTON_E: inst = (inst + 8) & 0x7f note_shift = set_inst(midiout, inst) if data3 & BUTTON_O: inst = (inst + 32) & 0x7f note_shift = set_inst(midiout, inst) if data3 & BUTTOU_SHIFT: sustain = True midiout.write_short(0xB1, 64, 127) if not (data3 & BUTTOU_SHIFT) and sustain: sustain = False midiout.write_short(0xB1, 64, 0) if data3 & BUTTON_VIBRATO: moduration = MODURATION midiout.write_short(0xB1, 1, moduration) if not (data3 & BUTTON_VIBRATO): moduration = 0 midiout.write_short(0xB1, 1, moduration) if status == 0xF0: # SysEx start sysex = True if status == 0xF7: # SysEx end sysex = False if (status & 0xF0) == 0x90: # Note On if last_note is not None: midiout.note_off(last_note, data2, 1) last_note = data1 + note_shift midiout.note_on(last_note, data2, 1) if (status & 0xF0) == 0x80: # Note Off if (not sustain) and (last_note is not None): midiout.note_off(last_note, data2, 1) last_note = None if (status & 0xF0) == 0xE0: # ピッチベンド midiout.write_short(0xE1, data1, data2) screen.fill(BG_COLOR) text = u'''%s: %3d: %s %s %s ''' % ( GM_CATEGORY[inst // 8], inst + 1, GM[inst + 1][0], ('sustain', 'SUSTAIN')[sustain], ('vibrato', 'VIBRATO')[bool(moduration)]) put_text(screen, font, text, (10, 10)) clock.tick(FPS) pygame.display.flip() if __name__ == '__main__': try: main() finally: pygame.quit() # Public Domain. 好きに流用してください。
なんかサスティンの効き方がおかしい気がする。NSX-39 のせいなのかプログラムがマズイのか?
歌うキーボード ポケット・ミク (大人の科学マガジンシリーズ)
- 作者: 大人の科学マガジン編集部
- 出版社/メーカー: 学研プラス
- 発売日: 2014/04/03
- メディア: ムック
- この商品を含むブログ (24件) を見る