Nucleo F401REでFM音源を実装するやつ 外部DACとオペアンプを利用 現在はMCP4922とNJM2737

Dependencies:   AOTTrigon I2CEEPROM MCP4922 AQM0802A mbed

Fork of NuMidi401 by Yuu Kobayashi

NuFM401

Nucleo F401用の自作ソフトウェアMIDI音源

概要

だいたいそんなもんです。

特徴

  • ブレッドボードの上で組める程度には簡単な回路構成
  • 外部のDACにMCP4922を採用
  • 念のためのボルテージフォロアとしてNJM2737Dを採用
  • バンク用EEPROMに24FC1025を採用
  • シリアル経由でMIDIデータを受信することで操作

補足

シリアル <=> MIDI のドライバにはHairless-MIDISerialをオススメします。 仮想MIDIケーブルはとりあえずMIDI Yokeで。

Revision:
12:7408b85fba39
Parent:
10:0ffdefe75566
--- a/Operator.cpp	Mon Dec 29 12:48:44 2014 +0000
+++ b/Operator.cpp	Tue Dec 30 05:53:06 2014 +0000
@@ -1,54 +1,98 @@
 #include "Operator.h"
 
 static const float pi2 = 6.283185307179586476925286766559f;
+inline float getFloatRate(char cr);
 
 FMOperator::FMOperator()
 {
     attack = 0.0f;
-    delay = 0.0f;
+    decay = 0.0f;
+    sustain = 1.0f;
+    sustainRate = 0.0f;
+    release = 0.0f;
+
+    frequencyMultiple = 1.0f;
+    totalLevel = 1.0f;
+    modulation = 0.0f;
+    
+    startTime = 0.0;
+    releaseTime = 0.0;
+    baseFrequency = 1.0f;
+    feedback = 0.0f;
+    prev = 0.0f;
+    released = false;
+}
+
+FMOperator::FMOperator(Timer *tm, AOTTrigon *t)
+{
+    attack = 0.0f;
+    decay = 0.0f;
     sustain = 0.0f;
     sustainRate = 1.0f;
     release = 0.0f;
 
     frequencyMultiple = 1.0f;
     totalLevel = 1.0f;
-    
-    startTime = 0.0;
-    releaseTime = 0.0;
-    baseFrequency = 1.0f;
-}
-
-FMOperator::FMOperator(Timer *tm, AOTTrigon *t)
-{
-    attack = 0.0f;
-    delay = 0.0f;
-    sustain = 0.0f;
-    sustainRate = 1.0f;
-    release = 0.0f;
-
-    frequencyMultiple = 1.0f;
-    totalLevel = 1.0f;
+    modulation = 1.0f;
     
     tri = t;
     master = tm;
     startTime = 0.0;
     releaseTime = 0.0;
     baseFrequency = 1.0f;
+    feedback = 0.0f;
+    prev = 0.0f;
+    released = false;
 }
 
-float FMOperator::calculate(float fmIn) {
+float FMOperator::calculate(double time, float fmIn) {
     if (startTime == 0.0) return 0.0f;
     float pos = (pi2 * baseFrequency * frequencyMultiple);
-    double nt = (master->read_us() / 1000000.0) - startTime;
-    return tri->sin(fmIn + (float)(pos * nt));
+    double abt = master->read_us() / 1000000.0;
+    double nt = abt - startTime;
+    float e;
+    if (released) {
+        e = getEnvelopeReleaseRate(abt - releaseTime,getEnvelopeRate(nt));
+    } else {
+        e = getEnvelopeRate(nt);
+    }
+    if (feedback != 0.0f) {
+        return prev = tri->sin((float)(pos * nt) + prev * feedback) * e * totalLevel * modulation;
+    }
+    return tri->sin(fmIn + (float)(pos * nt)) * e * totalLevel * modulation;
+}
+
+void FMOperator::attackNote(float freq, double time) {
+    baseFrequency = freq;
+    startTime = time;
+    released = false;
 }
 
-void FMOperator::attackNote(float freq) {
-    baseFrequency = freq;
-    startTime = master->read_us() / 1000000.0;
+void FMOperator::releaseNote(double time) {
+    releaseTime = time;
+    released = true;
 }
 
-void FMOperator::releaseNote() {
-    startTime = 0.0;
-    releaseTime = master->read_us() / 1000000.0;
-}
\ No newline at end of file
+float FMOperator::getEnvelopeRate(double reltime) {
+    //アタック・ディケイ・リリースに要する時間は
+    //127の時0s、0のとき1s
+    if (attack != 0.0f && reltime <= attack) {
+        return reltime * (1.0f / attack);
+    }
+    if (decay != 0.0f && reltime <= (attack + decay)) {
+        reltime -= attack;
+        return 1.0f - reltime * (1.0f - sustain) / (decay);
+    }
+    return (sustainRate != 0.0f) ? sustain - (reltime - decay) * (sustain / sustainRate) : sustain;
+}
+
+float FMOperator::getEnvelopeReleaseRate(double rlst,float orate) {
+    //どう考えても無音・1回も鳴らしてない・リリースなし
+    //ってどれが一番頻度高いかね
+    if (rlst >= (attack + decay + release) || release == 0.0f || startTime == 0.0) {
+        return 0;
+    } else {
+        return orate - rlst * (orate / release);
+    }
+}
+