shirachan が 2024年01月30日02時06分07秒 に編集
GUIの方も少し記述
本文の変更
# 作品概要 この作品は誰でも簡単にかっこよく弾けるギターとなっています。新型コロナの影響でおうち時間が増え、楽器を始める人が増えた。中でもある調査ではギターを始めた人は多くいます。しかし、ギターは初心者が挫折しやすい楽器になっています。ギターの難しさは右手と左手にそれぞれあります。 ![ギターの演奏方法](https://camo.elchika.com/a4e8946484779e24715a91bb752076b761d6f6b7/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f62633063393036322d316133622d343530642d613039372d3163366237376262623035322f61663832613166392d633662342d346465612d393835662d353431636563633966313862/) 左手は弦を押さえることにより、鳴らす音を決めています。鳴らす音はAm,などコードによって決められます。しかし、コードは代表的なものだけで約160種類あるためコードの暗記、弦を押すのが大変です。 右手では弦を弾くことにより音を鳴らします。音を鳴らすには弦が均一に鳴るように弾くことが必要です。そのため習得が難しいです。 そこで、このような課題を解決し、簡単でかっこよくひけるギター「らくラクギター」を作成しました。 # 構成 らくラクギターの構成は以下の図のようになっています。 ![らくラクギターの構成](https://camo.elchika.com/1ab5256cf282ecca5ac3a08963261f85a74c247b/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f62633063393036322d316133622d343530642d613039372d3163366237376262623035322f38616138386237322d666339622d346465352d386335612d323931623361633136643031/) ### ホイール ![ホイール拡大](https://camo.elchika.com/f1c5c86c7b119acf86f8e9477ecafefe67cacfb2/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f62633063393036322d316133622d343530642d613039372d3163366237376262623035322f65353765393734662d353430392d346366352d393266342d323132383537356437616566/) 弦を弾くようにホイールを回すことで音が鳴ります。ホイールの回転はエンコーダで読み取り ### タッチセンサ ![タッチセンサ](https://camo.elchika.com/6a9fb9a035571e53e4447a8cf3221126ddcde761/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f62633063393036322d316133622d343530642d613039372d3163366237376262623035322f31336337643530652d633836362d343835322d393966622d653431646437613434643230/) タッチセンサを押さえることで鳴らす音を決めます。金属線一本一本がセンサになっており合計○個使われています。静電容量式のセンサを使って います。 ### モードスイッチ ![モードスイッチ](https://camo.elchika.com/ea836d99c050b7be339446ccae372b4e9e07b972/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f62633063393036322d316133622d343530642d613039372d3163366237376262623035322f36333430396336612d343964302d343464342d623266352d393661333534363135646530/) モードと音源の種類を切替します。 ### スピーカー ここから音が鳴ります。 # 機能 本作品は本物のギターと同じように左手でタッチセンサを押さえ右手でホイールを回すことで演奏します。演奏には演奏者のレベルに合わせた3つのモードが実装されています。 ### コードモード ### ビギナーモード ### マニュアルモード # 回路図 全体回路図は以下のようになります。 ![全体回路図](https://camo.elchika.com/567896399b0785e38afb3b0a8bb44fa318269bdd/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f62633063393036322d316133622d343530642d613039372d3163366237376262623035322f32646364366231642d663238612d343730312d613037632d343036383334643632343834/) タッチセンサの値を読み取るためのタッチセンサ制御回路は複数のシフトレジスタ部とコンパレータ部から構成されています。 ![1番目のシフトレジスタ部](https://camo.elchika.com/cc726fde46ee326cd5296bf15f2c8b7365bd793f/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f62633063393036322d316133622d343530642d613039372d3163366237376262623035322f34646238376361352d643239342d343562362d383332652d363665633564333136323161/) ああ ![k番目のシフトレジスタ部](https://camo.elchika.com/89a4ded6832b045873a18a3694350dcc7bc367e5/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f62633063393036322d316133622d343530642d613039372d3163366237376262623035322f38616334643264342d633765342d346637382d386137392d616637343461303533303665/) ![コンパレータ部](https://camo.elchika.com/6fb7ab72bdfcdd9a0fd26c4c079a8228a17345d9/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f62633063393036322d316133622d343530642d613039372d3163366237376262623035322f33333037373861632d306432352d346436662d396263622d386135363732396364393731/) タッチセンサの値読み取りは、複数のシフトレジスタを使用し順番に値を読み取っています。 # 部品 使用した部品は以下の通りです。 | A | B | |:---:|:---:| | C | D | | C | D | | C | D | ギター本体はギター組み立てキットから作成しました ホイールは3Dプリンタで印刷しました タッチセンサ部とタッチセンサの制御部分は別途基板を作成しました。 # ソースコード SPRESENSEのマルチコアを利用。SPRESENSEとPC側のプログラムを分けて解説 ## SPRESENSE ### MainCore ### SubCore1 ### SubCore2 ### SubCore3 ## PC側
あああ ```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 : oooo 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')]] # layout4 = [[sg.Text('送信しますか')], # [sg.Text(txt_filepath)], # [sg.Text(txt_filepath, text_color='black', background_color='white')], # [sg.Column([[sg.Text(txt_data, text_color='black', background_color='white')]], size=(245, 115), scrollable=True, background_color='white')], # [sg.Column([[sg.Text(reshape_data, text_color='black', background_color='white')]], size=(245, 115), scrollable=True, background_color='white')], # [sg.Column([[sg.Text(send_data, text_color='black', background_color='white')]], size=(245, 115), scrollable=True, background_color='white')], # [sg.Button('Spresenseに送信'), sg.Button('キャンセル')]] # 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() ``` img1、img2は以下を使用 ![画面遷移](https://camo.elchika.com/f0ebc40a6d6fa1d407221c576b49b45ee556b727/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f62633063393036322d316133622d343530642d613039372d3163366237376262623035322f36343733303230352d343863322d343639352d393862332d643461393361663634393638/)