チーム名
ふーぎー
概要
みんなが一度は触れたことのある「傘」によって音楽を奏でるゆる楽器です。
演奏しない時は「閉じて」小さくなり、演奏する時は大きく「開く」ことで演奏前と演奏中の楽器のビジュアルが大きく変化するという傘の持つ特徴を活かした楽器となっています。
傘を開くことでスイッチがオンになり、傾け方に応じた和音が鳴り、華やかな演奏を体験できます。
制御については傘に加速度・ジャイロセンサーを取り付け、その値から傘の傾き方向や大きさを算出し、その値に応じた音を鳴らす仕組みとなっています。
ダイアトニックコードを採用することでおおよその楽曲の演奏に対応しており、小さな子でも馴染みのある傘の形をしたゆる楽器は雨の日を楽しい演奏の時間に変えます。
部品
_________________________________________________________________________________________________________________________
部品名 | 個数(個) | 価格 (円) | 販売元 |
---|---|---|---|
spresense | 1 | 500 | Sony |
T1-1942SB | 1 | 3806 | Tangband |
BMI160-P280 | 1 | 1980 | SWITCH SCIENCE |
SAM2625 | 1 | - | UdaDensi |
SS-5GL2 | 1 | 398 | Omron Electronics |
IFD-216 | 1 | - | IFUDO |
傘 | 1 | 500 | 3COINS |
回路図
_________________________________________________________________________________________________________________________
↓演奏動画
https://youtu.be/dbyB-02GIMA
ソースコード
_________________________________________________________________________________________________________________________
Spresense用ソースコード
#include <math.h>
#include <MadgwickAHRS.h>
#include <BMI160Gen.h>
#define PI 3.141592653589793
Madgwick madgwick; // Madgwickフィルタ
unsigned long lastMs = 0; // 周期計測用
unsigned int selected_num = 0;
// Switch
const int ONOFF_PIN = 24;
int onoff_old = 1;
// Speaker
unsigned int MIM_VOLUME = 150;
unsigned int MAX_VOLUME = 150;
float MIN_INCLINATION = 0.5; // 傾き0.5以上で出力
float MAX_INCLINATION = 0.9;
// Code
int chord_table[6][3]={
{60,64,67}, // C
{62,65,69}, // Dm
{64,67,71}, // Em
{65,69,72}, // F
{67,71,74}, // G
{69,72,76} // Am
};
void setup() {
// Switch ===========
pinMode(ONOFF_PIN, INPUT_PULLUP );
// Speaker ===========
Serial2.begin(31250);
pinMode(LED3, OUTPUT);
//send2bytes(0xC0,78); // 音色選択 くちぶえ
send2bytes(0xC0,19); // 音色選択 チャーチオルガン
send3bytes(0xB0, 7,127); // vol
delay(100);
// ======================
// Sensor ===========
Serial.begin(115200);
while (!Serial);
// 6軸センサ初期化
BMI160.begin();
BMI160.setGyroRate(100);
BMI160.setAccelerometerRate(100);
BMI160.setGyroRange(250);// ジャイロのレンジ設定
BMI160.setAccelerometerRange(2);// 加速度のレンジ設定
delay(100);
// Madgwickフィルタの初期化
madgwick.begin(100); //100Hz (10msec周期)
// 周期計測用
lastMs = micros();
// ======================
}
void loop() {
int onoff = digitalRead( ONOFF_PIN );
if (onoff == 1) {
send_exp(0);
return;
}
//if (onoff_old == 1) {
// 起動音
// chord_sel(0);
// send_exp(100);
// delay(500);
//}
//onoff_old = onoff;
// 10msec周期で処理
float dt = (micros() - lastMs) / 1000000.0;
lastMs = micros();
// 6軸センサ読み出し
int ax, ay, az, gx, gy, gz;
BMI160.readMotionSensor(ax, ay, az, gx, gy, gz);
// 加速度値を分解能で割って加速度[G]に変換する
float acc_omg = float(0x7FFF) / 2;
float acc_x = ax / acc_omg; // LSB = 2G / 2^15 = 1/16384 G
float acc_y = ay / acc_omg;
float acc_z = az / acc_omg;
// 角速度値を分解能で割って角速度[deg/sec]に変換する
float gyro_omg = float(0x7FFF) / 250;
float gyro_x = gx / gyro_omg; // LSB = 2000deg/sec / 2^15 = 1/16.384 deg/sec
float gyro_y = gy / gyro_omg;
float gyro_z = gz / gyro_omg;
// Madgwickフィルタの計算
madgwick.updateIMU(gyro_x, gyro_y, gyro_z, acc_x, acc_y, acc_z);
// オイラー角の取得
float roll = madgwick.getRoll();
float pitch = madgwick.getPitch();
float yaw = madgwick.getYaw();
// ラジアン角取得
float roll_r = madgwick.getRollRadians();
float pitch_r = madgwick.getPitchRadians();
float yaw_r = madgwick.getYawRadians();
float angle = atan(roll_r / pitch_r);
float inclination = sqrt(roll_r * roll_r + pitch_r * pitch_r);
unsigned int selecting_num = selected_num;
// コード選択
if (pitch_r <= 0) {
if (angle < 5 * PI / 12.0 && angle > 3 * PI / 12.0) {
selecting_num = 1;
} else if (angle < PI / 12.0 && angle > -1 * PI / 12.0) {
selecting_num = 2;
} else if (angle < -3 * PI / 12.0 && angle > -5 * PI / 12.0) {
selecting_num = 3;
}
} else if (pitch_r > 0) {
if (angle < 5 * PI / 12.0 && angle > 3 * PI / 12.0) {
selecting_num = 4;
} else if (angle < PI / 12.0 && angle > -1 * PI / 12.0) {
selecting_num = 5;
} else if (angle < -3 * PI / 12.0 && angle > -5 * PI / 12.0) {
selecting_num = 6;
}
} else {
// なし
}
if (selected_num != selecting_num) {
chord_sel(selecting_num); // コード発音
selected_num = selecting_num;
}
// 音量調整
int volume = 0;
if (inclination > MIN_INCLINATION) {
volume = MAX_VOLUME * ((inclination - MIN_INCLINATION) / (MAX_INCLINATION - MIN_INCLINATION));
}
send_exp(volume);
// Debug
Serial.print(roll);
Serial.print(",");
Serial.print(pitch);
Serial.print(",");
Serial.print(yaw);
Serial.print(",");
Serial.print(angle);
Serial.print(",");
Serial.print(selected_num);
Serial.print(",");
Serial.print(inclination);
Serial.println();
delay(10); // 10msec周期のためここで10msecdelay
}
// MIDIメッセージ 3bytes送る
void send3bytes(int sts, int data1, int data2){
Serial2.write(sts);
Serial2.write(data1);
Serial2.write(data2);
}
// MIDIメッセージ 2bytes送る
void send2bytes(int sts, int data){
Serial2.write(sts);
Serial2.write(data);
}
// num:0 音を消す(note off)
// num:1-6 番号に応じたコードを発音
void chord_sel(int num){
send3bytes(0xB0,123,0); // all note off
if((1<=num)&&(num<=6)){
int velo = 127;
//int capo = 0;
int capo = 4;
int n = num-1;
send3bytes(0x90,capo+chord_table[n][0],velo);
send3bytes(0x90,capo+chord_table[n][1],velo);
send3bytes(0x90,capo+chord_table[n][2],velo);
}
}
void send_exp(int val){
send3bytes(0xB0,11, val); // exp
}
Processingによる角度のシミュレート用ソースコード
import processing.serial.*;
Serial myPort;
String arduinoPort = "/dev/cu.usbserial-143240"; // Required to modify
float []data = new float [6];
void setup() {
lights();
size(300, 300, P3D);
myPort = new Serial(this, arduinoPort, 115200);
}
void draw() {
background(230);
if (data[5] > 0.75) {
fill(0, 0, 255);
} else {
fill(255, 255, 255);
}
translate(width / 2, height / 2, 0);
rotateZ(radians(data[1]));
rotateX(radians(-data[0]));
//rotateY(radians(-data[2]));
int size = 10;
box(20 * size, 1 * size, 15 * size);
translate(0, -4 * size, 0);
textSize(30);
fill(0);
text("num: " + int(data[4]) + "\nvalue: " + data[5], -10, -20);
}
void serialEvent(Serial p) {
String inString = myPort.readStringUntil('\n');
if (inString != null) {
inString = trim(inString);
data = float(split(inString, ','));
println(data);
}
}
-
Genki
さんが
2022/09/26
に
編集
をしました。
(メッセージ: 初版)
ログインしてコメントを投稿する