2017年5月23日火曜日

ベースマシン フィルターにエンベロープを掛けてみる。

ファームウェアを改修してFilterのCutoffにエンベロープを掛けられるようにしてみた。

構想段階では、まずはエンベロープはVCAにかけて音量を変化させることだけを考えていて、フィルターの変調は「できたらやってみる」という努力目標にしていた。

アナログ的に値を設定するPOT×8パネルの中でエンベロープの初期音量を設定する「Level」ツマミはあまり使わないので、これをフィルターにエンベロープをかける量を調整する用途で使ってみることにしてみた。


ほんとは、VCAにかけるエンベロープとVCFにかけるエンベロープを分けて2系統にできればいいんだが、Nucleo F401のI/O pinが足りなくてこれ以上はかんたんに入力デバイスを増やせない。

スイッチでエンベロープをかける対象を選択するという方法も、エンベロープを設定するデバイスが「可変抵抗」なのでなかなかむずかしい。モードを切り替えると、現在のPOTの位置が別のエンベロープの値に反映されてしまう。

これはMIDI入力デバイスでもよくあり、高価なMIDI入力機器はスライドボリュームにモーターが仕込まれていてシーケンサーから送った値でツマミの位置を力ずくで変更するようになっている。さらにタッチ・センスも仕込まれていて人が触っているかどうか判別して、ツマミの位置制御の入力/出力を切り替えるようにもなっている。

さすがに今回はそこまでやるつもりはないので、できる範囲で(ファームウェアもあまりいじらない方向で)考えることにした。

エンベロープは1波形しか使えないので、VCAのエンベロープを浅くかけるか、深くかけるかを「FLT MOD」ツマミで調節するだけ。

↓シーケンサーのプログラムの一部

void updateTicks(int ticks)
{
    uint16_t level = EnvelopeGenerator.update(sequences[SequenceSender.getStep()].isTie());
    
    // Amp
    uint16_t ampLevel = level;
    if (sequences[SequenceSender.getStep()].isNoteOn())
    {
        ampLevel = ampLevel >> 1;
        if (sequences[SequenceSender.getStep()].isAccent())
        {
            ampLevel = ampLevel * (1.0f + ((float)UIController.getAccentLevel()) / 128.0f);
        }
    }
    else
    {
        ampLevel = 0;
    }
    AmpController.outDca(ampLevel);
    
    // Oscillator
    OscController.setFrequency10(SequenceSender.getFrequency10());
    OscController.outDco();
    
    // Filter Env Mod
    uint16_t cutoff16 = FilterController.getCutoff();
    uint8_t resonance = FilterController.getResonance();
    uint8_t mod = FilterController.getModLevel();
    float modCoeff = (float)mod / (float)(cutoff16 << 1);
    cutoff16 = cutoff16 * (1.0f + ((float)level / 0x8000) * modCoeff);
    if (cutoff16 > 255) {
        cutoff16 = 255;
    }
    FilterController.outDcf(cutoff16, resonance);
    
    if (ticks == 0)
    {
        EnvelopeGenerator.init(envelope);
        playingStep = SequenceSender.getStep();
        UIController.setPlayingStep(playingStep);
    }
}

シーケンサーの本体で1msごとにシンセ(DCO, DCF, DCA)に値を送っている部分。

BaseMachineのファームウェアは最初はArduino Unoでやっていたので基本的に16bit整数演算にしていたが、Nucleo F401はCortex-M4でFPU付きなのでFilterにエンベロープをかけるあたりは単精度浮動小数点演算で妥協した。

もう少し考えれば整数演算に落とし込めると思うが、なんと言ってもめんどくさい(@@;

Cortex-M4もM0とくらべて高いわけでもないし、次にシーケンサーを作るとしたらCortex-M4の浮動小数点数演算でどこまでできるかという話になるかもしれません。

mbed repository:
https://developer.mbed.org/users/ryood/code/BaseMachine/ revision:29

メモ:


DCAのエンベロープはDACのMCP4922で受けて電流値に変換し、OTAのNJM13700をDCAとして使っているので、自然と(あ、いやLPFを一回かましているのでわざとです)アナログチックな波形になっているが、今回はDigital POTのAD8403でデジタル的に受けているので、エンベロープ波形もデジタル的だと思う。過激なので面白いと言えば面白いですが。

演算で対数カーブにするのもコンセプトとずれるし、アナログ素子を通してなんとかならないかなあ。

今日はもう疲れたので次こそは音出し動画をつくります。

やったことを書き留めておかないと1週間後には何をやってたか忘れてしまいますゆえ。

0 件のコメント:

コメントを投稿