つい先日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の実装もこだわった部分ですが、最後のビブラート機能も外せないですね。ぜひ、iPhoneやiPadをフリフリしながら演奏してください(笑)。自分でも弾いてみるわけですが、まじめな話、演奏時の表情付けにはこのビブラート、結構重宝します。モジュレーションホイールなど外部インターフェイスがなくても、割と直感的に音を変化させられるのって面白いなーと、プログラム作りの観点からも思いました。
一見しただけで分かるとおり、プログラム内は同じような記述箇所が何度も出てきます。ラベルをうまく使えば整理できたかもですが、挙動があやしい場面があったこともあり、今回は完全に書き下しです。発音状態とか管理するために変数も右往左往してるし、完全にスパゲッティですね・・。あまりいいプログラムではないですが、もしお役に立ちそうな部分があればぜひご参考に。
DSC-100自体は、現状でも3和音まで出せるので、ポリフォニックバージョンも作れるかもしれませんね。3音の管理はかなり厳しそうだったのであえて手を出しませんでしたが、挑戦されるかたはぜひ!(笑)