900円で作るOverflow folding VCO-モジュラーシンセ自作
見出し画像

900円で作るOverflow folding VCO-モジュラーシンセ自作

HAGIWO/ハギヲ

Seeeduino xiaoを用いて、モジュラーシンセサイザー のデジタルVCOを自作したので、その備忘録。

背景

自作モジュラーシンセの37作品目。
プログラミングを初めて1年も経過して、そろそろシンセサイザー用のライブラリに頼らず、自分でデジタルVCOを作ってみたいと思った。
自分にとっては、技術的難易度が高い試みである。失敗するかもしれない、満足するものが作れないかもしれない。しかし、挑戦しないと出来るか出来ないかも分からないので、まずは挑戦してみることにした。

今回用いるマイコンはseeeduino xiaoだ。samd21を使用した48Mhzのプロセッサで、arduino IDEで開発ができる。音声処理をするには、やや力不足なマイコンだが、DACを内蔵しており使い勝手が良い。
将来、rasberry pi pico やteensyで本格的なVCOを作るために、まずは技術検証用で使い慣れたseeeduino xiaoを用いる。橋頭堡としてのプロジェクトである。

結果から言うと、満足したものは作れなかったが、最低限動作するVCOを作ることが出来た。音はLo-Fiで、稀にノイズも発生する欠点がある。
あくまで技術検証用であり。全員におすすめできるモジュールではないことを、始めに記しておく。

画像1


制作物のスペック

ユーロラック規格 3U 6HPサイズ
電源:25mA 以下( at 5V ) 

knob1:周波数
knob2:Overflow folding rate
knob3:波形選択(全8波形)
V/OCT:周波数 (レンジ0-5V)
CV1:Overflow folding rate (レンジ0-5V)
CV2:波形選択(レンジ0-5V)
OUT:音声出力(5Vp-p)

出力波形
鋸歯状波、正弦波などの基本波形を8種類内蔵する。Wavetable形式で保存してあるため、任意の波形に変更が可能。

Overflow folding
出力波形の変調方法。Wavetableを増幅しオーバーフローさせることで波形を大きく変調し、倍音を加える。ハードSYNCやディストーションのような音になる。

倍音を大きく変えるため、位相シフトSYNCやサブオシレータ等の実験をしたが、音の変化が小さく満足できなかった。

しかし試行錯誤する中で、偶然プログラムのミスをしてwavetableの値がオーバーフローしたところ、面白い音が出たので、そのまま採用した。

周波数
70Hz~2.4kHz。オクターブに換算すると6オクターブ。
ショパンは弾けないが、ベートーベンなら弾けるオクターブレンジだ。
2.4kHz以上の音を出すと、マイコンの動作が不安定になるため、リミットを設けている。

なお、パネル上にはプッシュスイッチが設置してあるが、今回は使用していない。将来、音色を追加する際に使用する予定だ。

製作費

総額約900円
---------------------------------
Seeeduino xiao 550円
フロントパネル 100円
オペアンプ 45円
他(汎用部品は下記リンク先参照)

妥協、欠点

マイコンの性能、及び私の技術力不足で、いくつかの妥協と欠点がある。
1.音域
6octはモジュラーシンセサイザーのVCOとしては音域が狭い。
SAMD21の48MHzクロックの割り込み処理の限界が原因である。
wavetableのサンプル数を128から64に減らせば、計算上は7octまで出せるが、サンプル数を減らすと不要な高調波が発生してしまう欠点がある。

2.まれに発生するノイズ
出力周波数を頻繁に変更すると、稀にノイズが発生する。原因は明確になっていないが、おそらく割り込み頻度の変更処理の際にノイズが発生している様子だ。
対策として、出力周波数の変化を少なくしている。。
具体的には
・ADCの読み取り頻度を5msec毎にする。
・ADCの前回値と今回値が近い場合は、周波数を変更させない。
・V/oct回路のバッファ回路により外来ノイズの低減する。

シーケンサや鍵盤を使って演奏する場合、周波数の変更頻度は少ないため、ノイズが気になることは無い。LFOやエンベロープジェネレータでV/octを制御する場合、周波数の変更頻度が多いため、ノイズが気になることがある。

3.V/oct分解能
分解能は10bitにしている。理由は2のノイズ対策にあるように、頻繁な周波数変更を避けるため。
10bitでも楽器としては十分な分解能だと私は思っている。理由は過去に作成したmozzi libraryベースのVCO記事に記載がある。1LSBのずれは、6centのずれなので許容できるのではという、私の持論だ。

50万円するBuchlaや、ビンテージアナログシンセサイザーと比べたら、ピッチは正確に出力できるので、よしとする。

4.低周波数での高調波ノイズ
wavetableのサンプル数が128、分解能は256なので波形を拡大すると荒い。ローパスフィルタがあるので、高い周波数では高調波ノイズは気にならないが、200Hz以下では高調波ノイズが気になる。特にサイン波や三角波のように高調波が少ない波形ではノイズが顕著に表れる。

以上の通り、多くの欠点があるが、値段相応と割り切る。今回のプロジェクトは技術検証が目的なので、出来ないことが判ったのは、むしろ歓迎すべきだ。エイリアシング等、他にも考えなくてはいけない事もあるだろうが、完璧よりも完成を優先させる。

画像2


プログラミング

1.wavetable
音声出力の場合、一定間隔で割り込み処理をする。この割り込み処理では、簡単な掛け算、足し算、tableの値の参照は出来る。しかし、sinや複雑な条件分岐の処理はできない。

よって、あらかじめ計算結果をwavetableとして保持して、割り込みではwavetableの値を参照するようにしている。

  for (int i = 0; i < 128; i++) {  //saw
   wavetable[0][i] = i * 2;
 }

 for (int i = 0; i < 128; i++) {  //sin
   wavetable[1][i] = (sin(2 * M_PI * i  / 128) + 1) * 127;
 }

2.割り込み処理
割り込み処理のプログラムはSAMD21_Audio_Playerをベースにしている。

Seeeduino xiaoにもタイマー割り込みのライブラリサンプルが用意されているが、意図した通りに動かなかった。

3.音声合成をソフトウェアで実装する方法
webの情報ではなく、書籍から知識を得た。

「青木 直史 著 ■ サウンドプログラミング入門 - 音響合成の基本とC言語による実装 -」からは、シンセサイズを数式で表す方法を学んだ。

「Interface 2021年8月号 ラズパイのマイコンPico攻略本」から、マイコンで音声合成をする方法を学んだ。

ハードウェア

画像3

V/oct入力回路(A7)
220kohmと330kohmで5Vから3.3Vにレベルシフトしている。抵抗の誤差を補正するための半固定抵抗を設置してもよい。一応、ソフトウェア上で抵抗誤差の補正をすることも出来るので、半固定抵抗は必須ではない。
330kohmという高いインピーダンスを設定してるのは、分圧によるV/octの低下の影響を小さくするためだ。

オペアンプはノイズ対策。前述の通り、周波数の変更が多いとノイズが発生するリスクが大きくなるので、周波数を安定させる必要があった。
オペアンプによるバッファを設けることで、V/octのノイズは大きく低減することができた。10kohmの抵抗は溜まった電荷を抜くためのもの。

出力回路(DAC)
2-poleのローパスフィルタは高調波ノイズ対策用。カットオフ周波数は16kHz。1-poleと2-poleでは結構違いがある。

非反転増幅回路で3.3Vから5Vへのレベルシフトをしている。正確には5Vでなく4.7Vくらいか。5Vを狙うとクリップする可能性があるので、増幅率に若干の余裕を持たせている。

ソースコード

前述の通り、SAMD21_Audio_Playerのソースコードを含んでいる。ライセンスについては下のリンク先を参照のこと。

粗末だが公開する。悪い点があれば教えてもらえると勉強になる。

const uint8_t *sampleName;
uint32_t sampleSize = 128;//wavetable sample size
int osc_freq, voct, old_freq,adc,param1, param2;
int f0 = 70;//base osc frequency , You can change it to any value.
int wavetable[8][128];//8kind waves , 128 samples
long timer;
bool old_sw, sw;//Switch functions will be installed in the future.
byte table_number;
float calb = 0.94;//calibration for reduce resistance error

const static float voctpow[1230] = {//Covers 6(=1230) octaves. If it is 1230 or more, the operation becomes unstable.
 0,  0.0048820.0097650.0146480.0195310.0244140.0292960.0341790.0390620.0439450.0488280.05371,  0.0585930.0634760.0683590.0732420.0781250.0830070.08789,  0.0927730.0976560.1025390.1074210.1123040.1171870.12207,  0.1269530.1318350.1367180.1416010.1464840.1513670.15625,  0.1611320.1660150.1708980.1757810.1806640.1855460.1904290.1953120.2001950.2050780.20996,  0.2148430.2197260.2246090.2294920.2343750.2392570.24414,  0.2490230.2539060.2587890.2636710.2685540.2734370.27832,  0.2832030.2880850.2929680.2978510.3027340.3076170.31250.3173820.3222650.3271480.3320310.3369140.3417960.3466790.3515620.3564450.3613280.36621,  0.3710930.3759760.3808590.3857420.3906250.3955070.40039,  0.4052730.4101560.4150390.4199210.4248040.4296870.43457,  0.4394530.4443350.4492180.4541010.4589840.4638670.46875,  0.4736320.4785150.4833980.4882810.4931640.4980460.5029290.5078120.5126950.5175780.52246,  0.5273430.5322260.5371090.5419920.5468750.5517570.55664,  0.5615230.5664060.5712890.5761710.5810540.5859370.59082,  0.5957030.6005850.6054680.6103510.6152340.6201170.625,  0.6298820.6347650.6396480.6445310.6494140.6542960.6591790.6640620.6689450.6738280.67871,  0.6835930.6884760.6933590.6982420.7031250.7080070.71289,  0.7177730.7226560.7275390.7324210.7373040.7421870.74707,  0.7519530.7568350.7617180.7666010.7714840.7763670.78125,  0.7861320.7910150.7958980.8007810.8056640.8105460.8154290.8203120.8251950.8300780.83496,  0.8398430.8447260.8496090.8544920.8593750.8642570.86914,  0.8740230.8789060.8837890.8886710.8935540.8984370.90332,  0.9082030.9130850.9179680.9228510.9277340.9326170.93750.9423820.9472650.9521480.9570310.9619140.9667960.9716790.9765620.9814450.9863280.99121,  0.9960931.0009761.0058591.0107421.0156251.0205071.02539,  1.0302731.0351561.0400391.0449211.0498041.0546871.05957,  1.0644531.0693351.0742181.0791011.0839841.0888671.09375,  1.0986321.1035151.1083981.1132811.1181641.1230461.1279291.1328121.1376951.1425781.14746,  1.1523431.1572261.1621091.1669921.1718751.1767571.18164,  1.1865231.1914061.1962891.2011711.2060541.2109371.21582,  1.2207031.2255851.2304681.2353511.2402341.2451171.251.2548821.2597651.2646481.2695311.2744141.2792961.2841791.2890621.2939451.2988281.30371,  1.3085931.3134761.3183591.3232421.3281251.3330071.33789,  1.3427731.3476561.3525391.3574211.3623041.3671871.37207,  1.3769531.3818351.3867181.3916011.3964841.4013671.40625,  1.4111321.4160151.4208981.4257811.4306641.4355461.4404291.4453121.4501951.4550781.45996,  1.4648431.4697261.4746091.4794921.4843751.4892571.49414,  1.4990231.5039061.5087891.5136711.5185541.5234371.52832,  1.5332031.5380851.5429681.5478511.5527341.5576171.56251.5673821.5722651.5771481.5820311.5869141.5917961.5966791.6015621.6064451.6113281.61621,  1.6210931.6259761.6308591.6357421.6406251.6455071.65039,  1.6552731.6601561.6650391.6699211.6748041.6796871.68457,  1.6894531.6943351.6992181.7041011.7089841.7138671.71875,  1.7236321.7285151.7333981.7382811.7431641.7480461.7529291.7578121.7626951.7675781.77246,  1.7773431.7822261.7871091.7919921.7968751.8017571.80664,  1.8115231.8164061.8212891.8261711.8310541.8359371.84082,  1.8457031.8505851.8554681.8603511.8652341.8701171.875,  1.8798821.8847651.8896481.8945311.8994141.9042961.9091791.9140621.9189451.9238281.92871,  1.9335931.9384761.9433591.9482421.9531251.9580071.96289,  1.9677731.9726561.9775391.9824211.9873041.9921871.99707,  2.0019532.0068352.0117182.0166012.0214842.0263672.03125,  2.0361322.0410152.0458982.0507812.0556642.0605462.0654292.0703122.0751952.0800782.08496,  2.0898432.0947262.0996092.1044922.1093752.1142572.11914,  2.1240232.1289062.1337892.1386712.1435542.1484372.15332,  2.1582032.1630852.1679682.1728512.1777342.1826172.18752.1923822.1972652.2021482.2070312.2119142.2167962.2216792.2265622.2314452.2363282.24121,  2.2460932.2509762.2558592.2607422.2656252.2705072.27539,  2.2802732.2851562.2900392.2949212.2998042.3046872.30957,  2.3144532.3193352.3242182.3291012.3339842.3388672.34375,  2.3486322.3535152.3583982.3632812.3681642.3730462.3779292.3828122.3876952.3925782.39746,  2.4023432.4072262.4121092.4169922.4218752.4267572.43164,  2.4365232.4414062.4462892.4511712.4560542.4609372.46582,  2.4707032.4755852.4804682.4853512.4902342.4951172.5,  2.5048822.5097652.5146482.5195312.5244142.5292962.5341792.5390622.5439452.5488282.55371,  2.5585932.5634762.5683592.5732422.5781252.5830072.58789,  2.5927732.5976562.6025392.6074212.6123042.6171872.62207,  2.6269532.6318352.6367182.6416012.6464842.6513672.65625,  2.6611322.6660152.6708982.6757812.6806642.6855462.6904292.6953122.7001952.7050782.70996,  2.7148432.7197262.7246092.7294922.7343752.7392572.74414,  2.7490232.7539062.7587892.7636712.7685542.7734372.77832,  2.7832032.7880852.7929682.7978512.8027342.8076172.81252.8173822.8222652.8271482.8320312.8369142.8417962.8466792.8515622.8564452.8613282.86621,  2.8710932.8759762.8808592.8857422.8906252.8955072.90039,  2.9052732.9101562.9150392.9199212.9248042.9296872.93457,  2.9394532.9443352.9492182.9541012.9589842.9638672.96875,  2.9736322.9785152.9833982.9882812.9931642.9980463.0029293.0078123.0126953.0175783.02246,  3.0273433.0322263.0371093.0419923.0468753.0517573.05664,  3.0615233.0664063.0712893.0761713.0810543.0859373.09082,  3.0957033.1005853.1054683.1103513.1152343.1201173.125,  3.1298823.1347653.1396483.1445313.1494143.1542963.1591793.1640623.1689453.1738283.17871,  3.1835933.1884763.1933593.1982423.2031253.2080073.21289,  3.2177733.2226563.2275393.2324213.2373043.2421873.24707,  3.2519533.2568353.2617183.2666013.2714843.2763673.28125,  3.2861323.2910153.2958983.3007813.3056643.3105463.3154293.3203123.3251953.3300783.33496,  3.3398433.3447263.3496093.3544923.3593753.3642573.36914,  3.3740233.3789063.3837893.3886713.3935543.3984373.40332,  3.4082033.4130853.4179683.4228513.4277343.4326173.43753.4423823.4472653.4521483.4570313.4619143.4667963.4716793.4765623.4814453.4863283.49121,  3.4960933.5009763.5058593.5107423.5156253.5205073.52539,  3.5302733.5351563.5400393.5449213.5498043.5546873.55957,  3.5644533.5693353.5742183.5791013.5839843.5888673.59375,  3.5986323.6035153.6083983.6132813.6181643.6230463.6279293.6328123.6376953.6425783.64746,  3.6523433.6572263.6621093.6669923.6718753.6767573.68164,  3.6865233.6914063.6962893.7011713.7060543.7109373.71582,  3.7207033.7255853.7304683.7353513.7402343.7451173.753.7548823.7597653.7646483.7695313.7744143.7792963.7841793.7890623.7939453.7988283.80371,  3.8085933.8134763.8183593.8232423.8281253.8330073.83789,  3.8427733.8476563.8525393.8574213.8623043.8671873.87207,  3.8769533.8818353.8867183.8916013.8964843.9013673.90625,  3.9111323.9160153.9208983.9257813.9306643.9355463.9404293.9453123.9501953.9550783.95996,  3.9648433.9697263.9746093.9794923.9843753.9892573.99414,  3.9990234.0039064.0087894.0136714.0185544.0234374.02832,  4.0332034.0380854.0429684.0478514.0527344.0576174.06254.0673824.0722654.0771484.0820314.0869144.0917964.0966794.1015624.1064454.1113284.11621,  4.1210934.1259764.1308594.1357424.1406254.1455074.15039,  4.1552734.1601564.1650394.1699214.1748044.1796874.18457,  4.1894534.1943354.1992184.2041014.2089844.2138674.21875,  4.2236324.2285154.2333984.2382814.2431644.2480464.2529294.2578124.2626954.2675784.27246,  4.2773434.2822264.2871094.2919924.2968754.3017574.30664,  4.3115234.3164064.3212894.3261714.3310544.3359374.34082,  4.3457034.3505854.3554684.3603514.3652344.3701174.375,  4.3798824.3847654.3896484.3945314.3994144.4042964.4091794.4140624.4189454.4238284.42871,  4.4335934.4384764.4433594.4482424.4531254.4580074.46289,  4.4677734.4726564.4775394.4824214.4873044.4921874.49707,  4.5019534.5068354.5117184.5166014.5214844.5263674.53125,  4.5361324.5410154.5458984.5507814.5556644.5605464.5654294.5703124.5751954.5800784.58496,  4.5898434.5947264.5996094.6044924.6093754.6142574.61914,  4.6240234.6289064.6337894.6386714.6435544.6484374.65332,  4.6582034.6630854.6679684.6728514.6777344.6826174.68754.6923824.6972654.7021484.7070314.7119144.7167964.7216794.7265624.7314454.7363284.74121,  4.7460934.7509764.7558594.7607424.7656254.7705074.77539,  4.7802734.7851564.7900394.7949214.7998044.8046874.80957,  4.8144534.8193354.8242184.8291014.8339844.8388674.84375,  4.8486324.8535154.8583984.8632814.8681644.8730464.8779294.8828124.8876954.8925784.89746,  4.9023434.9072264.9121094.9169924.9218754.9267574.93164,  4.9365234.9414064.9462894.9511714.9560544.9609374.96582,  4.9707034.9755854.9804684.9853514.9902344.9951175,  5.0048825.0097655.0146485.0195315.0244145.0292965.0341795.0390625.0439455.0488285.05371,  5.0585935.0634765.0683595.0732425.0781255.0830075.08789,  5.0927735.0976565.1025395.1074215.1123045.1171875.12207,  5.1269535.1318355.1367185.1416015.1464845.1513675.15625,  5.1611325.1660155.1708985.1757815.1806645.1855465.1904295.1953125.2001955.2050785.20996,  5.2148435.2197265.2246095.2294925.2343755.2392575.24414,  5.2490235.2539065.2587895.2636715.2685545.2734375.27832,  5.2832035.2880855.2929685.2978515.3027345.3076175.31255.3173825.3222655.3271485.3320315.3369145.3417965.3466795.3515625.3564455.3613285.36621,  5.3710935.3759765.3808595.3857425.3906255.3955075.40039,  5.4052735.4101565.4150395.4199215.4248045.4296875.43457,  5.4394535.4443355.4492185.4541015.4589845.4638675.46875,  5.4736325.4785155.4833985.4882815.4931645.4980465.5029295.5078125.5126955.5175785.52246,  5.5273435.5322265.5371095.5419925.5468755.5517575.55664,  5.5615235.5664065.5712895.5761715.5810545.5859375.59082,  5.5957035.6005855.6054685.6103515.6152345.6201175.625,  5.6298825.6347655.6396485.6445315.6494145.6542965.6591795.6640625.6689455.6738285.67871,  5.6835935.6884765.6933595.6982425.7031255.7080075.71289,  5.7177735.7226565.7275395.7324215.7373045.7421875.74707,  5.7519535.7568355.7617185.7666015.7714845.7763675.78125,  5.7861325.7910155.7958985.8007815.8056645.8105465.8154295.8203125.8251955.8300785.83496,  5.8398435.8447265.8496095.8544925.8593755.8642575.86914,  5.8740235.8789065.8837895.8886715.8935545.8984375.90332,  5.9082035.9130855.9179685.9228515.9277345.9326175.93755.9423825.9472655.9521485.9570315.9619145.9667965.9716795.9765625.9814455.9863285.99121,  5.9960936.000976,
};
float freq_table[2048];

void setup() {
 analogReadResolution(12);
 pinMode(2, INPUT);//freq pot
 pinMode(4, INPUT);//param1 fold pot
 pinMode(10, INPUT);//param2 wave pot
 pinMode(3, INPUT);//param1 fold CV
 pinMode(6, INPUT);//param2 wave CV
 pinMode(7, INPUT);//freq V/oct
 pinMode(9, INPUT_PULLUP);//mode change ;reserve
 DACSetup(36000);
 timer = millis();
 table_set(3);

 // V/oct calculation
 for (int i = 0; i < 1230; i++) {//Covers 6(=1230) octaves. If it is 1230 or more, the operation becomes unstable.
   freq_table[i] = f0 * pow(2, (voctpow[i]));
 }
 for (int i = 0; i < 2048 - 1230; i++) {
   freq_table[i + 1230] = 6;
 }

 REG_TC4_CTRLA |= TC_CTRLA_ENABLE;                       // Enable timer TC4
 while (TC4->COUNT16.STATUS.bit.SYNCBUSY);               // Wait for synchronization
}

void loop() {
 if (timer + 5 <= millis()) {
   param1 = constrain(analogRead(10) + analogRead(6), 04095);
   table_number = param1 / 512;
   param2 = constrain(analogRead(4) + analogRead(3), 04095);

   //--------------------------freq change-----------------------------
   for (int j = 0; j < 5; j++) {
     adc += analogRead(2) / 4 + analogRead(7) / 4 * calb;
   }
   adc = constrain(adc / 501225) ;//Covers 6(=1220) octaves. If it is 1230 or more, the operation becomes unstable.
   osc_freq = freq_table[adc ]; // V/oct apply
   timer = millis();
   if ( abs(old_freq - osc_freq) > 2 ) { //countermeasure of click noise
     DAC_frq_change(osc_freq * 70.381);
     old_freq = osc_freq;
   }
 }
}

void table_set(byte table_number) {//make wavetable

 for (int i = 0; i < 128; i++) {  //saw
   wavetable[0][i] = i * 2;
 }

 for (int i = 0; i < 128; i++) {  //sin
   wavetable[1][i] = (sin(2 * M_PI * i  / 128) + 1) * 127;
 }

 for (int i = 0; i < 64; i++) {  //squ
   wavetable[2][i] = 255;
   wavetable[2][i + 64] = 0;
 }

 for (int i = 0; i < 64; i++) {  //tri
   wavetable[3][i] = i * 4;
   wavetable[3][i + 64] = 255 - i * 4;
 }


 for (int i = 0; i < 64; i++) {  //oct saw
   wavetable[4][i] = i + i * 2;
   wavetable[4][i + 64] = i + 63 + i * 2;
 }

 for (int i = 0; i < 128; i++) {  //FM1
   wavetable[5][i] = (sin(2 * M_PI * i  / 128 + sin(2 * M_PI * 3 * i  / 128)) + 1) * 127;
 }

 for (int i = 0; i < 128; i++) {  //FM2
   wavetable[6][i] = (sin(2 * M_PI * i  / 128 + sin(2 * M_PI * 7 * i  / 128)) + 1) * 127;
 }

 for (int i = 0; i < 128; i++) {  //FM3
   wavetable[7][i] = (sin(2 * M_PI * i  / 128 + sin(2 * M_PI * 4 * i  / 128 + sin(2 * M_PI * 11 * i  / 128))) + 1) * 127;
 }
}

inline void DACWrite(uint16_t sample) {//internal DAC output
 DAC->DATA.reg = sample;                               // Shortened version
 DAC->CTRLA.bit.ENABLE = 0x01;                         // in analogWrite()
}

void DAC_frq_change(int sampleFreq) {
 uint32_t top = 47972352 / (sampleFreq);  // Calculate the TOP value
 REG_TC4_COUNT16_CC0 = top;                              // Set the TC4 CC0 register to top
}

void DACSetup(int sampleFreq) {
 uint32_t top = 47972352 / (sampleFreq);  // Calculate the TOP value
 REG_GCLK_GENDIV = GCLK_GENDIV_DIV(1) |                  // Divide the 48MHz clock source by 1 for 48MHz
                   GCLK_GENDIV_ID(3);                    // Select GCLK3
 while (GCLK->STATUS.bit.SYNCBUSY);                      // Wait for synchronization

 REG_GCLK_GENCTRL = GCLK_GENCTRL_IDC |                   // Set the duty cycle to 50/50
                    GCLK_GENCTRL_GENEN |                 // Enable GCLK3
                    GCLK_GENCTRL_SRC_DFLL48M |           // Set the 48MHz clock source
                    GCLK_GENCTRL_ID(3);                  // Select GCLK3
 while (GCLK->STATUS.bit.SYNCBUSY);                      // Wait for synchronization

 REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN |                 // Enable clock
                    GCLK_CLKCTRL_GEN_GCLK3 |             // Select GCLK3
                    GCLK_CLKCTRL_ID_TC4_TC5;             // Feed the GCLK3 to TC4 and TC5
 while (GCLK->STATUS.bit.SYNCBUSY);                      // Wait for synchronization

 REG_TC4_COUNT16_CC0 = top;                              // Set the TC4 CC0 register to top
 while (TC4->COUNT16.STATUS.bit.SYNCBUSY);               // Wait for synchronization

 NVIC_SetPriority(TC4_IRQn, 0);                          // Set the interrupt priority for TC4 to 0 (highest)
 NVIC_EnableIRQ(TC4_IRQn);                               // Connect TC4 to Nested Vector Interrupt Controller

 REG_TC4_INTFLAG |= TC_INTFLAG_OVF;                      // Clear the interrupt flags
 REG_TC4_INTENSET = TC_INTENCLR_OVF;                     // Enable TC4 interrupts

 REG_TC4_CTRLA |= TC_CTRLA_PRESCALER_DIV1 |              // Set prescaler to 1 for 48MHz
                  TC_CTRLA_WAVEGEN_MFRQ;                 // Put the timer TC4 into Match Frequency Mode
 while (TC4->COUNT16.STATUS.bit.SYNCBUSY);               // Wait for synchronization
}

void TC4_Handler() {                                      // Interrupt Service Routine for timer TC4
 static uint16_t currentSample = 0;
 static uint8_t sampleInterruptCounter = 0;
 static uint32_t sampleNumber = 1;
 if (sampleInterruptCounter == 0) {                   // If this is the first pass for this sample:
   currentSample = wavetable[table_number][sampleNumber] * (param2 + 425) / 512 ;//425 is offset magic number
 }
 currentSample <<= 2;                                  // Go to 10 bits for calculations,
 DACWrite(currentSample);                              // Send the current sample to the DAC
 sampleInterruptCounter = 0;                           // Reset the interrupt counter
 sampleNumber++;//   Go to the next sample
 if (sampleNumber >= sampleSize) {                     // At the end of the samples array:
   sampleNumber = 0 ;                                   // Reset sample number to second sample
 }
 REG_TC4_INTFLAG = TC_INTFLAG_OVF;                       // Clear the OVF interrupt flag
}



この記事が気に入ったら、サポートをしてみませんか?
気軽にクリエイターの支援と、記事のオススメができます!
HAGIWO/ハギヲ
モジュラーシンセを始めた人。仕事はレガシーなエンジニア。