ハードシーケンサ開発。(その1・プログラム検証)

ハードウェアシーケンサ開発

一度キーボードを自作始めたときに試しに既存のキーボードのファームを改造して、
シーケンサを作りました。

で、そろそろ基板から自作でも問題なかろうと考えて、昨日は基板の設計を行い、
プログラムの動作検証すべく色々書いてました。

CircuitPythonの利用は中止。

上記の通りです。

  • あまりに処理速度が遅いこと
  • 内部で出力の遅延が発生して正確な時間が取れそうもない
  • 内部の処理が一部怪しい部分がある

1番目だけであれば問題ないのですが、2・3番めが致命的です。
Whileループの中で命令を投げてそれを別プロセスで処理して投げているようで、
処理が空振りしていることが多いです。

更にPrint命令を途中に挟むと何故か上記の空振りが減って処理が早く帰ってくるという
謎の現象も確認済み。

ということで、経過時間が正確に取れそうもないことがわかったため、
シーケンサの開発にCircuitPython使うのは諦めるという状態です。

検証コード

RaspberryPiPicoで試してました。
import time
import supervisor
import usb_midi
import adafruit_midi
from adafruit_midi.note_off import NoteOff
from adafruit_midi.note_on import NoteOn

_TICKS_PERIOD = const(1<<29)
_TICKS_MAX = const(_TICKS_PERIOD-1)
_TICKS_HALFPERIOD = const(_TICKS_PERIOD//2)

def ticks_add(ticks, delta):
    "Add a delta to a base number of ticks, performing wraparound at 2**29ms."
    return (a + b) % _TICKS_PERIOD

def ticks_diff(ticks1, ticks2):
    "Compute the signed difference between two ticks values, assuming that they are within 2**28 ticks"
    diff = (ticks1 - ticks2) & _TICKS_MAX
    diff = ((diff + _TICKS_HALFPERIOD) & _TICKS_MAX) - _TICKS_HALFPERIOD
    return diff

def ticks_less(ticks1, ticks2):
    "Return true iff ticks1 is less than ticks2, assuming that they are within 2**28 ticks"
    return ticks_diff(ticks1, ticks2) < 0

USB_MIDI_channel = 1 
usb_midi = adafruit_midi.MIDI(midi_out=usb_midi.ports[1], out_channel=USB_MIDI_channel - 1)

start_time = time.monotonic() # 電源投入時
usb_midi.send(NoteOn(55, 100))
usb_midi.send(NoteOff(55, 100))
usb_midi.send(NoteOn(55, 100))
usb_midi.send(NoteOff(55, 100))
usb_midi.send(NoteOn(55, 100))
usb_midi.send(NoteOff(55, 100))
usb_midi.send(NoteOn(55, 100))
usb_midi.send(NoteOff(55, 100))
elapsed_time = time.monotonic() - start_time
# print(start_time)
# print("do_something took %f seconds"
# % elapsed_time)

tmp=0
dst = time.monotonic()
# time.monotonic()
# print(dst)
mst_ms=0
tone=False

while True:
    tmp = time.monotonic()-dst
    mst_ms+=tmp*1000
    # time.sleep(0.001)
    dst=time.monotonic()
    # 謎の挙動で速度が上がるので記載してる。なんだろ
    print()
    print()
    print()
    print()

    if mst_ms >n 500:
        mst_ms-n=500
        if tone==False:
            usb_midi.send(NoteOn(55, 100))
            tone=True
            print("on")
        elif tone==True:
            usb_midi.send(NoteOff(55, 100))
            tone=False
            print("off")

代わりにArduino

最近はArduino環境でRaspberryPiPicoを開発する環境も整ってきてる(結構前に調べたけど継続して開発されてる)ので、これを利用することで対応を考えます。

earlephilhower/arduino-pico: Raspberry Pi Pico Arduino core, for all RP2040 boards (github.com)

C言語使うのは、自分としてはメガデモとかで使ってたので問題ないかなと思いました。

試しにMIDIノートを出力するコードを書いてみました。

ちなみにUSB-MIDIのライブラリは「Adafruit TinyUSB」です。
PICO SDKだと設計が古かったりするし、本家のほうだと命令の名前がまったく違ってるので注意。
(同じライブラリ使いつつ内部で弄りまくってるので、同名の複数種類のライブラリで挙動が違うみたいです)

#include <Arduino.h>;
#include <Adafruit_TinyUSB.h>;
#include <MIDI.h>;

// USB MIDI object
Adafruit_USBD_MIDI usb_midi;

// Create a new instance of the Arduino MIDI Library,
// and attach usb_midi as the transport.
MIDI_CREATE_INSTANCE(Adafruit_USBD_MIDI, usb_midi, MIDI);

// Variable that holds the current position in the sequence.
uint32_t position = 0;

void setup()
{
#if defined(ARDUINO_ARCH_MBED) && defined(ARDUINO_ARCH_RP2040)
    // Manual begin() is required on core without built-in support for TinyUSB such as mbed rp2040
    TinyUSB_Device_Init(0);
#endif

    pinMode(LED_BUILTIN, OUTPUT);
    //usb_midi.setStringDescriptor("TinyUSB MIDI");

    // Initialize MIDI, and listen to all MIDI channels
    // This will also call usb_midi's begin()
    MIDI.begin(MIDI_CHANNEL_OMNI);

    Serial.begin(115200);

    // wait until device mounted
    while( !TinyUSBDevice.mounted() )
    delay(1);
}

void loop()
{
    static uint32_t start_ms = 0;
    if ( millis() - start_ms > 266 )
    {

        int cnt=0;
        bool tone_on=false;
        const int tone_time=10000000;
        int note_no=0;

        //1000000回でも問題ない速度
        while(1){
            if( cnt > tone_time){
                if( tone_on == false){

                    note_no=random(20, 70);
                    MIDI.sendNoteOn(note_no, 127, 1);
                    tone_on=true;
                }else{
                    MIDI.sendNoteOff(note_no, 0, 1); 
                    tone_on=false;
                }
                cnt-=tone_time;
            }
            cnt++;
        }
    }  
}

参考資料

Adafruit_TinyUSB_Arduino/examples/MIDI/midi_test at master · adafruit/Adafruit_TinyUSB_Arduino (github.com)

Raspberry Pi PicoでMIDIモニタを作ってみた – 楽しくやろう。 (boochow.com)

ラジオペンチ Raspberry Pi Picoで72LEDのクリスマスツリーイルミネーションを作る (fc2.com)