レス数が1スレッドの最大レス数(1000件)を超えています。残念ながら投稿することができません。
おちゃめくらぶ掲示板
-
プチコンのBEEPで音階演奏をしよう
DSiウェア「プチコン」について11月6日にはプチコンのスプライトに関して書いたわけ
だけどCHRSETを使えばリスト内でスプライト定義できることがあれから分かったにょ。
とはいえ、16x16ドット16色のキャラを1つで256バイトのデータになってしまうにょ。
自キャラをアニメ4パターン×4方向、敵キャラを4パターン×4方向×2種類用意したら
それだけで全部で48パターンになるにょ。
それに背景を8キャラ分のデータを用意したら全部で256×56=14336バイトのデータに
なってしまうにょ。
これを別ファイルから読み込むだけならば大した量ではないとはいえ、手入力に拘るならば
極めて大きなサイズと言えるにょ。(100文字/分の速度で入力できたとしても入力には
2時間半かかってしまうし、データであるためエラーによって入力ミスを発見できないと
いうことを考えればより慎重になるためさらに時間がかかる)
というわけで、個人的にはそこまでのものをプチコンで作ろうとは思わないにょ。
さて、キャラを自作するならば音楽も自作したいところにょ。
しかし、プチコンにはPLAY文のようなMMLを演奏する命令は用意されていないにょ。
その代わりBGMPLAYによってプリセットされた31曲を手軽に演奏することができるにょ。
そのためBGM付きのゲームを誰でも簡単に作ることができるにょ。
このプリセットもいいんだけどやはりゲームのオリジナリティを出すならばキャラだけで
なく音楽もプリセット以外のものを使いたいにょ。
それを可能にするのがBEEP命令にょ。
BEEPは数多くのポケコンにも用意されている命令だけど古くはBEEPでは鳴らす回数しか
指定できなかったにょ。
それがシャープのポケコンではPC-1360の世代からは BEEP 回数,音程,音長 という
感じで指定可能になり標準で音階演奏も実現可能に変化したにょ。(高級ポケコンである
PC-1500/01では早くから採用されてきたけど)
とはいえ、どの数値を入れたらどんな高さの音が出るのかというのは機種毎にまったく
変わってくるため音階演奏するためにはマニュアルを見てから「ドが222」「レが195」と
いう感じで手動で変換していきDATA文で羅列する必要があったにょ。
http://ww5.tiki.ne.jp/~ochame/E500/TECH/BEEP.HTM
別に手動変換をしなくても音程指定値には公式があるためそれをリアルタイムで計算
すればMMLの演奏も可能に思えるにょ。
これが古い世代のポケコンだったら演算性能の問題でMMLからデータを読み取ってそれを
リアルタイム演奏するなんて無理(演算部分で明かな間が出来てしまい音がブツ切れ
状態になる)だけどPC-1360よりも5倍演算性能に優れたPC-E500ならばそれが実現可能に
なったといえるにょ。
とはいえ、CDEFGABに加えてシャープやオクターブが入ったMMLをリアルタイムで解析して
BEEPの音調データに変換するにはポケコンとしては高速な部類であるPC-E500でも少々荷が
重いにょ。
しかも、自作ゲームに組み込んで発表するには音階演奏ルーチン部分のサイズもやはり
気になってしまったにょ。
そこでできるだけ高速処理が可能でリストも短くなる新しい音階演奏ルーチンを考えて
長年かけて作ったのがOMPにょ。
http://ww5.tiki.ne.jp/~ochame/E500/TECH/OMP.HTM
OMPはMMLが分かりにくいという問題があるけどその分ポケコンにとっては優しい(つまり
処理速度向上によって音のブツ切れが軽減されている)わけだしリストもデータも短くなる
ということでメリットが非常に大きいと私は考えているにょ。(何せOMPはポケコンBASICで
リアルタイムにMMLを解析しているわけだからね)
このOMPによってポケコンでも音楽付きのゲームを手軽に作れるようになったにょ。(とは
いってもせいぜいスタート時やゲームオーバー時などで音楽が流れる程度だけど)
そこで、プチコンを使い始めた時にすぐに行ったのがOMPのプチコンへの移植にょ。
http://ww5.tiki.ne.jp/~ochame/petitcom/1page.htm#omp
すでにプチコンではMMLによる音階演奏プログラムがいくつも発表されており、このOMPは
機能面ではそれらよりも大きく劣っている代わりにリストやデータが短いというポケコンの
OMPと同様の特徴を持つにょ。(速度面に関してはプチコンがポケコンと比べて100倍近い
演算速度であるためポケコンのOMPほどのメリットはない)
プチコンのBEEP命令はポケコンとは大きく仕様が異なるにょ。
BEEP [波形番号], [周波数],[音量 ],[パンポット] となっているからね。
プチコンのBEEPではプリセットされた70種類の音源が使用できるだけではなく周波数
(音程)も4オクターブ、16384段階で指定できるにょ。
ただ、これを見て分かるように音長指定はできないにょ。
したがって、OMPでも音長指定に対応しているもののその意味はほとんどないにょ。
とはいえ、これはピアノなどのように自然減衰する音色ならばそれほど気にはならないにょ。
単純なBEEP音(例えばBEEP 0など)では長い音を鳴らせばこれは非常に目立ってしまう
ため厄介にょ。
それとは逆に短い音も出せないにょ。
ただし、これは裏技で何とかなるにょ。
というのもDSでは8つまでの音を重ねることはできるけどそれ以上は仕様上できないため
一旦音を鳴らしてからすぐに無音の音を8個鳴らせば自動的に短い音になるにょ。
もっともこれは単音演奏だから可能な裏技であり、和音演奏ではそうもいかないにょ。
さて、プチコンのBEEPは音長指定できないという難点はあるものの上記のように8つまでの
音を重ねることができるため和音演奏プログラムは簡単に出来てしまうにょ。
ここで問題となるのが音を鳴らすタイミングにょ。
単音であればVSYNCを使うことで1/60秒単位のウェイトを入れることができた(ポケコン
ではFOR〜NEXTを使ってウエイト指定している)けれど和音であれば簡単にはいかない
からね。
実際に具体例を考えてみるにょ。
例えばトラック1が「三連符でドミソドミソ」、トラック2が「八分音符でドミ、四分音符で
ソ」という場合にテンポ120で演奏する場合に図示すると下記のようになるにょ。(以下
どの音が対応しているのか説明しやすくするためにa〜iで指定する)
PLAY "O3T120{CEG}4{CEG}4","04C8E8G4"
《図1》
a b c d e f
1 ド→→→→→ミ→→→→→ソ→→→→→ド→→→→→ミ→→→→→ソ→→→→→
g h i
2 ド→→→→→→→→ミ→→→→→→→→ソ→→→→→→→→→→→→→→→→→
テンポ120は四分音符を1分間に120回演奏する速度であるため四分音符はVSYNCでいえば
VSYNC 30となるにょ。
同様に八分音符はVSYNC 15、三連符はVSYNC 10となるにょ。
《図2》
a b c d e f
1 ド→→→→→ミ→→→→→ソ→→→→→ド→→→→→ミ→→→→→ソ→→→→→
10 10 10 10 10 10
g h i
2 ド→→→→→→→→ミ→→→→→→→→ソ→→→→→→→→→→→→→→→→→
15 15 30
これを見るとトラック1のド(aの音)、トラック2のド(gの音)を鳴らすというのは簡単
できるけどその後のウエイトをどうするかが問題となるにょ。
トラック1のaの音からはVSYNC 10でウエイトを入れてbの音を鳴らせばいいけど次のcの
音はVSYNC 10でウエイトを入れてしまったらトラック2のhの音が出せないからね。
したがって、トラック1のbの音を鳴らした後に15-10=5でVSYNC 5のウエイトを入れて
hの音を鳴らすことになるにょ。
cの音を鳴らすまでにはbからは10のウエイトを入れる必要があるけどすでに5のウエイトを
実行しているため残り5のウエイトを実行すればいいにょ。
以下同じようにすれば完了にょ。
まとめると次のようになるにょ。
《図3》
aとgの音を鳴らす→10のウエイト→bの音を鳴らす→5のウエイト→hの音を鳴らす→
5のウエイト→cの音を鳴らす→10のウエイト→dとiの音を鳴らす→10のウエイト→
eの音を鳴らす→10のウエイト→fの音を鳴らす→10のウエイト(終了)
これはトラック数が2つだからまだいいけどトラック数が増えればどんどん複雑になって
くるにょ。
しかし、もしもすべてトラックを合算したウエイトではなくトラックごとにウエイトが
管理できれば図2のウエイトをそのまま適用すればいい(わざわざ図3のように考える必要は
ない)ので格段に楽になるにょ。
これは各トラックのウエイトを別々に記録していけばいいだけにょ。
そしてメインルーチン1回実行ごとにウエイトを1ずつ減らしていけばいいだけだからね。
トラック0からトラック7の8和音演奏をするプログラムは下記のようにすることが可能に
なるにょ。
《和音演奏プログラム》
N=8
@LOOP
FOR I=0 TO N-1
IF W(I)>0 THEN @LABEL
(音を鳴らす処理)
@LABEL
W(I)=W(I)-1
NEXT
????VSYNC 1
GOTO @LOOP
※変数W(I)には音を鳴らすフレーム数(=ウエイト数)が入っている
このプログラムには終了処理がないため実際に使用する場合には最後のウエイトが
終わったらメインルーチンに復帰するIF文が必要になるにょ。
VSYNC 1のウエイト+音階演奏ルーチンの実行速度となるためテンポが速い曲の場合は若干
長さが変わってくるけど幸いにしてプチコンは演算速度が速いのでそれほど気になるレベル
ではないと思うにょ。
MMLを実装しなければ極めて簡単に和音演奏が可能であることが分かると思うにょ。
配列変数にBEEPの音程指定値とウエイト指定地をあらかじめ計算しておけば音を鳴らす
処理は、BEEP 22,M(I,J) と言う感じで出来てしまうからね。(配列変数Mに数値化した
音符データが入っていると想定し、Jが各トラックの何番目の音が鳴っているのかを示す)
MMLを実装する場合にはMID$を使って1つずつ読んでいき音程に関しては1オクターブで
4096に相当するため半音では4096/12ほど変えればいいにょ。
C(ド)がゼロ(基準値)であり、レは全音分(=半音2つ分)高くなるため
BEEP ,4096/12*2 という感じで鳴らすことが可能になるにょ。
MMLを解析する場合にはIF文7つでCDEFGABを判断可能になるにょ。
その場合にはテーブル処理を行えばIF文は1つでも可能になるにょ。
C D E F G A B
アスキーコード−65 2 3 4 5 6 0 1
C基準で半音いくつ分高いか 0 2 4 5 7 9 11
音程データを配列変数A()にA(0)=9、A(1)=11、A(2)=0、・・・という感じで格納していけば
MID$で読み出した文字がアスキーコード65〜71の範囲内(A〜G)だったら自動的に半音
いくつ分プラスしていけば良いかが分かると思うにょ。
あとはシャープとオクターブの上げ下げのIF文を付ければ簡易MMLの完成・・・と言いたい
けれどあとは音長が問題にょ。
これがないとウエイト指定もできないからね。
どういう実装方法によるかによって変わるけど4分音符を「4」、8文音符を「8」という
感じで実装する(「C8E8G4」のような感じ)ならばMID$で読み込んだ時点で音を鳴らしては
駄目で先読みをする必要があるにょ。(「C」の後に「8」がきていれば八分音符を鳴らし
「E」などの別の音程データが入っていればデフォの音長で鳴らす必要があるためCを
読み込んだ時点では分からない)
そのため処理が煩雑になってしまうためリストも長くなってしまうという問題があるにょ。
ポケコンではそれは大きな問題となってしまうためポケコン用のMMLとして私が考案した
OMPでは先読み不要にするため音長データの方を音程データよりも前に表記するようにして、
さらに音長も読み込んだ値そのままの割合で済むようにしているにょ。(アスキーコードの
大小で判断できるためオクターブ処理やシャープの処理は不要でOMPでは音程、音長、休符の
3つのIF文で済んでしまうし、ポケコンの速度でもMMLをリアルタイム解析可能となる)
そのため三連符が必要な場合でも「6」を四分音符、「3」を八分音符、「2」を三連符という
感じで考えれば簡単に使うことができるにょ。
6を四分音符とした場合には一般的なテンポ120の曲はOMPでは120÷6=20でテンポ20と
なるにょ。
そして出来たのがこのOMP 和音対応バージョンにょ。
http://ww5.tiki.ne.jp/~ochame/petitcom/1page.htm#omp2
ただ、最初に発表した単音版のOMPとは異なりこの和音対応バージョンにはテンポ指定が
省かれているにょ。
これは1画面に収まらなかったというのもあるけど正確なテンポ指定をされてもVSYNC 1に
よる1/60秒が最小単位であるため正確な設定が実現できないというのもあるしテンポを
変えた場合は音ずれの可能性もあるからにょ。
というのもウエイトは60×音長÷テンポで計算されているけどこれが整数にならない場合は
音符数が増えるごとに誤差が増えていくからね。(単音版OMPでは音長÷テンポ×60で
テンポを求めていたけどこれだとテンポで割った時点で丸め誤差が発生するためこの和音
対応版では60を先に掛けている)
例えば音長4でテンポ7の場合は60x4/7=34.2857…でウエイトが35になるけど音長8はその
ジャスト2倍の70にならなくてはいけないのに60x8/7=68.5714…でウエイト69になり
1フレーム分(1/60秒分)だけずれてしまうからにょ。
これが気にならない人ならば問題ないのでTの値を変えることでテンポ設定ができるように
なっているにょ。
そういう問題があるせいかプチコン用ですでに発表されている音階演奏ソフトは私が見る
限りはすべてテンポ固定となっているにょ。
さらにVSYNCの値を固定にするために最小分解能を8分音符や16分音符にしていることで
音ずれが起きないようにしているにょ。
せっかくなのでOMPをBGMにも利用したいけどそれは簡単なことではないにょ。
というのもこの音階演奏ルーチンはVSYNCのウエイトに依存しているからにょ。
メインルーチンの処理速度が極めて速いというのでれば多少の遅れは問題ないけど60fpsの
ゲームでもテンポが半減してしまうにょ。(もっともメインルーチンの実行速度が分かって
いればあらかじめテンポをそれに合わせて変えておけばいいだけなのだけと)
常に60fpsのゲームならばOMPの中にあるVSYNC1を取り除いてやれば問題ないけどそれより
速かったり遅かったりする場合では問題になるからね。
これを解決可能にするのがMAINCNTLにょ。
MAINCNTLはプチコンが立ち上げられてから1/60秒ごとにインクリメントしているカウンタで
これを使えばVSYNCと同様に1/60秒単位でのウエイト指定が可能になるにょ。
それを使ってBGMに使えるOMPを作ってみたにょ。(11月14日追記:このOMP for BGMから
機能を削ってさらにコンパクトサイズにしたOMP miniも作ってみた)
http://ww5.tiki.ne.jp/~ochame/petitcom/1page.htm#omp2bgm
このOMP for BGMは自動ループの設定になっており、メインルーチンから常時呼び出すことで
BGMとして使うことができるというものにょ。
実際に実行してみれば分かるけど音ずれに関してはVSYNCを使った上記の和音対応バージョン
以上にシビアになるにょ。
和音対応バージョンではウエイト数が小数にならない限りは音ずれはほぼ発生しないの
だけどこちらのBGM用はメインルーチンの処理速度に依存してしまうからね。
メインルーチンが60fpsより速いゲームならば問題ないけれどそれより遅い場合は音ずれの
可能性がどんどん高くなっていくにょ。
20fpsのゲームならば分解能は1/20秒になってしまうわけだからね。
例えばテンポ20の場合(四分音符を6と考えれば一般的なMMLではテンポ120に相当)は
最も短い音長1の音は1/20秒だけ鳴るにょ。これだとMAINCNTLによる1/60秒単位のカウンタ
だと3つ分に相当するにょ。
それを考えると20fpsのゲームまでは対応できそうだけどこれはジャスト20fpsでないならば
そう甘くはないにょ。
20fpsより若干速い25fpsのゲームのBGMに使用する場合を考えてみるにょ。
三連符を使用する楽曲で四分音符を6に設定した場合は下記のようになるにょ。(三連符が
不要ならば四分音符を4に設定した方が都合がいいし、32分音符があるならば四分音符は
8に設定しなくてはならないけどこの自由度の高さもOMPの特徴)
音長1(1/20秒)・・・・・・・2フレーム分のウエイト(2/25秒)
音長2(2/20秒)三連符・・・・3フレーム分のウエイト(3/25秒)
音長3(3/20秒)八分音符・・・4フレーム分のウエイト(4/25秒)
音長4(4/20秒)・・・・・・・5フレーム分のウエイト(5/25秒)
音長5(5/20秒)・・・・・・・7フレーム分のウエイト(7/25秒)
音長6(6/20秒)四分音符・・・8フレーム分のウエイト(8/25秒)
見ての通り、音長2は音長1のジャスト2倍の時間になるはずなのにそうなっていないために
1フレーム分の音ずれが発生することになるにょ。
同様に音長4は音長2のジャスト2倍の時間になってないにょ。
これはOMPがリストを最も簡素化するために切り上げで処理している(カウンタがウエイト
値を超えるまで実行を繰り返す)ということで誤差が大きくなっているのだけど音長1が
1.25フレーム分であり小数点以下が発生しているため上記の和音対応バージョンのように
ウエイト値が小数点になるようなテンポを設定するのと同じく音ずれは避けられないにょ。
VSYNC 1での音ずれは1/60秒であっても分かるのに1/25秒もずれたら明らかに分かって
しまうにょ。
これがどの程度のものなのか、実際にOMP for BGMを使って演奏した結果がこの動画にょ。
http://www.youtube.com/watch?v=GIoF-95VlqM
この動画ではゲームのBGMに使うことを想定してスプライト100個を動作させており、この
実行速度は20fps前後となっているにょ。
有名な曲だけど打ち込みの負担軽減と音ずれ緩和のために2重和音に抑えたアレンジ版と
なっているにょ。(原理的には8重和音まで鳴らせる)
OMPのテンポは20(原曲の2倍近い速度)で最小の音長1ということで上記の設定条件と
同じであるため私が書いた文章はこの動画を見れば(聴けば)一目瞭然だと思うにょ。
分解能ギリギリの速度であるためかなり微妙な感じだけどこの速度でも一応はちゃんと
鳴っているというのは伝わると思うにょ。
音ずれ問題はメインルーチンの実行速度が60fps以上であればウエイト値が小数にならない
ようなテンポ指定をすることで避けることができるけどメインルーチンが60fpsより遅い
場合にはMAINCNT2回単位でカウントする(分解能を1/30秒にする)ことで30fpsまで音ずれ
無しで対応できるわけだし、カウンタの実装方法を変えることでも音ずれは緩和が可能
だと思うにょ。(1つの音符単位だと誤差が大きくなるけど曲の頭からの積算ウエイトならば
ほぼ誤差を無くすことができる)
そういった対策をするのが面倒でなおかつ音ずれが我慢できない場合は和音演奏は諦めて
単音演奏にすれば音ずれは全く気にならなくなるにょ。
あとMAINCNTLを使用していて注意しなくてはならないのがオーバーフロー対策にょ。
プチコンで扱えるのは1/4096単位の32ビット固定小数点のみだからにょ。
つまり、扱える数値は-524287.999〜+524287.999の範囲内にょ。
そのためこのMAINCNTLのカウンタは0〜524287の間を約8738秒かけて繰り返しているにょ。
OMPではMAINCNTLの値+ウエイト値を計算しているためカウンタが524287近辺に差し掛かった
時に実行するとオーバーフローを起こしてしまうにょ。
もちろんこれはウエイト値とプラスする前にオーバーフローを起こすかどうか条件判断を
して超えるようならば桁上がりのフラグを適当な変数に入れるだけで解決する問題にょ。
ただし、このOMPでは1画面に収めるためにその対策は行ってないのでこのOMP for BGMを
使用する人は各自で工夫して欲しいにょ。
PLAY文による自作曲によるBGM演奏はできないけど工夫次第ではBEEPでBGM演奏も可能である
ことがこれで分かると思うにょ。
実際にプチコンの自作ゲームで自作曲を使用している人もいるわけだしね。
ほとんどがソースが公開されてないので分からないけど恐らく音ずれ対策としてその
ゲームに最適化しているのではないかと思われるにょ。
OMPは使い勝手の面では決して優れてるというわけではないけどサイズ面は他の音階演奏
ルーチンより優れていると思われるにょ。
それでも専用カスタマイズしたものであればこれよりコンパクトサイズにすることも
可能になるにょ。
OMPも自由に改変しても構わないにょ。(この和音対応のものは最も簡単に実現できそうな
もので作ったのでさらに良いアルゴリズムがあれば私自身も手を加えるかもしれない)
トラック毎に音色やボリューム変更がしたい人もいるだろうし音ずれ対策も専用に作った
方が楽にできるしね。
ただし、可能な限りはデータ互換は維持してもらいたいところにょ。
せっかく作った曲が使い回せないというのは嫌だからね(笑)
OMPのポケコン版はデータ互換を維持しているため機種が変わっても問題無くそのデータを
使えることがウリの1つになっているくらいにょ。
それから、プチコンはver1.20が11月11日にリリースされたにょ。
現在のver1.1のバグ修正という感じだけど3DSではまだニンテンドーe-shopでは対応して
ないみたいなので現在はDSi/DSi LLのみとなっているにょ。(バグ修正ではなく機能追加
となる場合には新規リリースする必要があるらしい)
私も様子を見てver1.2へとバージョンアップする予定にょ。
「プチコンプラス」を開発中なのでこれが最後のバージョンアップになるかも・・・。
バグが多かったプチコン(3DSだとタッチパネルの誤検知があったりとか)だけどこれで
かなり安定して使えるようになればいいにょ。
|
|
|
掲示板管理者へ連絡
無料レンタル掲示板