USB-MIDIキーボードでpyxelの音を鳴らしてみるテスト その1

問題点

☑️同時に鳴らせる音を3つにしたかったけど方法がわからず
☑️鍵盤を離すと音が途切れるようにしてあるので、前の鍵盤を離してから次の鍵盤を押さないと、次の鍵盤の音が途切れてしまう

pyxelをシーケンサーにしたい💫

https://x.com/0_game_it/status/1924523468827300058


```
import pyxel
import mido
import time

# Pyxel の初期化
pyxel.init(256, 64, fps=30)
pyxel.NUM_SOUNDS = 3  # 同時発音数を 3 に制限

# MIDI ノート番号から Pyxel のノート文字列を生成する関数
def midi_to_pyxel_note(midi_note):
    note_names = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"]
    # MIDI ノート 36 (C2) を Pyxel の C0 に対応させる (調整が必要かもしれません)
    base_midi_note = 36
    if midi_note < base_midi_note:
        return "C0"  # 下限
    octave = (midi_note - base_midi_note) // 12
    note_index = (midi_note - base_midi_note) % 12
    if octave > 7:
        return "B7"  # 上限
    return f"{note_names[note_index]}{octave}"

# Pyxel のサウンドバンクに初期設定 (矩形波、固定スピード)
fixed_speed = 22050 // 10  # 適当な固定値
initial_notes = "A3"
initial_tones = "0"
initial_effects = "n"
initial_volume = "7"
for i in range(pyxel.NUM_SOUNDS):
    pyxel.sounds[i].set(notes=initial_notes, tones=initial_tones, volumes=initial_volume, effects=initial_effects, speed=fixed_speed)

class App:
    def __init__(self):
        self.inport = None
        self.playing_notes = {}  # 再生中の MIDI ノートと Pyxel サウンド ID の対応
        try:
            self.inport = mido.open_input('MPK mini 3')
            pyxel.run(self.update, self.draw)
        except OSError as e:
            print(f"MIDI デバイスが見つかりません: {e}")
        except Exception as e:
            print(f"エラーが発生しました: {e}")
        finally:
            if self.inport:
                self.inport.close()

    def update(self):
        if self.inport:
            received_message = self.inport.poll()
            if received_message:
                print(f"poll() の戻り値の値: {received_message}") # デバッグ用
                if received_message.type == 'note_on':
                    if received_message.velocity > 0:
                        midi_note = received_message.note
                        if midi_note not in self.playing_notes:
                            # 空いているサウンド ID を探す
                            available_sound_id = -1
                            for i in range(pyxel.NUM_SOUNDS):
                                if i not in self.playing_notes.values():
                                    available_sound_id = i
                                    break
                            if available_sound_id != -1:
                                pyxel_note = midi_to_pyxel_note(midi_note)
                                try:
                                    pyxel.sounds[available_sound_id].set(notes=pyxel_note, tones=initial_tones, volumes=initial_volume, effects=initial_effects, speed=fixed_speed)
                                    pyxel.play(0, available_sound_id)
                                    self.playing_notes[midi_note] = available_sound_id
                                except ValueError as e:
                                    print(f"ValueError during sound set: {e}, pyxel_note: {pyxel_note}, midi_note: {midi_note}")
                                except Exception as e:
                                    print(f"Unexpected error during sound set/play: {e}")
                elif received_message.type == 'note_off':
                    midi_note = received_message.note
                    print(f"Note Off received for note: {midi_note}") # デバッグ用
                    if midi_note in self.playing_notes:
                        del self.playing_notes[midi_note]
                        # キーを離したらチャンネル 0 の再生を停止する
                        pyxel.stop(0)

    def draw(self):
        pyxel.cls(0)
        pyxel.text(10, 10, "Playing MIDI Chords (Up to {} notes)".format(pyxel.NUM_SOUNDS), 7)

if __name__ == "__main__":
    App()
```