作品概要
この作品は、誰でも簡単にかっこよく弾けるギターです。昨今の新型コロナウイルスの影響によるおうち時間の増加をきっかけに、楽器の演奏にチャレンジする人が増えました。とりわけ、多くの方が挑戦する楽器としてギターが挙げられます。
しかし、ギターは初心者が挫折しやすい楽器でもあります。その原因は、左右の手で異なる働きを求められる、ギターの演奏方法の難しさにあります。
ギターの演奏では、左手で指板上の弦を押さえることによって鳴らす音を決定します。複数弦を押さえるとき、この押さえ方は「コード」と呼ばれ、Am、Em7、Csus4などの名前が付いています。しかし、コードは代表的なものだけでも約160種類あるため、コードを暗記して弦を押すことは初心者にとってとても大変です。
また、右手は弦を弾くことにより音を鳴らします。音を鳴らすには弦が均一に鳴るように弾く必要があり、習得には時間がかかります。
そこで、このような課題を解決し、簡単にかっこよく演奏ができるギター「らくラクギター」を作成しました。
構成
タッチセンサ
左手でタッチセンサを押さえることで、ギターで鳴らす音を決めます。指板部分のセンサは弦を模しており、合計88個の金属線一本一本が個別のセンサになっています。タッチセンサは静電容量式で、人が金属線に触れたときに生じる波形の遅延時間を読み取ることで、押下の判定をしています。
ホイール
右手で弦を弾くようにホイールを回すことで音が鳴ります。ホイールの回転軸にはエンコーダが付いており、ホイールの回転を読み取ることで音の鳴らし方を決定します。
モードスイッチ
下記で解説するモードの切り替えと音源の切り替えをします。音源の切り替えではアコースティックギターの音とエレキギターの音の切り替えができるようになっています。
スピーカー
ここから音が鳴ります。
機能
本作品は、本物のギターと同じように左手でタッチセンサを押さえ、右手でホイールを回すことで演奏します。演奏には演奏者のレベルに合わせた3つのモードを実装しました。
コードモード
コードモードでは、左手でタッチする部分と鳴らすコードを対応付け、1か所を押さえるだけで任意のコードを鳴らせます。これにより、普通のギターと同様の弦を押さえる格好で簡単に演奏ができるようになっています。コードと押さえる位置の対応は以下のようになっています。
例えば、Dmというコードは3か所押さえる必要がありますが、コードモードでは1か所を押さえるだけで演奏ができます。
ビギナーモード
ビギナーモードはホイールを回すだけで演奏ができるモードです。
SPRESENSEをWi-Fiのアクセスポイントにし、PCと接続します。PCからSPRESENSEにコード譜を送信することで、コード譜の曲が演奏できます。コード譜は以下のようなtxtファイルになっており中にコードが書かれています。
ドライフラワー_さび.txt
C G D B7 Em7 C G D D#dim Em7 C G B7 Em7 Am7 Cm D G
マニュアルモード
本物のギターと同じコードの押さえ方で演奏ができるモードになっています。
システム図
全体のシステム図は以下の通りです。
回路図
全体回路図は以下のようになります。
タッチセンサの値を読み取るためのタッチセンサ制御回路は、複数のシフトレジスタ部とコンパレータ部から構成されており、指板部の金属線への接触を判定します。
1番目のシフトレジスタ部の回路は以下のようになっています。シフトレジスタを用いることで、指板部の金属線に一本ずつ順番に波形を送信します。また、ダイオードを入れることで、複数箇所の押下も識別可能です。
2~11番目のシフトレジスタ部は以下のようになっています。k番目のシフトレジスタのSER端子はk-1番目のシフトレジスタのQH’端子と接続します。k番目のシフトレジスタのQH'端子はk+1番目のシフトレジスタのSER端子と接続します。このようにし複数のシフトレジスタを連結しています。
コンパレータ部は以下のようになっています。コンパレータ部ではシフトレジスタから出力された波形の読み取りをしています。-INPUTA端子に接続されている可変抵抗でタッチセンサの感度の調整ができるようになっています。コンパレータからの出力であるOUTPUTA端子の状態をSPRESENSEで読み取っています。
部品・作成
使用した部品は以下の通りです。
部品 | 使用個数 | 備考 |
---|---|---|
SONY SPRESENSE メインボード | 1 | |
SONY SPRESENSE 拡張ボード | 1 | |
SPRESENSE Wi-Fi Add-onボード is110B | 1 | |
LM393 | 1 | |
2SC1815 | 1 | |
74HC595 | 11 | |
PAM8403ステレオデジタルアンプボード | 1 | 音の増幅に使用。他のもので代用可能 |
ダイオード1S4 | 88 | |
抵抗4.7kΩ | 1 | |
抵抗100kΩ | 88 | |
抵抗470kΩ | 1 | |
可変抵抗10kΩ | 1 | |
コンデンサ0.1µF | 11 | |
スピーカーPS95-8 | 1 | |
32GB microSDカード | 1 | |
エンコーダKY-040 | 1 | |
L字型ピンヘッダ(1x8) | 11 | |
L字型ピンヘッダ(2x10) | 3 | |
DIP8P ICソケット | 1 | |
DIP16P ICソケット | 11 | |
3.5φ3極プラグ P-92 | 1 | |
QIコネクタハウジング 2P | 7 | |
QIコネクタハウジング 3P | 1 | |
QIコネクタハウジング 5P | 1 | |
QIコネクタハウジング 6P | 6 | |
QIコネクタハウジング 8P | 11 | |
QIコネクタ(オス) | 適宜 | 配線に使用 |
QIコネクタ(メス) | 適宜 | 配線に使用 |
ケーブル | 適宜 | |
ステンレス線 | 適量 | |
指板部基板 | 1 | 自作基板 |
タッチセンサ制御基板 | 3 | 自作基板 |
ブレッドボード | 1 | 電源線の分岐に使用。他のもので代用可能 |
ギター組み立てキット | 1 | |
MDF板(910x450x5.5) | 1 | 表板を強化するために使用 |
端材 | 2 | 回転軸を支えるために使用。他のもので代用可能 |
スペーサ ねじ呼びM3 長さ10mm | 15 | |
M3ナット | 7 | |
とめねじ付きベアリングTB6000ZZ | 2 | |
カップリングCPLS20-6-10 | 1 | |
回転軸SFMR10-230 | 1 | |
スライドスイッチms-12aas1 | 3 | |
ボルト CSH-ST-M4-14 | 4 | |
ナット FNTLP-STU-M4 | 4 | |
なべ小ねじ CSPTRT-SUS-M2.3-8 | 4 |
ネックの裏にはギターを握ったときに手のひらが触れるようにGND線を通しています。GND線を通さないとタッチセンサが上手く反応しません。
ホイールは3Dプリンタで印刷しました。
タッチセンサ制御回路と指板部は別途基板を作成しました。
ソースコード
SPRESENSEとPC側のプログラムを分けて解説します。
SPRESENSE
SPRESENSEではマルチコアを使用しています。Spresense Referense Board バージョン2.5.1の環境下で動かしました。
また、各コードの音源はCakewalk by BandLabを用いて作成しました。プラグイン(ギターの音)はAmple Guitar M v3.7のトライアルバージョン(アコースティック)とAmple Guitar LP v3.7のトライアルバージョン(エレキ)を使いました。作成した音源はSDカード内に保存し、SPRESENSEの拡張ボードに挿入します。
MainCore
MainCoreでは音源の再生・停止、スイッチの読み取り、SubCoreから受信した情報の処理をしています。音源の再生・停止はSDカードに入れた音源に番号を登録し、番号を指定することで再生可能となっています。
モードスイッチより演奏音源・モードを切り替えることができます。
SubCore3から受信した文字列から音源番号を羅列したコード譜を作成します。SubCore2から受信したタッチセンサの値から各モードでのコード音源を検索します。SubCore1から受信した加速検出と回転方向より、音源を決定し、音を再生します。
書き込む際はメモリを640KByteに設定してします。
MainCore
////////////////////////////// Main Core //////////////////////////////////////////////////////////////////////////////////////////
#ifdef SUBCORE // SubCore指定時にはエラー
#error "Core selection is wrong!!"
#endif
#include <MP.h>
int subcore1 = 1;
int subcore2 = 2;
int subcore3 = 3;
#include <SDHCI.h>
#include <Audio.h>
#include <vector>
#include <stdint.h>
AudioClass *theAudio;
File myFile;
bool ErrEnd = false;
// error callback
static void audio_attention_cb(const ErrorAttentionParam *atprm)
{
printf("************Attention!!!!!**********\n");
//theAudio->stopPlayer(AudioClass::Player0);
if (atprm->error_code == AS_ATTENTION_CODE_INFORMATION)
{
printf("*****************INFORMATION*******************\n");
ErrEnd = true;
}
if (atprm->error_code == AS_ATTENTION_CODE_WARNING)
{
printf("****************WARNING*******************\n");
ErrEnd = true;
}
if (atprm->error_code == AS_ATTENTION_CODE_ERROR)
{
printf("*****************ERROR*******************\n");
ErrEnd = true;
}
if (atprm->error_code == AS_ATTENTION_CODE_FATAL)
{
printf("*****************FATAL*******************\n");
ErrEnd = true;
}
}
class PlayGuitar
{
public:
// wavファイル読込用
struct Item
{
int16_t note;
String path;
};
PlayGuitar(const Item *table, size_t table_length);
void setup();
void begin();
void end();
bool sendNoteOn(int16_t note,int16_t volume);
void sendNoteOff();
//void sendNoteEnd();
void update();
private:
// class
SDClass sd;
WavContainerFormatParser theParser;
// wavの1つのデータ
struct PcmData
{
int16_t note;//番号
String path;
uint32_t offset;
uint32_t size;
};
std::vector<PcmData> pcm_table_;
// param
int samplingrate = 192000;
AsClkMode AS_CLKMODE = AS_CLKMODE_HIRES;//AS_CLKMODE_NORMAL or AS_CLKMODE_HIRES
int bit = 16;//16 or 24
int channel = 1;
int max_buffer = 24000;
uint8_t s_buffer[720];
uint8_t sc_store_frames = 10;// 最初に読み込むframe数
uint8_t sc_store_frames_loop = 1;//loopで読み込むframe数
// sendNoteOn Off End updateで使う
size_t supply_size = 0;
uint32_t s_remain_size = 0;
bool started = false;
bool carry_over = false;
bool file_open(int16_t note);
};
// wavのnote,pathをpcm_table_に書き込む
PlayGuitar::PlayGuitar(const Item *table, size_t table_length)
{
printf("PlayGuitar Constructor\n");
for (size_t i = 0; i < table_length; i++)
{
PcmData pcm_data;
pcm_data.note = table[i].note;
pcm_data.path = table[i].path;
pcm_table_.push_back(pcm_data);
}
}
// wavのoffsetとsizeをpcm_table_に書き込む
void PlayGuitar::setup()
{
printf("PlayGuitar setup\n");
while (!sd.begin())
{
printf("please Insert SD card");
}
for (size_t i = 0; i < pcm_table_.size(); i++)
{
fmt_chunk_t fmt;
// sd cardからWavContainerFormatParserでwav情報読み込////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////み
handel_wav_parser_t *handle = (handel_wav_parser_t *)theParser.parseChunk(("/mnt/sd0/"+pcm_table_[i].path).c_str(), &fmt);
if (handle == NULL)
{
printf("Wav parser error note %d\n",pcm_table_[i].note);
exit(1);
}
printf("framerate %d = %d\n",i,fmt.rate);
printf("bit %d = %d\n",i,fmt.bit);
pcm_table_[i].offset = handle->data_offset;
pcm_table_[i].size = handle->data_size;
theParser.resetParser((handel_wav_parser *)handle);
}
printf("note, path, offset, size\n");
for (size_t i = 0; i < pcm_table_.size(); i++)
{
printf("%3d, %s, %d, %d\n",pcm_table_[i].note, pcm_table_[i].path.c_str(), pcm_table_[i].offset,pcm_table_[i].size);
}
}
//Audioの初期設定
void PlayGuitar::begin()
{
theAudio = AudioClass::getInstance();
theAudio->begin(audio_attention_cb);// HWとの接続
theAudio->setRenderingClockMode(AS_CLKMODE);
theAudio->setPlayerMode(AS_SETPLAYER_OUTPUTDEVICE_SPHP, max_buffer,max_buffer);// 出力先,player1 maxbuffer,player2 maxbuffer
//sampling,16or24,mono or stereo
err_t err = theAudio->initPlayer(AudioClass::Player0, AS_CODECTYPE_WAV, "/mnt/sd0/BIN",samplingrate,bit,channel);
if (err != AUDIOLIB_ECODE_OK)
{
printf("Player0 initialize error\n");
exit(1);
}
printf("initialization Audio Library\n");
}
// noteからwavファイルを検索し、
// wavファイルのoffsetの位置から読み込み、s_remain_sizeをset
bool PlayGuitar::file_open(int16_t note)
{
// noteからwavファイルを検索
int table_index_ = -1;
for (size_t i = 0; i < pcm_table_.size(); i++)
{
if (note == pcm_table_[i].note)
{
table_index_ = i;
break;
}
}
myFile.close();
if (table_index_ < 0)
{
printf("error: note is nothing(note=%d)\n", note);
return false;
}
//printf("play %s"pcm_table_[table_index_].path.c_str());
myFile = sd.open(pcm_table_[table_index_].path.c_str());
if (!myFile)
{
printf("error: file open error(%s)\n", pcm_table_[table_index_].path.c_str());
return false;
}
//offsetの位置から読込
//wavファイルのsizeをset
myFile.seek(pcm_table_[table_index_].offset);
s_remain_size = pcm_table_[table_index_].size;
return true;
}
// note番号と一致するものを最初のフレーム分、読み取る
// volumeも決まる
// started をset
bool PlayGuitar::sendNoteOn(int16_t note,int16_t volume)
{
// reset
supply_size = 0;
carry_over = false;
// 開始時のみファイルの読み込み
if(!file_open(note))
{
printf("file can't read");
return false;
}
// 入力が変化したらここのloopから抜け出せると良い
for (uint8_t i = 0; i < sc_store_frames; i++)
{
supply_size = myFile.read(s_buffer, (s_remain_size < sizeof(s_buffer)) ? s_remain_size : sizeof(s_buffer));
s_remain_size -= supply_size;
//Serial.print("remain_size=");
//Serial.println(s_remain_size);
int err = theAudio->writeFrames(AudioClass::Player0, s_buffer, supply_size);
//Serial.print("first write");
// 開始時のみチェック
if (err != AUDIOLIB_ECODE_OK)
{
Serial.println("SUDIOLIB_ECODE_OK");
break;
}
// 終了
if (s_remain_size == 0)
{
break;
}
}
theAudio->setVolume(volume);
theAudio->startPlayer(AudioClass::Player0);
started = true;
//puts("Play!");
return true;
}
// startedの時、数フレーム分読み取る
// s_remain_sizeが0になったら終了し、sendNoteOff(startedをreset)
void PlayGuitar::update()
{
if(started)
{
for (uint8_t i = 0; i < sc_store_frames_loop; i++)
{
if(!carry_over)
{
supply_size = myFile.read(s_buffer, (s_remain_size < sizeof(s_buffer)) ? s_remain_size : sizeof(s_buffer));
s_remain_size -= supply_size;
}
carry_over = false;
//sbufferのsupply_size(読み取れた量)をFIFOに変換
int err = theAudio->writeFrames(AudioClass::Player0, s_buffer, supply_size);
// bufferが満タン
if (err == AUDIOLIB_ECODE_SIMPLEFIFO_ERROR)
{
//Serial.println("SUDIOLIB_ECODE_SIMPLEFIFO_ERROR");
carry_over = true;
break;
}
else
{
//Serial.println("not full");
}
//Serial.println("s_remain_size =");
//Serial.println(s_remain_size);
// 終了
if (s_remain_size == 0)
{
//Serial.print("remain_size =0");
sendNoteOff();
break;
}
}
}
}
// 再生をstopし、startedをreset
void PlayGuitar::sendNoteOff()
{
if(started)
{
theAudio->stopPlayer(AudioClass::Player0,AS_STOPPLAYER_NORMAL);
started = false;
}
}
//void PlayGuitar::sendNoteEnd()
//{
// if(started)
// {
// theAudio->stopPlayer(AudioClass::Player0);
// started = false;
// }
//}
// 終了
void PlayGuitar::end()
{
theAudio->setReadyMode();
theAudio->end();
printf("Exit player\n");
exit(1);
}
//////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
const PlayGuitar::Item table[437] = {
{999,"A/free_u.wav"}, // setup.wav
{-1,"A/free_u.wav"}, // A/free_u.wav
{-11,"A/Caug_u.wav"},
{-12,"A/Cdim_u.wav"},
{-13,"A/CM_u.wav"},
{-14,"A/Cm__u.wav"},
{-15,"A/C7_u.wav"},
{-16,"A/Csus4_u.wav"},
{-17,"A/CM7_u.wav"},
{-18,"A/Cm7__u.wav"},
{-19,"A/C7sus4_u.wav"},
{-21,"A/C#aug_u.wav"},
{-22,"A/C#dim_u.wav"},
{-23,"A/C#M_u.wav"},
{-24,"A/C#m__u.wav"},
{-25,"A/C#7_u.wav"},
{-26,"A/C#sus4_u.wav"},
{-27,"A/C#M7_u.wav"},
{-28,"A/C#m7__u.wav"},
{-29,"A/C#7sus4_u.wav"},
{-31,"A/Daug_u.wav"},
{-32,"A/Ddim_u.wav"},
{-33,"A/DM_u.wav"},
{-34,"A/Dm__u.wav"},
{-35,"A/D7_u.wav"},
{-36,"A/Dsus4_u.wav"},
{-37,"A/DM7_u.wav"},
{-38,"A/Dm7__u.wav"},
{-39,"A/D7sus4_u.wav"},
{-41,"A/D#aug_u.wav"},
{-42,"A/D#dim_u.wav"},
{-43,"A/D#M_u.wav"},
{-44,"A/D#m__u.wav"},
{-45,"A/D#7_u.wav"},
{-46,"A/D#sus4_u.wav"},
{-47,"A/D#M7_u.wav"},
{-48,"A/D#m7__u.wav"},
{-49,"A/D#7sus4_u.wav"},
{-51,"A/Eaug_u.wav"},
{-52,"A/Edim_u.wav"},
{-53,"A/EM_u.wav"},
{-54,"A/Em__u.wav"},
{-55,"A/E7_u.wav"},
{-56,"A/Esus4_u.wav"},
{-57,"A/EM7_u.wav"},
{-58,"A/Em7__u.wav"},
{-59,"A/E7sus4_u.wav"},
{-61,"A/Faug_u.wav"},
{-62,"A/Fdim_u.wav"},
{-63,"A/FM_u.wav"},
{-64,"A/Fm__u.wav"},
{-65,"A/F7_u.wav"},
{-66,"A/Fsus4_u.wav"},
{-67,"A/FM7_u.wav"},
{-68,"A/Fm7__u.wav"},
{-69,"A/F7sus4_u.wav"},
{-71,"A/F#aug_u.wav"},
{-72,"A/F#dim_u.wav"},
{-73,"A/F#M_u.wav"},
{-74,"A/F#m__u.wav"},
{-75,"A/F#7_u.wav"},
{-76,"A/F#sus4_u.wav"},
{-77,"A/F#M7_u.wav"},
{-78,"A/F#m7__u.wav"},
{-79,"A/F#7sus4_u.wav"},
{-81,"A/Gaug_u.wav"},
{-82,"A/Gdim_u.wav"},
{-83,"A/GM_u.wav"},
{-84,"A/Gm__u.wav"},
{-85,"A/G7_u.wav"},
{-86,"A/Gsus4_u.wav"},
{-87,"A/GM7_u.wav"},
{-88,"A/Gm7__u.wav"},
{-89,"A/G7sus4_u.wav"},
{-91,"A/G#aug_u.wav"},
{-92,"A/G#dim_u.wav"},
{-93,"A/G#M_u.wav"},
{-94,"A/G#m__u.wav"},
{-95,"A/G#7_u.wav"},
{-96,"A/G#sus4_u.wav"},
{-97,"A/G#M7_u.wav"},
{-98,"A/G#m7__u.wav"},
{-99,"A/G#7sus4_u.wav"},
{-101,"A/Aaug_u.wav"},
{-102,"A/Adim_u.wav"},
{-103,"A/AM_u.wav"},
{-104,"A/Am__u.wav"},
{-105,"A/A7_u.wav"},
{-106,"A/Asus4_u.wav"},
{-107,"A/AM7_u.wav"},
{-108,"A/Am7__u.wav"},
{-109,"A/A7sus4_u.wav"},
{-111,"A/A#aug_u.wav"},
{-112,"A/A#dim_u.wav"},
{-113,"A/A#M_u.wav"},
{-114,"A/A#m__u.wav"},
{-115,"A/A#7_u.wav"},
{-116,"A/A#sus4_u.wav"},
{-117,"A/A#M7_u.wav"},
{-118,"A/A#m7__u.wav"},
{-119,"A/A#7sus4_u.wav"},
{-121,"A/Baug_u.wav"},
{-122,"A/Bdim_u.wav"},
{-123,"A/BM_u.wav"},
{-124,"A/Bm__u.wav"},
{-125,"A/B7_u.wav"},
{-126,"A/Bsus4_u.wav"},
{-127,"A/BM7_u.wav"},
{-128,"A/Bm7__u.wav"},
{-129,"A/B7sus4_u.wav"},
{1,"A/free_d.wav"}, // A/free_d.wav
{11,"A/Caug_d.wav"},
{12,"A/Cdim_d.wav"},
{13,"A/CM_d.wav"},
{14,"A/Cm__d.wav"},
{15,"A/C7_d.wav"},
{16,"A/Csus4_d.wav"},
{17,"A/CM7_d.wav"},
{18,"A/Cm7__d.wav"},
{19,"A/C7sus4_d.wav"},
{21,"A/C#aug_d.wav"},
{22,"A/C#dim_d.wav"},
{23,"A/C#M_d.wav"},
{24,"A/C#m__d.wav"},
{25,"A/C#7_d.wav"},
{26,"A/C#sus4_d.wav"},
{27,"A/C#M7_d.wav"},
{28,"A/C#m7__d.wav"},
{29,"A/C#7sus4_d.wav"},
{31,"A/Daug_d.wav"},
{32,"A/Ddim_d.wav"},
{33,"A/DM_d.wav"},
{34,"A/Dm__d.wav"},
{35,"A/D7_d.wav"},
{36,"A/Dsus4_d.wav"},
{37,"A/DM7_d.wav"},
{38,"A/Dm7__d.wav"},
{39,"A/D7sus4_d.wav"},
{41,"A/D#aug_d.wav"},
{42,"A/D#dim_d.wav"},
{43,"A/D#M_d.wav"},
{44,"A/D#m__d.wav"},
{45,"A/D#7_d.wav"},
{46,"A/D#sus4_d.wav"},
{47,"A/D#M7_d.wav"},
{48,"A/D#m7__d.wav"},
{49,"A/D#7sus4_d.wav"},
{51,"A/Eaug_d.wav"},
{52,"A/Edim_d.wav"},
{53,"A/EM_d.wav"},
{54,"A/Em__d.wav"},
{55,"A/E7_d.wav"},
{56,"A/Esus4_d.wav"},
{57,"A/EM7_d.wav"},
{58,"A/Em7__d.wav"},
{59,"A/E7sus4_d.wav"},
{61,"A/Faug_d.wav"},
{62,"A/Fdim_d.wav"},
{63,"A/FM_d.wav"},
{64,"A/Fm__d.wav"},
{65,"A/F7_d.wav"},
{66,"A/Fsus4_d.wav"},
{67,"A/FM7_d.wav"},
{68,"A/Fm7__d.wav"},
{69,"A/F7sus4_d.wav"},
{71,"A/F#aug_d.wav"},
{72,"A/F#dim_d.wav"},
{73,"A/F#M_d.wav"},
{74,"A/F#m__d.wav"},
{75,"A/F#7_d.wav"},
{76,"A/F#sus4_d.wav"},
{77,"A/F#M7_d.wav"},
{78,"A/F#m7__d.wav"},
{79,"A/F#7sus4_d.wav"},
{81,"A/Gaug_d.wav"},
{82,"A/Gdim_d.wav"},
{83,"A/GM_d.wav"},
{84,"A/Gm__d.wav"},
{85,"A/G7_d.wav"},
{86,"A/Gsus4_d.wav"},
{87,"A/GM7_d.wav"},
{88,"A/Gm7__d.wav"},
{89,"A/G7sus4_d.wav"},
{91,"A/G#aug_d.wav"},
{92,"A/G#dim_d.wav"},
{93,"A/G#M_d.wav"},
{94,"A/G#m__d.wav"},
{95,"A/G#7_d.wav"},
{96,"A/G#sus4_d.wav"},
{97,"A/G#M7_d.wav"},
{98,"A/G#m7__d.wav"},
{99,"A/G#7sus4_d.wav"},
{101,"A/Aaug_d.wav"},
{102,"A/Adim_d.wav"},
{103,"A/AM_d.wav"},
{104,"A/Am__d.wav"},
{105,"A/A7_d.wav"},
{106,"A/Asus4_d.wav"},
{107,"A/AM7_d.wav"},
{108,"A/Am7__d.wav"},
{109,"A/A7sus4_d.wav"},
{111,"A/A#aug_d.wav"},
{112,"A/A#dim_d.wav"},
{113,"A/A#M_d.wav"},
{114,"A/A#m__d.wav"},
{115,"A/A#7_d.wav"},
{116,"A/A#sus4_d.wav"},
{117,"A/A#M7_d.wav"},
{118,"A/A#m7__d.wav"},
{119,"A/A#7sus4_d.wav"},
{121,"A/Baug_d.wav"},
{122,"A/Bdim_d.wav"},
{123,"A/BM_d.wav"},
{124,"A/Bm__d.wav"},
{125,"A/B7_d.wav"},
{126,"A/Bsus4_d.wav"},
{127,"A/BM7_d.wav"},
{128,"A/Bm7__d.wav"},
{129,"A/B7sus4_d.wav"},
{-1001,"E/free_u.wav"},// E/free_u.wav
{-1011,"E/Caug_u.wav"},
{-1012,"E/Cdim_u.wav"},
{-1013,"E/CM_u.wav"},
{-1014,"E/Cm__u.wav"},
{-1015,"E/C7_u.wav"},
{-1016,"E/Csus4_u.wav"},
{-1017,"E/CM7_u.wav"},
{-1018,"E/Cm7__u.wav"},
{-1019,"E/C7sus4_u.wav"},
{-1021,"E/C#aug_u.wav"},
{-1022,"E/C#dim_u.wav"},
{-1023,"E/C#M_u.wav"},
{-1024,"E/C#m__u.wav"},
{-1025,"E/C#7_u.wav"},
{-1026,"E/C#sus4_u.wav"},
{-1027,"E/C#M7_u.wav"},
{-1028,"E/C#m7__u.wav"},
{-1029,"E/C#7sus4_u.wav"},
{-1031,"E/Daug_u.wav"},
{-1032,"E/Ddim_u.wav"},
{-1033,"E/DM_u.wav"},
{-1034,"E/Dm__u.wav"},
{-1035,"E/D7_u.wav"},
{-1036,"E/Dsus4_u.wav"},
{-1037,"E/DM7_u.wav"},
{-1038,"E/Dm7__u.wav"},
{-1039,"E/D7sus4_u.wav"},
{-1041,"E/D#aug_u.wav"},
{-1042,"E/D#dim_u.wav"},
{-1043,"E/D#M_u.wav"},
{-1044,"E/D#m__u.wav"},
{-1045,"E/D#7_u.wav"},
{-1046,"E/D#sus4_u.wav"},
{-1047,"E/D#M7_u.wav"},
{-1048,"E/D#m7__u.wav"},
{-1049,"E/D#7sus4_u.wav"},
{-1051,"E/Eaug_u.wav"},
{-1052,"E/Edim_u.wav"},
{-1053,"E/EM_u.wav"},
{-1054,"E/Em__u.wav"},
{-1055,"E/E7_u.wav"},
{-1056,"E/Esus4_u.wav"},
{-1057,"E/EM7_u.wav"},
{-1058,"E/Em7__u.wav"},
{-1059,"E/E7sus4_u.wav"},
{-1061,"E/Faug_u.wav"},
{-1062,"E/Fdim_u.wav"},
{-1063,"E/FM_u.wav"},
{-1064,"E/Fm__u.wav"},
{-1065,"E/F7_u.wav"},
{-1066,"E/Fsus4_u.wav"},
{-1067,"E/FM7_u.wav"},
{-1068,"E/Fm7__u.wav"},
{-1069,"E/F7sus4_u.wav"},
{-1071,"E/F#aug_u.wav"},
{-1072,"E/F#dim_u.wav"},
{-1073,"E/F#M_u.wav"},
{-1074,"E/F#m__u.wav"},
{-1075,"E/F#7_u.wav"},
{-1076,"E/F#sus4_u.wav"},
{-1077,"E/F#M7_u.wav"},
{-1078,"E/F#m7__u.wav"},
{-1079,"E/F#7sus4_u.wav"},
{-1081,"E/Gaug_u.wav"},
{-1082,"E/Gdim_u.wav"},
{-1083,"E/GM_u.wav"},
{-1084,"E/Gm__u.wav"},
{-1085,"E/G7_u.wav"},
{-1086,"E/Gsus4_u.wav"},
{-1087,"E/GM7_u.wav"},
{-1088,"E/Gm7__u.wav"},
{-1089,"E/G7sus4_u.wav"},
{-1091,"E/G#aug_u.wav"},
{-1092,"E/G#dim_u.wav"},
{-1093,"E/G#M_u.wav"},
{-1094,"E/G#m__u.wav"},
{-1095,"E/G#7_u.wav"},
{-1096,"E/G#sus4_u.wav"},
{-1097,"E/G#M7_u.wav"},
{-1098,"E/G#m7__u.wav"},
{-1099,"E/G#7sus4_u.wav"},
{-1101,"E/Aaug_u.wav"},
{-1102,"E/Adim_u.wav"},
{-1103,"E/AM_u.wav"},
{-1104,"E/Am__u.wav"},
{-1105,"E/A7_u.wav"},
{-1106,"E/Asus4_u.wav"},
{-1107,"E/AM7_u.wav"},
{-1108,"E/Am7__u.wav"},
{-1109,"E/A7sus4_u.wav"},
{-1111,"E/A#aug_u.wav"},
{-1112,"E/A#dim_u.wav"},
{-1113,"E/A#M_u.wav"},
{-1114,"E/A#m__u.wav"},
{-1115,"E/A#7_u.wav"},
{-1116,"E/A#sus4_u.wav"},
{-1117,"E/A#M7_u.wav"},
{-1118,"E/A#m7__u.wav"},
{-1119,"E/A#7sus4_u.wav"},
{-1121,"E/Baug_u.wav"},
{-1122,"E/Bdim_u.wav"},
{-1123,"E/BM_u.wav"},
{-1124,"E/Bm__u.wav"},
{-1125,"E/B7_u.wav"},
{-1126,"E/Bsus4_u.wav"},
{-1127,"E/BM7_u.wav"},
{-1128,"E/Bm7__u.wav"},
{-1129,"E/B7sus4_u.wav"},
{1001,"E/free_d.wav"},// E/free_d.wav
{1011,"E/Caug_d.wav"},
{1012,"E/Cdim_d.wav"},
{1013,"E/CM_d.wav"},
{1014,"E/Cm__d.wav"},
{1015,"E/C7_d.wav"},
{1016,"E/Csus4_d.wav"},
{1017,"E/CM7_d.wav"},
{1018,"E/Cm7__d.wav"},
{1019,"E/C7sus4_d.wav"},
{1021,"E/C#aug_d.wav"},
{1022,"E/C#dim_d.wav"},
{1023,"E/C#M_d.wav"},
{1024,"E/C#m__d.wav"},
{1025,"E/C#7_d.wav"},
{1026,"E/C#sus4_d.wav"},
{1027,"E/C#M7_d.wav"},
{1028,"E/C#m7__d.wav"},
{1029,"E/C#7sus4_d.wav"},
{1031,"E/Daug_d.wav"},
{1032,"E/Ddim_d.wav"},
{1033,"E/DM_d.wav"},
{1034,"E/Dm__d.wav"},
{1035,"E/D7_d.wav"},
{1036,"E/Dsus4_d.wav"},
{1037,"E/DM7_d.wav"},
{1038,"E/Dm7__d.wav"},
{1039,"E/D7sus4_d.wav"},
{1041,"E/D#aug_d.wav"},
{1042,"E/D#dim_d.wav"},
{1043,"E/D#M_d.wav"},
{1044,"E/D#m__d.wav"},
{1045,"E/D#7_d.wav"},
{1046,"E/D#sus4_d.wav"},
{1047,"E/D#M7_d.wav"},
{1048,"E/D#m7__d.wav"},
{1049,"E/D#7sus4_d.wav"},
{1051,"E/Eaug_d.wav"},
{1052,"E/Edim_d.wav"},
{1053,"E/EM_d.wav"},
{1054,"E/Em__d.wav"},
{1055,"E/E7_d.wav"},
{1056,"E/Esus4_d.wav"},
{1057,"E/EM7_d.wav"},
{1058,"E/Em7__d.wav"},
{1059,"E/E7sus4_d.wav"},
{1061,"E/Faug_d.wav"},
{1062,"E/Fdim_d.wav"},
{1063,"E/FM_d.wav"},
{1064,"E/Fm__d.wav"},
{1065,"E/F7_d.wav"},
{1066,"E/Fsus4_d.wav"},
{1067,"E/FM7_d.wav"},
{1068,"E/Fm7__d.wav"},
{1069,"E/F7sus4_d.wav"},
{1071,"E/F#aug_d.wav"},
{1072,"E/F#dim_d.wav"},
{1073,"E/F#M_d.wav"},
{1074,"E/F#m__d.wav"},
{1075,"E/F#7_d.wav"},
{1076,"E/F#sus4_d.wav"},
{1077,"E/F#M7_d.wav"},
{1078,"E/F#m7__d.wav"},
{1079,"E/F#7sus4_d.wav"},
{1081,"E/Gaug_d.wav"},
{1082,"E/Gdim_d.wav"},
{1083,"E/GM_d.wav"},
{1084,"E/Gm__d.wav"},
{1085,"E/G7_d.wav"},
{1086,"E/Gsus4_d.wav"},
{1087,"E/GM7_d.wav"},
{1088,"E/Gm7__d.wav"},
{1089,"E/G7sus4_d.wav"},
{1091,"E/G#aug_d.wav"},
{1092,"E/G#dim_d.wav"},
{1093,"E/G#M_d.wav"},
{1094,"E/G#m__d.wav"},
{1095,"E/G#7_d.wav"},
{1096,"E/G#sus4_d.wav"},
{1097,"E/G#M7_d.wav"},
{1098,"E/G#m7__d.wav"},
{1099,"E/G#7sus4_d.wav"},
{1101,"E/Aaug_d.wav"},
{1102,"E/Adim_d.wav"},
{1103,"E/AM_d.wav"},
{1104,"E/Am__d.wav"},
{1105,"E/A7_d.wav"},
{1106,"E/Asus4_d.wav"},
{1107,"E/AM7_d.wav"},
{1108,"E/Am7__d.wav"},
{1109,"E/A7sus4_d.wav"},
{1111,"E/A#aug_d.wav"},
{1112,"E/A#dim_d.wav"},
{1113,"E/A#M_d.wav"},
{1114,"E/A#m__d.wav"},
{1115,"E/A#7_d.wav"},
{1116,"E/A#sus4_d.wav"},
{1117,"E/A#M7_d.wav"},
{1118,"E/A#m7__d.wav"},
{1119,"E/A#7sus4_d.wav"},
{1121,"E/Baug_d.wav"},
{1122,"E/Bdim_d.wav"},
{1123,"E/BM_d.wav"},
{1124,"E/Bm__d.wav"},
{1125,"E/B7_d.wav"},
{1126,"E/Bsus4_d.wav"},
{1127,"E/BM7_d.wav"},
{1128,"E/Bm7__d.wav"},
{1129,"E/B7sus4_d.wav"}
};
/////////////////////////////////////receive code list(Subcore 3)///////////////////////////////////////////////////////
#define MSGLEN 200
struct MyPacket {
volatile int status; /* 0:ready, 1:busy */
char message[MSGLEN];
};
std::vector<int16_t> number(char* str)
{
char *ptr;
std::vector<int16_t> v;
v.clear();
// _を区切りに文字列を分割
// 1回目
ptr = strtok(str, "_");
v.push_back(atoi(ptr));
//printf("ptr = %s\n",ptr);
// 2回目以降
while(ptr != NULL) {
// strtok関数により変更されたNULLのポインタが先頭
ptr = strtok(NULL, "_");
// ptrがNULLの場合エラーが発生するので対処
if(ptr != NULL) {
v.push_back(atoi(ptr));
//printf("ptr,i =%s,%d\n", ptr,i);
}
}
return v;
}
/////////////////////////////////////////// Main //////////////////////////////////////////////////////////////////////
#define Swich_12 (12)//音源
#define Swich_13 (13)//beginner_mode
PlayGuitar inst(table, 437);
void setup()
{
// シリアル通信設定
Serial.begin(2000000);
inst.setup();
inst.begin();
pinMode(Swich_12, INPUT);
pinMode(Swich_13, INPUT);
// マルチコア起動
int ret1 = 0;
int ret2 = 0;
int ret3 = 0;
ret1 = MP.begin(subcore1);
ret2 = MP.begin(subcore2);
ret3 = MP.begin(subcore3);
MP.RecvTimeout(MP_RECV_POLLING);
// 起動エラー
if (ret1 < 0)
printf("MP.begin 1 error = %d\n", ret1);
if (ret2 < 0)
printf("MP.begin 2 error = %d\n", ret2);
if (ret3 < 0)
printf("MP.begin 3 error = %d\n", ret3);
printf("start\n");
inst.sendNoteOn(999,-100);
}
bool speed_up = false;
uint32_t rcvdata2;
long sub2_time = 0;
bool beginner_mode_started = false;
std::vector<int16_t> code_list;
uint16_t list_count = 0;
int16_t note = 10000;
int16_t volume = 0;
void loop()
{
//////////////////////加速判定 (subcore 2)//////////////////////////
long now_time = micros();
double receive_time_million = now_time - sub2_time;
double receive_time = receive_time_million/1000000;
if(receive_time>0.08)
{
int ret2;
int8_t rcvid;
MP.RecvTimeout(MP_RECV_POLLING);
ret2 = MP.Recv(&rcvid, &rcvdata2, subcore2); // SubCore2からmsg受信
if (ret2 < 0) {
//printf("encorder can");
}
else
{
sub2_time = micros();
printf("Encorder data=%d \n", rcvdata2);
//音量の変更
if(rcvdata2 == 1||rcvdata2 == 2)
{
volume = 50;
}
else
{
volume = -100;
}
speed_up = true;
}
}
///////////////////////receive code list (subcore 3)//////////////////////////
MP.RecvTimeout(MP_RECV_POLLING);
int ret3;
int8_t msgid;
MyPacket *packet;
/* Receive message from SubCore */
ret3 = MP.Recv(&msgid, &packet, subcore3);
if (ret3 > 0) {
//printf("%s\n", packet->message);
char* note_message = packet->message;
code_list = number(note_message);
printf("size = %d\n",code_list.size());
for(int i=0;i< code_list.size();i++)
{
printf("%d,",code_list[i]);
}
printf("\n");
/* status -> ready */
packet->status = 0;
inst.sendNoteOff();
inst.sendNoteOn(1001,-100);
}
///////////////////////////////////////////////////////////////
bool beginner_mode = digitalRead(Swich_13);
if(beginner_mode_started == true&&beginner_mode == false)
{
beginner_mode_started = false;
}
if(speed_up)
{
//////////// beginner mode ///////////////////////////////////////
if(beginner_mode)
{
printf("beginner mode = %d\n",beginner_mode_started);
if(code_list.size()>0)
{
if(!beginner_mode_started)
{
list_count = 0;
note = code_list[list_count];
beginner_mode_started = true;
}
else
{
list_count = list_count+1;
if(list_count == code_list.size())
{
list_count = 0;
}
note = code_list[list_count];
}
}
}
/////////////// code or manual mode (subcore 1)//////////////////////////
else
{
int ret1;
uint32_t rcvdata = 1;
int8_t rcvid = 1;
ret1 = MP.Send(rcvid,rcvdata,subcore1);
if(ret1<0)
printf("fail connect SubCore1\n");
else
{
//Serial.println("to 1 send success");
MP.RecvTimeout(MP_RECV_BLOCKING);
ret1 = MP.Recv(&rcvid, &rcvdata, subcore1); // SubCore1からmsg受
if(ret1<0)
printf("can't receive SubCore1\n");
else
{
note = rcvdata;
printf("from SubCore1 data %d\n",note);
}
}
}
////////////////// 音を鳴らす //////////////////////
if(note == 10000)
{
}
else
{
// elekiの場合
bool Acoustic = digitalRead(Swich_12);
if(!Acoustic)
{
note = note + 1000;
}
//ダウンストロークの場合
if(rcvdata2 == 2||rcvdata2 == 3)
{
note = -note;
}
printf("play_number= %d\n",note);
inst.sendNoteOff();
inst.sendNoteOn(note,volume);
}
speed_up = false;
}
inst.update();
return;
}
SubCore1
SubCore1では、タッチセンサの触れている場所を検出しています。
以下の動画のように1つずつタッチセンサに触れていった場合、タッチセンサの値はそれぞれ以下の図のように変化します。
触れていないときの値と触れたときの差が閾値以上である場所を読み取り、これが5回中3回検出されたとき、その場所のタッチセンサに触れていると判定します。同時押しも読み取り可能です。
SubCore1
#if (SUBCORE != 1) // SubCoreの指定
#error "Core selection is wrong!!"
#endif
#include <MP.h>
#include <vector>
using namespace std;
class Touch_Sensor
{
private:
//pin番号
const uint8_t SRCLK = 2;
const uint8_t RCLK = 3;
const uint8_t G = 4;
const uint8_t SER = 5;
const uint8_t input_pin = 6;
public:
struct Touch_data
{
uint8_t note;
vector<uint8_t> touch_number;
};
int8_t touch_num = 88;
//初期設定
void setup();
//filter処理
void filter();
//タッチセンサ処理
void get_sensor(bool setup);
//押されたタッチセンサ番号
vector<uint8_t> touch_number;
uint8_t filter_k = 5; // 総フレーム数
uint8_t filter_threshold= 3; // このフレーム数反応した場所をtouch_number_filterに追加
vector<uint8_t> touch_number_k[5]; // 各フレームで反応したタッチセンサの場所
vector<uint8_t> touch_number_filter;
};
void Touch_Sensor::setup()
{
pinMode(SRCLK, OUTPUT);//SRCLK
pinMode(RCLK, OUTPUT);//RCLK
pinMode(SER, OUTPUT);//SER
pinMode(G, OUTPUT);//G
pinMode(input_pin, INPUT);//input_pin
for(uint8_t j = 0; j < 5; j++)
{
get_sensor(true);
}
for (uint8_t k=0; k<filter_k; k++)
{
touch_number_k[k].clear();
}
Serial.println("touch sensor setup end");
}
// 数フレームののタッチセンサの値で押弦判断
void Touch_Sensor::filter()
{
static uint8_t touch_k[88]={0};
touch_number_filter.clear();
// 最初のフレームでタッチした場所を-1引く
for(uint8_t j = 0; j<touch_number_k[0].size(); j++)
{
for(uint8_t i = 0; i < touch_num; i++)
{
if(touch_number_k[0][j]==i)
touch_k[i] = touch_k[i]-1;
}
}
// 前に詰める
for(uint8_t k=0; k < (filter_k-1); k++)
{
touch_number_k[k].clear();
for (uint8_t j=0; j< touch_number_k[k+1].size(); j++)
{
touch_number_k[k].push_back(touch_number_k[k+1][j]);
}
}
// 最新のフレームでタッチされた場所
get_sensor(false);
touch_number_k[filter_k-1].clear();
for (uint8_t j=0; j< touch_number.size(); j++)
{
touch_number_k[filter_k-1].push_back(touch_number[j]);
}
// 最新のフレームでタッチした場所を1足す
for(uint8_t j = 0; j<touch_number_k[filter_k-1].size(); j++)
{
for(uint8_t i = 0; i < touch_num; i++)
{
if(touch_number_k[filter_k-1][j]==i)
touch_k[i] = touch_k[i]+1;
}
}
// 各フレームでのタッチした位置確認用
for (uint8_t k=0; k<filter_k; k++)
{
printf("touch_number %d:",k+1);
for (uint8_t j=0; j<touch_number_k[k].size();j++)
{
printf("%d,",touch_number_k[k][j]);
}
printf("\n");
}
// 閾値(filter_threshold)を超えた場所をtouch_number_filterへ
for(uint8_t i = 0; i < touch_num; i++)
{
if(touch_k[i] >= filter_threshold)
touch_number_filter.push_back(i);
}
}
void Touch_Sensor::get_sensor(bool setup)
{
//タッチセンサの値
static uint8_t touch_flag[88];
//閾値
static uint8_t no_touch[88] = {0};
// static uint8_t no_touch_u[88] = {0};
//タッチセンサの初期化
for(uint8_t i = 0; i < touch_num; i++)
{
touch_flag[i] = 0;
}
touch_number.clear();
delayMicroseconds(150);
// 入力状態をHIGHにする
digitalWrite(SER, HIGH);
for (uint8_t i=0; i<touch_num; i++) {
// 閾値決定のための変数
uint8_t sub_threshold = 1;
// シフトレジスタをシフトさせてSERピンの状態を記憶させる
digitalWrite(SRCLK, LOW);
delayMicroseconds(2);
digitalWrite(SRCLK, HIGH);
// シフトレジスタの状態をストレージレジスタへ反映させる
digitalWrite(RCLK, LOW);
delayMicroseconds(2);
digitalWrite(RCLK, HIGH);
//出力を有効にする
digitalWrite(G, LOW);
while(digitalRead(input_pin) != HIGH)
{
if (setup)
{
sub_threshold++;
}
else
{
touch_flag[i]++;
}
}
if (setup)
{
// printf("%d",i);
//最大値を閾値とする
if(sub_threshold > no_touch[i]){
no_touch[i] = sub_threshold;
}
}
else
{
uint8_t threshold_u = (no_touch[i]/4)*5;
uint8_t threshold_s = (no_touch[i]/4)*2;
if((touch_flag[i] > threshold_u)||(touch_flag[i]<threshold_s))// no_touchが30のとき 7.5 60のとき15
{
touch_number.push_back(i);
}
}
delayMicroseconds(50);
//出力を無効にする
digitalWrite(G, HIGH);
// 放電するまで待つ
delayMicroseconds(200);
// 以降の入力状態をLOWにする
digitalWrite(SER, LOW);
}
// 閾値(no_touch)確認用
printf("no_touch\n");
for(int i=0;i<88;i++){
printf("%d, ",no_touch[i]);
}
printf("\n");
// タッチセンサの値確認用
for(int i=0;i<88;i++){
printf("%d, ",touch_flag[i]);
}
printf("\n");
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
const uint16_t code_mode_size = 109;
const Touch_Sensor::Touch_data code_mode[109] = {
{1, {}},
{11, {0}},
{12, {1}},
{13, {2}},
{14, {3}},
{15, {4}},
{16, {5}},
{17, {2,4}},
{18, {3,4}},
{19, {4,5}},
{21, {6}},
{22, {7}},
{23, {8}},
{24, {9}},
{25, {10}},
{26, {11}},
{27, {8,10}},
{28, {9,10}},
{29, {10,11}},
{31, {12}},
{32, {13}},
{33, {14}},
{34, {15}},
{35, {16}},
{36, {17}},
{37, {14,16}},
{38, {15,16}},
{39, {16,17}},
{41, {18}},
{42, {19}},
{43, {20}},
{44, {21}},
{45, {22}},
{46, {23}},
{47, {20,22}},
{48, {21,22}},
{49, {22,23}},
{51, {24}},
{52, {25}},
{53, {26}},
{54, {27}},
{55, {28}},
{56, {29}},
{57, {26,28}},
{58, {27,28}},
{59, {28,29}},
{61, {30}},
{62, {31}},
{63, {32}},
{64, {33}},
{65, {34}},
{66, {35}},
{67, {32,34}},
{68, {33,34}},
{69, {34,35}},
{71, {36}},
{72, {37}},
{73, {38}},
{74, {39}},
{75, {40}},
{76, {41}},
{77, {38,40}},
{78, {39,40}},
{79, {40,41}},
{81, {42}},
{82, {43}},
{83, {44}},
{84, {45}},
{85, {46}},
{86, {47}},
{87, {44,46}},
{88, {45,46}},
{89, {46,47}},
{91, {48}},
{92, {49}},
{93, {50}},
{94, {51}},
{95, {52}},
{96, {53}},
{97, {50,52}},
{98, {51,52}},
{99, {52,53}},
{101, {54}},
{102, {55}},
{103, {56}},
{104, {57}},
{105, {58}},
{106, {59}},
{107, {56,58}},
{108, {57,58}},
{109, {58,59}},
{111, {60}},
{112, {61}},
{113, {62}},
{114, {63}},
{115, {64}},
{116, {65}},
{117, {62,64}},
{118, {63,64}},
{119, {64,65}},
{121, {66}},
{122, {67}},
{123, {68}},
{124, {69}},
{125, {70}},
{126, {71}},
{127, {68,70}},
{128, {69,70}},
{129, {70,71}}
};
/////////////////////////////
const uint16_t manual_mode_size = 225;
const Touch_Sensor::Touch_data manual_mode[225] = {
{1, {}},
{11, {3,4,8,13}},
{11, {42,51,52,56}},
{12, {13,20,22,27}},
{12, {40,42,45}},
{13, {4,8,13}},
{13, {13,14,15,16,17,26,27,28}},
{14, {2,4,13}},
{14, {13,14,15,16,17,22,26,27}},
{14, {42,43,44,45,46,47,55,56}},
{15, {4,8,13,15}},
{15, {13,14,15,16,17,26,28}},
{16, {4,5,13,14}},
{16, {13,14,15,16,17,26,27}},
{17, {8,13}},
{17, {42,46,50,51}},
{18, {13,14,15,16,17,22,26}},
{18, {42,43,44,45,46,47,55}},
{19, {13,14,15,16,17,26,34}},
{19, {42,43,44,45,46,47,55,57}},
{21, {9,10,14,19}},
{21, {53,57,58,62}},
{22, {19,26,28,33}},
{22, {46,48,51}},
{23, {19,20,21,22,23,32,33,34}},
{23, {48,49,50,51,52,53,57,61,62}},
{24, {19,20,21,22,23,28,32,33}},
{24, {48,49,50,51,52,53,61,62}},
{25, {19,20,21,22,23,32,34}},
{25, {48,49,50,51,52,53,57,61}},
{26, {19,20,21,22,23,32,33,40}},
{26, {48,49,50,51,52,53,61,62,63}},
{27, {19,20,21,22,23,27,32,34}},
{27, {48,52,56,57}},
{28, {3,8,19}},
{28, {19,20,21,22,23,28,32}},
{28, {48,49,50,51,52,53,61}},
{29, {19,20,21,22,23,32,40}},
{29, {48,49,50,51,52,53,61,63}},
{31, {11,15,16}},
{31, {54,63,64,68}},
{32, {3,5}},
{32, {25,32,34,39}},
{33, {9,11,16}},
{33, {25,29,38,39,40}},
{34, {5,9,16}},
{34, {25,26,27,28,29,34,38,39}},
{35, {4,9,11}},
{35, {25,26,27,28,29,38,40}},
{36, {9,16,17}},
{36, {25,26,27,28,29,38,39}},
{37, {9,10,11}},
{37, {25,26,27,28,29,33,38,40}},
{38, {4,5,9}},
{38, {25,26,27,28,29,34,38}},
{39, {4,9,17}},
{39, {25,26,27,28,29,38}},
{41, {2,17}},
{41, {21,22,26,31}},
{41, {65,69,70,74}},
{42, {31,38,40,45}},
{42, {58,60,63}},
{43, {15,17,22,26,31}},
{43, {31,32,33,34,35,44,45,46}},
{44, {31,32,33,34,35,40,44,45}},
{44, {60,61,62,63,64,65,73,74}},
{45, {31,32,33,34,35,44,46}},
{45, {60,61,62,63,64,65,69,73}},
{46, {31,32,33,34,35,44,45,52}},
{46, {60,61,62,63,64,65,73,74,75}},
{47, {31,32,33,34,35,39,44,46}},
{47, {60,64,68,69}},
{48, {31,32,33,34,35,40,44}},
{48, {60,62,63,64}},
{49, {31,32,33,34,35,44,52}},
{49, {60,61,62,63,64,65,73,75}},
{51, {3,4,8,13}},
{51, {37,47,51,52}},
{52, {8,15,17}},
{52, {37,44,46,51}},
{53, {3,7,8}},
{53, {37,38,39,40,41,50,51,52}},
{54, {7,8}},
{54, {37,38,39,40,41,46,50,51}},
{55, {3,7}},
{55, {37,38,39,40,41,50,52}},
{56, {7,8,9}},
{56, {37,38,39,40,41,50,51}},
{57, {2,3,7}},
{57, {37,38,39,40,41,45,50,52}},
{58, {7}},
{58, {26,27,28,37,39}},
{59, {7,9}},
{59, {37,38,39,40,41,50}},
{61, {0,9,10,14}},
{61, {33,34,38,43}},
{62, {3,5,14}},
{62, {43,50,52,57}},
{63, {0,1,2,3,4,5,9,13,14}},
{63, {43,44,45,46,47,56,57,58}},
{64, {0,1,2,3,4,5,13,14}},
{64, {43,44,45,46,47,52,56,57}},
{65, {0,1,2,3,4,5,9,19}},
{65, {43,44,45,46,47,56,58}},
{66, {0,1,2,3,4,5,13,14,15}},
{66, {43,44,45,46,47,56,57}},
{67, {0,4,8,9}},
{67, {43,44,45,46,47,51,56,58}},
{68, {0,1,2,3,4,5,13}},
{68, {32,33,34,43,45}},
{69, {0,1,2,3,4,5,13,15}},
{69, {43,44,45,46,47,56}},
{71, {11,15,16,20}},
{71, {39,40,44,49}},
{72, {4,6,9}},
{72, {49,56,58,63}},
{73, {6,7,8,9,10,11,15,19,20}},
{73, {49,50,51,52,53,62,63,64}},
{74, {6,7,8,9,10,11,19,20}},
{74, {49,50,51,52,53,58,62,63}},
{75, {10,15,20}},
{75, {6,7,8,9,10,11,15,19}},
{75, {49,50,51,52,53,62,64}},
{76, {6,7,8,9,10,11,19,20,21}},
{76, {49,50,51,52,53,62,63}},
{77, {6,10,14,15}},
{77, {49,50,51,52,53,57,62,64}},
{78, {6,7,8,9,10,11,19}},
{78, {49,50,51,52,53,58,62}},
{79, {6,7,8,9,10,11,19,21}},
{79, {49,50,51,52,53,62}},
{81, {2,7,12,17}},
{81, {12,21,22,26}},
{82, {10,12,15}},
{82, {55,62,64,69}},
{83, {7,12,17}},
{83, {12,13,14,15,16,17,21,25,26}},
{84, {1,12}},
{84, {12,13,14,15,16,17,25,26}},
{84, {55,56,57,58,59,64,68,69}},
{85, {5,7,12}},
{85, {12,13,14,15,16,17,21,25}},
{86, {4,12,13,17}},
{86, {12,13,14,15,16,17,25,26,27}},
{87, {7,11,12}},
{87, {12,13,14,15,16,17,20,21,25}},
{88, {12,13,14,15,16,17,25}},
{88, {44,45,46,55,57}},
{89, {4,5,12,13}},
{89, {12,13,14,15,16,17,25,25}},
{91, {23,27,28,32}},
{91, {51,52,56,61}},
{92, {16,18,21}},
{92, {61,68,70,75}},
{93, {18,19,20,21,22,23,27,31,32}},
{93, {61,62,63,64,65,74,75,76}},
{94, {18,19,20,21,22,23,31,33}},
{94, {61,62,63,64,65,70,74,75}},
{95, {18,19,20,21,22,23,27,31}},
{95, {61,62,63,64,65,74,76}},
{96, {18,19,20,21,22,23,31,32,33}},
{96, {61,62,63,64,65,74,75}},
{97, {18,22,26,27}},
{97, {61,62,63,64,65,69,74,76}},
{98, {18,19,20,21,22,23,31}},
{98, {61,62,63,64,65,70,74}},
{99, {18,19,20,21,22,23,31,33}},
{99, {61,62,63,64,65,74}},
{101, {5,9,10,14}},
{101, {24,33,34,38}},
{102, {2,4,9}},
{102, {22,24,27}},
{103, {8,9,10}},
{103, {24,25,26,27,28,29,33,37,38}},
{104, {4,8,9}},
{104, {24,25,26,27,28,29,37,38}},
{105, {8,10}},
{105, {24,25,26,27,28,29,33,37}},
{106, {8,9,16}},
{106, {24,25,26,27,28,29,37,38,39}},
{107, {3,8,10}},
{107, {24,28,32,34}},
{108, {4,8}},
{108, {24,25,26,27,28,29,37}},
{109, {8,16}},
{109, {24,26,28,29,37,39}},
{111, {35,39,40,44}},
{111, {63,64,68,73}},
{112, {1,8,10,15}},
{112, {28,30,33}},
{113, {1,2,3,4,5,14,15,16}},
{113, {30,31,32,33,34,35,39,43,44}},
{114, {1,2,3,4,5,10,14,15}},
{114, {30,31,32,33,34,35,43,44}},
{115, {1,2,3,4,5,14,16}},
{115, {30,31,32,33,34,35,39,43}},
{116, {1,2,3,4,5,14,15,22}},
{116, {30,31,32,33,34,35,43,44,45}},
{117, {1,2,3,4,5,9,14,16}},
{117, {30,34,38,39}},
{118, {1,2,3,4,5,10,14}},
{118, {30,31,32,33,34,35,43}},
{119, {1,2,3,4,5,14,23}},
{119, {30,31,32,33,34,35,43,45}},
{121, {2,7,17}},
{121, {36,45,46,50}},
{122, {7,14,16,21}},
{122, {34,36,39}},
{123, {7,8,9,10,11,20,21,22}},
{123, {36,40,41,45,49,50}},
{124, {7,8,9,10,11,16,20,21}},
{124, {36,37,38,39,40,41,49,50}},
{125, {2,7,9,11}},
{125, {7,8,9,10,11,20,22}},
{125, {36,37,38,39,40,41,45,49}},
{126, {7,8,11}},
{126, {7,8,9,10,11,20,21,28}},
{126, {36,37,38,39,40,41,49,50,51}},
{127, {7,8,9,10,11,15,20,22}},
{127, {36,40,44,45}},
{128, {7,9,11}},
{128, {7,8,9,10,11,16,20}},
{129, {7,8,9,11}},
{129, {7,8,9,10,11,20,28}},
{129, {36,37,38,39,40,41,49,51}}
};
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Touch_Sensor touch_sensor;
#define Swich_11 (11)
int8_t ret1;
void setup()
{
// シリアル通信設定
Serial.begin(2000000);
while (!Serial);
touch_sensor.setup();
pinMode(Swich_11, INPUT);
// // マルチコア起動
ret1 = MP.begin();
MP.RecvTimeout(MP_RECV_POLLING);
// 起動エラー
if (ret1 < 0)
printf("SubCore start error = %d\n", ret1);
}
void loop()
{
uint16_t note = 10000;
////////////////////////////////////////// filter用 //////////////////////////////////////////////////////
touch_sensor.filter();
printf("touch_number_filter:");
for(uint8_t i=0;i<touch_sensor.touch_number_filter.size();i++)
{
printf("%d,",touch_sensor.touch_number_filter[i]);
}
printf("\n");
// 対応した音の番号の出力
bool Code_mode = digitalRead(Swich_11);
if(Code_mode)
{
for(uint8_t i=0;i<code_mode_size;i++)
{
if(touch_sensor.touch_number_filter.size() == code_mode[i].touch_number.size() &&
equal(touch_sensor.touch_number_filter.cbegin(),touch_sensor.touch_number_filter.cend(),code_mode[i].touch_number.cbegin()))
{
note = code_mode[i].note;
break;
}
}
}
else
{
for(uint8_t i=0;i<manual_mode_size;i++)
{
if(touch_sensor.touch_number_filter.size() == manual_mode[i].touch_number.size() &&
equal(touch_sensor.touch_number_filter.cbegin(),touch_sensor.touch_number_filter.cend(),manual_mode[i].touch_number.cbegin()))
{
note = manual_mode[i].note;
break;
}
}
}
/////////////////////////////////////////////////////////////////////////////////////////////
int8_t msgid;
uint32_t msgdata;
/* Echo back */
ret1 = MP.Recv(&msgid, &msgdata); // MainCoreからmsg受信
if (ret1 < 0) {
//printf("SubCore1 fail connect Main = %d\n", ret1);
}
else
{
msgdata = note;
ret1 = MP.Send(msgid, msgdata);
//msg送信エラー
if (ret1 < 0)
printf("SubCore 1 send error = %d\n", ret1);
else
printf("SubCore1 send: id=%d data=%d\n", msgid, msgdata);
}
return;
}
SubCore2
SubCore2ではエンコーダにより、ホイールの加速を検出します。エンコーダのa相、b相が変化するステップ時間を読み取り、ステップ時間の変化から加速を読み取ります。割り込み処理を利用し、読み飛ばしを防止しています。以下の動画のようにホイールを回転させると、加速度は図のように変化します。3つの図は縦軸のスケールが異なっており、閾値を赤線の位置としたとき、6回転させたことを読み取れます。
SubCore2
///////////////////////// SubCore2 //////////////////////////////////////////////////////////////////
#if (SUBCORE != 2) // SubCoreの指定
#error "Core selection is wrong!!"
#endif
#include <MP.h>
// pin設定と回転方向
int pinA = 9; //CLK
int pinB = 10;//DT
const int CW = 1;
const int CCW = -1;
// update で使う
int multiplication = 4;//逓倍
int resolution = 19;//分解能
int a_now=0;
int b_now=0;
int a_pri=0;
int b_pri=0;
// calculate_step_dtで使う
bool first = true;
double step_angular;
int count = 0;//エンコーダカウント
int direction_pri;
// dtで使う
double dt[6];
long t_now =0;
long t_pri=0;
// flagで使う(加速度判定)
double ddt_now;
// updateとflagで使う(速度判定)
double flag_time = 0;
bool velocity_check = false;
int velocity_count;
// param
double threshold = 0.1; //加速判定閾値0.008
double flag_to_flag_time = 0.2; //加速判定後の次の加速までの最低時間[s]
bool velocity_use = false; //速度判定行うかどうか
double check_limit = 0.04; //加速判定後速度計測にかける時間[s]
double velocity_count_threshold = 4;//check_limitでhighspeedと判定される最低ステップ数
int section_direction;//
int theta_now = 0;
bool highspeed = false;
void encorder_setup();
void encorder_update();
void caluculate_step_dt(int direction_now);
void dt_latest_update();
void dt_stock_update();
void flag();
void encorder_setup()
{
pinMode(pinA,INPUT);
pinMode(pinB,INPUT);
step_angular = 360/(resolution*multiplication);
for(int i=0;i<6;i++)
{
dt[i]=0;
}
}
void caluculate_step_dt(int direction_now)
{
if(first)
{
first = false;
dt_latest_update();//最新ステップ変化時間を更新
section_direction = direction_now;
}
else
{
//Serial.print(direction_pri);
//Serial.print(",");
//Serial.print(direction_now);
if(direction_pri != direction_now)// 同じ区間を行き来していたら
{
//Serial.print("direction");
dt_latest_update();//最新ステップ変化時間のみを更新
section_direction = section_direction + direction_now;
}
else
{
//Serial.print("#");
if(section_direction != 0)// 前回の角度変位が有効であれば(2ステップ同方向に変化した)
{
dt_stock_update();
flag();
}
else// 前回の角度変位が無効なら(結果的に現在のステップしか変化していない)
{
//Serial.print("&");
dt_latest_update();//最新ステップ変化時間のみを更新
}
section_direction = direction_now;
}
}
direction_pri = direction_now;
//count = count + direction_now;
//theta_now = count*step_angular;
}
void encorder_update()
{
a_now = digitalRead(pinA);
b_now = digitalRead(pinB);
if(a_now != a_pri&&b_now != b_pri)
{
//Serial.print("skip");
}
if(a_now != a_pri)
{
if(a_now != b_now){
//Serial.print("+a");
caluculate_step_dt(CW);
}
else if(a_now == b_now){
//Serial.print("-a");
caluculate_step_dt(CCW);
}
}
if(b_now != b_pri){
if(b_now == a_now){
//Serial.print("+b");
caluculate_step_dt(CW);
}
else if(b_now != a_now){
//Serial.print("-b");
caluculate_step_dt(CCW);
}
}
a_pri=a_now;
b_pri=b_now;
if(velocity_use)
{
if(velocity_check)
{
long time_now = micros();
double check_time_million=time_now-flag_time;
double check_time = check_time_million/1000000;
if(check_limit < check_time)
{
if(velocity_count < velocity_count_threshold-1)
{
Serial.println("0");
highspeed = false;
}
else if(velocity_count >= velocity_count_threshold)
{
Serial.println("1");
highspeed = true;
}
else
{
Serial.println("0.5");
}
velocity_check = false;
//return true;
}
}
//return false;
}
else
{
if(velocity_check)
{
velocity_check = false;
//Serial.print("FLAG!!!!!!!!!!!!!!!!");
//return true;
}
//return false;
}
}
void dt_stock_update()
{
for(int i=0;i<5;i++)
{
dt[i] = dt[i+1];
}
t_pri = t_now;
t_now = micros();
double dt_million = t_now - t_pri;
dt[5] = dt_million/1000000;
//Serial.print(",");
//Serial.print(dt[5],5);
}
void dt_latest_update()
{
t_now = micros();
double dt_million = t_now - t_pri;
dt[5] = dt_million/1000000;
//Serial.print(",");
//Serial.print(dt[5],5);
}
void flag()
{
//Serial.println("");
if(!velocity_check)
{
ddt_now = (dt[0]-dt[4])+(dt[1]-dt[5]);
//Serial.print(ddt_now,5);
if (ddt_now >= threshold)
{
long now_time = micros();
double flag_to_now_time = (now_time-flag_time)/1000000;
//Serial.println(flag_to_now_time);
if(flag_to_now_time > flag_to_flag_time)
{
flag_time = micros();
velocity_check = true;
velocity_count =0;
check_limit = check_limit - dt[5];
send_maincore();
}
}
}
else
{
velocity_count = velocity_count + 1;
}
}
void send_maincore()
{
int ret2;
int8_t msgid = 0;
uint32_t msgdata = 0;
Serial.print("flag!!!!!!!!!!!!!!!!!!");
if(section_direction>0 && !highspeed)
msgdata = 0;
else if(section_direction>0 && highspeed)
msgdata = 1;
else if(section_direction<0 && highspeed)
msgdata = 2;
else if(section_direction<0 && !highspeed)
msgdata = 3;
// printf("%d",msgdata);
//printf("SubCore2 send: id=%d data=%d\n", msgid, msgdata);
ret2 = MP.Send(msgid, msgdata);
// // msg送信エラー
if (ret2 < 0)
printf("Subcore2 MP.Send error %d\n", ret2);
}
void setup()
{
int ret2 = 0;
Serial.begin(2000000);
while (!Serial);
encorder_setup();
attachInterrupt(pinA,encorder_update,CHANGE);//3.1の場合はfalseと書かないとチャタリング処理される
attachInterrupt(pinB,encorder_update,CHANGE);
//マルチコア起動
ret2 = MP.begin();
MP.RecvTimeout(MP_RECV_POLLING);
// 起動エラー
if (ret2 < 0)
printf("SubCore2 MP error = %d\n", ret2);
}
void loop()
{
}
SubCore3
SubCore3ではWi-Fiで接続されたPCから送信されたコード譜の受信をしています。プログラム内では、SPRESENSEをWi-Fiアクセスポイントとして立ち上げています。GS2200-WiFiをライブラリとして利用しています。
SubCore3
#if (SUBCORE != 3) // SubCoreの指定
#error "Core selection is wrong!!"
#endif
#include <GS2200Hal.h>
#include <GS2200AtCmd.h>
#include <TelitWiFi.h>
#include "config.h"
#include <MP.h>
#include <vector>
#define CONSOLE_BAUDRATE 2000000
// ここに受信したデータが入る
extern uint8_t ESCBuffer[];
extern uint32_t ESCBufferCnt;
/////////////////////////////////////////////////////////////////////////
char* pc_msg;
#define MSGLEN 200
#define MY_MSGID 10
struct MyPacket {
volatile int status; /* 0:ready, 1:busy */
char message[MSGLEN];
};
MyPacket packet;
TelitWiFi gs2200;
TWIFI_Params gsparams;
/////////////////////////////////////////////////////////////////////////
void setup() {
/* initialize digital pin LED_BUILTIN as an output. */
pinMode(LED0, OUTPUT);
digitalWrite( LED0, LOW ); // turn the LED off (LOW is the voltage level)
Serial.begin( CONSOLE_BAUDRATE ); // talk to PC
/* Initialize SPI access of GS2200 */
Init_GS2200_SPI_type(iS110B_TypeC);//typeCに変更
/* Initialize AT Command Library Buffer */
gsparams.mode = ATCMD_MODE_LIMITED_AP;//アクセスポイントモード
gsparams.psave = ATCMD_PSAVE_DEFAULT;
if( gs2200.begin( gsparams ) ){
ConsoleLog( "GS2200 Initilization Fails" );
while(1);
}
/* GS2200 runs as AP *///アクセスポイントモード
if( gs2200.activate_ap( AP_SSID, PASSPHRASE, AP_CHANNEL ) ){
ConsoleLog( "WiFi Network Fails" );
while(1);
}
digitalWrite( LED0, HIGH ); // turn on LED
memset(&packet, 0, sizeof(packet));
int ret3;
ret3 = MP.begin();
MP.RecvTimeout(MP_RECV_BLOCKING);
// 起動エラー
if (ret3 < 0)
printf("SubCore3 start error = %d\n", ret3);
delay(50);
}
// the loop function runs over and over again forever
void loop()
{
ATCMD_RESP_E resp;
char server_cid = 0, remote_cid=0;
ATCMD_NetworkStatus networkStatus;
uint32_t timer=0;
resp = ATCMD_RESP_UNMATCH;
ConsoleLog( "Start TCP Server");
resp = AtCmd_NSTCP( TCPSRVR_PORT, &server_cid);
if (resp != ATCMD_RESP_OK) {
ConsoleLog( "No Connect!" );
delay(2000);
return;
}
if (server_cid == ATCMD_INVALID_CID) {
ConsoleLog( "No CID!" );
delay(2000);
return;
}
while( 1 )
{
// ConsoleLog( "Waiting for TCP Client");
if( ATCMD_RESP_TCP_SERVER_CONNECT != WaitForTCPConnection( &remote_cid, 5000 ) ){
continue;
}
ConsoleLog( "TCP Client Connected");
// Prepare for the next chunck of incoming data
WiFi_InitESCBuffer();
// Start the echo server
int loopcnt=0;
while( loopcnt++<100000 ){
while( Get_GPIO37Status() ){
resp = AtCmd_RecvResponse();
loopcnt=0;
if( ATCMD_RESP_BULK_DATA_RX == resp ){
if( Check_CID( remote_cid ) )
{
//ConsolePrintf( "Received : %s\r\n", ESCBuffer+1 );
pc_msg = ESCBuffer+1;
printf("pc_msg : %s\n",pc_msg);
int ret3;
if (packet.status == 0)
{
packet.status = 1;
snprintf(packet.message, MSGLEN, "%s",pc_msg);
ret3 = MP.Send(MY_MSGID, &packet);
if (ret3 < 0) {
printf("MP.Send error = %d\n", ret3);
}
}
if( ATCMD_RESP_OK != AtCmd_SendBulkData( remote_cid, ESCBuffer+1, ESCBufferCnt-1 ) )
{
// Data is not sent, we need to re-send the data
ConsolePrintf( "Sent Error : %s\r\n", ESCBuffer+1 );
delay(10);
}
}
WiFi_InitESCBuffer();
}
}
}
}
}
config.shでWi-Fiアクセスポイントの設定をしています
config.sh
/* * config.h - WiFi Configration Header * * This work is free software; you can redistribute it and/or modify it under the terms * of the GNU Lesser General Public License as published by the Free Software Foundation; * either version 2.1 of the License, or (at your option) any later version. * * This work is distributed in the hope that it will be useful, but without any warranty; * without even the implied warranty of merchantability or fitness for a particular * purpose. See the GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this work; if not, write to the Free Software Foundation, * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _CONFIG_H_ #define _CONFIG_H_ /*-------------------------------------------------------------------------* * Configration *-------------------------------------------------------------------------*/ #define AP_SSID "linksys" #define PASSPHRASE "0123456789" #define AP_CHANNEL 6 #define TCPSRVR_PORT "10001" #endif /*_CONFIG_H_*/
PC側
PC側ではコード譜をSPRESENSEに送信します。PCはWi-FiアクセスポイントになっているSPRESENSEに接続します。送信したいコード譜の書かれたtxtファイルを選択し、SPRESENSEに送信します。以下のようなGUIになっており操作しやすくなっています。
PC側のソースコード
import PySimpleGUI as sg
import sys
import requests
import re
import os
url = "http://192.168.1.99:10001"
name = "team_SOTA"
conversion_table = [[11,"Caug"],
[12,"Cdim"],
[13,"CM"],
[14,"Cm"],
[15,"C7"],
[16,"Csus4"],
[17,"CM7"],
[18,"Cm7"],
[19,"C7sus4"],
[21,"C#aug"],
[22,"C#dim"],
[23,"C#M"],
[24,"C#m"],
[25,"C#7"],
[26,"C#sus4"],
[27,"C#M7"],
[28,"C#m7"],
[29,"C#7sus4"],
[31,"Daug"],
[32,"Ddim"],
[33,"DM"],
[34,"Dm"],
[35,"D7"],
[36,"Dsus4"],
[37,"DM7"],
[38,"Dm7"],
[39,"D7sus4"],
[41,"D#aug"],
[42,"D#dim"],
[43,"D#M"],
[44,"D#m"],
[45,"D#7"],
[46,"D#sus4"],
[47,"D#M7"],
[48,"D#m7"],
[49,"D#7sus4"],
[51,"Eaug"],
[52,"Edim"],
[53,"EM"],
[54,"Em"],
[55,"E7"],
[56,"Esus4"],
[57,"EM7"],
[58,"Em7"],
[59,"E7sus4"],
[61,"Faug"],
[62,"Fdim"],
[63,"FM"],
[64,"Fm"],
[65,"F7"],
[66,"Fsus4"],
[67,"FM7"],
[68,"Fm7"],
[69,"F7sus4"],
[71,"F#aug"],
[72,"F#dim"],
[73,"F#M"],
[74,"F#m"],
[75,"F#7"],
[76,"F#sus4"],
[77,"F#M7"],
[78,"F#m7"],
[79,"F#7sus4"],
[81,"Gaug"],
[82,"Gdim"],
[83,"GM"],
[84,"Gm"],
[85,"G7"],
[86,"Gsus4"],
[87,"GM7"],
[88,"Gm7"],
[89,"G7sus4"],
[91,"G#aug"],
[92,"G#dim"],
[93,"G#M"],
[94,"G#m"],
[95,"G#7"],
[96,"G#sus4"],
[97,"G#M7"],
[98,"G#m7"],
[99,"G#7sus4"],
[101,"Aaug"],
[102,"Adim"],
[103,"AM"],
[104,"Am"],
[105,"A7"],
[106,"Asus4"],
[107,"AM7"],
[108,"Am7"],
[109,"A7sus4"],
[111,"A#aug"],
[112,"A#dim"],
[113,"A#M"],
[114,"A#m"],
[115,"A#7"],
[116,"A#sus4"],
[117,"A#M7"],
[118,"A#m7"],
[119,"A#7sus4"],
[121,"Baug"],
[122,"Bdim"],
[123,"BM"],
[124,"Bm"],
[125,"B7"],
[126,"Bsus4"],
[127,"BM7"],
[128,"Bm7"],
[129,"B7sus4"]]
def make_data(txt_data):
send_data = ''
reshape_data = []
# 正規表現を使ってコード文字列だけを抽出
extract_data = re.findall(r'[A-Z][M,a-z,7,#]*', txt_data)
# Mを追加
for s in extract_data:
if len(re.findall(r'[M,a-z,7]', s)) == 0:
reshape_data.append(s + 'M')
else:
reshape_data.append(s)
# 完全一致検索を使ってコードを番号に変換
for s in reshape_data:
for c in conversion_table:
if c[1] == s:
send_data = send_data + str(c[0]) + '_'
return reshape_data, send_data
def Window1():
# 画面レイアウト
col1 = [[sg.Image(filename=resource_path('img1.png'))]]
col2 = [[sg.Button('はじめる' ,font=('yu Gothic UI',18),size=(12,1)), sg.Button('終了', font=('yu Gothic UI',18),size=(12,1))]]
layout1 = [[sg.Column(col1, justification='c')],
[sg.Column(col2, justification='c')]]
# 1番目の画面を開く
window = sg.Window("らくラクギター chord sender", layout1, resizable=True, size=(600, 600))
event1, values1 = window.read()
if event1 == '終了':
sys.exit()
if event1 == 'はじめる':
pass
window.close()
return event1, values1
def Window2():
# 画面レイアウト
col1 = [[sg.Image(filename=resource_path('img2.png'))]]
col2 = [[sg.Text('PC と らくラクギターをWi-Fiで接続してください',font=('yu Gothic UI',18),size=(34,1))]]
col3 = [[sg.Text('SSID : linksys PATH : 0123456789',font=('yu Gothic UI',12),size=(28,1))]]
col4 = [[sg.Button('OK' ,font=('yu Gothic UI',18),size=(12,1)), sg.Button('終了', font=('yu Gothic UI',18),size=(12,1))]]
layout2 = [[sg.Column(col1, justification='c')],
[sg.Column(col2, justification='c')],
[sg.Column(col3, justification='c')],
[sg.Column(col4, justification='c')]]
# 2番目の画面を開く
window = sg.Window("らくラクギター chord sender", layout2, resizable=True, size=(600, 600))
event2, values2 = window.read()
if event2 == '終了':
sys.exit()
if event2 == 'OK':
pass
window.close()
return event2, values2
def Window3():
col1 = [[sg.Text('らくラクギターに送信するコード譜を選択してください',font=('yu Gothic UI',18),size=(48,1))]]
col2 = [[sg.Text("ファイル",font=('yu Gothic UI',18),size=(6,1)), sg.InputText(font=('yu Gothic UI',12),size=(38,1)), sg.FileBrowse(key="file1",font=('yu Gothic UI',18),size=(6,1))]]
col3 = [[sg.Submit("決定",font=('yu Gothic UI',18),size=(12,1)), sg.Cancel("キャンセル",font=('yu Gothic UI',18),size=(12,1))]]
layout3 = [[sg.Column(col1, justification='l')],
[sg.Column(col2, justification='l')],
[sg.Column(col3, justification='c')]]
# 3番目の画面を開く
window = sg.Window("らくラクギター chord sender", layout3, resizable=True, size=(600, 600))
event3, values3 = window.read()
window.close()
return event3, values3
def Window4(txt_filepath):
# txtファイルを開いてデータの読み取り
f = open(txt_filepath, 'r', encoding='UTF-8')
txt_data = f.read()
print(txt_data)
reshape_data, send_data = make_data(txt_data)
f.close()
col1 = [[sg.Text('このコード譜を送信しますか?',font=('yu Gothic UI',18),size=(24,1))]]
col2 = [[sg.Text(txt_filepath, text_color='black', background_color='white', font=('yu Gothic UI',12),size=(54,1))]]
col3 = [[sg.Column([[sg.Text(txt_data, text_color='black', background_color='white')]], size=(550, 250), scrollable=True, background_color='white')]]
col4 = [[sg.Button('Spresenseに送信' ,font=('yu Gothic UI',18),size=(20,1)), sg.Button('キャンセル', font=('yu Gothic UI',18),size=(20,1))]]
layout4 = [[sg.Column(col1, justification='c')],
[sg.Column(col2, justification='c')],
[sg.Column(col3, justification='c')],
[sg.Column(col4, justification='c')]]
# 4番目の画面を開く
window = sg.Window("らくラクギター chord sender", layout4, resizable=True, size=(600, 600))
event4, values4 = window.read()
if event4 == 'キャンセル':
pass
# sys.exit()
if event4 == 'Spresenseに送信':
spr = requests.post(url, data=send_data, timeout=(4.0,7.5))
window.close()
return event4, values4
def Window5():
col1 = [[sg.Text('送信しました',font=('yu Gothic UI',18),size=(10,1))]]
col2 = [[sg.Button('OK' ,font=('yu Gothic UI',18),size=(20,1))]]
layout5 = [[sg.Column(col1, justification='c')],
[sg.Column(col2, justification='c')]]
# 5番目の画面を開く
window = sg.Window("らくラクギター chord sender", layout5, resizable=True, size=(600, 600))
event5, values5 = window.read()
window.close()
return event5, values5
def resource_path(relative_path):
if hasattr(sys, '_MEIPASS'):
return os.path.join(sys._MEIPASS, relative_path)
return os.path.join(os.path.abspath("."), relative_path)
def main():
sg.theme('LightBrown4')
event1, values1 = Window1()
print('event1*****')
print(event1)
print('values1*****')
print(values1)
event2, values2 = Window2()
print('event2*****')
print(event2)
print('values2*****')
print(values2)
event3, values3 = Window3()
print('event3*****')
print(event3)
print('values3*****')
print(values3)
event4, values4 = Window4(values3['file1'])
print('event4*****')
print(event4)
print('values4*****')
print(values4)
event5, values5 = Window5()
print('event5*****')
print(event5)
print('values5*****')
print(values5)
if __name__ == "__main__":
main()
演奏動画
エレキギターの音でも演奏できます。
おわりに
今回は、SPRESENSEで誰でも簡単に演奏できるギターを作成しました。製作者の中には楽器の知識がない者もいましたが、製作を通じて楽器に興味を持つことができました。少しでも楽器に興味を持ち、さらなる上達を目指す人が増えるとうれしいです。
-
shirachan
さんが
2024/01/29
に
編集
をしました。
(メッセージ: 初版)
-
shirachan
さんが
2024/01/29
に
編集
をしました。
(メッセージ: 図を載せました)
-
shirachan
さんが
2024/01/29
に
編集
をしました。
(メッセージ: 図の大きさを変更)
-
shirachan
さんが
2024/01/29
に
編集
をしました。
(メッセージ: 図を追加)
-
shirachan
さんが
2024/01/29
に
編集
をしました。
(メッセージ: 図を追加)
-
shirachan
さんが
2024/01/29
に
編集
をしました。
-
shirachan
さんが
2024/01/30
に
編集
をしました。
(メッセージ: 回路図を追加)
-
shirachan
さんが
2024/01/30
に
編集
をしました。
(メッセージ: 回路図を変更)
-
shirachan
さんが
2024/01/30
に
編集
をしました。
(メッセージ: GUIの方も少し記述)
-
shirachan
さんが
2024/01/30
に
編集
をしました。
(メッセージ: タッチセンサの仕組みを追加)
-
Onichan
さんが
2024/01/30
に送った
編集リクエスト
が受理されました。
-
Onichan
さんが
2024/01/30
に
編集
をしました。
-
Onichan
さんが
2024/01/30
に送った
編集リクエスト
が確認されました。
-
shirachan
さんが
2024/01/30
に
編集
をしました。
(メッセージ: 表を追加、文も少し整形)
-
shirachan
さんが
2024/01/30
に
編集
をしました。
(メッセージ: 動画を追加)
-
shirachan
さんが
2024/01/30
に
編集
をしました。
(メッセージ: 動画を追加)
-
Onichan
さんが
2024/01/30
に送った
編集リクエスト
が受理されました。
-
shirachan
さんが
2024/01/30
に
編集
をしました。
(メッセージ: 画像を追加)
-
shirachan
さんが
2024/01/30
に
編集
をしました。
-
shirachan
さんが
2024/01/30
に
編集
をしました。
(メッセージ: 画像を追加)
-
shirachan
さんが
2024/01/30
に
編集
をしました。
(メッセージ: 文を追加)
-
shirachan
さんが
2024/01/30
に
編集
をしました。
(メッセージ: 説明を追加)
-
shirachan
さんが
2024/01/30
に
編集
をしました。
(メッセージ: モードの説明を追加)
-
Onichan
さんが
2024/01/30
に送った
編集リクエスト
が確認されました。
-
shirachan
さんが
2024/01/30
に
編集
をしました。
(メッセージ: 内部写真を追加)
-
shirachan
さんが
2024/01/30
に
編集
をしました。
(メッセージ: コード譜を追加)
-
shirachan
さんが
2024/01/30
に
編集
をしました。
(メッセージ: 一旦更新)
-
shirachan
さんが
2024/01/30
に
編集
をしました。
(メッセージ: タッチ部を指板部に変更)
-
shirachan
さんが
2024/01/30
に
編集
をしました。
(メッセージ: ちょっと追加)
-
shirachan
さんが
2024/01/30
に
編集
をしました。
(メッセージ: ソースをちょっと追加)
-
shirachan
さんが
2024/01/30
に
編集
をしました。
(メッセージ: ソースをすべて追加)
-
shirachan
さんが
2024/01/30
に
編集
をしました。
(メッセージ: おわりにを追加。何か書く)
-
Onichan
さんが
2024/01/30
に送った
編集リクエスト
が受理されました。
-
Onichan
さんが
2024/01/30
に
編集
をしました。
-
Onichan
さんが
2024/01/30
に送った
編集リクエスト
が受理されました。
-
Onichan
さんが
2024/01/30
に
編集
をしました。
-
Onichan
さんが
2024/01/30
に送った
編集リクエスト
が受理されました。
-
Onichan
さんが
2024/01/30
に
編集
をしました。
-
Onichan
さんが
2024/01/30
に送った
編集リクエスト
が受理されました。
-
Onichan
さんが
2024/01/30
に
編集
をしました。
-
shirachan
さんが
2024/01/30
に
編集
をしました。
(メッセージ: 概要・グラフの大きさを変更)
-
shirachan
さんが
2024/01/30
に
編集
をしました。
(メッセージ: 説明を修正)
-
shirachan
さんが
2024/01/30
に
編集
をしました。
-
shirachan
さんが
2024/01/31
に
編集
をしました。
(メッセージ: 文を修正)
-
shirachan
さんが
2024/01/31
に
編集
をしました。
(メッセージ: SubCore1の説明を更新)
-
shirachan
さんが
2024/01/31
に
編集
をしました。
(メッセージ: エンコーダのグラフを変更)
-
shirachan
さんが
2024/01/31
に
編集
をしました。
(メッセージ: ホイールのグラフに説明を追加)
-
shirachan
さんが
2024/01/31
に
編集
をしました。
(メッセージ: SubCore3の説明を追記)
-
shirachan
さんが
2024/01/31
に
編集
をしました。
(メッセージ: おわりにを追加)
-
shirachan
さんが
2024/01/31
に
編集
をしました。
(メッセージ: DMをDmに直しました)
-
shirachan
さんが
2024/01/31
に
編集
をしました。
(メッセージ: コメントアウトを削減)
-
shirachan
さんが
2024/01/31
に
編集
をしました。
(メッセージ: 名前を変更)
-
Onichan
さんが
2024/01/31
に送った
編集リクエスト
が受理されました。
-
Onichan
さんが
2024/01/31
に
編集
をしました。
-
shirachan
さんが
2024/01/31
に
編集
をしました。
(メッセージ: ミスを修正)
ログインしてコメントを投稿する