音階演奏ソフト OMP徹底解説

 では、OMPについてさらに詳しく書いていきます。


part 1 高速化処理について



 最初は周波数を求める部分の演算処理のについて解説をします。
 まず、知っておかなくてはならないのは次の2点です。

  (1)低い「ラ」の音が440Hzの高さである。
  (2)1オクターブ高くなるごとに周波数が2倍になる。
  

 1オクターブと言うのは低い「ラ」から中の「ラ」の間の差のことです。
 また、1オクターブと言うのはピアノの鍵盤12個分になります。つまり、「半音12個分で周波数が2倍になる」ということです。
 OMPはこの半音の計算がすごく簡単に出来るようなデータになっています。半音高くなるごとにアスキーコードが1大きくなるだけなのです。

 今、「低いド」を「A」(アスキーコード65)になるように設定しています。だから、440Hzの「低いラ」は「J」(アスキーコード74)になります(BEEP音階表参照)
 つまり、アスキーコードから周波数を求める式は次のようになります。

  X=440*2^((Z-74)/12) ・・・(1)
(周波数をX、アスキーコードをZとする)

 なぜこのようになるかというと、半音高くなるごとに2の12分の1乗ずつ周波数が高くなるからです。
 これで別に構わないのですが、この計算は見てのとおり、掛け算1回、割り算1回、引き算1回、べき乗1回のとても複雑な処理となっています。これでは処理速度の低下は免れません。

 では、式(1)を次のように変形します。

  X=440*2^(Z/12-74/12)
   =440*2^(Z/12)/2^(74/12) ・・・(2)

 式(2)を見てください。
ここで「2^(74/12)」というのは定数ですね。
ということでこれを計算すると次のようになります。

  X=6.124928697*2^(Z/12) ・・・(3)

 これで足し算1つと引き算1つ省くことができ、その分高速化されました。
 この周波数を使って最終的に求めるものは8ビットの整数値なのでそれほどの精度は要求されません。したがって式(3)は次のようにできます。

  X=6.125*2^(Z/12) ・・・(4)

 これでメモリの節約(E500シリーズではメモリの節約にはならないのでタイピング量が減っただけ)もできました。
 気になる有効桁数ですが全く問題ないこともすべてのパターンを調べることで明らかになりました。
 普通の人ならばこれで完璧と思うところですがまだまだ甘いですね(笑)。

 ワンポイントテクニックに書かれている「べき乗演算の高速化」を使えばまだ速くできます。

  X=6.125*TEN (0.02508*Z) ・・・(5)

 式(5)を見て分かるように「べき乗」を「TEN」へ変えることにより高速化をしているだけでなく、割り算を掛け算に直しています。割り算よりも掛け算の方が速いのでさらに高速化ができています。これにより約2倍の演算速度が実現できました。
 ところが、ここで問題発生です。本来ならば「0.025833…」となるところを小数点以下5桁の部分で四捨五入しているので、これにより誤差が大きくなり(指数部分なので数値が大きくなるほど誤差が拡大されるため)最終的に一部の数値が不正確(といっても値が1ずれるだけ)になってしまいました。
 要は、全体的に数値が小さくなる傾向があるのでどこかで補正してやればよいのです。
ということで最終的には次のようになりました。

  X=6.13*TEN (0.02508*Z) ・・・(6)

 多少強引ですが結果が良ければよいのです(笑)。これも、完璧にアルゴリズムを理解していないとあとからデバッグが大変ですので、初心者はここ(値の正確さを最低限に押さえ速度を重要視する方法)まで真似しない方がよいかもしれません(笑)。
 しかし、この倍速化の効果は非常に大きく、音質がはっきりと分かるまで良くなりました。どれ程の効果があるかは「幻想即興曲」のデータ(tikiさん投稿)を入力して周波数演算部分を式(1)に変更すれば一目(一聴?)で分かると思います。

 この周波数をBEEPで鳴らすための音の高さ(8ビット整数値)に変換するのですが、これにも公式があります。

 E500シリーズ 高さ=(256000/周波数-90)/4
 G800シリーズ 高さ=(1300000/周波数-166)/22

 これもOMPでは高速化を最優先するために変形してあります。詳しいことは省略しますので自分の目で確かめてください。


part2 OMPをBGMとして利用する



 さて、次はOMPのさらなる使い方としてBGMに使用する方法を書きます。
 とはいうものの完全にバックグランドで演奏するのはBASICでは不可能なので、また強引なテクニックを使用します。
 要はメインルーチン1回するごとにOMPで1音鳴らしていくだけです。これで、BGMっぽく聞こえるようになります。ただし、メインルーチンがある程度高速なプログラムでないとあまりきちんとした演奏になりません。

要はこんな感じです。
 100 *MAIN
   ・
   ・
   ・
 200 M$=MID$ (A$,N,1):GOSUB *OMP
 210 GOTO *MAIN
   ・
   ・
   ・
 500 *OMP 〜(以下省略)

 簡単に言えば、200行でA$に入っている音階データをM$に1つずつ取り出してOMPを実行するのですが、実は、OMPの内部でも同じく1文字取り出す処理が実行されていますね。つまり、2回連続して同じ処理を行うという非効率的な処理を行っているのです。  したがって、BGMとして使用するためにMID$を取り払ったOMPを発表します。その名も「バックグランドOMP」です。通常のOMPとの大きな違いはMID$がないこととそれに従い1音鳴らすごとに、メインルーチンに復帰することです。  B$に曲データを1音ずつ入れてください。

《バックグランドOMP プログラムリスト》

E500シリーズ用
100 *BOMP Z=ASC B$:IF Z>64LET X=6.13*TEN (0.02508*Z):BEEP 1,64000/X-22,X*Y/T ELSE IF Z>47LET Y=Z-48ELSE IF Z=36LET T=VAL RIGHT$ (B$,LEN B$-1)ELSE IF Z=42 FOR J=&1TO 730*Y/T+20:NEXT
110 RETURN

G800シリーズ用
(注)圧電ブザーを自作で取り付けないと音は出ません。
G850は演算速度の違いにより130行の535を1111に変更してください。

100 *BOMP Z=ASC B$:IF Z>64 LET X=6.13*TEN (0.02508*Z):BEEP 1,59091/X-7,X*Y/T:RETURN
110 IF Z>47 LET Y=Z-48:RETURN
120 IF Z=36 LET T=VAL RIGHT$ (B$,LEN B$-1)
130 IF Z=42 FOR J=1 TO 535*Y/T+20:NEXT
140 RETURN

これで、MID$が1つなくなった分、少し高速化ができましたが、BGMではなく普通の演奏をするために「OMP」と「バックグランドOMP」の両方を組み込むのは非常にメモリ効率が悪いですね。

 そういう人は次のリストを入力(行番号は適当でいいです)してみてください。
*OMP FOR I=1 LEN M$:B$=MID$ (M$,I,1):GOSUB *BOMP:NEXT :RETURN
(注)G800シリーズの人はM$をM$(0)に変更してください。
 これで、「GOSUB*OMP」で普通に演奏することが可能になります。
 しかし、これにも問題があるのです。というのも、MID$で1文字取り出す処理を「バックグランドOMP」のメインルーチンの外で行っているためにテンポ指定がうまくいかないのです。
 だから、テンポ指定のみ独立して次のように「バックグランドOMP」の方を利用しなくてはいけません

(テンポ指定の例) B$="$15":GOSUB *BOMP
(注)面倒ならば直接変数Tにテンポを入れてもいいです(あまり推奨はしませんが)。

 なお、BGMとして使用する場合はテンポ指定はメインルーチンの実行速度に大きく左右されるため全く正確ではありません。実行速度を速くするため(音がなっている間は他の処理ができないため)20〜50程度の速いテンポを指定しておいてください。
 また、音の長さの指定も無意味なので長さは1で統一しておいてください。だから、長い音は複数回鳴らすことで疑似的に表現することになります。

 このバックグランドOMPを使用する方法が分からない人もいることと思いますので、これを利用したBGM演奏サブルーチンのサンプルを用意しましたので、皆さんのプログラム内でご自由に使用ください。

《BGMプログラム(バックグランドOMP必須)》
(注)行番号は適当につけてください
*BGM B$=MID$ (M$,P,1):GOSUB *BOMP:Q=LEN M$:P=P+1+(P=Q)*(Q-1):RETURN

《使用方法》

まず、BGMを鳴らす前にP=1として、M$には音階データを入れておいてください。
あとは、メインルーチン内で「GOSUB *BGM」とすればOKです。
G800シリーズをご使用の方はM$に8文字以上の音階データを入れる場合にはM$の代わりにM$(0)としてください。その際にはプログラムの最初に「DIM M$(0)*250」を実行しておいてください。

《サンプルプログラム(BGMサブルーチン必須)》
10 CLS :B$="$30":GOSUB *BOMP:M$="1TRQOMOMOQQ":A=1:P=1
20 LOCATE 5,0:PRINT MID$ ("OCHAME CLUB OCHAME CLUB",A,8):GOSUB *BGM:A=A+1+(A=11)*10:GOTO 20
 全く面白味の無いサンプルですが「OCHAME CLUB」の文字がスクロールしつつ音が鳴っているのが分かるでしょう。処理速度があまり速くないのでこれが実用になるのかはちょっと疑問な部分がありますが、あくまでこういう使い方があるという参考にして戴けたら幸いです。

 まだまだ、OMPには様々な使いかたがありますが、これくらいにしておきましょう。

石板「o」
《最後に》

 実を言うとOMPの原型はすでに10年以上前にありました。当時はE500を買ってそれほどたって無い頃で、音階演奏させるためにBEEPの音程をDATAに羅列してそれをREADすることにより行っていました。
 多少それが煩わしくなって、自作の音階演奏ソフトをBASICで作りましたが非常に長く(1Kバイト以上)になり使い物にならないので、データ形式を簡略化することで作られたのがOMPの原型です。
 ところが、それでも、音質が悪い(PC-1350では自作のマシン語音階演奏ルーチンを使っていた)ので、速度最重要視の独自音階演奏ルーチンを現在まで使ってきました(まあOMPも独自だけどね)。
 希望があれば、その仕様を公開しますが、速度(音質)以外はすべてOMPが勝っていますからね。今更、汎用性の全くないルーチンなんて・・・でも、先程のBGMに関しては使えるかもしれません。
 まあ、そうやって出来たOMPですが、昔の2倍以上の速度で動いているわけですからすごいと思いませんか?


RETURN

inserted by FC2 system