概要
ラジコンの動作音を活かして演奏をさせてみたい。
同期
モーターで音を鳴らすと言えばステッピングモーターが有名だが、ラジコンでやるとどんな感じなんだろうか?
開発環境
Sphero SPRK+(ラジコン)
Sphero Edu(専用の制御用アプリ)
仕様
プログラムでSPRK+のモーターを制御して特定のパワーで回転運動をさせる
回転の速度による動作音の変化を利用して、曲を演奏
実装
とりあえず音を鳴らす
SPRK+が持つ1対のモーターは、-255~255の値を指定することで出力の調整が可能
左右に±nの値を指定(逆回転)すればその場で回転するので、余計に動き回ることがなく「スピーカー」として使える
await raw_motor(power, -1*power , 1)
# 右モーター出力, 左モーター出力, 継続秒
音程をつける
モーターのパワーを表す数値は、実際に動かした音を確認しながら指定した(開始時、変数に格納)
これをプログラム全体で定数として使用する
演奏時は、回転用の関数に音程(の数値)を指定し、逐一呼び出す形にする1
s3C = 91
# C3
s3Cs = 95
# C#3
s3D = 99
# D3
s3Ds = 103
# D#3
s3E = 107
# E3
#省略
#(↑昇順で実際と同じ名前の並びにしたかった)
note = 60/120 #1拍の秒数
adj = 4 #調整用
async def soundSpin(sP, last, st):
if st and last*note >= 0.5:
await raw_motor(sP+adj, -1*(sP+adj), last*note*0.9)
await delay(last*note*0.1)
#回転後に短時間停止を入れる
else:
await raw_motor(sP+adj, -1*(sP+adj), last*note)
演奏
定義した関数を各音符ごとに呼び出すわけだが、入力時の混乱やミスを防ぐため、1曲をある程度分割して関数で構造化してみた
async def Melody1():
await Ma()
await Mb()
..
async def Ma():
await soundSpin(s3C, 1, False)
..
似ている部分などを再利用することができ、入力が捗った
各音符の継続時間は、理論上、60秒を♩BPMで割った数値→noteになるが、実際はモーターの回転が安定するまでの時間分だけ動作時間が長くなってしまう2ため、狙ったBPMに合うように別途短めの時間noteForMotorDelayを設定し動かす必要があった
上記の理由もあり、演奏のタイミングを基準となるnoteを頼りに絶対的に求める処理を追加
動作の待ち時間で、発生した時間のズレを補正する
これでいくらパート重ねようが音はブレない(多分)
演奏してみた
ニコニコ動画にて↓
課題(?)
演奏の根幹となるモーターの出力はバッテリー残量(電圧)の影響を大きく受けるため、音程の再現性があまり無い…?
…
そもそも一音一音聴いて設定したから絶対的な基準というものが何一つない…