したらばTOP ■掲示板に戻る■ 全部 1-100 最新50 | |
レス数が1スレッドの最大レス数(1000件)を超えています。残念ながら投稿することができません。

おちゃめくらぶ掲示板

1454御茶目菜子:2013/01/18(金) 23:27:18
MMLで任意の周波数の音を出す
プチコンmkIIを買ってからBGMPLAYでをまともにMMLを使ってなかったので最近になってから
ようやくいろいろ試しているにょ。
まぁ基本的なMMLくらいは昔PC-8801mkIISRのCMDPLAY命令などを使ったときに覚えたけど
その時もそんなに使いこなしていたわけではないにょ。
ポケコンではBASICでは標準では音階演奏をサポートしていなかったにょ。
中には和音演奏ができるマシン語プログラムなどもあったけどゲームの中で使いづらかった
ため専ら自作のMMLを使っていたにょ。
そして、できたのがOMPにょ。
http://ww5.tiki.ne.jp/~ochame/E500/TECH/OMP.HTM

OMPは独自色がかなり強いもののプチコンと比べて100倍くらい遅いポケコンのBASICでも
リアルタイムのMML解析を行いつつ十分な処理速度が出せるように速度重視であるとともに
MMLのデータが極めて小さくなるようなものとなっているにょ。
さらに、複数の機種での発表によって他機種でもそのMMLでそのまま実行できるため機種間で
互換性がなく移植の際の妨げとなっていた音楽部分の互換性を取ることができているにょ。
このOMPは私がプチコンを入手した際にはプチコンに移植してポケコンで作ったMMLがそのまま
動作するようになったにょ。
http://ww5.tiki.ne.jp/~ochame/petitcom/1page.htm#omp

しかし、mkIIでは今までプリセット音楽しか鳴らせなかったBGMPLAYでユーザーの手による
任意のMMLが演奏できるようになってそのOMPを使用する意味があまり無くなったにょ。
とはいえ1画面プログラムをメインに作ってきたため自作曲を入れたゲームはプチコンでは
作ってこなかったにょ。
まぁそれでも音楽系ツールをこれだけ作っていてまともにBGMPLAYを使いこなしていないと
いうのも何なのでいろいろ試しているというわけにょ。

今回試してみた@Dコマンド(デチューン)だけどこれはいい用途が思いつかなかったにょ。
日本のオーケストラで流行っていると言われている442Hzで演奏する場合には(後述のことを
を元にすれば)@D5とすることで可能なんだけどね。
鳴らしたあとに自由に周波数の微調整ができるのならばギターのチョーキングビブラートも
再現できそうだけど鳴らす前に指定しなくてはならないため使いどころが限られそうにょ。
しかし、上手く使えば任意の周波数の音が出せそうと気が付いたので早速作ってみたにょ。

 《 任意の周波数の音を出すルーチン 》
  N=LOG(F)*17.313-36.378
  BGMPLAY"T1@D"+STR$(0OR N%1*64)+"N"+STR$(0OR N)

これは、簡単に言えばN69(ラの音)が440Hzというのを元にしているにょ。
そして半音変わるごとに周波数は2の1/12乗分変わるため簡単に計算ができるにょ。

  Nの値=LOG(F)/LOG(POW(2,1/12))-LOG(440)/LOG(POW(2,1/12))+69
      ※POW(2,1/12) は 2^(1/12) を示す

ただし、これをプチコン上で実行すると誤差が非常に大きくなるため関数電卓で正確に計算
したにょ。
演算誤差の蓄積を避けるため定数をすべて計算してプチコンで扱える小数第3位で表すの
だけど最後にLOGの計算でまた誤差が発生するため小数第3位はLOGの演算誤差を考慮して
一般的な周波数の範囲だと最も誤差が少なくなるように調整したにょ。
上記の複雑な式のままだとそのままプチコン上で計算したらN69から1オクターブ変わる
ごとに10セントくらいの誤差ができてしまうけど簡略化した式では0.1セント未満になって
いるため精度は数100倍まで高まっているにょ。
もちろん、メモリや速度面のアドバンテージも大きいにょ。

しかし、Nの値が分かったところで半音単位でしか音を鳴らすことができないにょ。
そこで出てくるのが@Dコマンドにょ。
とはいえ、@Dの値を変えるとどの程度音の高さが変わるかはマニュアルには記載して
いないにょ。
そこで、適当な値を入力してみた結果64でぴったり半音分、127でほぼ半音2つ分高さが
変わっていることが確認できたにょ。(まぁ絶対音感がない私の耳で確認しただけなので
正しい保証はないけど数セントの音のずれならば分かるためそれほど大きな誤差もないと
思う)
これを元にして考えると@Dコマンドの値1つにつき約1.5セント変わると言えそうにょ。

それが分かればNの値の小数部分を64倍すれば1.5セントを分解能として任意の周波数の音を
発生させるものは簡単にできるにょ。
それが上記のルーチンにょ。

さて、これを使って何ができるかというと・・・なかなか使いどころがないにょ(笑)
ということで、とりあえずドップラー効果のサンプルプログラムを作ってみたにょ。
ドップラー効果についてはWeb検索をすれば簡単に分かると思うけど最も身近なのは走って
いる救急車のサイレンの音が近づいている時と遠ざかっている時では聞こえる音の高さが
異なっているというものにょ。
これは単純な公式があるのでそれを元にすれば誰でも簡単に分かるにょ。

 観測者に聞こえる周波数=周波数×(音速−観測者の動く速度)÷(音速−音源の動く速度)

この公式を使って聞こえる周波数を計算してやれば先ほどの任意の周波数の音を出す
ルーチンによってそれをシミュレートできるにょ。
プチコンのMMLではMMLで変数が使えBASICでその値をやりとりも可能になっているにょ。
BGMSETVでMML内変数に書き出し、BGMGET()でMML内変数の値を読み出せるにょ。
これを使ってやればSTR$を使って文字列演算を繰り返すなんていう手間が省けるため
リストがシンプルになるにょ。(上記の任意の周波数の音を出すルーチンのように書き込む
ものがNと@Dだけならば大差はないけど沢山書き込めば書き込むほど差がでてくる)

さて、いろいろ試した結果どうもNコマンドに変数を書き込む場合だけ挙動がおかしいにょ。
私の耳ではちょうど半音4つ分ずれている感じにょ。(N69の音がN65に聞こえる)
使い方が悪いのかと思って様々な方法で試してみたけど改善されないためMML変数を使う
場合には上記のルーチンにおけるNの値を求める式は次のように変更する必要があるにょ。

 通常            MML内変数使用時
 N=LOG(F)*17.313-36.378 → N=LOG(F)*17.313-32.378

これであとは簡単なんだけど音量はどうやら数値に比例しない感じなのでそれっぽくなる
ように調整しているにょ。(そのため本来ならば距離はX座標とY座標の二乗の差分の
平方根をとる必要があるけど多少の誤差があるのを承知で簡易的な式にしている)
周波数の変化は観測者のY座標と音源のY座標が異なるためそれを加味して計算する必要が
あるけどその辺は考えておらず、上記の公式そのまんまとなっているにょ。

というわけで、以上のものを元にしたサンプルはこちらにょ。


◎踏切の遮断器の警告音
 QRコード http://ww5.tiki.ne.jp/~ochame/petitcom/qr/fumikiri.png

  INPUT"ソクド(km/h)=";S
  S=S/3.6:X=-500:L=200
  A$=":0@1[@V$0@D$1N$2]
  B$=":1@1[@V$0@D$3N$4]
  F(0)=329.6:F(1)=392
  BGMPLAY A$+B$

 @LOOP
  FOR I=0TO 1
 ?? F=F(I)*(340-S*SGN(X))/340
 ?? GOSUB @SOUND
 ?? BGMSETV 0,0????,V
 ?? BGMSETV 0,I*2+1,D
 ?? BGMSETV 0,I*2+2,N
  NEXT
  IF X>L THEN BGMSTOP:END
  X=X+S/60:?"キョリ="0OR X"m"
  WAIT 1:GOTO @LOOP

 @SOUND
  N=LOG(F)*17.313-32.378
  D=N%1*64
  V=255/SQR(ABS(X)+4)
  RETURN


◎救急車のサイレンの音
 QRコード http://ww5.tiki.ne.jp/~ochame/petitcom/qr/kyu_kyu_sya.png

  INPUT"ソクド(km/h)=";S
  S=S/3.6:X=-300:L=300
  A$="T100@60[P$0V$1@D$2N$3P$4V$5@D$6N$7]
  X=-300:L=300
  F(0)=493.8:F(1)=392
  BGMPLAY A$

 @LOOP
  FOR I=0TO 1
 ?? F=F(I)*340/(340+S*SGN(X))
 ?? GOSUB @SOUND
 ?? BGMSETV 0,I*4??,P
 ?? BGMSETV 0,I*4+1,V
 ?? BGMSETV 0,I*4+2,D
 ?? BGMSETV 0,I*4+3,N
  NEXT
  IF X>L THEN BGMSTOP:END
  X=X+S/60:?"キョリ="0OR X"m"
  WAIT 1:GOTO @LOOP

@SOUND
N=LOG(F)*17.313-32.378
D=N%1*64
V=255/SQR(ABS(X)+10)
P=ATAN(X/10)*40.7+64
RETURN

音量は上記のようにVコマンドの指定値と音量が比例しないため両方とも結構適当な計算
なんだけど救急車のサイレンの方のパンポットはATANを用いてそれなりに正しくなるように
計算しているにょ。(踏切は観測者から4m離れた地点を通過、救急車は10m離れた地点を
通過という設定でなおかつずっとまっすぐ走っているということを前提に計算している)
まぁ難しいことは何もやってないし、音色選択も適当なのでより近い音色を選択もしくは作る
などをした方がいいかもしれないにょ。
まず居ないと思うけどこれを使ったプログラムは自由に発表してもらってOKにょ。




掲示板管理者へ連絡 無料レンタル掲示板