DPC-100でシンセサイザー(っぽいもの)を作ってみた。

つい先日DETUNEさんがリリースされたBASIC環境アプリ、「DPC-100」でこの1,2週間、がっつり遊びこんでおりました。(Dead Space3と並行でしたが・・その話はまた今度)


実は今までBASIC系の言語は触ったことがなく、リリース当初はちょっと躊躇していたのですが、最近出たver.1.1.0で待望の発音機能が追加されたため、つい飛びついてしまいました(笑)。一行、それも16文字しか表示できない編集ウィンドウ、あっという間に溢れるメモリ領域、自分で定義できない関数、そして行番号・・。始めた当初は相当面食らった仕様も、1週間ほど触っていると慣れてしまうから不思議。なんというか、制限された世界で味わう楽しさっていうニッチなツボを、DETUNEさんは(DS-10もあえて含めて)的確に突いてくるなぁ、と(笑)。


で、今回は勉強がてら、新しく実装された発音機能を使って、単音シンセサイザーもどきのプログラムを作ってみました。DPC-100アプリを入れたマシンのSafariなどでこのページを開き、下記のプログラム全体をザザッとコピー、そしてDPC-100でPPASTEコマンドを使えば、内容をそのまま貼り付けられます。ちなみに、今編集中の内容はペースト時に全て上書きされてしまうのでご注意を!詳しくはDPC-100マニュアルページをご参照くださいね。

SET VERSION=1.2
SET LABEL=1
SET FORNEXT=0

  100 INT OCT=5
  110 INT DUT=8
  120 REAL PROGRESS=0.000000
  130 INT PARAM_A=1
  140 INT PARAM_S=7
  150 INT PARAM_D=4
  160 INT PARAM_R=5
  170 REAL VIRT_S
  180 INT SCH0KEY=99999,S0OLDKEY=99999
  190 INT SCH0VOL=100
  200 INT SOUNDVOL=12
  210 INT SCR_COUNT
  220 STRING SCR_MSG
  230 WHILE 1==1
  240 //PARAM_A
  250 IF KEYSTATE(0)==1 THEN INPUT "A.TIME(1-10):";PARAM_A
  260 IF PARAM_A<1||PARAM_A>10 THEN PARAM_A=1
  270 //PARAM_S
  280 IF KEYSTATE(1)==1 THEN INPUT "S.LEVEL(0-10):";PARAM_S
  290 IF PARAM_S<0||PARAM_S>10 THEN PARAM_S=7
  300 //PARAM_D
  310 IF KEYSTATE(2)==1 THEN INPUT "D.TIME(1-10):";PARAM_D
  320 IF PARAM_D<1||PARAM_D>10 THEN PARAM_D=4
  330 //PARAM_R
  340 IF KEYSTATE(3)==1 THEN INPUT "R.TIME(1-10):";PARAM_R
  350 IF PARAM_R<1||PARAM_R>10 THEN PARAM_R=5
  360 IF SCR_COUNT>0 THEN SCR_COUNT=SCR_COUNT-1
  370 REAL VOL_RATIO=100.000000
  380 //MINUS(27)
  390 IF KEYSTATE(27)==1 THEN PUSH_MINUS=1
  400 IF KEYSTATE(27)==0&&PUSH_MINUS==1&&DUT>1 THEN DUT=DUT-1
  410 IF KEYSTATE(27)==0&&PUSH_MINUS==1 THEN PRINT "DUTY=";DUT,
  420 IF KEYSTATE(27)==0&&PUSH_MINUS==1 THEN SCR_COUNT=500
  430 IF KEYSTATE(27)==0&&PUSH_MINUS==1 THEN PUSH_MINUS=0
  440 //EQUAL(24)
  450 IF KEYSTATE(24)==1 THEN PUSH_EQUAL=1
  460 IF KEYSTATE(24)==0&&PUSH_EQUAL==1&&DUT<8 THEN DUT=DUT+1
  470 IF KEYSTATE(24)==0&&PUSH_EQUAL==1 THEN PRINT "DUTY=";DUT,
  480 IF KEYSTATE(24)==0&&PUSH_EQUAL==1 THEN SCR_COUNT=500
  490 IF KEYSTATE(24)==0&&PUSH_EQUAL==1 THEN PUSH_EQUAL=0
  500 //LEFT(123)
  510 IF KEYSTATE(123)==1 THEN PUSH_LEFT=1
  520 IF KEYSTATE(123)==0&&PUSH_LEFT==1&&OCT>0 THEN OCT=OCT-1
  530 IF KEYSTATE(123)==0&&PUSH_LEFT==1 THEN PRINT "OCTAVE=";OCT,
  540 IF KEYSTATE(123)==0&&PUSH_LEFT==1 THEN SCR_COUNT=500
  550 IF KEYSTATE(123)==0&&PUSH_LEFT==1 THEN PUSH_LEFT=0
  560 //RIGHT(124)
  570 IF KEYSTATE(124)==1 THEN PUSH_RIGHT=1
  580 IF KEYSTATE(124)==0&&PUSH_RIGHT==1&&OCT<10 THEN OCT=OCT+1
  590 IF KEYSTATE(124)==0&&PUSH_RIGHT==1 THEN PRINT "OCTAVE=";OCT,
  600 IF KEYSTATE(124)==0&&PUSH_RIGHT==1 THEN SCR_COUNT=500
  610 IF KEYSTATE(124)==0&&PUSH_RIGHT==1 THEN PUSH_RIGHT=0
  620 //Q(12)
  630 IF KEYSTATE(12)==1&&SCH0KEY!=12&&S0OLDKEY!=12 THEN PROGRESS=0.000000
  640 IF KEYSTATE(12)==1&&SCH0KEY!=12&&S0OLDKEY!=12 THEN S0OLDKEY=SCH0KEY
  650 IF KEYSTATE(12)==1&&S0OLDKEY!=12 THEN SCHNOTE(0,OCT*12+0) SCHSTATE(0,1) SCH0KEY=12
  660 IF KEYSTATE(12)==0&&SCH0KEY==12 THEN SCH0KEY=99999
  670 IF KEYSTATE(12)==0&&S0OLDKEY==12 THEN S0OLDKEY=99999
  680 //2(19)
  690 IF KEYSTATE(19)==1&&SCH0KEY!=19&&S0OLDKEY!=19 THEN PROGRESS=0.000000
  700 IF KEYSTATE(19)==1&&SCH0KEY!=19&&S0OLDKEY!=19 THEN S0OLDKEY=SCH0KEY
  710 IF KEYSTATE(19)==1&&S0OLDKEY!=19 THEN SCHNOTE(0,OCT*12+1) SCHSTATE(0,1) SCH0KEY=19
  720 IF KEYSTATE(19)==0&&SCH0KEY==19 THEN SCH0KEY=99999
  730 IF KEYSTATE(19)==0&&S0OLDKEY==19 THEN S0OLDKEY=99999
  740 //W(13)
  750 IF KEYSTATE(13)==1&&SCH0KEY!=13&&S0OLDKEY!=13 THEN PROGRESS=0.000000
  760 IF KEYSTATE(13)==1&&SCH0KEY!=13&&S0OLDKEY!=13 THEN S0OLDKEY=SCH0KEY
  770 IF KEYSTATE(13)==1&&S0OLDKEY!=13 THEN SCHNOTE(0,OCT*12+2) SCHSTATE(0,1) SCH0KEY=13
  780 IF KEYSTATE(13)==0&&SCH0KEY==13 THEN SCH0KEY=99999
  790 IF KEYSTATE(13)==0&&S0OLDKEY==13 THEN S0OLDKEY=99999
  800 //3(20)
  810 IF KEYSTATE(20)==1&&SCH0KEY!=20&&S0OLDKEY!=20 THEN PROGRESS=0.000000
  820 IF KEYSTATE(20)==1&&SCH0KEY!=20&&S0OLDKEY!=20 THEN S0OLDKEY=SCH0KEY
  830 IF KEYSTATE(20)==1&&S0OLDKEY!=20 THEN SCHNOTE(0,OCT*12+3) SCHSTATE(0,1) SCH0KEY=20
  840 IF KEYSTATE(20)==0&&SCH0KEY==20 THEN SCH0KEY=99999
  850 IF KEYSTATE(20)==0&&S0OLDKEY==20 THEN S0OLDKEY=99999
  860 //E(14)
  870 IF KEYSTATE(14)==1&&SCH0KEY!=14&&S0OLDKEY!=14 THEN PROGRESS=0.000000
  880 IF KEYSTATE(14)==1&&SCH0KEY!=14&&S0OLDKEY!=14 THEN S0OLDKEY=SCH0KEY
  890 IF KEYSTATE(14)==1&&S0OLDKEY!=14 THEN SCHNOTE(0,OCT*12+4) SCHSTATE(0,1) SCH0KEY=14
  900 IF KEYSTATE(14)==0&&SCH0KEY==14 THEN SCH0KEY=99999
  910 IF KEYSTATE(14)==0&&S0OLDKEY==14 THEN S0OLDKEY=99999
  920 //R(15)
  930 IF KEYSTATE(15)==1&&SCH0KEY!=15&&S0OLDKEY!=15 THEN PROGRESS=0.000000
  940 IF KEYSTATE(15)==1&&SCH0KEY!=15&&S0OLDKEY!=15 THEN S0OLDKEY=SCH0KEY
  950 IF KEYSTATE(15)==1&&S0OLDKEY!=15 THEN SCHNOTE(0,OCT*12+5) SCHSTATE(0,1) SCH0KEY=15
  960 IF KEYSTATE(15)==0&&SCH0KEY==15 THEN SCH0KEY=99999
  970 IF KEYSTATE(15)==0&&S0OLDKEY==15 THEN S0OLDKEY=99999
  980 //5(23)
  990 IF KEYSTATE(23)==1&&SCH0KEY!=23&&S0OLDKEY!=23 THEN PROGRESS=0.000000
 1000 IF KEYSTATE(23)==1&&SCH0KEY!=23&&S0OLDKEY!=23 THEN S0OLDKEY=SCH0KEY
 1010 IF KEYSTATE(23)==1&&S0OLDKEY!=23 THEN SCHNOTE(0,OCT*12+6) SCHSTATE(0,1) SCH0KEY=23
 1020 IF KEYSTATE(23)==0&&SCH0KEY==23 THEN SCH0KEY=99999
 1030 IF KEYSTATE(23)==0&&S0OLDKEY==23 THEN S0OLDKEY=99999
 1040 //T(17)
 1050 IF KEYSTATE(17)==1&&SCH0KEY!=17&&S0OLDKEY!=17 THEN PROGRESS=0.000000
 1060 IF KEYSTATE(17)==1&&SCH0KEY!=17&&S0OLDKEY!=17 THEN S0OLDKEY=SCH0KEY
 1070 IF KEYSTATE(17)==1&&S0OLDKEY!=17 THEN SCHNOTE(0,OCT*12+7) SCHSTATE(0,1) SCH0KEY=17
 1080 IF KEYSTATE(17)==0&&SCH0KEY==17 THEN SCH0KEY=99999
 1090 IF KEYSTATE(17)==0&&S0OLDKEY==17 THEN S0OLDKEY=99999
 1100 //6(22)
 1110 IF KEYSTATE(22)==1&&OCT<10&&SCH0KEY!=22&&S0OLDKEY!=22 THEN PROGRESS=0.000000
 1120 IF KEYSTATE(22)==1&&OCT<10&&SCH0KEY!=22&&S0OLDKEY!=22 THEN S0OLDKEY=SCH0KEY
 1130 IF KEYSTATE(22)==1&&OCT<10&&S0OLDKEY!=22 THEN SCHNOTE(0,OCT*12+8) SCHSTATE(0,1) SCH0KEY=22
 1140 IF KEYSTATE(22)==0&&SCH0KEY==22 THEN SCH0KEY=99999
 1150 IF KEYSTATE(22)==0&&S0OLDKEY==22 THEN S0OLDKEY=99999
 1160 //Y(16)
 1170 IF KEYSTATE(16)==1&&OCT<10&&SCH0KEY!=16&&S0OLDKEY!=16 THEN PROGRESS=0.000000
 1180 IF KEYSTATE(16)==1&&OCT<10&&SCH0KEY!=16&&S0OLDKEY!=16 THEN S0OLDKEY=SCH0KEY
 1190 IF KEYSTATE(16)==1&&OCT<10&&S0OLDKEY!=16 THEN SCHNOTE(0,OCT*12+9) SCHSTATE(0,1) SCH0KEY=16
 1200 IF KEYSTATE(16)==0&&SCH0KEY==16 THEN SCH0KEY=99999
 1210 IF KEYSTATE(16)==0&&S0OLDKEY==16 THEN S0OLDKEY=99999
 1220 //7(26)
 1230 IF KEYSTATE(26)==1&&OCT<10&&SCH0KEY!=26&&S0OLDKEY!=26 THEN PROGRESS=0.000000
 1240 IF KEYSTATE(26)==1&&OCT<10&&SCH0KEY!=26&&S0OLDKEY!=26 THEN S0OLDKEY=SCH0KEY
 1250 IF KEYSTATE(26)==1&&OCT<10&&S0OLDKEY!=26 THEN SCHNOTE(0,OCT*12+10) SCHSTATE(0,1) SCH0KEY=26
 1260 IF KEYSTATE(26)==0&&SCH0KEY==26 THEN SCH0KEY=99999
 1270 IF KEYSTATE(26)==0&&S0OLDKEY==26 THEN S0OLDKEY=99999
 1280 //U(32)
 1290 IF KEYSTATE(32)==1&&OCT<10&&SCH0KEY!=32&&S0OLDKEY!=32 THEN PROGRESS=0.000000
 1300 IF KEYSTATE(32)==1&&OCT<10&&SCH0KEY!=32&&S0OLDKEY!=32 THEN S0OLDKEY=SCH0KEY
 1310 IF KEYSTATE(32)==1&&OCT<10&&S0OLDKEY!=32 THEN SCHNOTE(0,OCT*12+11) SCHSTATE(0,1) SCH0KEY=32
 1320 IF KEYSTATE(32)==0&&SCH0KEY==32 THEN SCH0KEY=99999
 1330 IF KEYSTATE(32)==0&&S0OLDKEY==32 THEN S0OLDKEY=99999
 1340 //I(34)
 1350 IF KEYSTATE(34)==1&&OCT<10&&SCH0KEY!=34&&S0OLDKEY!=34 THEN PROGRESS=0.000000
 1360 IF KEYSTATE(34)==1&&OCT<10&&SCH0KEY!=34&&S0OLDKEY!=34 THEN S0OLDKEY=SCH0KEY
 1370 IF KEYSTATE(34)==1&&OCT<10&&S0OLDKEY!=34 THEN SCHNOTE(0,OCT*12+12) SCHSTATE(0,1) SCH0KEY=34
 1380 IF KEYSTATE(34)==0&&SCH0KEY==34 THEN SCH0KEY=99999
 1390 IF KEYSTATE(34)==0&&S0OLDKEY==34 THEN S0OLDKEY=99999
 1400 SCHDUTY(0,DUT)
 1410 //UP(126)
 1420 IF KEYSTATE(126)==1 THEN PUSH_UP=1
 1430 IF KEYSTATE(126)==0&&PUSH_UP==1&&SOUNDVOL<15 THEN SOUNDVOL=SOUNDVOL+1
 1440 IF KEYSTATE(126)==0&&PUSH_UP==1 THEN PRINT "VOLUME=";SOUNDVOL,
 1450 IF KEYSTATE(126)==0&&PUSH_UP==1 THEN SCR_COUNT=500
 1460 IF KEYSTATE(126)==0&&PUSH_UP==1 THEN PUSH_UP=0
 1470 //DOWN(125)
 1480 IF KEYSTATE(125)==1 THEN PUSH_DOWN=1
 1490 IF KEYSTATE(125)==0&&PUSH_DOWN==1&&SOUNDVOL>0 THEN SOUNDVOL=SOUNDVOL-1
 1500 IF KEYSTATE(125)==0&&PUSH_DOWN==1 THEN PRINT "VOLUME=";SOUNDVOL,
 1510 IF KEYSTATE(125)==0&&PUSH_DOWN==1 THEN SCR_COUNT=500
 1520 IF KEYSTATE(125)==0&&PUSH_DOWN==1 THEN PUSH_DOWN=0
 1530 IF SCH0KEY!=99999&&PROGRESS<1000.000000 THEN PROGRESS=CLAMP(PROGRESS+(1000.000000/POW(PARAM_A,3)),0.000000,1000.000000)
 1540 IF SCH0KEY!=99999&&PROGRESS>=1000&&PROGRESS<2000 THEN PROGRESS=CLAMP(PROGRESS+(1000.000/POW(PARAM_D,3)),1000.00,2000.00)
 1550 IF SCH0KEY==99999&&PROGRESS>=2000.0&&PROGRESS<3000.0 THEN PROGRESS=CLAMP(PROGRESS+(1000.00/POW(PARAM_R,3)),2000.00,3000.00)
 1560 IF SCH0KEY==99999&&PROGRESS>=3000 THEN PROGRESS=0.000000
 1570 IF PROGRESS==0.000000 THEN SCHSTATE(0,0)
 1580 IF PROGRESS<=1000.000 THEN VOL_RATIO=VOL_RATIO*CLAMP(1.000000-POW(1.000000-ACCEL(3),2),0.000000,1.000000)*PROGRESS/1000.00
 1590 IF PROGRESS>1000&&PROGRESS<=2000 THEN VOL_RATIO=VOL_RATIO*CLAMP(1.00000-POW(1.00000-ACCEL(3),2),0.0000,1.0000)
 1600 IF PROGRESS>1000&&PROGRESS<=2000 THEN VOL_RATIO=VOL_RATIO*(1.000-(PROGRESS-1000.00)/1000.00*(10.0-PARAM_S)/10.0)
 1610 IF PROGRESS>=0&&PROGRESS<=2000&&SCH0KEY!=99999 THEN VIRT_S=PARAM_S
 1620 IF PROGRESS>0&&PROGRESS<2000&&SCH0KEY==99999 THEN VIRT_S=VOL_RATIO/10.0000
 1630 IF PROGRESS>0&&PROGRESS<2000&&SCH0KEY==99999 THEN PROGRESS=2000.00
 1640 IF PROGRESS>2000.000 THEN VOL_RATIO=VOL_RATIO*CLAMP(1.0000-POW(1.0000-ACCEL(3),2),0.0000,1.0000)
 1650 IF PROGRESS>2000.000 THEN VOL_RATIO=VOL_RATIO*(3000.00-PROGRESS)/1000.0*VIRT_S/10.000
 1660 IF SCR_COUNT==0 THEN PRINT "MDFY:ASDF&SHAKE!",
 1670 SCHVOLUME(0,CLAMP(SOUNDVOL*SCH0VOL*VOL_RATIO/10000,0,15))
 1680 WEND

「RUN」ボタンで実行開始すると、ウィンドウには「MDFY:ASDF&SHAKE!」だけシンプルに表示されます(まぁ16文字しか表示できないし・・)。実際の音は、DPC-100上にマッピングされたキーを押すことで発音されます。終了したいときは、「STOP」ボタンを押してください。

仕様は以下のとおり。

  • Q2W3ER5T6Y7UIキーを押すと、ドから次のドまでのキーに対応して音が出ます。(ピアノの鍵盤の白鍵・黒鍵の配置をイメージしてください。)
  • 左右の矢印キーを押すと、オクターブを上下させることができます。
  • 上下の矢印キーを押すと、ボリュームを上下させることができます。
  • マイナス(-)とイコール(=)キーを押すと、音質を変化させることができます。
  • ASDFキーを押すと、ボリュームのエンベロープについて、いわゆる「ADSR」をウィンドウに入力できるようになります(キーの並びの関係で、実際には「ASDR」の順で並べています)。指示された範囲内の数字を入力して、最後にRETURNキーを押してください。
    • Aキー:アタック・タイム(A)。音が完全に立ち上がるまでにかかる時間の長さ。範囲は1から10まで。
    • Sキー:サステイン・レベル(S)。音がいったん立ち上がった後、キーを押す間持続するときの音の大きさ。範囲は0から10まで。
    • Dキー:ディケイ・タイム(D)。音が立ち上がってから、上記のサスティン・レベルへ音の大きさが達するまでの時間の長さ。範囲は1から10まで。
    • Fキー:リリース・タイム(R)。キーを離してから、音が消えるまでの時間の長さ。範囲は1から10まで。
  • 本体を振ると、ビブラートをかけることができます。

ADSRの実装もこだわった部分ですが、最後のビブラート機能も外せないですね。ぜひ、iPhoneiPadをフリフリしながら演奏してください(笑)。自分でも弾いてみるわけですが、まじめな話、演奏時の表情付けにはこのビブラート、結構重宝します。モジュレーションホイールなど外部インターフェイスがなくても、割と直感的に音を変化させられるのって面白いなーと、プログラム作りの観点からも思いました。


一見しただけで分かるとおり、プログラム内は同じような記述箇所が何度も出てきます。ラベルをうまく使えば整理できたかもですが、挙動があやしい場面があったこともあり、今回は完全に書き下しです。発音状態とか管理するために変数も右往左往してるし、完全にスパゲッティですね・・。あまりいいプログラムではないですが、もしお役に立ちそうな部分があればぜひご参考に。

DSC-100自体は、現状でも3和音まで出せるので、ポリフォニックバージョンも作れるかもしれませんね。3音の管理はかなり厳しそうだったのであえて手を出しませんでしたが、挑戦されるかたはぜひ!(笑)

2013年02月11日のツイート

vQuantizer製作記 その2、すんなりな実装と紆余曲折な仕様決め

ボカロデータをパラメータごとクオンタイズするJob Plugin、「vQuantizer」製作記録その2です。

前回のその1で、主にぼかりす向けに「クオンタイズするジョブプラグインを作ろう!」と決めたわけですが・・これがすぐに、難題にぶつかりました。フツーにクオンタイズすると、かんたんにボカロが再生できなくなる(笑)。


DAWなどに搭載されているクオンタイズ機能。実行すると、ノートは決められた長さの単位(グリッド)に吸い付くように移動しますが、他のノートがどう動こうと関係はありません。基本的に、一番近いグリッドへ移動するので、ピアノで和音を弾いたような場合は、当然同じグリッドに複数のノートが揃うことになります。
しかし、これをそのままボカロに適用すると、どうなるか。すぐ近くのノート同士は同じグリッドに揃ってしまい、ボカロの大前提「ノートを2つ以上重ねない」に引っかかり、再生できなくなってしまうわけです。

同じグリッドを目指して、ノートが重なってしまう事態をどう避けるか、いろいろ検討しましたが・・現状では、「同じグリッドに向かってしまうノートが一組でもあった場合は、プラグインを停止する」という、かなり思い切った仕様にしています。その代わり、停止したときには、どの場所で問題が起きているかという情報と、修正の仕方を少し詳しくフォローするメッセージを出すようにしました。


また、仕様の決定が難しかったもうひとつのトピックは、移動後の「ノートの長さ」と「ノート間の間隔」の関係でした。クオンタイズすると、隣同士のノートがそれぞれ持つ始点と始点の距離は変化してしまうのが普通です。もしこの距離が短くなった場合、もし皆さんなら、手前のノートの長さを短くしますか?それとも、ノート間の間隔をつめて、ノートの長さ自体には手を加えないようにしますか?

これは多分、正解がない問題です。でも、なんらかの答えを出さないといけない(そうしないと2つのノートがキチンと並ばない)。自分が出した答えは、「ノート間の間隔」を極力尊重し、「ノートの長さ」のほうを柔軟に伸び縮みさせることでした。なぜかというと単純で、ボカロは2つのノート間の間隔を変化させると、ある値を境に2つがくっついて発音されるか、独立して発音されるかが切り替わるので、これを極力防ぎたかったからです。結果的にノートの長さは伸び縮みしやすくなってしまいますが、2つのノートがくっつくか離れるかというドラスティックな変化に比べればまだいいだろう・・という判断ですね。


こんな感じで、上記の2点以外にも、制作上決めなければいけないルール(仕様)は意外に多くあり、「クオンタイズする」と一言で言いつつも、実際にはある意味でかなり「俺仕様(OIE仕様)」となっています。決めなければ完成しないので決めていったわけですが、一応その中でも、「(自分以外の)通常ボカロを利用するシーンではどんな動きが一番フィットするか」を念頭に置くようにしていたので、あまりに現場・ニーズとかけ離れた挙動はそれほどないと思っています。・・ないといいなぁ・・。もし挙動で気になる点がありましたらぜひご連絡いただき、今後の参考にさせていただければ!


実装面ですが、実は今回はそれほど苦労していません。時間の流れを変化させる仕組み自体は、2つ前に作った「Swing & Shuffle」(id:OIE:20120210)で実装したし、プラグイン適用前・適用後のノート位置に応じてパラメータを変化させる仕組みも、前の「Humanizer」(id:OIE:20120605)で作ってあったし。逆に言うと、今回はほんとに仕様決めに苦労したプラグインでした。


ぼかりすで実際にデータを作り、「vQuantizer」を使ってみましたが、意外にしっかり機能してくれて安心しました(笑)。さすがに「感度」を 100 で適用すると、発音が不自然になったりするので、およそ 35 あたりを目安にして何度か試してもらえると使いやすいかと思います。


ぼかりすで出力されたデータは、通常の調声(調教)ではありえないほどの密度と、パラメータ間の緊密な関連性を持っています。そのため、現状のVOCALOID3 Editorでは対応しにくい部分があるのは、ある意味当たり前なのかな、というのが個人的な感想です。ベストなのは、ノートにしろパラメータにしろ、区間選択したらその枠内で自由にビヨビヨ拡大縮小できるようなGUIの実装な気がしますが、通常の調声ではそこまであまり必要ないという点と、上記の通りいろいろ「俺仕様」を決めないといけない点で、ぼかりすデータをより自由に動かせるようなVOCALOID3 Editor側のバージョンアップは難しいのかもしれません。その点で言えば、UG Job Pluginは機能が限定的ながらもピンポイントで目的達成の道具を提供できるので、このあたりにもUG Job Pluginの存在位置というか、面白さが見出せそうだな、と思った次第でした。


というわけで、パラメータ変更系から始まり、目的達成型(?)のプラグインをここ何回かで出してきたわけですが、次のアイディアも一応準備しています。ちょっと方向性を変えたプラグイン、また機会を見つけてこちらでも情報を出していきますね。(いつできるか、そもそも完成するかはまだ不明ですが・・。)

2012年11月12日のツイート

vQuantizer製作記 その1、製作決定まで

ずいぶんブログでの紹介が遅くなってしまいましたが、VOCALOID3 Editor用の新しいUG Job Plugin「vQuantizer」を先日リリースしました!平たく言うと、ぼかりすで出力したデータなどDYNやPITがたっぷり詰まったソングのノートを、パラメータごとクオンタイズするこのプラグインボーカロイドストアさん遊楽団サイトで公開中ですので、興味ありましたらぜひお試しを・・!
D


実際の利用感や操作方法は、動画とプラグイン付属のりーどみーに任せるとして、このブログでは、作った背景や動機みたいなのを補足しようと思います。

そもそもこのプラグイン、10月まで影も形もありませんでした。ところが、9月中旬にぼかりすの発売日が10月19日とアナウンスされ、自分でも今までの情報を集めていくうち、「これは何か作りたいな」、と・・。
もともとWAVのような形で表されていた人の歌のデータが、ぼかりすによってVSQX(VOCALOID3のファイル形式)に変換される。これによって何がもたらされるのか?画像データで例えるなら恐らく、ラスター形式からベクター形式への変換のようなことが行えるわけで。最終的な答えは出せないながらも、「ボーカロイドの表現の選択肢が拡張される」という方向だけは確かに感じました。


さて、拡がった表現の幅は、実際の音楽で体現するとして・・UG Job Pluginでは何が出来るか?以前作った「Swing & Shuffle」や「Humanizer」と違い、手元にはまだきっかけや手がかりもない状態でした。なにせ、何をもたらすか分からないぼかりすそのものさえ手元になかったので(ベータ版は残念ながら入手できていませんでした)。ということでまずは、ぼかりすを操作してるとこを妄想しつつ(笑)、公開される情報も集めながら、なにか困りそうなところや改善できそうなところを探してみることにしました。・・かっこよく言っていますが、要はある意味「あら捜し」。ちょっと申し訳ないとは思いつつも、かゆいところに手を届かせるのもUG Job Pluginのひとつのあり方だと考えているので、ここは踏みとどまらずに妄想を爆発させます(笑)。


で。結論の前に、今回出てきたほかの案としては、「ビブラート区間を、PITから(従来の標準的な方法である)ノートのビブラート情報へ変換するプラグイン」がありました。ビブラートの長さや種類をあとから変更できれば、かなり自由度は上がるなーとは思ったのですが・・技術的ハードルが高めのため、現在はペンディング中です。まず、現状の仕様ではビブラートの詳細情報(ノートのビブラート部分をダブルクリックすると出てくる、ビブラートの深さのグラフ)をプラグイン側から操作できないため、変換の過程でビブラート情報を大きく失ってしまいます。また、ビブラートによってPITの揺らぎは置き換えられるかもしれませんが、関連すると思われるDYNの揺らぎはビブラートで表現できないため、変換後のビブラートを操作するとDYNと不一致を起こすおそれがあります(変換の際にDYNの揺らぎを抑えてしまうのもひとつの方策ですが、これはこれでいろいろ問題が)。ということで、今のところは開発を予定してません。(将来のプラグイン開発環境の変化によっては復活はありえますが。)
ビブラート部分の検出技術は、産総研さんの資料でぼかりすに含まれているので、いつか本家で実装されるんじゃないかな、とも期待してます。そもそもどう検出するかは、自分のほうではノーアイディアに近いので、その意味でもこのプラグイン、自分の中のハードルはかなり高い・・。


そんなネタをいくつか考え、最終的に選んだのが、今回のvQuantizerのコンセプトである、「パラメータごとノートをクオンタイズさせる」プラグイン。実は、製品版のぼかりすではこの機能があるんじゃないかと少しビクビクしていたのですが(笑)、スクリーンショットなど事前に集めていた情報からの想像通り、クオンタイズのような機能はありませんでした。
なぜそんな心配をしたかというと、この機能、ぼかりすのようにDYNやPITを詳細に埋め込む仕組みにはどうしても必要になると考えられたからです。



↑vQuantizer起動時のスクリーンショット。入力項目はグリッドの大きさと感度(適用する度合い)だけとシンプルです


そもそも、VOCALOID3 Editorにはクオンタイズという機能はありません(同名の機能は、ノート入力時の補助機能であって、入力済みのノートやパラメータを調節する機能ではありません)。Editor単体としてはノート入力はマウス経由に限られており、そのノート入力後にDYNなどパラメータを入力・・といった標準の流れにはクオンタイズはあまり必要がないため、現在は搭載されていないのだと思っています(実は実装上、DAW等に比べて非常に面倒な点もあることが分かったので、それも原因かも。次回詳述)。

しかし、人の音声をそのまま変換するという特性上、マウス入力とは違って最初から意図通りの場所で発音をしているわけではないし、リズムも揺らぎます。「味」として許容する場合は別として、どうしてもその揺らぎを修正した場合に、どうすればいいか。・・VOCALOID3 Editorでは対応が難しいわけですね。ノート自体はラクに伸び縮みできますが、そこにはパラメータはついてきません。ぼかりすで出力されたデータは、ノート情報・DYN・PITが完全に一体となって初めて完成されるものなので、ノートだけを動かせば、その完成形は容易に崩壊してしまいます。ではDYNとPITもノートに併せて動かせばいいじゃん、という話になりますが、ノートの伸び縮みは横方向(時間軸方向)の「移動」ではなく「拡大・縮小」です。これを実現する機能もないため、最終的には、手書きでパラメータのラインを書き直すという苦行が待っています・・。


これは、いかんですよ。こんなヘンなところで、大げさに言えば、クリエイティビティを阻害されるのは、もったいなさすぎる。ぼかりすの目的は「元の音声を再現する(まねる)」ことにあるのだから、リズムの揺れについては、本来歌唱の時点か、あるいは録音後の編集でカバーすべきだとも思います。でも、ボカロを扱う人が、(自分も含め)歌唱経験が豊富なわけではないし、録音した音声の編集も、誰もが得意としているわけでもない。もしくは、歌唱力がある方に歌ってもらうか・・。なんにせよ、リズムを修正したいという、割と素朴な要求に対して、実現するためには相応の苦労をしなければならず、これってぼかりす的にもあまりいい環境じゃなくない?と、考えにいたり。他の案とも検討した結果、ようやくこの「クオンタイズする」ためのプラグインを作ることに決めたのでした。このとき、すでに9月末。


ずいぶん長くなってしまいました・・。いったん区切って、実際の作りはじめから公開までの顛末は、またの機会にまとめます!

2012年11月04日のツイート

Humanizer公開!その中身。

ちょっとご報告遅れましたが、Vocaloid3 Editor用Job Plugin「Humanizer」を5月31日に、遊楽団サイト上でリリースしました。先行してVOCALOID STOREさんにも公開申請を行っていましたが、こちらは少し遅れて6月上旬から中旬に公開していただけそうです。

D

・・で、このHumanizer。概要は上の紹介動画のとおりなんですが、動画やプラグイン紹介でよく使用している「揺らぎ」という処理、具体的にもう少し何をやっているかを書いておこうと思います。「実は、ピッチと発音タイミングをランダムにずらしてるだけじゃないんだよ」というお話(笑)。


エディタ上でノートを並べれば、基本的にボカロはその音階・タイミングのとおり正しく歌ってくれますが、人間が実際に歌う場合はそうではないですよね。長い音を「ラーーー」と歌う場合を想定すると、まずはじめに「ラー」と音を出して、正しいピッチからずれていたら途中で補正をして残りの「ーー」を歌います。さらに、もし音がもっと長く「ラーーーーーーーー」だったら、途中から音が震え始めたりします。このように、ベタに打ち込んだデータをボカロに歌わせる場合に比べて、人間の場合はもう少し複雑なピッチ曲線を描くわけです。(どっちがいいか悪いかではなく、性質的にそういうもんですよねっていう話。)また、ピッチだけではなく、発音するタイミングもやめるタイミングも、人間の場合は必ずしも毎回ピッタリ同じではないはずです。


今回のHumanizerは、そういう人間の歌い方を内部的にシミュレートする・・というのが最も基本的なコンセプトになっています。これを実現することで、多人数のボカロがコーラスしたときの不自然さを軽減させられるし、メインのボーカルにかければ人間らしいニュアンスが得られる、と。さらに、上記のピッチ曲線は歌のジャンルやその人のスキルレベルによっても大きく変わるため、「歌唱タイプ」と「スキルレベル」の組み合わせで、カンタンに、直感的にバリエーションを指定できるようにしました。


ほかにも、このプラグインで目指したいこと・野望(笑)や動画のお話などいくつかネタがあるので、順次このブログで紹介していきたいと思います!とりあえず今回はこのへんで。