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音の管理はかなり厳しそうだったのであえて手を出しませんでしたが、挑戦されるかたはぜひ!(笑)