はじめに
みなさんは、テルミンという楽器をご存知でしょうか? 空間に手をかざすと
音階と音量がかわる不思議な楽器です。
これをSpresenseで作ってみようと思いました。
Spresenseについて
Spresenseはスマートセンシングプロセッサ(CXD5602)が搭載されている
マイコンボードで、Spresense SDKに加えてArduino SDKでも開発が
できます。
私は、ソフトウェアは詳しい方ではないのでArduino SDKで開発を
しています。
詳しくはSpresenseのWebページをご覧ください
https://developer.sony.com/ja/develop/spresense/
VL53L0Xについて
Stmicro社が開発した、Time-of-Flight方式の測距センサです。
目には見えませんが光をだしてその反射を測定する方式なので
スマホでこの写真を取ると、紫の光が撮影できます。
SPRESENSEとはI2Cで通信します。
Neopixelについて
シリアルコマンドにてLEDの光を変化させることができるデバイスで
R,G,Bの小さなLEDがデバイスに実装されて、それらの輝度を変える
ことで様々な色に変化させることができます。
SPRESENSEとはGPIOから制御します。
ライブラリはライゾマティクスさんのエンジニアの方が
提供されており、ArduinoのSDKから簡単にダウンロード可能です。
Sound Signal Processing Library について
Spresenseで簡単に楽器を開発することが出来るライブラリで
MIDI規格をベースとしたAPIを実装しています。
詳しくは下記をご覧ください。
https://github.com/SonySemiconductorSolutions/ssih-music
構成概要図
SPRESENSEのメインボードと拡張ボードを使用します。
VL53L0XとはメインボードのI2Cと接続し
Neopixelとは今回は制御端子はD22(SEN_IRQ_IN)と接続します。
また、Neopixelは今回は別の用途で作成した5x5のマトリクス状に配置した
25個のNeopixelを使いました。
3.3Vの電源とGNDはメインボードと拡張ボードのそれぞれのピンヘッダから
供給しています。
ソースコード
ベースはライブラリのサンプルファイルのButtonDrum.inoが
スイッチに応じて音階が変わるプログラムなので、スイッチの部分を
測距センサの入力に応じたデータに変更をしました。
/*
* SPDX-License-Identifier: (Apache-2.0 OR LGPL-2.1-or-later)
*
* Copyright 2022 Sony Semiconductor Solutions Corporation
*/
#ifndef ARDUINO_ARCH_SPRESENSE
#error "Board selection is wrong!!"
#endif
#ifdef SUBCORE
#error "Core selection is wrong!!"
#endif
#include <MemoryUtil.h>
#include <SDSink.h>
//sensor
#include <Wire.h>
#include <VL53L0X.h>
VL53L0X sensor;
//neopixel
#include <SpresenseNeoPixel.h>
// const uint16_t PIN = 6;
//const uint16_t PIN = 24;
const uint16_t PIN = 22; //
const uint16_t NUM_PIXELS = 25;
SpresenseNeoPixel<PIN, NUM_PIXELS> neopixel;
// this file names are deifned middle C (60) as C4
const SDSink::Item table[12] = {
{60, "SawLpf/60_C4.wav"}, // C4
{61, "SawLpf/61_C#4.wav"}, // C#4
{62, "SawLpf/62_D4.wav"}, // D4
{63, "SawLpf/63_D#4.wav"}, // D#4
{64, "SawLpf/64_E4.wav"}, // E4
{65, "SawLpf/65_F4.wav"}, // F4
{66, "SawLpf/66_F#4.wav"}, // F#4
{67, "SawLpf/67_G4.wav"}, // G4
{68, "SawLpf/68_G#4.wav"}, // G#4
{69, "SawLpf/69_A4.wav"}, // A4
{70, "SawLpf/70_A#4.wav"}, // A#4
{71, "SawLpf/71_B4.wav"} // B4
};
SDSink inst(table, 12);
int selector = 0;
int button4 = HIGH;
int button5 = HIGH;
int button6 = HIGH;
void setup() {
// init built-in I/O
Serial.begin(115200);
pinMode(LED0, OUTPUT);
pinMode(LED1, OUTPUT);
pinMode(LED2, OUTPUT);
pinMode(LED3, OUTPUT);
// init buttons
pinMode(PIN_D04, INPUT_PULLUP);
pinMode(PIN_D05, INPUT_PULLUP);
pinMode(PIN_D06, INPUT_PULLUP);
// initialize memory pool
// initMemoryPools();
// createStaticPools(MEM_LAYOUT_RECORDINGPLAYER);
// setup instrument
if (!inst.begin()) {
Serial.println("ERROR: init error.");
while (true) {
delay(1000);
}
}
Serial.println("Ready to play DistDrum");
// VL53L0X setup
// Serial.begin(9600);
Wire.begin();
sensor.setTimeout(500);
if (!sensor.init())
{
Serial.println("Failed to detect and initialize sensor!");
while (1) {}
}
// Start continuous back-to-back mode (take readings as
// fast as possible). To use continuous timed mode
// instead, provide a desired inter-measurement period in
// ms (e.g. sensor.startContinuous(100)).
sensor.startContinuous();
// inst.sendNoteOn(61, DEFAULT_VELOCITY, DEFAULT_CHANNEL);
// inst.update();
// delay(1000);
neopixel.clear();
neopixel.framerate(40); // default framerate is 40[fps]
delay(1000);
neopixel.set(0, 0, 0); //R,G,B
}
int note1 = INVALID_NOTE_NUMBER;
int pre_note1 = INVALID_NOTE_NUMBER;
void loop() {
// Serial.print(sensor.readRangeContinuousMillimeters());
if (sensor.timeoutOccurred()) { Serial.print(" TIMEOUT"); }
// Serial.println();
int distance_input = sensor.readRangeContinuousMillimeters();
//測定している距離が一定の距離に入ったら音を出すようにする。以下は 300mm未満
if (distance_input < 300 ) {
//距離を12等分したものを割って、小数点が切り捨てられるようにし60に加算
//値が72になるとエラーし暴走してしまうので注意
note1 = 60 + int(float(distance_input / (300 / 12.0)-0.5));
Serial.println(note1);
if(note1 != pre_note1){
inst.sendNoteOff(pre_note1, DEFAULT_VELOCITY, DEFAULT_CHANNEL);
inst.sendNoteOn(note1, DEFAULT_VELOCITY, DEFAULT_CHANNEL);
// neopixel.set(1,50, 0, 0); //R,G,B
switch (note1)
{
case 60:
neopixel.set(50, 0, 0); //R,G,B
break;
case 61:
neopixel.set(0, 50, 0); //R,G,B
break;
case 62:
neopixel.set(0, 0, 50); //R,G,B
break;
case 63:
neopixel.set(50, 50, 0); //R,G,B
break;
case 64:
neopixel.set(0, 50, 50); //R,G,B
break;
case 65:
neopixel.set(50, 0, 50); //R,G,B
break;
case 66:
neopixel.set(0, 50, 50); //R,G,B
break;
case 67:
neopixel.set(50, 50, 50); //R,G,B
break;
case 68:
neopixel.set(25, 50, 0); //R,G,B
break;
case 69:
neopixel.set(0, 25, 50); //R,G,B
break;
case 70:
neopixel.set(25, 0, 50); //R,G,B
break;
case 71:
neopixel.set(50, 25, 0); //R,G,B
break;
default:
neopixel.set(0, 0, 0); //R,G,B
break;
}
neopixel.show();
}
pre_note1 = note1;
}
inst.update();
}
動作動画
最後に
Spresenseは小型ながらマルチコアのCPU搭載されており、それを使った楽器を作る
ライブラリが揃っているので、Arduinoで簡単に楽器が出来ると思います。
ライブラリ自体は和音の機能をもっているので、和音を出すことに挑戦してみたいと
思います。