Brainstorming: Arduino gesteuerter Analogsynth

Hallo,

wie ich im Beitrag: http://forum.arduino.cc//index.php?topic=192178.0 schon erwähnt habe, plane bzw. experimentiere ich mit einem Arduino gesteuerten Analogsynthesizer. Eckdaten des Projektes: (mittlerweile abgespeckt, sonst wird es nie fertig)

Analogteil:

  • 2 VCO´s (ICL8038 basierend)
  • 2x VCF (einmal Multimode HP/BP/LP) und einmal 4-Pole LP (Herzstück: SSM2164 bzw. V2164)
  • VCA: auch auf V2164 basierend

Digitalteil:

Midi/CV Converter (mittels MCP4922 DAC´s):

  • Pitch (für beide VCO´s getrennt)
  • Balance VCO1 zu VCO2
  • VCF Cutoff (möglicherweise Resonance)
  • VCA
  • div Zusätze (Modulatoren, wie VCO1 Out -> VCO2 In (FM)) usw..

analoge Matrixsteuerung (mittels mehreren CMOS 4054 und 4066, über Shiftregister gesteuert)

  • Schalten der analogen Wellenformen der VCO`s (sin,tri,pulse,saw)
  • Zuschalten vom Sub-OSC
  • Wählen der/des Filterboards
  • Wahl des Filtertyps

digital generiert:

  • ADSR Hüllkurven (Pitch, VCF, VCA) können ruhig linear sein, da die exp-Wandlung die Hardware übernimmt
  • 1-2 LFO´s, die dann digital geroutet werden können (z.B. LFO1 mit Faktor xy zu VCO1 Pitch DAC-Out)

Um die Last zu verteilen, plane mindestens 2 ATmega328 zu verbauen:

1x als reiner Midicontroller (Display, Potis, Buttons, Led´s, Arpeggiator (Zukunft), Patchspeicher, EEprom) - hatte schon ein ähnliches Projekt gebaut, d.h. macht mir keine Kopfschmerzen 1x als Midi-CV-Interface (DAC´s, Switches, ADSR, LFO)

beide 328er werden über das MIDI Protokoll gesteuert (hat den Vorteil, dass ich den reinen Controller erst viel später bauen kann und das CV-Interface zunächst mit einem Standardmidicontroller bzw. Software steuern kann).

Meine Frage ist nur, ob das CV-Interface die Rechnenleistung schafft, es muss ja folgendes können: Midi Noten + Velocity Controllerdaten auf die DAC´s verteilen ADSR auf die DAC´s Switching (da ist mir das Timing egal, da dies nicht unbedingt während des Spielbetriebs funktionieren muss) LFO...und der bereitet mir Kopfzerbrechen, ob der 328er mit der Rechenleistung klar kommt (würde ich über Timer1 oder Timer2 steuern und höchstens bis auf 500-1000Hz laufen lassen). Ausgangscode für den LFO hätte ich mir dieses Projekt gedacht: http://interface.khm.de/index.php/lab/experiments/arduino-dds-sinewave-generator/

Was meint ihr dazu?

Danke u. LG Matthias

Hallo,

vielleicht willst Du Dir ja mal das http://www.mikrocontroller.net/articles/Digitaler_Funktionsgenerator ansehen? Evtl. als Startpunkt für Deinen LFO.

Gruß, Ralf

...ist ähnlich dem DDS-Sinewavegenerator: Sinuskurve als Table, restlichen Wellenformen berechnet, "abgefeuert" mittels Interrupt (Timer). Werde wohl einfach mal das Projekt beginnen (dauert sicher sowieso mehrere Monate (Vollzeitjob + 1,5 jährige Tochter :) ), und den LFO "nachreichen", ist ja eine reine Softwarelösung und rechnet sich dann an Pitch/Cutoff/VCA usw. an die vorhanden DAC´s dran. Vermute nur, dass dem 328er die Luft ausgehen wird und entweder "eiert" oder dass Midisteuerdaten untergehen/verzögert werden. Da wäre wohl ein Due die performantere Wahl. Abgesehen davon, sind mehr als ein bis zwei unabhängi timergesteuerte LFO´s sowieso nicht möglich (es stehen nur Timer1 u. Timer2 zur Verfügung). Dh jeden weiteren LFO müsste man in ein proportionalen Verhältnis (Teilungsfaktor) setzen....

Im Übrigen bin ich mit meinen MCP4922 12-Bit DAC´s über das Ziel hinausgeschossen: Notwendig sind diese nur für die Pitch Werte (da kann man mit 4096 Teilschritten sehr genau tunen, werde auch für alle Notenwerte eine eigene Table machen.) Bei VCO/VCF/Routings sind 8 Bit vollkommen ausreichend, v.a. deswegen, wenn das Teil MIDI gesteuert wird und die Controller nur 7 Bit (128 Schritte) per Definition haben (außer Pitchbend).

madias: ...ist ähnlich dem DDS-Sinewavegenerator: Sinuskurve als Table, restlichen Wellenformen berechnet, "abgefeuert" mittels Interrupt (Timer).

Ja, wobei bei Deiner Quelle die Spannung per PWM erzeugt wird und bei meiner Quelle mittels einer R2R-Leiter. Also einfach einen kompletten Port (also 8 Pins) mit einem einzigen Befehl auf den entsprechenden Wert der Sinuskurve setzen und die gewünschte Spannung liegt an.

Was mich auf folgende Idee gebracht hat: An einem Pin des Controllers hängt ein 8-Bit-Counter. Dieser erzeugt die 8 niederwertigsten Adressbits für einen EPROM. An den Datenleitungen des EPROMs hängt die R2R-Leiter und im EPROM ist die Sinuskurve (mit 256 Byte) abgespeichert. Der Controller triggert jetzt einfach über einen Pin den Counter mit der 256-fachen Geschwindigkeit der gewünschten Frequenz und die Sinuskurve erzeugt sich quasi von alleine. Lustig wird es dann, wenn man vom Controller noch höherwertige Adressbits schalten lässt. Dann hast Du z.B. in den ersten 256 Byte eine Sinusspannung, in den nächsten 256 Byte eine Dreieckspannung, in den nächsten 256 Byte eine Sägezahnspannung in den nächsten 256 Byte eine "Was-immer-Du-willst"-Spannung usf. Wenn Du mit diesem Gebastel dann z.B. einen VCA steuerst, kannst Du Dir quasi jede gewünschte Hüllkurve erzeugen.

Die Sinuserzeugung mittels R2R habe ich schon mal ausprobiert. Das erzeugt zumindest auf dem Oszilloskop genial schöne Sinuskurven. Das mit den Werten aus dem EPROM werde ich noch testen, aber mir gebricht es im Moment noch an einem Programmiergerät für 2516-EPROMs (die habe ich schon hier, das werden meine "Versuchskaninchen").

Gruß, Ralf

die Idee mit dem 8-Bit Counter+R2R + EEProm gefällt mir! Da sind wir schon fast bei Wavetables! Hab auch schon ein Projekt gesehen, wo ein 555-Timer über einen Eingangspin die Frequenz im Microprozessor steuert, der dann über R2R/DAC´s/wasauchimmer die Wellenformen/Wavetables ausgibt, also fast der umgekehrte Weg deiner Idee.

Nur bei meinem Projekt stellt sich weniger die Frage nach der Hardwarelösung, hab es wohl noch nicht ganz klar beschrieben:

Der Haupt"Arduino" soll folgendermaßen funktionieren (ich speck jetzt die Funktionen ein bisschen ab) Empfängt Mididaten: Midiempfang: Noteon/off: geht an den Pitch DAC + generiert die ADSR Hüllkurve(n) (GATE on Gate off) für VCA und VCF Midiempfang: Controller(x) Wert y: Beispiel: Empfängt Controller 47 mit Wert 80, Intern hab ich festgelegt, falls Controllernummer 47 empfangen wird, dann gibt er den Wert(y) auf den VCF-Cutoff DAC aus. Das lässt sich beliebig fortsetzen (wie ich halt die DAC´s hardwareseitig beschaltet habe). Im Minmalbetrieb hab ich drei DAC´s: 1x für Pitch 1x für VCA 1x für VCF (Cutoff)

d.h. mein digitaler LFO rechnet sich dann an die Ausgangswerte hinzu, Beispiel: Note C1 wird gedrückt: Pitch-DAC sendet Wert für CV-Pitch (aus Wertetabelle) ADSR1 (für VCO) bekommt das Gatesignal (Note xx on) und generiert die Hüllkurve, die über den VCA-Dac ausgegeben wird ADSR2 (für VCF) bekommt das GAtesignal (Note xx on) und geniert die Hüllkurve für den Cutoff-DAC, aber jetzt: Ich habe definiert, dass LFO1 mit z.B. Amplitude 127 (maximal) auch das Cutoffsignal beeinflussen soll: D.h. die Ausgabe für den CutoffDAC: (Momentan)Wert der Hüllkurve + (LFO *Amplitude)

Somit lässt sich der LFO x-beliebig mit x-beliebiger Amplitude an jeden DAC addieren.

Vorteile der Software-LFO Lösung: kein zusätzlicher Hardwareaufwand Software lässt sich x-beliebig erweitern/verändern usw... ohne dass die Hardware umgebaut werden muss kompletter "Recall" über Midi: da alle Werte über Midi gesteuert werden, kann man Programs generieren.

Also ich muss nur 1-2 LFO´s zusätzlich berechnen, die sollen aber a) nicht "eiern" (wenn z.b. mehrere Controllerdaten gleichzeitig empfangen werden) und b) wenig CPU Last benötigen, damit die anderen Funktionen nicht gestört werden.

Mein "Vorbild" für das ganze Projekt ist der Oberheim Matrix 1000, der in einer 19' Höheneinheit ein 6-stimmiger analoger Synth war und über Software (oder Hardwarecontroller) unzählige Funktionen und Kombination beherrschte. (Sicher auch dank seiner 6x CEM3396, die eine "all inclusive" Lösung für Synths waren)

Liebe Grüße Matthias

madias: Vorteile der Software-LFO Lösung: kein zusätzlicher Hardwareaufwand Software lässt sich x-beliebig erweitern/verändern usw... ohne dass die Hardware umgebaut werden muss kompletter "Recall" über Midi: da alle Werte über Midi gesteuert werden, kann man Programs generieren.

Hai,

da stimme ich Dir in allen Punkten 100%ig zu. Ich sehe nur das Problem, dass der Arduino evtl. nicht schnell genug ist, vor allem, wenn Du mehr als einen LFO hast.

Außerdem leitet sich der Timerinterrupt vom Prozessortakt ab. Du kannst also nicht stufenlos jede Frequenz wählen. Z.B.

| Du möchtest Hz | Du bekommst Hz | | - | - | | 500 | 500,0000 | | 501 | 501,0020 | | 502 | 501,9923 | | 503 | 503,0023 | | 504 | 504,0005 |

usf. bei 16MHz Taktfrequenz und Vorteiler 1. Ob und falls ja inwieweit sich das auf den Klang auswirkt kann ich allerdings überhaupt nicht abschätzen.

Die ganzen schönen Chips von Curtis Electromusic wie den CEM3396 oder den SN76477 oder SN76488 oder (tolles Teil) S50240 scheint es ja, wenn überhaupt nur noch bei Ebay in der Krabbelkiste zu Wahnsinnspreisen zu geben. Hat sich wohl alles durch die FPGA's überlebt.

Ich würde mich auf jeden Fallen freuen, wenn Du soweit bist, mal ein paar Sounddemos zu hören.

Gruß, Ralf

Lt. deiner Tabelle dürften die Centbereiche recht egal sein, nunmal da ein LFO nicht unbedingt die Rolle eines VCO´s übernehmen muss. Wie gesagt, ob der 328er die "Power" besitzt, da kann ich nur eines machen: Es ausprobieren :) Realistisch gesehen, werden mehr als 500HZ-1kHZ nicht drinnen sein. Zu den ganzen alten analog(digital) IC´s: Die hat restlos alle Dave Smith aufgekauft und verbaut sie in seinen Neuerscheinungen :) Ironischerweise bin ich ja Behringer zum Dank verpflichtet, da diese CoolAudio vor x Jahren aufgekauft haben und dadurch sind zumindest noch VCA-Chips und BBD-Chips zu humanen Preisen erhältlich, wenn auch schwer zu bekommen. http://www.coolaudio.com/products.html Die einzige andere Firma ist THAT, die Preise sind sind halt nicht mehr im Budgetbereich anzusehen. Sounddemos werden wohl noch länger auf sich warten lassen, da ich noch mitten in der Experimenterphase bin, wo ich alles austeste bevor ich meine Prototypplatinen löte. Interessanterweise ist mir bei meinen ganzen Überlegungen und Experimenten ein "Abfallprodukt" eingefallen, dass - zumindest am analog Retro Synthiemarkt ein Renner wäre: Das Midi-CV Interface mit Wertetabelle (könnte man sogar als Autotune konzipieren) - die meisten alten Analogsynths sind, alterungsbedingt - vollkommen out of tune (auch in sich), DAS wäre dann die Abhilfe ;)

LG Matthias

hab mittlerweile auch einen einfachen, brauchbaren Code gefunden, muss natürlich modifiziert werden:

Glaube aber, dass in seinem Text ein Fehler ist:
i’m only using timer2 for output in this code. i’ve started implementing the LFO code into my 8-bit melotronium where timer 2 is dedicated for the audio output.
Ist wohl ein Tippfehler und er meint, dass Timer0 verwendet wird.

Hallo,

ich habe den Code auf dem ATMega328 ausprobiert. Der Sketch tut nichts außer den Arduino kontinuierlich gegen die Wand zu fahren.

Warum habe ich nicht getestet.

Gruß, Ralf

Hab ihn leider auch noch nicht getestet, beim kurzen Durchlesen ist mir folgendes aufgefallen:
Funktionsweise:
1 Poti am AnalogIn0 = Wellenformauswahl (7 Werte)
1 Poti am AnalogIn1 = Speed
Pin3 ist die Ausgabe (einfach mal ein TestLED daranhängen)
Die Timer2 Config (/— TIMER2 CONFIG —/) würde ich mal entfernen. (sehe den Sinn nicht im Sketch)
Serial.begin ist auch nicht notwendig.
Werd´s selbst mal testen

Am LCD Shield hab ich den Code schon erfolgreich getestet (selbst geschrieben):
mit Timer1.initialize(100000); legt man die Rate fest, Ausgabe ist 12 Bit

#include <TimerOne.h>
 #include <LiquidCrystal.h>
 LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
 byte zaehler     = 0;
 int sintable[] = {
   2048,2098,2148,2198,2248,2298,2348,2398,2447,2496,2545,2594,2642,2690,2737,2784,2831,2877,2923,2968,3013,3057,3100,3143,3185,3226,3267,3307,3346,3385,3423,3459,3495,3530,3565,3598,3630,3662,3692,3722,3750,3777,3804,3829,3853,3876,3898,3919,3939,3958,3975,3992,4007,4021,4034,4045,4056,4065,4073,4080,4085,4089,4093,4094,4095,4094,4093,4089,4085,4080,4073,4065,4056,4045,4034,4021,4007,3992,3975,3958,3939,3919,3898,3876,3853,3829,3804,3777,3750,3722,3692,3662,3630,3598,3565,3530,3495,3459,3423,3385,3346,3307,3267,3226,3185,3143,3100,3057,3013,2968,2923,2877,2831,2784,2737,2690,2642,2594,2545,2496,2447,2398,2348,2298,2248,2198,2148,2098,2048,1997,1947,1897,1847,1797,1747,1697,1648,1599,1550,1501,1453,1405,1358,1311,1264,1218,1172,1127,1082,1038,995,952,910,869,828,788,749,710,672,636,600,565,530,497,465,433,403,373,345,318,291,266,242,219,197,176,156,137,120,103,88,74,61,50,39,30,22,15,10,6,2,1,0,1,2,6,10,15,22,30,39,50,61,74,88,103,120,137,156,176,197,219,242,266,291,318,345,373,403,433,465,497,530,565,600,636,672,710,749,788,828,869,910,952,995,1038,1082,1127,1172,1218,1264,1311,1358,1405,1453,1501,1550,1599,1648,1697,1747,1797,1847,1897,1947,1997
};
void setup() 
{
  // Initialize the digital pin as an output.
  // Pin 13 has an LED connected on most Arduino boards
//  pinMode(13, OUTPUT);    
   lcd.begin(16, 2);              // start the library
 lcd.setCursor(0,0);
 lcd.print("Welcome");
  Timer1.initialize(100000); // set a timer of length 100000 microseconds (or 0.1 sec - or 10Hz => the led will blink 5 times, 5 cycles of on-and-off, per second)
  Timer1.attachInterrupt( timerIsr ); // attach the service routine here
}
 
void loop()
{
  // Main code loop
  // 
      lcd.setCursor(0,1);
 lcd.print(sintable[zaehler]); 
 lcd.print("   "); 
 if (zaehler ==0)
 {
         lcd.setCursor(0,1);
 lcd.print("         "); 
 }
}
/// --------------------------
/// Custom ISR Timer Routine
/// --------------------------
void timerIsr()
{
    // Toggle LED
    zaehler++;
}

madias: 1 Poti am AnalogIn0 = Wellenformauswahl (7 Werte) 1 Poti am AnalogIn1 = Speed Pin3 ist die Ausgabe (einfach mal ein TestLED daranhängen)

Habe ich auch so gesehen. Ich hatte allerdings statt der LED mein gutes, altes Hameg-Oszi dran.

Wenn Du das zum laufen bekommst, kannst Du ja mal Bescheid geben.

Gruß, Ralf