前回はラーメンのチャルメラを流すコードだったけど、今回はもう少し長めのメロディーを作ってみた。
作ったもの
作ったメロディーはシューティングゲーム、東方風神録の3面テーマ「神々が恋した幻想郷」。
折角なのでYouTubeにUploadした。(音が鳴るので注意)
youtu.be
知らない方向けに原作もご紹介。※私のプレイじゃないです。
youtu.be
配線は前回のチャルメラと同じ。
コード
チャルメラのときはドレミの周波数を直接指定していたけど、今回は関数にして簡単に呼び出せるようにしつつ、中身も音階ごとの周波数を12平均律という方法で計算で求めるということをやってみた。
ラの音が440Hzと定められているので、そこに2の12乗根をn乗するとn音階あがり、-n乗するとn音階下がる。
これをさらにm倍すると、mオクターブ上がり、mで割るとmオクターブ下がるという仕組み。
ド♯・レ♯とかは今回定義しなかったのでドレミファソラシの7音のみ定義。
const double FREQUENCY_PITCH = 1.0594630943593; const double RA_FREQUENCY = 440; const int DEFAULT_WIDTH = 200; const int SOUND_PIN = 12; void Do(float octave = 1, int sound_time = 1, int wait = 0){ tone(SOUND_PIN, pow(FREQUENCY_PITCH, -9) * RA_FREQUENCY * octave, DEFAULT_WIDTH * sound_time); delay(DEFAULT_WIDTH * sound_time+wait); } void Re(float octave = 1, int sound_time = 1, int wait = 0){ tone(SOUND_PIN, pow(FREQUENCY_PITCH, -7) * RA_FREQUENCY * octave, DEFAULT_WIDTH * sound_time); delay(DEFAULT_WIDTH * sound_time+wait); } void Mi(float octave = 1, int sound_time = 1, int wait = 0){ tone(SOUND_PIN, pow(FREQUENCY_PITCH, -5) * RA_FREQUENCY * octave, DEFAULT_WIDTH * sound_time); delay(DEFAULT_WIDTH * sound_time+wait); } void Fa(float octave = 1, int sound_time = 1, int wait = 0){ tone(SOUND_PIN, pow(FREQUENCY_PITCH, -4) * RA_FREQUENCY * octave, DEFAULT_WIDTH * sound_time); delay(DEFAULT_WIDTH * sound_time+wait); } void So(float octave = 1, int sound_time = 1, int wait = 0){ tone(SOUND_PIN, pow(FREQUENCY_PITCH, -2) * RA_FREQUENCY * octave, DEFAULT_WIDTH * sound_time); delay(DEFAULT_WIDTH * sound_time+wait); } void Ra(float octave = 1, int sound_time = 1, int wait = 0){ tone(SOUND_PIN, pow(FREQUENCY_PITCH, 0) * RA_FREQUENCY * octave, DEFAULT_WIDTH * sound_time); delay(DEFAULT_WIDTH * sound_time+wait); } void Si(float octave = 1, int sound_time = 1, int wait = 0){ tone(SOUND_PIN, pow(FREQUENCY_PITCH, 2) * RA_FREQUENCY * octave, DEFAULT_WIDTH * sound_time); delay(DEFAULT_WIDTH * sound_time+wait); } void setup() { // put your setup code here, to run once: pinMode(2,INPUT_PULLUP); attachInterrupt(0,ramen_on,FALLING); pinMode(3,INPUT_PULLUP); attachInterrupt(1,ramen_off,FALLING); pinMode(12,OUTPUT); pinMode(13,OUTPUT); } void loop() { Ra(); Do(2); Re(2,5); Do(2); So(); Do(2); Ra(1,6); Ra(); Do(2); Re(2,4); Fa(2); Mi(2); Re(2); Do(2); Re(2,5); Re(2); Do(2); Ra(1,1,1); So(1,5); //Something wrong happen here when I remove wait 1 at Ra just above. Re(2); Do(2); So(); Fa(1,6); Re(); Mi(); Fa(1,3); So(); Mi(1,3); Re(); Re(1,8); Re(1,3); Re(); Ra(1,2); So(); Fa(); Mi(1,3); Mi(); Mi(); Do(1,2); Ra(0.5); Re(1,12); Re(1,2); Mi(1,2); Fa(1,4); Fa(); So(1,2); Ra(); Ra(1,4); Ra(1,2); Si(); Do(2); Do(2,2); Si(1,2); Ra(1,2); Do(2,2); Re(2,3); Re(2); Mi(2,4); Re(2,2); Ra(); So(); So(1,2); Fa(); So(); Re(1,6); Re(); Mi(); Fa(1,2); Mi(1,2); Re(1,2); Do(1,2); Re(1,4); Mi(1,4); Re(2,2); Ra(); So(); So(1,2); Fa(); So(); Re(1,6); Re(1,2); Mi(); Fa(); Fa(1,2); Mi(); Fa(); So(1,2); Fa(); So(); Ra(1,2); Si(1/FREQUENCY_PITCH,2); Ra(1,10); } void ramen_on(){ digitalWrite(13,HIGH); } void ramen_off(){ digitalWrite(13,LOW); }
苦労した点
音階データ(ドレミ)はすぐ見つかったけど、長さが分からないので苦労した。
楽譜なんてものはもちろん読めないし。
使った方法が、一旦すべての伸ばし音を短く切って、各音を同じ長さで歌いながら確認するという手法。
たとえばこの曲の始まりはこんな感じなんだけど、
「ラドレーーーードソドラーーーーー」
「ラドレレレレレドソドララララララ」という風に歌いながら机でも叩いて、叩いた回数を数えれば、何個分伸ばせばいいか分かる。
あ、昼休み終わってしまったので以上。