ハードシーケンサ開発。(その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++; } } }
参考資料
Raspberry Pi PicoでMIDIモニタを作ってみた – 楽しくやろう。 (boochow.com)
ラジオペンチ Raspberry Pi Picoで72LEDのクリスマスツリーイルミネーションを作る (fc2.com)