レーザーハープ
平沢進のレーザーハープ的な電子工作。
音はスピーカーではなくLogicからMIDIで。pythonとarduinoはシリアル通信して、pythonからMIDIをLogicに送っている。
構成
Arduino(光センサ) → シリアル → Python → MIDI → IAC Driver → Logic
Arduino Uno、Nano、ESP32の3種で試した。ビルドと書き込みはarduino-cliをMakefileから叩いている。
BOARD_ESP32 := esp32:esp32:esp32
PORT_ESP32 := /dev/cu.usbserial-21240
upload-esp32: compile-esp32
$(ARDUINO_CLI) upload -p $(PORT_ESP32) --fqbn $(BOARD_ESP32) $(INPUT)
マイコン側
やっているのは、トグルスイッチでレーザーのON/OFFを切り替えることと、光センサの値をシリアルに流すことだけ。
void loop() {
bool currentState = digitalRead(switchPin);
int val1 = analogRead(32);
if (lastState == HIGH && currentState == LOW) {
isRunning = !isRunning;
Serial.println(isRunning ? "START" : "STOP");
digitalWrite(laserPin, isRunning ? HIGH : LOW);
delay(200);
}
lastState = currentState;
if (isRunning) {
Serial.println(val1);
delay(10);
}
}
スイッチは INPUT_PULLUP で受けて、押した瞬間(HIGHからLOWへ落ちた瞬間)だけを見ている。押しっぱなしで何度も反転しないようにするのと、接点のばたつきを避けるために200ms待つ。
センサ値は10ms間隔、つまり毎秒100回ほど送っている。
Python側
pyserialで読んで、midoでIAC Driverに流す。
ser = serial.Serial('/dev/cu.usbmodem212101', 9600)
outport = mido.open_output('IAC Driver Bus 1')
絶対値ではなく差分で判定する
光センサの値をしきい値と直接比べる方式にはしていない。部屋の明るさで基準値が動いてしまい、場所を変えるたびに調整が要るため。
代わりに前回の値との差を見て、変化したときだけ音を鳴らす。
THRESHOLD_DIFF = 100
diff1 = val1 - prev_val1
if diff1 > THRESHOLD_DIFF and not note1_on_sent:
outport.send(Message('note_on', note=60, velocity=100))
note1_on_sent = True
elif diff1 < -THRESHOLD_DIFF and note1_on_sent:
outport.send(Message('note_off', note=60))
note1_on_sent = False
手がビームを横切った瞬間に大きな差が出るので、その立ち上がりでnote_on、抜けた立ち下がりでnote_offになる。環境光はゆっくりしか変化しないので、この方式なら引っかからない。
note1_on_sent のフラグは、ビームを遮っている間ずっとnote_onが送られ続けるのを防ぐためのもの。MIDIは鳴らしっぱなしとレガートの区別がないので、片方を送ったらもう片方が来るまで送らない、という状態を持たせている。
複数弦のバージョンでは、センサの値をカンマ区切りで送り、それぞれに別のノート番号を割り当てている。