Forum: Projekte & Code AVR Synthesizer mit ATxmega128A1


von Rolf D. (rolfdegen)


Angehängte Dateien:

Lesenswert?

Hallo Ihr Lieben..

Ich entwickel zur Zeit einen Synthesizer auf Basis eines ATxmega128A1
Prozessors. Falls euch mein Projekt interessiert schaut mal unter
http://www.cczwei-forum.de/cc2/thread.php?threadid=5878 vorbei. Da gibts
jede Menge an Informationen Rund um die Musikelektronik und auch
Programmier-Tips in C, um einen eigenen Synthesizer zu bauen.



Viel Spaß beim stöbern wünscht euch Rolf

: Verschoben durch Admin
von Rene B. (themason) Benutzerseite


Lesenswert?

Hallo Rolf,

erstmal möchte ich bekunden das du sehr schöne Erklärungen auf der CC2 
Seite zu dem Thema gemacht hast. Die anderen Links sind auch sehr 
schick. Find das immer wieder erstaunlich was man klangtechnisch aus nem 
AVR holen kann. Dafür das das "nur" nen popeliger 8-bitter ist :-)
Werd deinen Beitrag weiterhin verfolgen und bin mal gespannt was du aus 
dem Xmega rauskitzeln kannst. Ich selbst bin bei meinem Synthesizer 
(schau mal hier im Forum unter Audio-DSP mit Spartan 3) einen anderen 
weg gegangen. Da mache ich die Klangerzeugung/Bearbeitung/Filterung in 
einem FPGA, und gesteuert wird das ganze von einem AVR. In dem Artikel 
sind auch noch Links zu You-Tube Videos. Das ganze ist allerdings schon 
was länger existent und liegt momentan auch was rum. Aber evtl 
überarbeite ich den Synth mal mit einem neueren Spartan. Insgesamt hat 
man mit der Platform (wenn sie mal fertig bzw dokumentiert wäre) recht 
viele Möglichkeiten, da ich von vornherein auf der Verarbeitungsebene 
modular bin.

Weiterhin viel Spaß beim Synth-DIYing :-)

von Rolf D. (rolfdegen)


Lesenswert?

Hallo Rene

Ich habe jetzt die ersten Sounds von meinem Synthi ins Netz gestellt. 
Kann man sich auf SoundCload unter 
http://soundcloud.com/rolfdegen/sets/testsounds-avr-synthesizer/ 
anhören. Die Sound sind zwar nix Besonderes, weil der Synthi zur Zeit 
nur ein Oszillator und ein Hüllkurvengenerator besitzt. Aber ich bin 
gerade dabei, das ganze auf drei Oszilatoren und 3 Hüllkurvengeneratoren 
auszubauen.

In Bezug auf dein Audio-Projekt: Ich habe mir schon vor längerer Zeit 
dein "Audio-DSP mit Spartan 3"  hier auf mikrocontroller.net angesehen 
bzw angehört und bin von der Soundqualität einfach begeistert. Ist schon 
eine ganz andere Liga :)

Ich habe mir jetzt bei Watterott ein STM32F4 Discovery Board bestellt 
und bin schon gespannt was das Bord im Audiobereich so alles kann. Die 
Eckdaten sind vielversprechend zB. 168MHz CPU-Takt, 1 MB Flash, 192 KB 
RAM, FPU und 24Bit DA-Wandler Chip onBoard. Werde das Board ausführlich 
testen und vielleicht wird ja ein Wave-2 daraus werden. Schaun wir mal..

Gruß Rolf

von Maik M. (myco)


Lesenswert?

Hallo Synthbastler :)

Ich habe mir auch schon einen Synth mit einem AVR gebastelt, allerdings 
habe ich dann sehr bald aufgegeben weil mir der Sound nicht gefallen 
hat. Ich bin halt besseres gewohnt, da ich aus der VST-DSP Ecke komme.

Habe mir dann mal ein paar Dev-Boards für schnellere Controller gesucht 
(PIC32, dsPIC, AVR32...), allerdings bin ich da nicht sonderlich weit 
gekommen. Der dsPIC ist recht simpel und billig, und man kommt schnell 
zum DSP-Teil des Projekts, allerings ist er nicht sonderlich schnell. 
AVR32 ist irgendwie sehr kompliziert, dafür ist er einigermaßen schnell. 
Gleiches gilt für den PIC32.

Rene´s Projekt find ich recht interessant, und ich hatte schon lange vor 
mal mit FPGAs zu arbeiten, weil damit geht man den Großteil der 
Geschwindigkeits-Probleme aus dem Weg.

Jetzt habe ich wieder etwas mehr Zeit, und wollte das mal angehen. Habe 
mich schon etwas informiert, und nach Dev-Boards umgesehen. Evtl. werde 
ich mir ein CycloneII-Board holen, ist zwar nicht mehr der aktuellste, 
aber das Board hat alles drauf was ich brauche, und eine 
Synthesizer-Demo wird gleich mitgeliefert. Das einzige was mir da noch 
etwas Sorgen macht ist: bei der freien Version von Quartus steht 
"Windows 32bit only", und meine 32-bit Kisten habe ich alle ausgemustert 
:)

von Rene B. (themason) Benutzerseite


Lesenswert?

@Rolf

Also die Sounds klingen schon recht witzig. Old-School-8-Bit-Style :-)
Bin mal gespannt auf weitere Demos. Wie willst das denn mit den Filtern 
machen ? Analoge Filter die digital gesteuert werden ? Oder direkt 
digitale Filter ?

Und danke noch für das Lob :-))

@Maik

was würde denn gegen Xilinx sprechen ?
Also den Spartan 6 zu routen war sehr angenehm. Mein (noch nicht 
phsyisch vorhandenes, aber schon teilgeroutetes Test-Board) schaut schon 
ganz gut aus und hat neben nem Codec, nem AVR noch 16MB SDRAM an dem 
FPGA angeklebt bekommen. Aus dem Teil ne Platform zu machen ist 
mittlerweile nen Klacks.
Würde das Audio-Projekt ja gerne nochmal reaktiveren (fürs Forum) aber 
bei der "alten" Version war die Resonanz schon nicht so groß. Wobei das 
Board als solches vllt noch für den einen oder anderen Interessant sein 
könnte. Ist auch "relativ" einfach zu Löten (144'er QFP und 0603 SMD auf 
ner doppelseitigen Platine). Na ja .. mal schauen. Vllt starte ich das 
Projekt erneut.

von Rolf D. (rolfdegen)


Lesenswert?

Rene B. schrieb:
> Also die Sounds klingen schon recht witzig. Old-School-8-Bit-Style :-)

Hallo Rene. Wenn schon.. denn schon. Es sind 12Bit Sounds :) Gerechnet 
wird intern mit 16Bit und das Ergebnis wird dann für den Xmega-DAC auf 
12Bit reduziert.

Eine Filtertechnik habe ich im Moment noch nicht integriert. Es läuft 
aber mit Sicherheit auf analoge Filter-Bausteine hinaus, weil der Xmega 
einfach zu wenig Rechengeschwindigkeit besitzt. Der ist schon voll mit 
der Soundausgabe, Hüllkurve, Midi und Grafischen Touch-Display 
beschäftigt, da bleibt keine Zeit mehr für eine Filterberechnungen. 
Allerdings weis ich noch nicht, was ich für Filter-Chips nehmen soll. 
Hatte Anfang der 90er Jahre mit dem Bau meines 1.Synthesizers von der 
Firma Doepfer (http://www.doepfer.de/home_d.htm) begonnen und dafür ein 
paar Curtis IC bestellt. Aber ich war "jung" und habe den Synthi nie 
fertig gebaut. Und nach den vielen Umzügen sind die Bauteile einfach 
verschwunden.

Hallo Maik. Ich habe mir die Beschreibung des Cyclone II Board mal 
angesehen. Kann man sich hier herunterladen: 
http://www.altera.com/products/devkits/altera/kit-cyc2-2C20N.html#note2
Das Board ist ja ganz schön groß und teuer im Verhältnis zum STM32F4 
Discovery Board, hat aber alles was man so brauch für einen Synthi. Für 
den Anfang bleibe ich erst einmal bei einem kleinen und preisgünstigen 
Board um damit Erfahrungen zu sammeln. Später trau ich mich dann an die 
größeren Boards wzB das EVK1104 evaluation Board von Atmel: 
http://www.atmel.com/tools/EVK1104.aspx und als Entwicklungs-Tool AVR 
Studio das ich jetzt schon für den Xmega benutze.

Gruß Rolf

von Rene B. (themason) Benutzerseite


Lesenswert?

@Rolf und Maik

was vllt auch noch als Platform sehr interessant wäre ist der Raspberry 
PI. Der hat ja nen 700MHz ARM am werkeln. Das sollte für digitale 
Synthies dicke reichen. Jedenfalls werd ich mich mit der Platform 
auchmal auseinandersetzen, selbst wenns eben nichts zum nachbauen ist.

@Rolf

Die Curtis-Chips hätte ich gerne mal in den Fingern. Aber die sind so 
schwer zu bekommen. Ich glaube da würd ich eher ein diskretes Design 
(State-variable-Filter, oder den klassischen Moog-Filter) nehmen.
Hab damals auch mal nen "klassischen" Filter analog aufgebaut. Aber das 
ganze ist dann irgendwann liegen geblieben.
Würde gerne aber bei meinem Audio-Projekt noch analoge Filter 
integrieren und mit jeweils nem eigenen Codec versehen. Dann hätte ich 
die Möglichkeit Signale zu mischen und beliebig zu routen (und eben auch 
durch den Filter). Wäre für nen Mischpult mit Effektgerät ganz nützlich, 
oder eben für nen Synthie (selbst wenn der dann nur max x-stimmig werden 
kann wobei x dann die Anzahl analoger Filter ist).
Aber reizvoll sind analoge Synthies allemale. Egal ob mit oder ohne 
digitale Komponenten.

von Rolf D. (rolfdegen)


Lesenswert?

Das "RasPi"-Board hat leider keinen eigenen DA-Wandler onBoard. Der 
Sound wird per PWM im Prozessor erzeugt und über einen einfachen 
RC-Filter nach außen geführt. Hier ist der Schaltplan: 
http://www.raspberrypi.org/wp-content/uploads/2012/04/Raspberry-Pi-Schematics-R1.0.pdf

Gruß Rolf

von Rene B. (themason) Benutzerseite


Lesenswert?

Na und ?

Der hat doch USB-Schnittstellen. Und ne Soundkarte ist doch schnell da 
dran gekorkt :-)
Wobei es mich mal interessieren würde wie schwer bzw unmöglich es ist 
bzw wäre das Board ohne Linux zu betreiben. Also quasi die gesamte 
Rechenleistung mit allem drum und dran für sich ganz alleine zu haben 
:-)
Und ich denke von den Schnittstellen her ist es bestimmt möglich auch 
nen guten Wandler noch dranzuflanschen. Notfalls per FPGA-Daughter Board 
:-)

von Rolf D. (rolfdegen)


Lesenswert?

Hinweis zum "Raspberry Pi". Der Erik Bartmann ist gerade dabei, ein Buch 
über den RasPi mit dem Titel "Raspberry Pi für Frühaufsteher" zu 
schreiben.

CC2-Link: 
http://www.cczwei-forum.de/cc2/thread.php?postid=76123#post76123

Gruß Rolf

von Maik M. (myco)


Lesenswert?

@Rolf: Ja das Board ist recht teuer im Vergleich zu den 
Controller-Dev-Boards. Das relativiert sich aber in dem Moment, wo man 
es mit anderen FPGA-Dev-Boards vergleicht. Auch kostet der FPGA auf dem 
Board alleine schon mehr als 50 Euro :)

Ich habe das EVK1105, ansich ist das nicht schlecht, nur hat das einen 
PCB-Fehler, was es extrem schwer macht das Teil zu programmieren. Und 
DFU wird in Atmel Studio 6 nicht mehr unterstützt, deswegen benötigt man 
ab da auch noch einen JTAG-Programmer.

@Rene: Ich habe mir ein paar Xilinx-Boards angeschaut, das günstigste 
mit Codec was ich gefunden habe ist das Digilent Atlys, was aber schon 
doppelt soviel kostet wie das CycloneII

Dein eigenes Layout ware natürlich optimal, weil das quasi das nötigste 
enthält. Ich würde gern eins nehmen, allerdings benötige ich es fertig 
aufgebaut. Da ich am Anfang meine "Lötfehler" als "mögliche 
Fehlerquelle" ausschließen möchte.

Das Pi kommt für mich nicht in Frage. Mein Ziel ist es ein 
Synthesizer/Effektgerät zu bauen, dh. später möchte ich eine eigene 
Platine fertigen können.

von Sam .. (sam1994)


Lesenswert?

Auf einem AVR ist schon einiges möglich - C, viele Kanäle und komplexe 
Sounds passen da aber nicht mehr zusammen. Das größte Problem ist die 
Rechenleistung für die vielen Kanäle. Wenn man 8 Kanäle realisieren 
möchte hat man bei 44100 Hz @32Mhz

32Mhz : 44100 : 8 = 90 Takte pro Kanal Zeit.

In meinem Synthesizerprojekt konnte ich die Takte/Kanal auf ~15 
reduzieren, dafür waren keine komplexen Sounds mehr möglich.

von Rene B. (themason) Benutzerseite


Lesenswert?

@Samuel

Hast du mal nen Link zu deinem Projekt ?
Würd mich mal interessieren. Im Prinzip ist mit den AVRs ja 
soundtechnisch schon einiges machbar wenn man Kompromisse bei der 
Samplingrate macht.
Aber Filter-Operationen sind halt bei der begrenzten Rechenleistung 
schwierig. Oder sehr Tricky :-)

von Sam .. (sam1994)


Lesenswert?

http://www.mikrocontroller.net/articles/AVR-Synthesizer
1
  add freqcnt1, hrfreq1      ;16-bit Zähler
2
  adc freqcnt2, hrfreq2
3
  brcs 1f
4
4:
5
  ;Tonerzeugung
6
  ;Im Register ueber freqcnt2 liegt der Index zur richtigen Soundtabelle  
7
  movw ZL, freqcnt2 
8
  lpm temp, Z
9
10
  ;Lautstärke
11
  mulsu temp, volsum1
12
13
        ;mischen
14
  add sample, 0
15
  adc sample2, 1

Ich habe die Hauptroutine nochmal herausgekramt:
Für einen Kanal sind es hier 11 Takte, allerdings kommt noch ein 
bisschen dazu für Register laden etc.
Wenn man so viel optimiert wird irgendwann der Ram zum Flaschenhals mit 
2 Zyklen pro Zugriff. Also werden alle Daten von bis zu drei Kanälen 
geladen und die nächsten 64 Samples werden berechnet.

von Rolf D. (rolfdegen)


Lesenswert?

Hallo zusammen

Zufällig habe ich auf einer japanischen Website ein interessante FM 
Sound Programm für das ST32F4 Discovery Board entdeckt. So wie es 
verstanden habe (mein Japanisch ist nicht besonders gut :), ist das 
Soundmodul 16stimmig polyphon, Samplingrate 48KHz und Midi fähig. Ich 
habe die Firmware über ST-Link Utility direkt mal ins Discovery geflasht 
. Nach dem Reset läuft sofort eine Sounddemo ab. Klingt nicht schlecht 
wenn ihr mich fragt..

Link: http://d.hatena.ne.jp/pcm1723/20120418/1334746384
Download: http://www.geocities.jp/pcm1723/html2/download.htm

MfG Rolf

von Rene B. (themason) Benutzerseite


Lesenswert?

@Rolf

Hast du vllt mal das Sound-Demo als MP3 oder nen Link wo man sich dieses 
Demo anhören kann ?
Ich hab leider keinen STM zur Hand um das mal ausprobieren zu können. 
Würde mich aber dennoch mal interessieren wie das klingt. FM-Synthese 
ist ja klanglich schon recht mächtig bzw bietet viele Möglichkeiten. Vor 
allem wenn man FM-Synthese als Klangquelle nutzt und mit einem 
nachgeschalteten analogen oder digitalen Filter "verfeinert" :-)
Das ist auch eins der Dinge die ich meinem Audio-Projekt gerne noch 
beibringen würde. Aber bisher hab ich mich mit der FM noch nicht weiter 
beschäftigt (also das ich das aufm PC mal "durchrechne" und einfach mal 
was rumspielen kann). Von daher danke schonmal für die Links. Vllt kann 
ich mir da was für aufn PC mal selbststricken.

von Rolf D. (rolfdegen)


Lesenswert?

Hallo Rene

Ich werde die Demo Heute in soundcloud.com bereitstellen und dich hier 
informieren. Ferner versuch ich Heute einen Optokopler an das Discovery 
Board "anzukleben", um  die Demosounds anzuspielen. Mal schaun obs 
funktioniert..

Hab noch eine interessante Projekt-Seite im INet gefunden. Nennt sich 
"Jo-Midi-FM". Ist eine japanische Website. Mit Google-Translate in 
Deutsch kann man es uebersetzen lassen.

Link: http://www.geocities.jp/pcm1723/

MfG Rolf

von Rolf D. (rolfdegen)


Angehängte Dateien:

Lesenswert?

Hallo Rene

Im Anhang findest du eine c.File aus dem FM Sound Projekt fuer das 
Discovery Board. Vielleicht hilft es dir.

Gruss Rolf

von Rolf D. (rolfdegen)


Lesenswert?

Hallo zusammen..

Ich habe an das Discovery-Board jetzt einen Midi-Anschluss gelötet, 
damit ich die Demo Sound des FM Sound Generator über ein Midi-Keyboard 
abspielen kann.

Schaltplan gibts hier: 
http://www.cczwei-forum.de/cc2/thread.php?postid=76348#post76348

Gruß Rolf

von Rolf D. (rolfdegen)


Lesenswert?

Hallo Rene

Ich habe den Demo-Sound des STM32F4 Discovery Boards mal ins Netz 
gestellt. Kann man sich als MP3 hier auf soundcloud.com anhören oder 
downloaden: http://soundcloud.com/rolfdegen/st32mf4-demo-02

Gruß Rolf

von Rene B. (themason) Benutzerseite


Lesenswert?

Hallo Rolf,

erstmal danke für das Demo. Hört sich ja recht schnuckelig an. (Hatte 
mir schon irgendwie gedacht das das so sanft und soft klingt, irgendwie 
haben die Japaner glaub ich einen Faible dafür :-))
Aber klingt sehr sauber das ganze. Ich glaub ich werd mich auch mal mit 
FM Synthese beschäftigen. Vllt bringe ich meinem FPGA sowas auch noch 
bei :-)

von Rolf D. (rolfdegen)


Lesenswert?

Hallo Rene

Naja.. Im Vergleich zu den Demo-Sound von deinem Spartan3 Board klingen 
die Sounds das Discovery sehr bescheiden.

Ich habe noch ein MP3-File mit einzelnen Klängen vom Discovery Board 
erzeugt und auf soundcloud.com hochgeladen. Hier der Link: 
http://soundcloud.com/rolfdegen/st32mf4-demo-03

Gruß Rolf

von Rolf D. (rolfdegen)


Lesenswert?

Ich werde jetzt erst einmal weiter an meinem 12Bit AVR-Synthi basteln 
und ein GUI mit einem Tochdisplay integrieren. Die Programmierung mit 
dem AVR ist auf jeden Fall leichter und unkomplizierter im Vergleich zum 
Cortex M4 Prozessor.

Gruß Rolf

von Rene B. (themason) Benutzerseite


Lesenswert?

Na ja ... aber im Endeffekt ist es nur ne Frage der Software. Das was in 
meinem Audio-Projekt abläuft lässt sich mit recht wenig aufwand 
sicherlich auch auf nem Discovery Board laufen lassen.
Die Filter die ich verwende sind ganz normale IIR-Biquads. Und die 
Berechnung der Koeffizienten erfolgt in einem AVR :-) Also alles im 
Prinzip kein Hexenwerk. Daher fänd ich das ja so interessant die FM mal 
so aufzuschnibbeln das ich das mit in meinem Synth reinbekäme :-)

von Rene B. (themason) Benutzerseite


Lesenswert?

Hab mir gerade mal die Sounds angehört. Sehr schöne Demos. Ideal als 
Oszillator. Dahinter noch nen schönen Resonanzfilter mit Hüllkurve und 
LFOs ... Yammy :-)

von Rolf D. (rolfdegen)


Lesenswert?

Der Hall bzw. die Echos in den Sounds kommen auch vom Discovery. Ich 
habe die Sounds über den Line-Out vom Discovery übers Mischpult und ohne 
Effekte aufgenommen.

Gruß Rolf

von Rene B. (themason) Benutzerseite


Lesenswert?

Ach ja ... der Hall in dem Demo. Wäre auch mal ein Interessanter 
"Opcode" für mein Audio-Projekt. Hab dir übrigens ne PN geschickt.
Die Demos waren vom TGFM oder von dem Jo-MIDI FM ?

von Rolf D. (rolfdegen)


Lesenswert?

Von Jo-MIDI FM Version 1.2

von Maik M. (myco)


Lesenswert?

Hab jetzt mein Altera-Board, leider wurde da nur ein Ami-Netzteil 
mitgeliefert, was mir hier natürlich nicht hilft. Hab es mal an mein 
Labornetzteil gehangen, und es scheint zu gehen. Mehr kann ich da aber 
nicht machen, weil ich in meiner Bastelecke keine Anschlussmöglichkeiten 
habe.

Der Code der mitgeliefert wird, ist in Verilog, was ein herber 
Rückschlag ist, da ich mich auf VHDL eingerichtet habe. Dennoch ist er 
recht überschaubar, und ich begreif wenigstens wie sie zum Ziel kommen. 
Es sind einige Audio-Demos dabei. Die Synthesizer-Demo nutzt 
Wavetable-Synthese (vom FPGA Ram), was auch keine Überraschung ist, da 
man damit schnell zum Ziel kommt.

Zum Thema FM: Ich habe nie verstanden, weshalb das immernoch so gefragt 
ist. Mit den Sounds kann ich irgendwie nichts anfangen. Vom techn. her, 
ist es auch nichts anderes als dass man den Ausgang eines Osc an den 
Frequenzeingang eines anderen Osc hängt.

Beim Hall wird es schon interessanter, ab einer gewissen Dichte wird 
dann auch einem M4 die Luft ausgehen. Bei meinem letzten Reverb den ich 
gebaut habe, brauchte ich alleine 74 Filter:
1 Lowpass
1 Highpass
8 Allpass
64 Comb

von Rolf D. (rolfdegen)


Lesenswert?

Hallo Maik

Welches Altera-Board hast du genau ?

von Rene B. (themason) Benutzerseite


Lesenswert?

@Maik

FM als alleiniger Sound ist auch recht langweilig. Aber FM als 
Oszillator mit schönen "dicken" Resonanz-Filtern dahinter macht schon 
was her.
Wenn ich mir den FM7 von NI z.b. anhöre. Ok, da ist die FM-Synthese ein 
büüschen aufgebohrt, aber gerade das zeigt ja eigentlich das man mit ein 
bissl Spielerei doch noch recht brauchbare Sounds rausholen kann.
Und es ist ja auch immer eine Frage wie komplex die Anordnung ist. Mit 
zwei/drei Sinusoszillatoren gewinnt man auch keinen Blumentopf. Aber bei 
6-8 Operatoren, entsprechenden Wellenformen, Filtern und Effekten klingt 
das durchaus mehr als brauchbar.

Zum Thema selbstgebautes FPGA-Board. Falls ich mein Redesign für mein 
Board mache und Interesse besteht kann ich dir (und auch dir Rolf) auch 
gerne eins zusammenlöten und testen.

von Maik M. (myco)


Lesenswert?

Rolf Degen schrieb:
> Welches Altera-Board hast du genau ?

Der komplette Name lautet: DK-DE2-2C35N/UNIV
Es ist dieses:
http://www.altera.com/education/univ/materials/boards/de2/unv-de2-board.html

Im Prinzip tut es auch das DE1, aber ich wollte ein Board, wo ich ohne 
zu basteln alles drauf habe (LCD fehlt beim DE1). Und der Vorteil bei 
Dev-Boards ist ja, dass für alles was darauf ist, auch Demos vorhanden 
sind, und ich lerne am funktionierenden Code einfach schneller :)

Rene B. schrieb:
> Zum Thema selbstgebautes FPGA-Board. Falls ich mein Redesign für mein
> Board mache und Interesse besteht kann ich dir (und auch dir Rolf) auch
> gerne eins zusammenlöten und testen.

Ja, Interesse ist immernoch da. Ein simples FPGA-Audio Modul wäre eine 
feine Sache, sowas findet man leider nirgends (--> Marktlücke :))

von Rolf D. (rolfdegen)


Lesenswert?

Dem kann ich nur zustimmen. Hätte auch Interesse an einem Sound-Board 
von Rene. Zumal man dann auch ein Ansprechpartner bei Problemen und 
Ideen hat.

Gruß Rolf

von Rene B. (themason) Benutzerseite


Lesenswert?

Na ja.

Ich hab damals schon nach "mitstreitern" gesucht und auch vereinzelt 
gefunden. Aber das Problem ist eben immer der Preis. Der hängt eben 
stark davon ab wieviele Leute sich finden. Zumal der "Löwenanteil" des 
Preises in erster Linie durch die Platine bestimmt wird. Ein Einzelstück 
(nur die Platine) kostet dann gerne 60-70 Euro. Bei 10 Leuten sinds dann 
nur noch ca 15 Euro (oder so).
Die restlichen Bauteile liegen dann so im Bereich 50-60 Euro.

Also ich geb mal die Eckdaten was ich mir Vorstelle an Hardware bzw was 
ich Schaltplan und Layoutmäßig schon fertig habe :

- Spartan 6 FPGA (LX9) nebst SPI-Flash
- 8/16/32MB SDRAM (je nach Beschaffbarkeit)
- Audio-Codec (TLV320AIC23B) mit Line In/Out, Kopfhöreranschluß, 
Mikrofoneingang (aber nur der Kopfhörerausgang hat ne Klinkenbuchse)
- µSD-Karten-Slot
- AVR ATMega644P/1284P
- FTDI FT232 um mit dem AVR quatschen zu können und gleichzeitig die 5V 
für die Platine
- 8 Analogeingänge (über einen Multiplexer)
- Stiftleisten für den FPGA
- Stfitleisten für den AVR

Was ich noch am überlegen bin da drauf zu flanschen ist :
- MIDI-Interface (je nach Platz)
- einfaches R-Netzwerk um VGA-Signale auf eine Stiftleiste zu geben


Ich schau mal das ich heute Abend den Schaltplan so wie er momentan ist 
hier mal reinstelle.
Vllt schaffe ich es beim dritten Anlauf ja mal genug Leute für so ein 
Projekt zusammenzutrommeln.

@Maik

Wenn das wirklich so ne Marktlücke ist würde ich diese gerne Ausfüllen. 
Aber alleine bekomm ich das nicht hin (Vertrieb, Gewerbe, WEEE oder wie 
das heißt usw). Tät mir gerne damit was dazuverdienen :-)

von Maik M. (myco)


Lesenswert?

Ich helfe, soweit es mir möglich ist. Mit FPGAs fang ich "praktisch" 
gerade erst an, "theoretisch" bin ich schon weiter.

Auf das Board würde ich nur das nötigste packen, alles das womit ein 
einfacher µC-User bisher nichts zu tun hatte. Also im groben:

- das Board hat nur eine einzige Eingangsspannung (5V oder 3,3V)
- daraus folgt: alle Spannungsregler onboard
- FPGA + Config-Rom/Flash
- RAM
- Codec (ggf. mit Amp)

Mehr müsste da nicht drauf sein. Aber es gibt noch ein paar Dinge wo ich 
mir nicht sicher bin. zB. man könnte die Klinkenbuchsen draufbauen, aber 
was ist wenn man das Board nur als "Kern" einsetzt, und das Trägerboard 
die eigentlichen externen Anschlüsse trägt?
Weiß nicht, wie es beim Programmieren vom Spartan ist, beim Cyclone 
braucht man JTAG+AS und natürlich den passenden Programmieradapter 
(Original: 200 Euro).

Den µC würde ich keinesfalls mit auf das Board nehmen, weil jeder 
bevorzugt ja einen anderen. Auch kann man den zur Not auf ein Breadboard 
setzen. Gleiches gilt für Midi. Man könnte den Midi-Anschluss ja an den 
FPGA hängen oder halt an den µC. Das sollte dem User überlassen werden.

von Rolf D. (rolfdegen)


Angehängte Dateien:

Lesenswert?

Ich werde das Soundmodul später in mein kleines Midi-Keyboard einbauen 
und über ein Grafisches Display mit Touch-Panal-Funktion ansteuern. Ich 
hab mal eine Fotomotage im Anhang gemacht. So solls später aussehen. Im 
Midi-Keyboard ist Platz genug für zwei Eurokarten voll Elektronik.

@Rene. Hab auf der CC2-Website mal ein wenig Werbung für dein Projekt 
gemacht. Vielleicht meldet sich ja noch der Eine oder Andere bei dir.

LG Rolf

von Rene B. (themason) Benutzerseite


Lesenswert?

@Maik

Die Platine wird mit 5V versorgt (über USB) und hat die LDOs für 3.3V 
und 1.2V drauf.
Den uC möchte ich mir nur ungern "klemmen" da mich schon bei den 
früheren Platinen genervt hat das ich immer nen uC separat drankorken 
musste.
Und der AVR kann ja auch anfangs die Programmierung des SPI-Datenflashs 
übernehmen. Ist ja nicht zwingend erforderlich das du den nutzt um den 
FPGA zu "bespaßen" :-)
Und außerdem kann man ja über die Stiftleisten noch nen anderen uC an 
den FPGA drankorken.

@Rolf
Vielen dank für die Werbung :-)
Und dein "Preview" zum AVR WAVE 1 im Keyboard sieht recht schnuckelig 
aus :-)
Wollte das mit meiner Platform auch machen. Bin aber noch nicht dazu 
gekommen.

von Rene B. (themason) Benutzerseite


Angehängte Dateien:

Lesenswert?

So ... hier mal Schaltplan und die Bauteilplatzierung von dem neuen 
Board so wie es ist.

von Maik M. (myco)


Lesenswert?

Schaut recht kompakt aus. Wieviel Layer sind das?

Wie gesagt, würde ich das unnötige weglassen. Wenn das Board so klein 
ist, könnte man das sogar als Arduino-Shield auslegen... damit hätte man 
eine wesentlich größere Reichweite. In diesem Fall könnte man den 
restlichen Platz für die 2 freien Audio-I/O nutzen und sogar Midi noch 
mit draufbauen.

von Rene B. (themason) Benutzerseite


Lesenswert?

@Maik

Es sind genau 2 Layer :-)

Die Idee das Teil zu nem Arduino Shield umzubauen wäre evtl noch ne 
Idee.
Nur sollte man dann die großen Arduino nehmen. Mit 32K kommt man u.u. 
nicht so weit. Und es müssten noch einige Levelshifter platziert werden, 
um die 5V Signale des Arduinos kompatibel mit den 3.3V Signalen des 
FPGAs zu machen. Und man müsste sich überlegen wie man das macht wenn 
das Board an einen 3.3V uC statt eines 5V uCs angekorkt wird (obwohl es 
da u.u. reicht die Spgsversorgung für die Levelshifter Jumperbar zu 
machen.

von Rolf D. (rolfdegen)


Lesenswert?

Hallo

Ich habe diese Tage ein wenig an der Soundquallität meines Synthis 
gefeilt und die Auflösung von 8Bit auf 12Bit geändert. Zusätzlich habe 
ich die 12Bit Sinustabelle von 256 Tabellenwerten auf 4096 Werte erhöht. 
Die Soundausgabe klingt jetzt rauschärmer und erzeugt bei der Wiedergabe 
von hohen Tönen weniger Verzerrungen.

Sinuston (12Bit und 8Bit) auf einem ATxmega128A1 mit internem 12Bit DAC
http://soundcloud.com/rolfdegen/sinus_12bit_8bit

Gruß Rolf

von Rolf D. (rolfdegen)


Angehängte Dateien:

Lesenswert?

Hallo

Ich möchte die benötigte Zeit für die Soundaugabe von zwei 
Sinus-Oszillatoren in meinem C-Code verringern (siehe Anhang), um einen 
dritten Oszillator zu implementieren . Die Interrupt-Routine benötigt 
für die Ausgabe von zwei Sinustönen auf einen DAC-Kanal schon über 
17µsec. Für einen dritter Oszillator reicht die Zeit nicht mehr aus.

Hat jemand von euch vielleicht eine Idee, was ich an meinem Code 
verbessern könnte ? Jeder Ratschlag ist willkommen. Ich dachte auch 
schon daran, die DAC-Ausgabe in Assembler zu schreiben und in den C-Code 
mit einzubinden. Aber ich habe keine Ahnung, wie ich das in Atmel Studio 
6.0 machen könnte.

Ich bedanke mich schon im Voraus für eure Tips.

LG Rolf

von Maik M. (myco)


Lesenswert?

schrittweite_temp kannst du weglassen, und direkt mit 
schrittweite1/schrittweite2 rechnen. Spart Ram und mind. 4 Zyklen.

Die Accus kannst du Global auch als Register definieren, dann dürftest 
du einiges an Zyklen sparen.

Kannst auch mal hier schauen:
http://www.mikrocontroller.net/articles/AVR-GCC-Codeoptimierung

Aber so etwas Zyklenkritisches würde ich eh komplett in Assembler 
angehen. Dann hat man eine genaue Kontrolle.

von Rolf D. (rolfdegen)


Angehängte Dateien:

Lesenswert?

Hallo Maik

Danke für deinen Tip. Das Ändern des Phasenaccus auf eine globale 
Register-Variable mit "register32_t"  hat leider nicht viel gebracht. 
Ich habe testweise die Bit-Breite des Phasenaccus von 32Bit auf 16Bit 
reduziert und komme jetzt auf eine Berechnungszeit von 2µsec pro 
Oszillator. Ingesammt benötigt die Interrupt-Routine jetzt etwas weniger 
als 9µsec (vorher 17µsec).

Durch die Änderung des Phasenaccus von 32Bit auf 16Bit reduziert leider 
auch die Frequenzauflösung auf ca. 1,34 Hz pro Schritt. Als nächstes 
werde ich versuchen, die Interrupt-Routine in Assembler zu schreiben und 
in meinen C-Code mit einzubinden. Mal schaun obs funktioniert..

Gruß Rolf

von klaus (Gast)


Lesenswert?

Hallo,

wie berechnest Du die Hüllkurve bzw. die Variable adsr_1?

Kannst Du den C code mal einstellen?

Gruß

von Rolf D. (rolfdegen)


Angehängte Dateien:

Lesenswert?

Hallo Klaus

Ja gerne, ist kein Geheimnis. Die Routine läuft über einen 
Timer-Interrupt und wird alle 1msec aufgerufen. Der berechnete 
Hüllkurvenwert für den DAC hat eine Auflösung von 12Bit. Die Routine ist 
alledings noch nicht ganz fertig, funktioniert aber in groben Zügen 
schon.

Gruß Rolf

von Rolf D. (rolfdegen)


Lesenswert?

@Maik

Hallo

Ich konnte die Ausführungszeit meiner Sound-Routine nochmals von 9µsec 
auf 6.5µsec reduzieren, indem ich die Compiler-Optimierung in Atmel 
Studio 6 von "Os" auf "O2" geändert habe.

Mehr Infos zum Thema gibts hier: 
http://www.cczwei-forum.de/cc2/thread.php?postid=76427#post76427

Gruß Rolf

von Musikus (Gast)


Lesenswert?

Hallo ihr Musikbegeisterten,

habe selbst mit einem 80MHz-16Bit-DSP Musik in C erzeugt, dort geht 
alles eleganter: z.B. 16x16-Mul in 25ns.
Finde es jedoch umso beachtlicher, dass es auch mit 8Bit und 20 MHz 
akzeptabel klingt.

Wenn man den Asm-Code anschaut, merkt man, wo noch gespart werden kann:
Die häufigst verwendeten Variablen sollen in festen Registern stehen.
Ich weiß nicht aus dem Stegreif, wie man das dem GCC beibringt.

Die im oberen Link genannte Anote AVR200 ist völlig ungeeignet, da sie 
für kleine AVRs ohne MUL-Befehl gedacht ist.

Auf jeden Fall lesenswert sind folgende Beiträge:
Beitrag "DDS normal ?"
Beitrag "DDS Sinus erzeugung"
Beitrag "Rechnen mit AVR"

von Rolf D. (rolfdegen)


Lesenswert?

Hallo

Ich versuch mich gerade mal mit Assembler. Ich möchte die komplette 
Soundausgabe über eine Assembler-Routine in meinem C-Programm machen.

Sie dazu den Beitrag im CC2-Forum: 
http://www.cczwei-forum.de/cc2/thread.php?postid=76548#post76548

Gruß Rolf

von Alessandro R. (alessandro_r)


Lesenswert?

Hallo an alle,

Wenn es von Ihrem Interesse ist, schauen Sie bitte auf
http://www.youtube.com/watch?v=4JkhzH57Je4
und geben Sie mir Ihre Meinung. Jeder Vorschlag wird zu schätzen wissen 
!!

von Rolf D. (rolfdegen)


Lesenswert?

Hallo. My name is Rolf Degen from germany.
It is a very good project. I'm I also currently developing a synthesizer 
on an AVR 8-bit processor basis.

My Link: http://www.cczwei-forum.de/cc2/thread.php?threadid=5878

I'm German and all information on this site is in german. Sorry
Greetings from germany

I can't writen a link on your youtube-site. Why ???

Greetings Rolf

von alessandro_r (Gast)


Lesenswert?

Hello Rolf,

This is a YouTube restriction. I see that you have avoided the obstacle 
!
I suggest you to open a parallel english page (for example on EmbDev.net 
) to give more visibility at your project.
I also have developed some project in my native language, but due to the 
poor quality of the various translator utility, I get only local 
audience.

Best regards, Alessandro.

von Rolf D. (rolfdegen)


Angehängte Dateien:

Lesenswert?

Hallo

Rolf Degen schrieb:
> Ich versuch mich gerade mal mit Assembler. Ich möchte die komplette
> Soundausgabe über eine Assembler-Routine in meinem C-Programm machen.

Zum Programm: Der Assembler-Code beinhaltet jetzt die komplette Timer1 
Interrupt-Routine für die Soundausgabe der zwei DCO's inklusive der 
Hüllkurve. Die Laufzeit beträgt insgesammt 5,53 µsec. Der Aufruf der 
Interrupt Routine erfolgt alle 25 µsec durch die Interrupt-Freigabe im 
Hauptprogramm (C-Code).

Mehr Info gibts hier: 
http://www.cczwei-forum.de/cc2/thread.php?postid=76539#post76539

Gruß Rolf

von Rolf D. (rolfdegen)


Angehängte Dateien:

Lesenswert?

Hallo zusammen

Dank großartiger Unterstützung von Wolfgang aus dem CC2-Forum 
(http://www.cczwei-forum.de/cc2/), konnte ich die Assembler-Routine für 
meinen AVR-Synthi noch etwas verbessern. Hinzugekommen ist die 
Möglichkeit, außer einer Sinus-Wellenform auch eine Sägezahn-Wellenform 
auszuwählen. Der Parameter dafür wird als 8Bit Volatile Variable aus dem 
C-Programm (Main-Programm) übergeben.

Wolfgangs Website: http://www.wiesolator.de/

Gruß Rolf

von Rolf D. (rolfdegen)


Lesenswert?

Hallöchen..

Gibt noch ein paar neue Sounds vom AVR-Synthi.

SAW-Sounds mit zwei Oszillatoren:
http://soundcloud.com/rolfdegen/saw-oszillator

FM-Sounds (1 Operator):
http://soundcloud.com/rolfdegen/fm-01

Gruß Rolf

von Rolf D. (rolfdegen)


Angehängte Dateien:

Lesenswert?

Hallo

In meinem Code ist noch eine einfache FM-Sound Erweiterung 
hinzugekommen.

LG Rolf

von Rolf D. (rolfdegen)


Angehängte Dateien:

Lesenswert?

Hallo

Ich bin gerade dabei, die Hüllkurvenansteuerung in meinem AVR-Synthi zu 
verbessern. Problematisch war die Release-Phase, da sie nur einen 
geraden Verlauf hatte. Aber was wäre ich ohne die Unterstützung von 
Wolfgang (ein Genie wenn ihr mich fragt). Er hatte mich auf die Idee 
gebracht, die Release-Phase mit Hilfe einer kleinen Umrechnung in eine 
angeglichene e-Funktion umzusetzen (siehe Bild).

Hier die Berechnung für die Release-Phase:

ADSR_Hoehe = 65535
Release_Time = 2- 65535

if (ADSR_Hoehe > 0)
  {
    ADSR_Hoehe = ADSR_Hoehe - ((ADSR_Hoehe / Release_Time)+1);
  }

Der Übergabewert für ADSR-Steuerung in meiner Assembler-Routine muss auf 
12Bit reduziert werden: adsr_1 = ADSR_Hoehe >>4


Bild-Anhang: Neue Release-Phase im AVR-Synthi

Mehr zum Thema hier: 
http://www.cczwei-forum.de/cc2/thread.php?postid=76736#post76736

Gruß Rolf

von Sam .. (sam1994)


Lesenswert?

Ich würde mir überlegen, ob du das ganze wirklich so genau brauchst. In 
meinem Synthesizer ist der Akkumulator 16bit groß (In meinem Quelltext 
steht davon nichts, da ich beim Programmieren nicht mal DDS kannte, 
sondern selbst überlegt habe wie ich verschiedene Frequenzen erzeuge). 
Die Hüllkurve ist nur 8bit groß, zusätzlich gibt es eine 
Lautstärkeregelung für jeden Kanal (8bit).
Ich würde in die Tonerzeugung nicht einfach mal so eine 16bit Division 
reinhauen. Bei mir hat es quadratisch schon ziemlich gut funktioniert. 
Pseudo-Code:
1
counter = 255;
2
...
3
if(counter < release_time)
4
   Relase_volume = 0;
5
else
6
{
7
   counter -= release_time;
8
   Release_volume = counter * counter >> 8; 
9
}

Das lässt sich in Assembler in wenigen Takten schreiben.

Eine Optimierung, die ich dir dringend empfehle ist einen Puffer 
anzulegen, in den du die Samples berechnest. Das kannst du dann in der 
Hauptschleife machen, während DMA die Daten ausgibt. Wichtig ist, dass 
die Erzeugung immernoch in Assembler geschrieben wird. Allerdings werden 
immer zuerst die Register mit den Werten geladen, dann z.B 64 Samples am 
Stück berechnet und schließlich die Register zurückgeschrieben. Dadurch 
fallen alle Schreibzugriffe auf das SRAM während der Tonerzeugung weg. 
In meinem Synth werden alle Register während der Tonerzeugung genutzt: 
Dadurch ist es möglich 3 Kanäle gleichzeitig zu berechnen und zu 
mischen.

PS: Die Ideen zur Sounderzeugung in deinem Forum sind wirklich gut. Mein 
Synthesizer spielt zurzeit zwischen um die 35 Kanäle gleichzeitig bei 
25Mhz (44,1kHz). Allerdings sind die Sound relativ einfach, da sie nur 
aus 512Byte-Tabellen geladen werden. Vielleicht werde ich versuchen auf 
Kosten der Kanäle die Sounds zu verbessern.

von Rolf D. (rolfdegen)


Lesenswert?

Hallo Samuel

Erst einmal Danke für dein Interesse an meinem Synthi-Projekt. Die Idee 
mit einem Sample-Buffer ist schon interessant. Ich frag mich nur, wie 
ich andere Dinge wzB den Midi-Buffer, Touch-Panel und Grafik-Display 
noch ansteuern bzw auswerten kann, wenn der Prozessor im Main-Programm 
mit der Berechnung  von Sample-Daten beschäftigt ist.

Meine Konstruktion sieht so aus, das ich die komplette Berechnung und 
Ausgabe der 12Bit-Sounds in einer Timer Interrupt-Routine alle 25µsec 
durchführe (Samplerate: 40Khz). Die Laufzeit der Interrupt-Routine 
beträgt insgesamt je nach Syntheseart (SUB,FM) und Wellenform (Sinus, 
Sägezahn) zwischen 5.4µsec und 5.9µsec für zwei Oszillatoren 
gleichzeitig. Dadurch bleibt noch genügend Reserve übrig, um 
Grafik-Display, Touch-Panel und Midi-Daten zu verarbeiten.

Später soll am DAC-Ausgang auch noch ein Analoger Filterbaustein wzB. 
SSM2044 oder FM10 hinzu kommen. Dieser wird dann über die Hüllkuvendaten 
in der Assembler-Routine gesteuert.

Gruß Rolf

von Rene B. (themason) Benutzerseite


Lesenswert?

Wenn ich das richtig verstanden habe macht die Blockweise berechnung nur 
dann wirklich Sinn wenn man DMA nutzt.
Einzig der Overhead der durch das Wegspeichern und Wiederherstellen der 
Prozessor-Register pro Sample ansteht wird minimiert. Die Berechnung 
selbst wird ja nicht unbedingt kürzer. Ok, es hängt noch vom Algorithmus 
ab, ob man beispielsweise vorher noch was "größeres" berechnen muss 
bevor die eigentliche Audio-Verarbeitung losgeht. Aber im Prinzip spielt 
das bei den "kleinen" Prozessoren keine Rolle. Gravierender wird das 
wenn (wie beim PC) Cache ins Spiel kommt. Dann macht das u.u. erheblich 
was aus einen Buffer vorzuberechnen, da dann der "Kontext-Wechel" nicht 
bei jedem Sample neu gemacht werden muß, bzw benötigte Variablen nicht 
erst mühsam bzw u.u. zeitaufwendig aus einem anderen Speicherbereich 
geholt werden müssen.
Aber im Prinzip spricht nichts dagegen direkt Blockweise zu arbeiten. 
Man muß sich nur je nach Blockgröße klarmachen das dadurch eben auch 
eine Latenz entsteht (ok das Problem hat man eher auf einem PC), bzw bei 
Blöcken die größer als 10ms sind die zeitliche Zuordnung der 
MIDI-Signale auch nicht ganz unproblematisch ist. Aber wer berechnet 
denn schon 480 Samples im voraus :-)
Ich hätte vllt auch noch einen Ansatz für die Hüllkurvengeschichte. Ist 
aber auch was aufwendiger.
Ich habe die einzelnen Phasen anders definiert. Und zwar wird auf ein 
Register immer der jeweilige Attack/Decay/Release Wert aufaddiert. Der 
Akku ist quasi eine Skalierung für eine Tabelle (beispielsweise 0-255). 
In dieser Tabelle steht dann linear/exponentiell/umgekehrt exponentiell 
der Lautstärke Verlauf (von 0-1). Die nächste Phase beginnt dann wenn 
der Akku überläuft. In der Sustain Phase wird der Akku immer genullt.
Man kann dann somit für jede steigende/fallende Phase unterschiedliche 
Charakteristika definieren. Bei der Lautstärke macht sich das vllt nicht 
so bemerkbar, aber wenn die Hüllkurve z.b. einen Filter langsam steuert 
hört sich das dann schon unterschiedlich an. Außerdem kann man mit 
dieser Phasen/Zeit-Unterteilung auch andere Hüllkurven leichter 
realisieren. z.b. eine AHDSR, HADSHR oder die mehrphasigen Hüllkurven 
von z.b. Roland Synthies recht einfach implementieren, ohne jedesmal den 
Code umzuschreiben. Mal so als Anregung für deinen FM-Synthie :-)

von Rolf D. (rolfdegen)


Lesenswert?

Hallo Rene

Ich werde erst einmal bei meinem Konzept bleiben und langsam mein 
Touch-Panel und Grafik-Display für Benutzereingaben programmieren. 
Dadurch spare ich mir die ständige Neuprogrammierung des 
Xmega-Prozessors für diverse Sound- und Hüllkurven-Test.

Deine Idee für die Hüllkurvensteuerung finde ich sehr interessant und 
werde es Später mal ausprobieren.

Hab gerade mal nach dem Filter IC "SSM2044P" gegooglet. Bei 
http://darisusgmbh.de/shop/index.php gibts das IC für nur 14,28 Euro. 
Lieferzeit zwei Wochen.

Gruß Rolf

von Sam .. (sam1994)


Lesenswert?

Der Puffer bringt schon ziemlich viel: Ich hab dein Asm-Programm mal 
überflogen, da sind > 50 Ramzugriffe drin -> mehr als 100 Takte werden 
für das Laden/Speichern/Sichern verbraucht.

Wenn man 64 Samples am Stück rechnet ist es nur noch 1/64 der Zeit.

Wenn du noch etwas optimieren willst, dann ist der Puffer wahrscheinlich 
die beste Variante.

@Rene

Selbst ohne DMA macht es Sinn. Bei mir hat diese Optimierung das 
Programm mehr als doppelt so schnell gemacht (Atmega328 hat leider kein 
DMA).

von Rolf D. (rolfdegen)


Angehängte Dateien:

Lesenswert?

Hallo zusammen

Ich habe die Hüllkurvensteuerung für den AVR-Synthi noch einmal 
überarbeitet (siehe C-Code im Anhang). Die Parameter habe ich auf 16Bit 
Breite belassen, weil dadurch auch außergewöhnlich lange Attack- und 
Release-Phasen für spezielle Soundeffekte möglich sind. Im Anhang ein 
Bild vom neuen Hüllkurvenverlauf im AVR-Synthi.

@Samuel
Ich werde auf jeden Fall deine Idee mit dem Sample-Buffer im Sinn 
behalten und es Später versuchen umzusetzen. Zur Zeit arbeite ich noch 
an vielen Baustellen im Synthi und die Optimierungen stehn da noch ganz 
hinten an.

Gruß Rolf

von Rolf D. (rolfdegen)


Lesenswert?

Fortsetzung:

Ich glaube das Wolfgang aus dem CC2-Forum ein ähnliche Buffer-Funktion 
in seinem Synthi-Projekt realisiert hat (Link: 
http://www.wiesolator.de/index.php?area=Musik&topic=AVR-PolySynth).

Gruß Rolf

von Klaus (Gast)


Lesenswert?

Hallo,

kannst Du bitte mal den kompletten code posten?
Ich möchte das auf einem AVR32 testen.



Viele Grüße

von Rolf D. (rolfdegen)


Angehängte Dateien:

Lesenswert?

Hallo Klaus

Im Anhang findes du meinen gepackten Quellcode. Es sind zwei Dateien. 
Das Main-Programm in C und die Assembler-Routine, die per 
Timer-Interrupt im Main-Programm aufgerufen wird. Ich arbeite unter 
Atmel Studio 6 mit GCC 4.7.1.

LG Rolf

von Klaus (Gast)


Lesenswert?

Hallo Rolf,

vielen Dank, ich versuche mich jetzt daran den code auf dem AVR32 zum 
laufen zu kriegen.

Viele Grüße

von Rolf D. (rolfdegen)


Lesenswert?

Hallo liebe Musikliebhaber auf mikrocontroller.net

Es gibt mal wieder was auf die Ohren von meinem Synthi. Aus purer Freude 
an den Klängen aus meinem Synthi und weil es irren Spass macht, damit 
rum zuspielen, habe ich im Synthi einen kleinen Live-Sequenzer 
programmiert. Das Ergebnis könnt ihr euch auf SoundCloud anhören. 
Allerdings hege ich keinen Anspruch auf kompositorische Fähigkeiten :-)

Link SoundCloud: http://soundcloud.com/rolfdegen

Aber Vorsicht, die Demo-Sequenzen sind von mir live gespielt und nur für 
Ohren gedacht, die Elektronischen Musik wzB von Klaus Schulze, 
Kraftwerk, Stockhausen und Co lieben :)

Wünsch euch viel Spass beim hören.

Gruß Rolf

von Rolf D. (rolfdegen)


Angehängte Dateien:

Lesenswert?

Hallo zusammen

Ich habe die Attack-Phase in der Hüllkurvensteuerung meines AVR-Synthis 
etwas verbessert. Die neue Hüllkurve kommt einem analogen Synthesizer 
schon sehr nahe. Mehr Infos gibts hier: 
http://www.cczwei-forum.de/cc2/thread.php?postid=76877#post76877

LG Rolf

von Rolf D. (rolfdegen)


Lesenswert?

Hallo liebe Synthi-Fans

Es gibt mal wieder was auf die Ohren. Dieses mal habe ich ein paar 
Demo-Sounds durch den spannungsgesteuerten LowPass-Filter (CEM3320) im 
AVR-Synthi gejagt. Die Filterfrequenz wird durch einen eigenen 
ADSR-Generator gesteuert. Die Filterresonanz manuell über einen Regler 
am Keyboard. Was leider noch fehlt, ist die Modulation durch einen LFO 
(LowFrequenzOszillator).


AVR-Synthi Demo-Sounds mit LP-Filter:
http://soundcloud.com/rolfdegen/filter-wave-01


Die Steuerung der Filterfrequenz- und Resonaz erfolgt über zwei 
PWM-Ausgänge am Xmega-Prozessor.

Gruß Rolf

von Rolf D. (rolfdegen)


Lesenswert?

Hallo

Es gibt wieder etwas neues im AVR-Synthi. Ich habe jetzt drei LFO's für 
die Modulation des Oszilators, Filter und DAC implementiert.

Mehr Infos gibts hier: 
http://www.cczwei-forum.de/cc2/thread.php?postid=77145#post77145

Gruß Rolf

von Rolf D. (rolfdegen)


Angehängte Dateien:

Lesenswert?

Hallo zusammen

Ich habe einen Digitalen Noise-Generator (nach einer Idee von Wolfgang 
aus dem CC2-Forum) in den AVR-Synthi implementiert (siehe 
Klangbeispiel). Zusätzlich habe ich ein aktuelles Prinzipschaltbild mit 
den Funktionsblöcken gezeichnet.

Klangbeispiel "Noise": http://soundcloud.com/rolfdegen/avr-synthi-noise

Im Anhang das Prinzipschaltbild und mein Programm. Das ganze ist leider 
noch eine Baustelle und soll euch nur eine Stütze für eigene Ideen für 
euer Projekt sein.

Gruß Rolf

von Sascha W. (arno_nyhm)


Lesenswert?

Hallo Rolf,

ich möchte Deinen Thread nicht hijacken, aber gestatte eine kurze 
OT-Frage:
Das scheint ein (Xplain?) Experimentierboard mit einem ATxmega128A1 + 
S(D)RAM zu sein - genau das könnte ich für ein eigenes Projekt zur Zeit 
gut gebrauchen...
Könntest Du mir sagen wie sich das Board nennt und ggf. woher man es 
bekommen kann?

Grüße
Sascha

von Jörg (Gast)


Lesenswert?

Sascha W. schrieb:
> Das scheint ein (Xplain?) Experimentierboard mit einem ATxmega128A1 +
> S(D)RAM zu sein - genau das könnte ich für ein eigenes Projekt zur Zeit
> gut gebrauchen...
> Könntest Du mir sagen wie sich das Board nennt und ggf. woher man es
> bekommen kann?


http://www.cczwei-forum.de/cc2/thread.php?postid=72107#post72107

von Rolf D. (rolfdegen)


Lesenswert?

Hallo Sascha

Es handelt sich um das  Atmel XMEGA-A1 Xplained Evaluation-Kit. Ich habe 
es bei Reichelt gekauft (siehe Link).

Link Reichelt: 
http://www.reichelt.de/Programmer-Entwicklungstools/AT-AVR-XPLAIN/3/index.html?;ACTION=3;LA=2;ARTICLE=96802;GROUPID=2969;artnr=AT+AVR+XPLAIN;SID=10T4MPon8AAAIAADLU5TM70bd5d4d8cb84aecb656096e17edfa00

Gruß Rolf

von Sascha W. (arno_nyhm)


Lesenswert?

Super - Danke Euch für die Hinweise! Ist zur Abwechslung ja auch mal 
einfach zu bekommen, nicht nur direkt aus Hongkong ;)

Grüße
Sascha

von Rolf D. (rolfdegen)


Lesenswert?

Hallo

Es gibt wieder was neues über die Stromversorgung des AVR-Synthis. 
Näheres auf: 
http://www.cczwei-forum.de/cc2/thread.php?postid=77648#post77648

Gruß Rolf

von Rolf D. (rolfdegen)


Lesenswert?

Hallo und guten Morgen

Ich habe einen Schaltplan für den AVR-Synthi gezeichnet. Auf 
http://www.cczwei-forum.de/cc2/thread.php?postid=78070#post78070 kann 
man sich jetzt den Schaltplan ansehen.

Gruß Rolf

von Rolf D. (rolfdegen)


Angehängte Dateien:

Lesenswert?

Hallo

Ich habe jetzt das Bedienschema fuer das Touch Panal Display fertig 
gestellt. Die Systemeinstellungen fuer den Synthesizer werden ueber 
mehrere Menue-Seiten aufgerufen und koennen dort veraendert und 
abgespeichert werden. Das Aufrufen der einzelnen Menue-Seiten erfolgt 
ueber die Tabreiter (siehe Youtube-Video).

Bild: Touch Panal Menue AVR-Synthi

Youtube-Video: 
http://www.youtube.com/watch?v=KFm-GHSx_5o&feature=youtu.be


Im Moment sind die Menue-Seiten noch leer. Ich werde mir jetzt Gedanken 
darueber machen muessen, wie ich die Synthesizer-Funktionen in den 
Menue-Seiten sinnvoll gestalte.


Gruß Rolf

von Rolf D. (rolfdegen)


Angehängte Dateien:

Lesenswert?

Hallo

Habe Heute das 1.Menü für den Audio-Filter im Synthesizer entwickelt 
(siehe Bild). Die Fader sind groß genug und lassen sich auch leicht mit 
einem Finger bedienen. Leichter und feinfühliger gehts natürlich auch 
mit einem Tabstift. Ich benutze zum Beispiel einen alten Kugelschreiber 
mit einer Kunststoffspitze. Funktioniert damit prima und hat nix 
gekostet.

Bild: VCF1-Menü auf dem Touch Panel des AVR-Synthis

Durch die Anregung von verschiedenen Usern, habe ich mich dazu 
entschieden, für die Parameter-Eingaben zusätzlich einen Drehencoder mit 
Rast- und Tasterfunktion zu verwenden. Damit werden die Parametereingabe 
noch leichter und komfortabler.


Gruß Rolf

von Rolf D. (rolfdegen)



Lesenswert?

Hallo zusammen

Ich habe Probleme mit Phasen-Jitter in meinem Synthi-Projekt. Der
Phasen-Jitter entsteht, wenn der Wert des Phasenaccus mit dem Wert für
die Schrittweite nicht gerade teilbar ist. In meinem Fall beträgt der
Jitterwert max 25µsec = 40KHz und das ist genau die Sample-Frequenz für
die Tonausgabe (siehe Bild 1+2). Wenn ich für die Schrittweite eine Wert
nehme, mit dem der Phasenaccu (24Bit) gerade teilbar ist, sind keine
Störungen warnehmbar und auf dem Oszilloskop ist eine saubere
Saw-Wellenform zu sehen (Bild 3+4).

Phasenaccu 24Bit
Schrittweite 24Bit
Samplerate 25usec (40KHz)

Bild 1: SAW-Wellenform ca. 2031Hz mit Phasen-Jitter (max. 25usec / Div.
100µsec)

Bild 2: SAW-Wellenform ca. 2031Hz mit Phasen-Jitter (max. 25usec / Div.
2msec)

Bild 3: Saw-Wellenform ca. 2038Hz ohne Phasen-Jitter (Div. 100µsec)

Bild 4: Saw-Wellenform ca. 2038Hz ohne Phasen-Jitter (Div. 2msec)



Das Problem lässt sich nach meiner Meinung nur durch ein Zurücksetzen
des Phasenaccus auf 0 beheben, wenn die Wellenform neu beginnt . Dadurch
wird der Phasenaccu mit der Wellenformerzeugung synchronisiert und es
ensteht kein Jitter mehr. Allerdings ist dann keine genau
Frequenzeinstellung mehr möglich. Versuche mit Erhöhung der Bitbreite
des Phasenaccus waren nicht erfolgreich. Der Phasen-Jitter reduzierte
sich dadurch nicht..

Zur Information habe ich noch eine Infoseite gefunden, die das
Phasen-Jitter Problem bei der DDS Synthese sehr anschaulich beschreibt:
http://www.elektronik-labor.de/AVR/DDSGenerator.htm

Gruß Rolf

von Rolf D. (rolfdegen)


Lesenswert?

Hallo
Stundenlagens Grübeln und die Notizen beim Schalfen unters Kopfkissen 
haben leider nicht geholfen
Ich finde einfach keinen Lösungsansatz für mein Jitter-Problem. Würde 
man die Soundausgabe über einen Timer steuern, könnte man die 
DDS-Problematik vielleicht vergessen. Einen Lösungsweg wie es Olivier 
Gillet bei seinem Shruthi-Synth macht (Soundausgabe über PWM und ext. 
VCF+VCA) will ich nicht nachbauen. Ich möchte mein eigenes Hardware 
Konzept beibehalten.

Link Shruthi Synth: http://mutable-instruments.net/shruthi1

von Rolf D. (rolfdegen)


Lesenswert?

Hallo

Ich habe hier einmal den relevanten Programmteil der für die Erzeugung 
der Sinus und Saw-Wellenform verantwortlich ist dargestellt. Die 
gesammte Sound-Routine (hier nicht abgebildet) wird in einem 
Timer-Interrupt alle 25µsec im Main-Programm (C-Code) aufgerufen.

Wellenformspeicher für Sinus (4096 Werte 12Bit)
12Bit DAC-Wandlung im Xmega-Prozessor 1MHz-Wandlerate

1
//===============================================================
2
// SUBTRAKTIVE KLANGSYSYNTHESE
3
//===============================================================
4
5
SubSynthese:
6
7
//---------------------------------------------------------------
8
// DCO-1 
9
//   * 24-Bit Akku-Breite
10
//   * 24-Bit Phasen-Delta (2,384185mHz/Unit)
11
//   * 12-Bit Sample
12
// ( 38 Takte = 1,188 µsec)
13
//---------------------------------------------------------------
14
15
  // Phasen-Akku 1 incrementieren
16
  // ----------------------------
17
DCO1Calc:
18
  LDS   delta0, schrittweite1+0  ; 2   Phasen-Delta aus SRAM laden
19
  LDS   delta1, schrittweite1+1  ; 2
20
  LDS   delta2, schrittweite1+2  ; 2
21
22
  LDS   phakku0, phaccu1+0       ; 2   Phasen-Akku aus SRAM laden
23
  LDS   phakku1, phaccu1+1       ; 2
24
  LDS   phakku2, phaccu1+2       ; 2
25
26
  SUB   phakku0, delta0          ; 1   Phasen-Akku + Phasen-Delta
27
  SBC   phakku1, delta1          ; 1
28
  SBC   phakku2, delta2          ; 1
29
30
  STS   phaccu1+0, phakku0       ; 2   Phasen-Akku in SRAM zurückschreiben
31
  STS   phaccu1+1, phakku1       ; 2
32
  STS   phaccu1+2, phakku2       ; 2
33
34
  // Die oberen 12Bit des Phasen-Akkus extrahieren
35
  // ---------------------------------------------
36
  ANDI  phakku1, 0xF0            ; 1   Lower Nibble in Byte 0 abnullen
37
  LSR   phakku2                  ; 1   Division durch 8 (16-Bit)
38
  ROR   phakku1                  ; 1
39
  LSR   phakku2                  ; 1
40
  ROR   phakku1                  ; 1
41
  LSR   phakku2                  ; 1
42
  ROR   phakku1                  ; 1
43
44
  // Waveform-Selektion
45
  // ------------------
46
  LDS   dcowave, DcoWaveForm     ; 2   Wellenform-Selektion laden
47
  SBRS  dcowave, 0               ; 1/2 
48
  RJMP  DCO1Sine                 ; 2   Sinus bestimmen
49
50
  // Saw über 1:1 Phase ausgeben
51
  // ---------------------------
52
DCO1Saw:
53
  LSR   phakku2                  ; 1   Phasen-Akku / 2
54
  ROR   phakku1                  ; 1
55
  MOV   dcomix0, phakku1         ; 1   Phase umladen
56
  MOV   dcomix1, phakku2         ; 1
57
  RJMP  DCO1End                  ; 2   Fertig
58
59
  // Sample über aktive Phase aus Wavetable laden
60
  // --------------------------------------------
61
DCO1Sine:
62
  LDI   R30, 0xFC                ; 1   Basis-Adresse Sinus-Tabelle (Low-Byte)
63
  LDI   R31, 0x03                ; 1                               (High-Byte)
64
  ADD   R30, phakku1             ; 1   Phasen-Pointer addieren
65
  ADC   R31, phakku2             ; 1
66
  LPM   dcomix0, Z+              ; 3   Sample aus Wavetable laden (16-Bit)
67
  LPM   dcomix1, Z               ; 3   => in MixerSumme als Initialwert
68
69
DCO1End:


Die Zwischentöne, die der Phasen-Jitter verursacht, kann man sehr 
deutlich ab einer Frequenz von 1KHz warnehmen. Hier ein Soundbeispiel 
mit einer SAW-Wellenform. Start 100Hz, Ende 5000Hz: 
http://soundcloud.com/rolfdegen/saw-sweeb

Gruß Rolf

von Maik M. (myco)


Lesenswert?

1
// Die oberen 12Bit des Phasen-Akkus extrahieren
2
  // ---------------------------------------------
3
  ANDI  phakku1, 0xF0            ; 1   Lower Nibble in Byte 0 abnullen
4
  LSR   phakku2                  ; 1   Division durch 8 (16-Bit)
5
  ROR   phakku1                  ; 1
6
  LSR   phakku2                  ; 1
7
  ROR   phakku1                  ; 1
8
  LSR   phakku2                  ; 1
9
  ROR   phakku1                  ; 1

Der KOMMENTAR ist falsch... extrahiert werden die 13Bit, wobei das 
unterste Bit immer 0 ist.
Ich betone KOMMENTAR, weil der Code ansich richtig ist, aber auf den 
ersten Blick schwer zu durchschauen, weil der Kommentar falsch ist. Du 
brauchst sicher die 13Bit-Adressierung für den Sinus, wegen der 16Bit 
Wortlänge im Programmspeicher.

Den Jitter kann man vermeiden durch Reduzierung der Akkumulator-Breite 
(nicht beim Akkumulieren, sondern beim Ausgeben der Saw, bzw. beim 
Adressieren des Wavetables) ab bestimmten Frequenzen. Also im Prinzip 
kann man es so machen, dass man zB. ab 500Hz immer das LSB nullt, ab 
1000Hz nullt man dann das LSB und das nächst höhere, usw. Diese 
Grenz-Frequenzen kann man anhand des Phasen-Deltas setzen.
Diese Methode ist im Prinzip immer ein Abrunden, was dazu führt, das der 
geringfügige Jitter wegbleibt, weil er weggerundet wird. Das hat aber 
auch Nachteile:
1. Man verliert bei höheren Frequenzen die Auflösung
2. Höhere Frequenzen werden leiser
3. Man erzeugt automatisch ein DC-Offset, was man berücksichtigen 
sollte, oder besser noch, gleich im Oscillator kompensiert.

Software-Instrumente nutzen, um diese Problem generell zu umgehen 
größere Wavetable, so hat man zB. für niedrigere Frequenzen ein besser 
aufgelöste Wave als bei höheren. Im Schnitt teilt man diesen Wavetable 
in 8 Frequenzbereiche. Für so einen kleinen µC ist das aber undenkbar.

von Rolf D. (rolfdegen)


Lesenswert?

Maik M. schrieb:
> Den Jitter kann man vermeiden durch Reduzierung der Akkumulator-Breite
> (nicht beim Akkumulieren, sondern beim Ausgeben der Saw, bzw. beim
> Adressieren des Wavetables) ab bestimmten Frequenzen.

Hallo Maik,
das ist ein guter Tip. Werde ich Morgen gleich mal testen. Besten Dank.

In Bezug auf den Kommentar: Der Phasenaccu hat eine Auflösung von 24Bit 
und ich benötige die oberen 12Bit für den Zugriff auf die 4KByte große 
Sinus-Tabelle im Speicher des Xmegas.

Gruß Rolf

von Rolf D. (rolfdegen)


Lesenswert?

Hallo Maik
Hat leider nicht funktioniert. Der Phasen-Jitter bleibt unverändert. 
Aber vielleicht mach ich was falsch ???

Meinen Programmcode habe ich wegen der Übersicht etwas vereinfacht. Die 
markierte Zeile "ANDI phakku1,0b11111110" soll das Phasen-Jitter 
eliminieren indem sie das LSB im Low-Byte der Tabellenadresse löscht. 
Leider ohne Erfolg. Ich habe es auch mit anderen Werten versucht, aber 
ohne Erfolg. Der Phasen-Jitter bleibt.
1
//**************************************************************
2
// ATMEL Studio 6 Inline Assembler-Routine 
3
// Soudausgabe auf DACA Chanal 0
4
// TimeR1 Interruptroutine: alle 25usec = 40.0 KHz Samplerate
5
//      
6
//      (Laufzeit: ?,??? µsec = ??? Takte bei 32MHz)
7
// 
8
// 
9
//           (c) 06.10.2012  Version 1.0 
10
//**************************************************************
11
12
  #include "avr/io.h"
13
14
  .extern sound_out               // Name der Assembler-Funktion
15
  .global TCC1_OVF_vect           // Timer1 Interrupt-Vektor
16
17
 //---------------------------------------------------------------
18
// Benutzte Prozessor-Register (Definition als Namen)
19
//---------------------------------------------------------------
20
  dcoout0   = 16      ; R16  DCO-Out      Byte 0
21
  dcoout1   = 17      ; R17               Byte 1
22
23
  phakku0   = 18      ; R18  Phasen-Akku  Byte 0
24
  phakku1   = 19      ; R19               Byte 1
25
  phakku2   = 20      ; R20               Byte 2
26
27
  delta0    = 21      ; R21  Phasen-Delta Byte 0
28
  delta1    = 22      ; R22               Byte 1
29
  delta2    = 23      ; R23               Byte 2
30
31
  
32
33
//---------------------------------------------------------------
34
// Prozessor-Register inkl. Status-Register sichern
35
// Interrupt-Routine Timer1-Overflow (40.000Hz)
36
//---------------------------------------------------------------
37
38
TCC1_OVF_vect:           
39
  PUSH  R0                       ; 2   R0  auf Stack schieben
40
  IN    R0, SREG                 ; 1   Status-Register über bereits gesichertes
41
  PUSH  R0                       ; 2     R0 auf Stack schieben
42
  PUSH  R1                       ; 2   R1  auf Stack schieben
43
  PUSH  R16                      ; 2   R16 auf Stack schieben 
44
  PUSH  R17                      ; 2   R17 auf Stack schieben
45
  PUSH  R18                      ; 2   R18 auf Stack schieben
46
  PUSH  R19                      ; 2   R19 auf Stack schieben
47
  PUSH  R20                      ; 2   R20 auf Stack schieben
48
  PUSH  R21                      ; 2   R21 auf Stack schieben
49
  PUSH  R22                      ; 2   R22 auf Stack schieben
50
  PUSH  R23                      ; 2   R23 auf Stack schieben
51
  PUSH  R30                      ; 2   R30 auf Stack schieben (ZL)
52
  PUSH  R31                      ; 2   R31 auf Stack schieben (ZH)
53
54
55
//===============================================================
56
// SUBTRAKTIVE KLANGSYSYNTHESE
57
//===============================================================
58
59
SubSynthese:
60
61
//---------------------------------------------------------------
62
// DCO-1 
63
//   * 24-Bit Akku-Breite
64
//   * 24-Bit Phasen-Delta (2,384185mHz/Unit)
65
//   * 12-Bit Sample
66
//---------------------------------------------------------------
67
68
  // Phasen-Akku 1 incrementieren
69
  // ----------------------------
70
71
  LDS   delta0, schrittweite1+0  ; 2   Phasen-Delta aus SRAM laden
72
  LDS   delta1, schrittweite1+1  ; 2
73
  LDS   delta2, schrittweite1+2  ; 2
74
75
  LDS   phakku0, phaccu1+0       ; 2   Phasen-Akku aus SRAM laden
76
  LDS   phakku1, phaccu1+1       ; 2
77
  LDS   phakku2, phaccu1+2       ; 2
78
79
  ADD   phakku0, delta0          ; 1   Phasen-Akku + Phasen-Delta
80
  ADC   phakku1, delta1          ; 1
81
  ADC   phakku2, delta2          ; 1
82
    
83
  STS   phaccu1+0, phakku0       ; 2   Phasen-Akku in SRAM zurückschreiben
84
  STS   phaccu1+1, phakku1       ; 2
85
  STS   phaccu1+2, phakku2       ; 2
86
 
87
  // Die oberen 12Bit des Phasen-Akkus extrahieren
88
  // ---------------------------------------------
89
  ANDI  phakku1, 0xF0            ; 1   Lower Nibble in Byte 0 abnullen
90
  LSR   phakku2                  ; 1   Division durch 8 (16-Bit)
91
  ROR   phakku1                  ; 1
92
  LSR   phakku2                  ; 1
93
  ROR   phakku1                  ; 1
94
  LSR   phakku2                  ; 1
95
  ROR   phakku1                  ; 1
96
    
97
  // SAW-Sample über aktive Phase aus Wavetable laden
98
  // -------------------------------------------------
99
  LDI   R30, 0xFC                ; 1   Basis-Adresse Saw-Tabelle (Low-Byte)
100
  LDI   R31, 0x03                ; 1                             (High-Byte)
101
  //********************************************************************************
102
  ANDI  phakku1,0b11111110     ;     Phasen-Jitter eliminieren 
103
  //********************************************************************************
104
  ADD   R30, phakku1             ; 1   Phasen-Pointer addieren
105
  ADC   R31, phakku2             ; 1
106
  LPM   dcoout0, Z+              ; 3   Sample aus Wavetable laden (12-Bit)
107
  LPM   dcoout1, Z               ; 3   
108
109
 // --------------------------------------------------------------
110
 // Ausgabe am DAC-Converter (DACA Chanal 0)
111
 // --------------------------------------------------------------
112
  STS   0x0318, dcoout0          ; 2   L-Byte to DAC-Register (CH0DATAL Adr. 0x0318)
113
  STS   0x0319, dcoout1          ; 2   H-Byte to DAC Register (CH0DATAH Adr. 0x0319)
114
    
115
// --------------------------------------------------------------
116
// Prozessor-Register inkl. Status-Register wiederherstellen
117
// --------------------------------------------------------------
118
  
119
  POP   R31                      ; 2   R31 von Stack wiederherstellen (ZH)
120
  POP   R30                      ; 2   R30 von Stack wiederherstellen (ZL)
121
  POP   R23                      ; 2   R23 von Stack wiederherstellen
122
  POP   R22                      ; 2   R22 von Stack wiederherstellen
123
  POP   R21                      ; 2   R21 von Stack wiederherstellen
124
  POP   R20                      ; 2   R20 von Stack wiederherstellen
125
  POP   R19                      ; 2   R19 von Stack wiederherstellen
126
  POP   R18                      ; 2   R18 von Stack wiederherstellen
127
  POP   R17                      ; 2   R17 von Stack wiederherstellen
128
  POP   R16                      ; 2   R16 von Stack wiederherstellen
129
  POP   R1                       ; 2   R1  von Stack wiederherstellen
130
  POP   R0                       ; 2   Status-Register über R0 wieder
131
  OUT   SREG, R0                 ; 1     herstellen
132
  POP   R0                       ; 2   R0  von Stack wiederherstellen
133
  RETI                           ; 4   Return Interrupt und I-Flag quittieren
134
135
// --------------------------------------------------------------
136
  .end
137
//

von Maik M. (myco)


Lesenswert?

1
ANDI  phakku1,0b11111110     ;     Phasen-Jitter eliminieren

Das bringt nichts, das unterste Bit ist bei deiner Addressierung immer 
0, da du mit 13 Bit adressierst, nicht mit 12Bit. Jetzt bist du genau 
auf den falschen Kommentar("Die oberen 12Bit des Phasen-Akkus 
extrahieren") reingefallen, den ich dir mit dem letzen Post zeigen 
wollte ;)

Diese Andrundungs-Maske ist etwas schwer zu bestimmen, aber evtl. gibt's 
eine andere Lösung. Probier mal das:
1
//**************************************************************
2
// ATMEL Studio 6 Inline Assembler-Routine 
3
// Soudausgabe auf DACA Chanal 0
4
// TimeR1 Interruptroutine: alle 25usec = 40.0 KHz Samplerate
5
//      
6
//      (Laufzeit: ?,??? µsec = ??? Takte bei 32MHz)
7
// 
8
// 
9
//           (c) 06.10.2012  Version 1.0 
10
//**************************************************************
11
12
  #include "avr/io.h"
13
14
  .extern sound_out               // Name der Assembler-Funktion
15
  .global TCC1_OVF_vect           // Timer1 Interrupt-Vektor
16
17
 //---------------------------------------------------------------
18
// Benutzte Prozessor-Register (Definition als Namen)
19
//---------------------------------------------------------------
20
  dcoout0   = 16      ; R16  DCO-Out      Byte 0
21
  dcoout1   = 17      ; R17               Byte 1
22
23
  phakku0   = 18      ; R18  Phasen-Akku  Byte 0
24
  phakku1   = 19      ; R19               Byte 1
25
  phakku2   = 20      ; R20               Byte 2
26
27
  delta0    = 21      ; R21  Phasen-Delta Byte 0
28
  delta1    = 22      ; R22               Byte 1
29
  delta2    = 23      ; R23               Byte 2
30
31
  
32
33
//---------------------------------------------------------------
34
// Prozessor-Register inkl. Status-Register sichern
35
// Interrupt-Routine Timer1-Overflow (40.000Hz)
36
//---------------------------------------------------------------
37
38
TCC1_OVF_vect:           
39
  PUSH  R0                       ; 2   R0  auf Stack schieben
40
  IN    R0, SREG                 ; 1   Status-Register über bereits gesichertes
41
  PUSH  R0                       ; 2     R0 auf Stack schieben
42
  PUSH  R1                       ; 2   R1  auf Stack schieben
43
  PUSH  R16                      ; 2   R16 auf Stack schieben 
44
  PUSH  R17                      ; 2   R17 auf Stack schieben
45
  PUSH  R18                      ; 2   R18 auf Stack schieben
46
  PUSH  R19                      ; 2   R19 auf Stack schieben
47
  PUSH  R20                      ; 2   R20 auf Stack schieben
48
  PUSH  R21                      ; 2   R21 auf Stack schieben
49
  PUSH  R22                      ; 2   R22 auf Stack schieben
50
  PUSH  R23                      ; 2   R23 auf Stack schieben
51
  PUSH  R30                      ; 2   R30 auf Stack schieben (ZL)
52
  PUSH  R31                      ; 2   R31 auf Stack schieben (ZH)
53
54
55
//===============================================================
56
// SUBTRAKTIVE KLANGSYSYNTHESE
57
//===============================================================
58
59
SubSynthese:
60
61
//---------------------------------------------------------------
62
// DCO-1 
63
//   * 24-Bit Akku-Breite
64
//   * 24-Bit Phasen-Delta (2,384185mHz/Unit)
65
//   * 12-Bit Sample
66
//---------------------------------------------------------------
67
68
  // Phasen-Akku 1 incrementieren
69
  // ----------------------------
70
71
  LDS   delta0, schrittweite1+0  ; 2   Phasen-Delta aus SRAM laden
72
  LDS   delta1, schrittweite1+1  ; 2
73
  LDS   delta2, schrittweite1+2  ; 2
74
75
  LDS   phakku0, phaccu1+0       ; 2   Phasen-Akku aus SRAM laden
76
  LDS   phakku1, phaccu1+1       ; 2
77
  LDS   phakku2, phaccu1+2       ; 2
78
79
  ADD   phakku0, delta0          ; 1   Phasen-Akku + Phasen-Delta
80
  ADC   phakku1, delta1          ; 1
81
  ADC   phakku2, delta2          ; 1
82
    
83
  STS   phaccu1+0, phakku0       ; 2   Phasen-Akku in SRAM zurückschreiben
84
  STS   phaccu1+1, phakku1       ; 2
85
  STS   phaccu1+2, phakku2       ; 2
86
 
87
  // MyCo
88
  // hohes Delta-Wort durch 2 teilen
89
  LSR   delta2                   ; 1
90
  ROR   delta1                   ; 1
91
  
92
  // MyCo
93
  // 1/2 Delta von der Adressierung abziehen
94
  SUB   phakku1, delta1          ; 1
95
  SBC   phakku2, delta2          ; 1
96
97
  // Die oberen 12Bit des Phasen-Akkus extrahieren
98
  // ---------------------------------------------
99
  ANDI  phakku1, 0xF0            ; 1   Lower Nibble in Byte 0 abnullen
100
  LSR   phakku2                  ; 1   Division durch 8 (16-Bit)
101
  ROR   phakku1                  ; 1
102
  LSR   phakku2                  ; 1
103
  ROR   phakku1                  ; 1
104
  LSR   phakku2                  ; 1
105
  ROR   phakku1                  ; 1
106
    
107
  // SAW-Sample über aktive Phase aus Wavetable laden
108
  // -------------------------------------------------
109
  LDI   R30, 0xFC                ; 1   Basis-Adresse Saw-Tabelle (Low-Byte)
110
  LDI   R31, 0x03                ; 1                             (High-Byte)
111
112
  ADD   R30, phakku1             ; 1   Phasen-Pointer addieren
113
  ADC   R31, phakku2             ; 1
114
  LPM   dcoout0, Z+              ; 3   Sample aus Wavetable laden (12-Bit)
115
  LPM   dcoout1, Z               ; 3   
116
117
 // --------------------------------------------------------------
118
 // Ausgabe am DAC-Converter (DACA Chanal 0)
119
 // --------------------------------------------------------------
120
  STS   0x0318, dcoout0          ; 2   L-Byte to DAC-Register (CH0DATAL Adr. 0x0318)
121
  STS   0x0319, dcoout1          ; 2   H-Byte to DAC Register (CH0DATAH Adr. 0x0319)
122
    
123
// --------------------------------------------------------------
124
// Prozessor-Register inkl. Status-Register wiederherstellen
125
// --------------------------------------------------------------
126
  
127
  POP   R31                      ; 2   R31 von Stack wiederherstellen (ZH)
128
  POP   R30                      ; 2   R30 von Stack wiederherstellen (ZL)
129
  POP   R23                      ; 2   R23 von Stack wiederherstellen
130
  POP   R22                      ; 2   R22 von Stack wiederherstellen
131
  POP   R21                      ; 2   R21 von Stack wiederherstellen
132
  POP   R20                      ; 2   R20 von Stack wiederherstellen
133
  POP   R19                      ; 2   R19 von Stack wiederherstellen
134
  POP   R18                      ; 2   R18 von Stack wiederherstellen
135
  POP   R17                      ; 2   R17 von Stack wiederherstellen
136
  POP   R16                      ; 2   R16 von Stack wiederherstellen
137
  POP   R1                       ; 2   R1  von Stack wiederherstellen
138
  POP   R0                       ; 2   Status-Register über R0 wieder
139
  OUT   SREG, R0                 ; 1     herstellen
140
  POP   R0                       ; 2   R0  von Stack wiederherstellen
141
  RETI                           ; 4   Return Interrupt und I-Flag quittieren
142
143
// --------------------------------------------------------------
144
  .end
145
//

Ist halt nur eine Idee, keine Ahnung ob das genau das macht, was ich 
meine.

von Rolf D. (rolfdegen)


Lesenswert?

Mein Fazit: Das Jitter von 25µsec (Taktzyklus für den Zähler des 
Phasenaccus) werde ich prinzipiell nicht los. Beim Sinus ist das auch 
kein großes Problem. Dieser wird sauber und störungsfrei wiedergegeben . 
Aber für die anderen Wellenformen wzB. Sägezahn, Puls oder Rechteck ist 
die Softwaregenerierte DDS-Synthese bei einer Taktrate von 25µsec nicht 
zu gebrauchen.

Muss mir also was anderes überlegen. Gruß Rolf

von Rene B. (themason) Benutzerseite


Lesenswert?

Der Ansatz den Rolf meinte von wegen den Phasenakku zu nullen nach einem 
Wellenformdurchgang ist doch im Prinzip richtig. Man darf den Akku an 
sich aber nicht nullen. Man muß, sobald ein Neustart der Wellenform 
erfolgt sich den aktuellen Phasenakku sichern und ihn als Offset zum 
eigentlichen Phasenakku betrachten. Wenn der Phasenakku (gerade bei 
höheren Frequenzen) eben nicht mit dem Index 0 auf den Wavetable bzw 
Saw-Erzeugung liegt verschiebt man diesen für den Durchlauf der 
Wellenform. Man hat dann aber dennoch das Problem das die Wellenform 
eben nicht voll "ausgefahren" wird.

Die einzige Lösung die mir vllt einfallen würde wäre die Samplefrequenz 
soweit zu erhöhen das der Jitter nicht mehr auftritt und dann per 
Down-Sampling auf die Ausgabefrequenz zu shiften, aber das heißt dann 
auch den Wavetable entsprechend aufzubohren.

von Rene B. (themason) Benutzerseite


Lesenswert?

Ich habe gerade nochmal gestöbert.

http://musicdsp.org/archive.php?classid=1#90

Vllt hilft das weiter. Ist zwar eher für Software-Synthies und DSP, aber 
ich denke das ein oder andere könnte evtl nützlich sein.

von Maik M. (myco)


Lesenswert?

Dieser Jitter wird immer auftreten, sobald
1
Wiedergabefrequenz > Samplerate/Wavetable_Länge

Bei ihm ist's 40.000 / 4096, also ab ca. 10Hz wird der Jitter 
auftauchen. Der ist zwar ab dieser Frequenz noch sehr gering, wird aber 
größer, je höher die Frequenz ist. Das kann man aber umgehen, indem man 
bei höheren Frequenzen einen kleinere Wavetable verwendet. Oder man 
adressiert bei höheren Frequenzen einfach mit geringerer Auflösung 
(nullen der niedrigen Bits).
Den Drift/Jitter kann man sich in etwa errechnen:
1
Drift in Prozent = (Wiedergabefrequenz*Wavetable_Länge)/(Samplerate*DAC_Auflösung)*100

von Rolf D. (rolfdegen)


Lesenswert?

Hallo

Danke an alle.

Zum Vorschlag von Maik:
1
// MyCo
2
  // hohes Delta-Wort durch 2 teilen
3
  LSR   delta2                   ; 1
4
  ROR   delta1                   ; 1
5
  
6
  // MyCo
7
  // 1/2 Delta von der Adressierung abziehen
8
  SUB   phakku1, delta1          ; 1
9
  SBC   phakku2, delta2          ; 1

Die Code-Änderungen hat leider nichts gebracht. Der Phasen-Jitter 
bleibt.
Ich werde es wohl mit der Idee von Maik, bei höheren Frequenzen kleinere 
Wavetables zu verwenden, versuchen.


Zum Hinweis von Rene "Bandlimited sawtooth synthesis":
Ich hab Gestern mit Oliver Gillert, dem Entwickler des genialen Shruthi 
Synth (http://mutable-instruments.net/shruthi1) gemailt. Ich habe ih 
gefragt, wie er das so mit der Sounderzeugung im Shruthi macht und eine 
Antwort erhalten, die ich hier mit Hilfe von Google übersetzt habe:


"Für die klassischer analoger Wellenformen auf dem Shruthi (Saw, 
Rechteck, Dreieck),
verwende ich bandbegrenzten Wavetables .
(Anmerkung von mir: Wolfgang hat es oben in einen der letzten Beiträge 
schon vorhergesagt hat).

Die Wavetables werden durch integrierte bandbegrenzte Impulse 
(sinc-Funktionen) für verschiedene f0 / fs-Verhältnis
Abstand mit 2 ** (16 / 12,0) Verhältnisse. Als Ergebnis gibt es ein
Wavetable für jede Gruppe von 16 Noten. Um zu vermeiden, grobe Übergänge 
I
Interpolieren zwischen jeder Wavetable. Für die höheren Töne, die 
Wellenform
nähert sich einer sinusförmigen - von der erwartet wird, weil einer 
quadratischen
Welle bei 8 kHz auf 44 kHz abgetastet ist in der Tat eine Sinuswelle 
(die dritte
Oberwelle bei 24kHz oberhalb Nyquist-Frequenz).

Hier ist eine Kurve der Wellenformen aus der Shruthi bei 
unterschiedlichen
Frequenzen (50, 125, 320, 800, 2k, 5k, 12k):
http://i.imgur.com/nZmMx.png

Die quadratischen und Sägezahnwellen gefärbt sind - ich denke, diese 
Wellenformen
sind viel interessanter als die "reinen" Sägezahn und "pure" eckig
Welle.

Die PWM-Wellenform wird durch Summieren von zwei bandbegrenzten Sägezahn 
erhalten
mit einer Phasendifferenz - die Phasendifferenz steuert die 
PWM-Verhältnis.

Ich habe versucht mit minBLEP und es verwendet, 70% der CPU. Auf der 
Shruthi-1 ist
war keine Option, aber in Ihrem Projekt, das Sie mit einem XMega bei 32
MHz, so dass es machbar sein, um minBLEP verwenden. Ich würde empfehlen, 
diese Option wählen,
da es klingt sehr gut und kann tun hardsync! minBLEP nicht tun können
Dreiecke, aber Sie können bandbegrenzte Wavetables für diese Verwendung.

Die Shruthi-1-Code ist Open Source und finden Sie Antworten auf Ihre zu 
finden
Fragen Sie sich den Code:
https://github.com/pichenettes/shruthi-1/";




Auf http://www.experimentalscene.com/articles/minbleps.php habe ich eine 
kleine Anleitung (C++ Quellcode) für einen "minBLEP"-Oszilator gefunden. 
Mal schaun wie ich das für einen Xmega nutzen kann.


Gruß Rolf

von Rolf D. (rolfdegen)


Angehängte Dateien:

Lesenswert?

Hallo

Ich wollte mal wissen wie sich so ein Square-Sound mit Bandbegrenzung 
(eng. band-limited square)
auf meinem Synthi so anhört und habe mir die Mühe gemacht zwei 
Hörbeispiele
aufzunehmen. Die Aufnahmen wurden am LowPass-Filterausgang bei offenem
Filter gemacht.
Die 8Bit Square-Sounds werden aus einer 256 Byte großen Waveform-Tabelle 
mit 40KHz Sampling-Frequenz an den DAC Ausgang gesendet.


Bild 1: 1KHz Square-Sound 8Bit (Rechteck) ohne Bandbegrenzung auf dem 
AVR-Synthi
Link soundcloud.com: http://soundcloud.com/rolfdegen/square-0

Bild 2: 1KHz Square-Sound (aus dem Shruthi-Synth gestohlen   ) mit 
Bandbegrenzung auf dem AVR-Synthi.
Link soundcloud.com: 
http://soundcloud.com/rolfdegen/bandlimited-square-0


Wie man hört, ist der Square-Sound mit Bandbegrenzung viel sauberer und 
ohne Nebengeräusche.

Mal schaun wie ich als "Nicht Studierter" so einen minBlep-Oszillator 
auf meinen Xmega implementieren kann.

Gruß Rolf

von Rolf D. (rolfdegen)


Lesenswert?

Hallo

Ich habe mich dazu entschieden, fertige Bandbegrenzte Wavefiles zu 
benutzen und diese in den Flash-Speicher des Xmegas zu schreiben. 
Dadurch spar ich mir das ganze Rumrechnen mit Sinus und Cosinus und 
haste nicht gesehen zur Laufzeit. Die Bandbegrenzten Wavefiles liegen 
mir als bin.Datei vor und ich möchte diese als ext. Datei in meinen 
Quellcode mit einbinden. Leider weis ich nicht wie das in Atmel Studio 
funktioniert. Ich habe mir ersteinmal damit geholfen, das ich die 
Wave-Datei über einen Hexeditor als Hexadezimal-Tabelle exportiere und 
in meinen C-Quellcode kopiere. Das funktioniert problemlos. Dadurch wird 
aber der Quellcode sehr lang und unübersichtlich. Ein Tip wär nicht 
schlecht. Danke euch.

Gruß Rolf

von Rene B. (themason) Benutzerseite


Angehängte Dateien:

Lesenswert?

@Rolf

Ich hab gestern mal mit bandlimited waveforms auf dem PC 
rumexperimentiert.
Ich habe dabei einen Oszillator programmiert der 64 einzelne Sinüsse 
zusammenrechnet und deren Frequenz-Anteile (also 64 Koeffizienten) 
vorgegeben werden können.
Den Sinus selbst hole ich über eine Tabelle. Ich habe mal deine 
Erkenntnis darin verwurstet das der Sinus auch bei "ungenauen" 
Frequenzen keine "Sprünge" macht und somit eine "ganz normale" DDS 
genutzt. Also das Ergebnis klingt doch schon ganz gut, selbst wenn man 
bei tiefen Frequenzen eine Art Tiefpass mit raushört, das aber eben 
daran liegt das ich "nur" 64 Anteile berechne, die Wellenform aber noch 
hörere Frequenzen beinhalten würde. (wenn meine Grundfrequenz 
beispielsweise 100Hz ist, wäre der letzte Frequenzanteil bei 6,4kHz).
Das Ergebnis klingt aber recht brauchbar, und ich werd da auch mal was 
weiter probieren. Das ganze ist somit quasi eine einfache Additive 
Synthese.
Das was für mich son bissl den Reiz dabei ausmacht ist das ich die 
Frequenzanteile zur Laufzeit ändern kann, und somit die 
ausgangswellenform schon dynamisch gestalten kann (z.b. zwischen 
Sägezahn und Rechteck "morphen") oder einer Wellenform einfach 
Oberwellen hinzufügen kann, die z.b. von einer Hüllkurve gesteuert 
werden.
Anbei mal die zusammengehackte Datei (ist noch viel Float drin, lässt 
sich aber problemlos durch integer ersetzen), in der allerdings der 
Sinus noch nicht drin ist (der ist bei mir in einer separaten Datei und 
hat den Sinus-Kurvenzug von 0 bis pi/2).

von Maik M. (myco)


Lesenswert?

@Rene:
Füge mal vor der Zeile:
1
rT = fOscGetSine (ulPhi);

diese eine:
1
if (ulPhi & 80000000)break;

Damit wird verhindert, das Frequenzen über der Nyquisit generiert 
werden.

Diese Zeile:
1
if (rFrq > 23000.0f) { rFrq = 20000.0f; }

ist wohl nur ein Provisorium, oder? Besser wäre es, bei einer hohen 
Basis-Frequenz, den Osc komplett zu umgehen.

von Rene B. (themason) Benutzerseite


Lesenswert?

@Maik

>if (rFrq > 23000.0f) { rFrq = 20000.0f; }

Da war noch ein Fehler drin. Hatte das ganze ursprünglich für 48kHz 
ausgelegt, bis mir dann bei der Durchsicht eingefallen ist das meine 
Soundkarte auf 44,1kHz läuft :-S

>if (ulPhi & 0x80000000) break;

Stimmt. Gute Idee. Vor allem hätte ich dann auch einen anhaltspunkt 
wieviele Sinüsse ich bei hohen Frequenzen effektiv durchrechnen muß, 
damit die 64. Oberwelle noch unterhalb meiner Nyquist Frequenz liegt.

>ist wohl nur ein Provisorium, oder? Besser wäre es, bei einer hohen
>Basis-Frequenz, den Osc komplett zu umgehen.

Wie meinst das "komplett umgehen" ?

von Rolf D. (rolfdegen)


Angehängte Dateien:

Lesenswert?

Hallo Rene

Hab ein einfaches Programm namens "WinFilter" für die Filterberechnung 
gefunden. Vielleicht hilfts es dir :)

Bild: WinFilter


Download Link: http://www.winfilter.20m.com/

Gruß Rolf

von Rene B. (themason) Benutzerseite


Lesenswert?

@Rolf

Danke für den Link. Das Problem bei solchen Filter Tools ist das sie 
zwar Koeffizienten ausspucken, aber den Weg wie man diese dann selbst 
berechnen kann eben nicht. Und das wäre gerade für 
Synthesizer/Mischpulte/Effektgeräte wo sich die Filter ja in 
Frequenz/Güte/Typ/Charakteristik dynamisch ändern können interessant.

von Rolf D. (rolfdegen)


Lesenswert?

Hallo Rene

Dann schau mal hier: http://musicdsp.org/archive.php?classid=1

Gruß Rolf

von Rene B. (themason) Benutzerseite


Lesenswert?

@Rolf

Die musicdsp-Seite hab ich vor einiger Zeit schon entdeckt. Daher kam 
ich ja auf die bandlimited-Geschichte. Ich glaub das ich da auch die 
Neuüberarbeitung meiner Filter-klamotten her hab (Koeffizientenseitig).
Ist schon recht lesenswert die Seite.
Daher dachte ich das dir das vllt bei deinem Problemchen evtl hilfreich 
sein könnte.

von Rolf D. (rolfdegen)


Lesenswert?

Ich habe bei diesen Problemen wieder viel interessantes gelernt aber ich 
bin nicht so der Mathetyp und mich schreckt der ganze Berechnungskram 
eher ein wenig ab. Zumal der Xmega in meinem Projekt noch andere 
Aufgaben nebenbei erledigen soll wz. Midi-Daten empfangen, Daten zum 
Touch-Display senden und empfangen, Analoge Filter steuern uvm. Später 
soll es auch  eine Möglichkeit geben, die Soundbank von einer SD-Karte 
in den 8MB SDRAM Speicher des Xplained Board zu laden.

Zur Zeit arbeite ich an der Soundbank für meinen Synthi. Ich habe im 
Internet einige Bandlimited Wavetables gefunden, die ich in meine 
Soundbank integriert habe. Somit bleibt meinem Xmega-Prozessor die 
Berechnung wärend der Laufzeit erst einmal erspart.

Hier ein Code-Ausschnitt vom Shruthi Synth:
1
// ------- Band-limited PWM --------------------------------------------------
2
void Oscillator::RenderBandlimitedPwm(uint8_t* buffer) {
3
  uint8_t balance_index = U8Swap4(note_ - 12);
4
  uint8_t gain_2 = balance_index & 0xf0;
5
  uint8_t gain_1 = ~gain_2;
6
7
  uint8_t wave_index = balance_index & 0xf;
8
  const prog_uint8_t* wave_1 = waveform_table[
9
      WAV_RES_BANDLIMITED_SAW_1 + wave_index];
10
  wave_index = U8AddClip(wave_index, 1, kNumZonesHalfSampleRate);
11
  const prog_uint8_t* wave_2 = waveform_table[
12
      WAV_RES_BANDLIMITED_SAW_1 + wave_index];
13
  
14
  uint16_t shift = static_cast<uint16_t>(parameter_ + 128) << 8;
15
  // For higher pitched notes, simply use 128
16
  uint8_t scale = 192 - (parameter_ >> 1);
17
  if (note_ > 64) {
18
    scale = U8Mix(scale, 102, (note_ - 64) << 2);
19
    scale = U8Mix(scale, 102, (note_ - 64) << 2);
20
  }
21
  
22
  phase_increment_ = U24ShiftLeft(phase_increment_);
23
  
24
  BEGIN_SAMPLE_LOOP
25
    phase = U24AddC(phase, phase_increment_int);
26
    *sync_output_++ = phase.carry;
27
    *sync_output_++ = 0;
28
    if (sync_input_[0] || sync_input_[1]) {
29
      phase.integral = 0;
30
      phase.fractional = 0;
31
    }
32
    sync_input_ += 2;
33
    
34
    uint8_t a = InterpolateTwoTables(
35
        wave_1, wave_2,
36
        phase.integral, gain_1, gain_2);
37
    a = U8U8MulShift8(a, scale);
38
    uint8_t b = InterpolateTwoTables(
39
        wave_1, wave_2,
40
        phase.integral + shift, gain_1, gain_2);
41
    b = U8U8MulShift8(b, scale);
42
    a = a - b + 128;
43
    *buffer++ = a;
44
    *buffer++ = a;
45
    size--;
46
  END_SAMPLE_LOOP
47
}


Viele Ideen kann man sich auch auf der Website des Shruthi Synth 
abkucken. Olivier Gillert der Macher des Shruthi Synths hat seine 
Entwicklungsarbeit für jederman frei zugänglich gemacht. Das Ding ist 
einfach genial. Er arbeitet z.Zt. an einer mehrstimmigen Version des 
Shruthis.

Link zur Shruthi Website: http://mutable-instruments.net/

Gruß Rolf

von Maik M. (myco)


Lesenswert?

Rene B. schrieb:
>>ist wohl nur ein Provisorium, oder? Besser wäre es, bei einer hohen
>>Basis-Frequenz, den Osc komplett zu umgehen.
>
> Wie meinst das "komplett umgehen" ?

Das bezog sich auf diese Zeile:
1
if (rFrq > 23000.0f) { rFrq = 20000.0f; }

Die ja soviel macht wie: Bei einer zu hohen Frequenz, generiere eine 
völlig andere. Das ist ungünstig, weil der Osc. dann evtl. sinnlos eine 
disharmonie spielt.

Mit "Osc. umgehen" meine ich, das man ihn bei einer zu hohen Frequenz 
garnicht erst berechnet, sondern 0 (Stille) ausgibt.

von Rene B. (themason) Benutzerseite


Lesenswert?

@Maik

Achso meintest das. Ja, so gesehen ist das nen Proviorium. Und 
Frequenzanteile oberhalb von fS/2 zu Nullen wird noch eingebaut ...

von Rolf D. (rolfdegen)


Lesenswert?

Hallo

Bezüglich der Wave-Files habe von Humfrey aus dem CC2-Forum einen guten 
Vorschlag erhalten. Die Deklaration der Wave-Daten als Hexadezimal Werte 
in eine extra Quelltext-Datei schreiben und diese dann per #include in 
den normalen C-Quelltext mit einbinden. Funktioniert bestens.

Gruß Rolf

von Rolf D. (rolfdegen)


Angehängte Dateien:

Lesenswert?

Hallo zusammen

Ich war mal wieder fleißig und habe die letzten Wochen am AVR-Synthi 
gebastelt bzw programmiert. Herausgekommen ist eine neu gestaltete 
Menü-Seite für die DCO Einstellung. Was eigentlich sehr einfach klingt, 
hat mir doch etwas Kopfzerbrechen bereitet. Die Einstellung der DCO's 
sollte übersichtlich und einfach zu bedienen sein.


Bild 1: DCO-Menü


Herausgekommen ist eine Wellenform Auswahl-Matrix für beide DCO's um 
ver-
schiedene Wellenformen zu kombinieren. Mit der internen 
Wellenform-Tabelle
(ca. 22KByte) bestehend aus den Bandlimited Waves (Square, Saw, 
Triangle)
und Synthezizer-Waves sind zur Zeit etwas mehr als 500 Wellenform Kombi-
nationen möglich. Soundbeispiele wirds hier später geben.

Ferne besteht die direkte Möglichkeit, mit dem Detune-Regler die DCO's 
gegeneinander zu verstimmen um einen Schwebungeffekt im Sound zu 
erreichen. Mit den Schaltern "Soft" und "Hard" kann man die Stärke des 
Reglers einstellen. Der "Sync"-Button synchronisiert beide DCO's wieder 
und stellt den Detune-Wert wieder auf null.


Bild 2: Auswahl der Synthesizer-Waves


Eine etwas schwierige Aufgabe war die Wellenform Auswahl für die 
Synthesizer- Waves. Das habe ich durch ein Untermenü bewerkstelligt. 
Klickt man auf den "WAV"-Button öffnet sich eine neue Menüseite für die 
Auswahl der Waves und der grafischen Ansicht der Wellenform.


Bild 3: Wellenform-Kombination einer Saw-Waveform und 
Synthesizer-Waveform


Gruß Rolf

von Rolf D. (rolfdegen)


Angehängte Dateien:

Lesenswert?

Hier noch ein paar Bilder von speziellen Wellenform-Kombinationen.

Gruß Rolf

von Rene B. (themason) Benutzerseite


Lesenswert?

@Rolf

Sieht ja recht schick aus. Bin mal gespannt auf die Sound-Beispiele.
Aber ich hab mal ne Frage zum Sync. Heißt Sync nicht das ein Oszillator 
beim Nulldurchgang den anderen Synchronisiert, also diesen dann 
ebenfalls neustartet ? Das macht im Falle das beide Detune = 0 haben in 
dem Sinne keinen Sinn, da beide dann ja exakt diesselbe Frequenz haben. 
Aber interessant wird das meine ich dann sobald der zu Synchronisierende 
Oszillator höher schwingt (egal ob mit einem Detune im Halbton bzw 
Cent-Halbton) Hauptsache höher als der erste Oszillator, denn sonst 
würde die Wellenform ja nicht voll ausschwingen können. Aber ich meine 
das gerade das Synchronisieren den Sound was fetter bzw 
charakteristischer macht. Was vllt auch noch ne schöne Variante für 
deinen Oszillator wäre (wenn du ohnehin schon 2 davon hast ;-)) wäre 
Ringmodulation. Also einfach die beiden Oszillator-Ausgänge miteinander 
multiplizieren. Das würde die 500 möglichen Kombinationen noch ein bissl 
aufstocken :-) Und wäre ne prima Klangquelle für nen analogen Filter. 
Ich weiß ja nicht wie es in deine Programmierung passt. Aber mal so als 
Vorschlag.

von Maik M. (myco)


Lesenswert?

Ich hab gerade mal in meinen AVR-Synth code geschaut, wie ich das so 
aufgebaut habe. Die Klangerzeugung lief bei mir über einen "morphable 
wavetable". Der Wavetable selbst hatte 8 Wellenformen, wobei man immer 
von einer Wellenform zur nächsten morphen kann. Das ging im Prinzip so:
1
Ausgang = (Wellenform[index][sample] * (255-morph) + Wellenform[(index+1)&0b111][sample] * morph)
2
3
morph = Byte (0..255)
4
index = Byte (0..7)
5
sample = Wavetable Akku

Multiplikation war bei mir eine spezielle Funktion, die Word mit Byte 
Multipliziert hat, und als Ergebnis kam Word raus (die untersten 8Bit 
des 24Bit Ergebnisses werden ignoriert)

Ich habe auch mit einem Filter experimentiert, der so auschaute:
1
v0 =  R*C*v0 + C*in - C*v1;
2
v1 =  R*C*v1 + C*v0;
3
output = v1;
4
5
C = Cutoff (0..255)
6
R = Resonance(255..0)

Hierbei habe ich die Multiplikation R*C in einem nebenherlaufenden, sehr 
langsamen Timer erledigt, weil es nicht so kritisch war. Soweit ich mich 
erinnern kann, war der Filter sehr gut, und dank dem 
Hardware-Multiplikator auch von den Zyklen her recht vertretbar.

von Rolf Degen (Gast)


Lesenswert?

Rene B. schrieb:
> Heißt Sync nicht das ein Oszillator
> beim Nulldurchgang den anderen Synchronisiert, also diesen dann
> ebenfalls neustartet ?

Hallo Rene. Ja das ist richtig. Ich sollte meine Sync Funktion wohl 
besser umbenennen, denn sie setzt lediglich beide Oszillator auf die 
gleiche  Frequenz und den gleichen Phasenwinkel zurueck. Ich bin mir 
allerdings nicht im klaren darueber, ob das musikalisch gesehen 
ueberhaupt sinnvoll ist.

Nemen wir mal an, das mit dem Einschalten des Synthis beide Oszillatoren 
mit gleicher Frequenz und Phase starten. Nun verstelle ich im DCO Menue 
den Detune Regler und dadurch schwingen beide Oszillatoren mit 
unterschiedlicher Frequenz und als Ergebnis ensteht am Ausgang eine 
gewollte Amplitdenmodulation. Mit dem alleinigen Zuruecksetzen des 
Detune Reglers auf null, schwingen zwar beide Oszillatoren wieder mit 
gleicher Frequenz aber nicht unbedingt synchron zueinander, was 
ebenfalls zu einer Amplitudenmodulaton am Ausgang fuehrt. Aus diesem 
Grund stelle ich mit dem Sync-Button auch die Phasen beider Oszillatoren 
zurueck.

Danke fuer den Tip mit der Ringmodulation. Werde ich Morgen gleich 
umsetzen :)

von Maik M. (myco)


Lesenswert?

Hab die Preview von damals wiedergefunden und raufgeladen:
http://soundcloud.com/maikmenz/avr-8bit-filter

Der Reverb stammt nicht vom Synth, den hab ich nachträglich 
draufgepackt. Der Filter in der Demo ist der den ich erwähnt habe. In 
der Demo hört mach auch das Wavetable morphing von Sägezahn zu Rechteck 
und zurück.

von Rolf D. (rolfdegen)


Angehängte Dateien:

Lesenswert?

Hallo

Irgendwie sieht bei mir die Ringmodulation auf dem Oszi (Bild 1) etwas 
merkwürdig aus.

Hier mein Code:
1
//-------------------------------------------------------------------
2
// Ringmodulation Sample1 (dco1out0 8Bit) * Sample2 (dco2out0 8Bit)
3
// Multiplikationsergebnis durch 16 teilen für 12Bit Ausgabe auf DAC
4
//-------------------------------------------------------------------
5
  Ringmod:
6
  MUL   dco1out0, dco2out0    ; DCO1_out * DCO2_out
7
  MOVW  dco1out0, R0
8
  LSR   dco1out1        ; Ausgabewert auf 12Bit (div 4)
9
  ROR   dco1out0
10
  LSR   dco1out1
11
  ROR   dco1out0
12
  LSR   dco1out1
13
  ROR   dco1out0
14
  LSR   dco1out1
15
  ROR   dco1out0
16
  STS   0x0318, dco1out0          ; 2   L-Byte to DAC-Register (CH0DATAL Adr. 0x0318)
17
  STS   0x0319, dco1out1          ; 2   H-Byte to DAC Register (CH0DATAH Adr. 0x0319)

Der Sound klingt aber wie eine Ringmodulation. Ich wunder mich halt über 
das Aussehen der Wellenform. Diese entspricht nicht der üblichen aus dem 
Lehrbuch wie zB in Bild 2.

Mach ich was flasch in meiner Berechnung oder ist das so korrekt. Mir 
scheint es so, als ob der Nullpunkt der Sinuskurve nach unten verschoben 
ist. Die Frequenz von Oszillator 1 (dco1) beträgt 200Hz und von 
Oszillator 2 (dco2) 6000Hz. Die beiden Sinuswerte stammen direkt aus 
einer 8Bit Sinus-Tabelle im Flash des ATxmegas.

Gruß Rolf

von Maik M. (myco)


Lesenswert?

Dein Code operiert nur im positivem Bereich, das ist generell schlecht 
für DSP.

von Rolf D. (rolfdegen)


Lesenswert?

Wie könnte ich das ändern ?

von Maik M. (myco)


Lesenswert?

du müsstest deinen Wavetable als Signed speichern, und von da an alle 
Unsigned-Operationen durch Signed-Operationen ersetzen. Dann am Ende, 
vor der Ausgabe konvertierst du dann erst wieder zurück auf Unsigned

von Rene B. (themason) Benutzerseite


Lesenswert?

Mach deine Wellenformen und die Multiplikation signed.
Wie Maik schon meinte : Nur unsigned (also positiv) ist eher schlecht, 
selbst wenns sich nach dem gewünschten Ergebnis anhört.
Du wirst dann tlw Probleme bekommen mit nem DC-Offset. Dadurch das der 
Nullpunkt dann ja eben irgendwo liegt, in Abhängigkeit von der 
Beschaffenheit deines Ausgangssignals.

von Rolf D. (rolfdegen)


Lesenswert?

Die Tabellen kann ich nicht ändern. Besteht nicht die Möglichkeit den 
ausgelesenen Tabellenwert als signed Byte zu konvertieren. Die Auflösung 
würde dadurch auf 7Bit reduziert.

von Maik M. (myco)


Lesenswert?

das kannst du machen, aber hast dann halt wieder einen Zyklus weniger.

Konvertieren von Signed nach Unsigned und umgekehrt geht relativ 
einfach:
1
subi register,0x80

Allerdings musst du bei Signed-Operationen bedenken, dass das 
höchstwertige Bit immer das Vorzeichen-Flag ist. Operationen wie lsl, 
lsr, ror, rol, usw. darfst du auf das höchstwertige Byte eines Signed 
nicht anwenden, weil sich sonst evtl. das Vorzeichen ändert. Für LSR 
gibt es allerdings schon ersatz: ASR. Für LSL hingegen gibt es keinen, 
aber da kannst du sowas machen:
1
bst reg,7
2
bld reg,6
3
lsl reg

Dabei bleibt das Vorzeichen erhalten. Auch musst du bei der 
Multiplikation aufpassen, schau dir dazu die Operationen MULS und MULSU 
an.

von Rolf D. (rolfdegen)


Angehängte Dateien:

Lesenswert?

Hallo

Ich habe es wie folgt gelöst:
1
//-------------------------------------------------------------------
2
// Ringmodulation Sample1 (dco1out0 8Bit) * Sample2 (dco2out0 8Bit)
3
// Multiplikationsergebnis durch 16 teilen für 12Bit Ausgabe auf DAC
4
//-------------------------------------------------------------------
5
  Ringmod:
6
  subi  dco1out0,0x80      ; make signed Wave
7
  subi  dco2out0,0x80
8
  MULS  dco1out0, dco2out0    ; Multipl signed DCO1_out * DCO2_out
9
  MOVW  dco1out0, R0      ; save Result
10
  
11
  ldi   dco2out0,0x80      ; make unsigned Wave
12
  add   dco1out1,dco2out0
13
14
  LSR   dco1out1        ; DCO_out reduction to 12Bit (div 4)
15
  ROR   dco1out0
16
  LSR   dco1out1
17
  ROR   dco1out0
18
  LSR   dco1out1
19
  ROR   dco1out0
20
  LSR   dco1out1
21
  ROR   dco1out0
22
  STS   0x0318, dco1out0          ; 2   L-Byte to DAC-Register (CH0DATAL Adr. 0x0318)
23
  STS   0x0319, dco1out1          ; 2   H-Byte to DAC Register (CH0DATAH Adr. 0x0319)

Das Ergebnis sieht jetzt richtig aus (Bild). Allerdings ist die 
Ausgangsspannung nur noch halb so groß wie vorher. Eine Division durch 
drei vor der DAC-Ausgabe funktioniert nicht. Dadurch entstehen wilde 
Bit-Muster.

Gruß Rolf

Gruß Rolf

von Rene B. (themason) Benutzerseite


Lesenswert?

Das die Ausgangsspannug nur noch halb so groß ist, liegt daran das du 2 
Vorzeichenbits hast. Da aber durch die Multiplikation ja nur noch 1 
VZ-Bit übrigbleibt geht dir in der Dynamik ja ein Bit flöten. Du 
müsstest quasi eienn Shift nach links machen um die Ausgangsspannung 
wieder auf de vollen Bereich zu bringen.

von Rolf D. (rolfdegen)


Lesenswert?

Das funktioniert eben nicht. Egal wo ich es mache. Es ensteht am Ausgang 
ein Wildes Bit-Chaos.
1
...
2
  ldi   dco2out0,0x80      ; make unsigned Wave
3
  add   dco1out1,dco2out0
4
5
  LSR   dco1out1        ; DCO_out reduction to 12Bit (div 4)
6
  ROR   dco1out0
7
  LSR   dco1out1
8
  ROR   dco1out0
9
  LSR   dco1out1
10
  ROR   dco1out0
11
  LSR   dco1out1
12
  ROR   dco1out0
13
14
  LSL   dco1out0       ; Ausgabewert multi 2
15
  ROL   dco1out1
16
17
  STS   0x0318, dco1out0          ; 2   L-Byte to DAC-Register (CH0DATAL Adr. 0x0318)
18
  STS   0x0319, dco1out1          ; 2   H-Byte to DAC Register (CH0DATAH Adr. 0x0319)

von Maik M. (myco)


Lesenswert?

Vorzeichenbehaftetes Multiplizieren ist etwas tricky, wie Rene schon 
schrieb, verliert man 1 Bit. Genauer:

S XXX XXXX * S XXX XXXX = SS YY YYYY YYYY YYYY

Wobei S das Vorzeichenbit ist, X und Y sind Wertebits.

Probier mal folgendes (bisher ungetestet):
1
//-------------------------------------------------------------------
2
// Ringmodulation Sample1 (dco1out0 8Bit) * Sample2 (dco2out0 8Bit)
3
// Multiplikationsergebnis durch 16 teilen für 12Bit Ausgabe auf DAC
4
//-------------------------------------------------------------------
5
  Ringmod:
6
  subi  dco1out0,0x80      ; make signed Wave
7
  subi  dco2out0,0x80
8
  MULS  dco1out0, dco2out0    ; Multipl signed DCO1_out * DCO2_out
9
10
  ; Multiply signed word with 2
11
  bst r1,7
12
  bld r1,6
13
  lsl r0
14
  rol r1
15
16
  ; Make unsigned word
17
  subi r1,0x80
18
  
19
  ; save Result
20
  movw dco1out1:dco1out0, r1:R0      
21
  
22
  LSR   dco1out1        ; DCO_out reduction to 12Bit (div 4)
23
  ROR   dco1out0
24
  LSR   dco1out1
25
  ROR   dco1out0
26
  LSR   dco1out1
27
  ROR   dco1out0
28
  LSR   dco1out1
29
  ROR   dco1out0
30
  STS   0x0318, dco1out0          ; 2   L-Byte to DAC-Register (CH0DATAL Adr. 0x0318)
31
  STS   0x0319, dco1out1          ; 2   H-Byte to DAC Register (CH0DATAH Adr. 0x0319)

wenn das überhaupt funktionieren sollte, dann müssen dco1out1:dco1out0 
Registerpaare sein, ansonsten würde MOVW generell fehlschlagen.

von Maik M. (myco)


Lesenswert?

hier nochmal was zum Testen... etwas abgekürzt:
1
//-------------------------------------------------------------------
2
// Ringmodulation Sample1 (dco1out0 8Bit) * Sample2 (dco2out0 8Bit)
3
// Multiplikationsergebnis durch 16 teilen für 12Bit Ausgabe auf DAC
4
//-------------------------------------------------------------------
5
  Ringmod:
6
  subi  dco1out0,0x80      ; make signed Wave
7
  subi  dco2out0,0x80
8
  muls  dco1out0, dco2out0    ; Multipl signed DCO1_out * DCO2_out
9
10
  ; remove first sign bit
11
  andi  r1, 0x7F
12
  
13
  ; shift 3 times left
14
  lsr   r1
15
  ror   r0
16
  lsr   r1
17
  ror   r0
18
  lsr   r1
19
  ror   r0
20
  
21
  ; make unsigned 12bit
22
  subi  r1, 0x08
23
24
  ; save Result
25
  movw dco1out1:dco1out0, r1:r0      
26
  
27
  sts   0x0318, dco1out0          ; 2   L-Byte to DAC-Register (CH0DATAL Adr. 0x0318)
28
  sts   0x0319, dco1out1          ; 2   H-Byte to DAC Register (CH0DATAH Adr. 0x0319)

von Rolf D. (rolfdegen)


Lesenswert?

Danke Maik. Es funktioniert. Ich musste es allerdings etwas anpassen.
1
Ringmod:
2
  subi  dco1out0,0x80      ; make signed Wave
3
  subi  dco2out0,0x80
4
  MULS  dco1out0, dco2out0    ; Multipl signed DCO1_out * DCO2_out
5
6
  ; Multiply signed word with 2
7
  bst r1,7
8
  bld r1,6
9
  lsl r0
10
  rol r1
11
12
  ; Make unsigned word
13
  //subi r1,0x80
14
  
15
  ; save Result
16
  //movw dco1out1:dco1out0, r1:R0 
17
  movw  dco1out0,r0     
18
  subi  dco1out1,0x80
19
20
  LSR   dco1out1        ; DCO_out reduction to 12Bit (div 4)
21
  ROR   dco1out0
22
  LSR   dco1out1
23
  ROR   dco1out0
24
  LSR   dco1out1
25
  ROR   dco1out0
26
  LSR   dco1out1
27
  ROR   dco1out0
28
29
  STS   0x0318, dco1out0          ; 2   L-Byte to DAC-Register (CH0DATAL Adr. 0x0318)
30
  STS   0x0319, dco1out1          ; 2   H-Byte to DAC Register (CH0DATAH Adr. 0x0319)

von Maik M. (myco)


Lesenswert?

das mit dem abkürzen (2. Code-Beitrag von mir) wird wohl nicht 
funktionieren... jetzt bin ich mit dem Signed-Kram auch 
durcheinandergekommen.

von Maik M. (myco)


Lesenswert?

evtl. geht's so, vielleicht:
1
//-------------------------------------------------------------------
2
// Ringmodulation Sample1 (dco1out0 8Bit) * Sample2 (dco2out0 8Bit)
3
// Multiplikationsergebnis durch 16 teilen für 12Bit Ausgabe auf DAC
4
//-------------------------------------------------------------------
5
  Ringmod:
6
  subi  dco1out0,0x80      ; make signed Wave
7
  subi  dco2out0,0x80
8
  muls  dco1out0, dco2out0    ; Multipl signed DCO1_out * DCO2_out
9
10
  ; shift 3 times left
11
  lsr   r1
12
  ror   r0
13
  lsr   r1
14
  ror   r0
15
  lsr   r1
16
  ror   r0
17
  
18
  ; make unsigned 12bit
19
  subi  r1, 0x88
20
21
  ; save Result
22
  movw dco1out1:dco1out0, r1:r0      
23
  
24
  sts   0x0318, dco1out0          ; 2   L-Byte to DAC-Register (CH0DATAL Adr. 0x0318)
25
  sts   0x0319, dco1out1          ; 2   H-Byte to DAC Register (CH0DATAH Adr. 0x0319)

von Rolf D. (rolfdegen)


Lesenswert?

Der Compiler meckert immer bei "subi  r1, 0x88"

Meldung: Error  1  register number above 15 required

Dieser Befehl funktioniert nur mit den oberen 16 Registern.


Ich hab es jetzt so geschrieben:
1
Ringmod:
2
  subi  dco1out0,0x80      ; make signed Wave
3
  subi  dco2out0,0x80
4
  muls  dco1out0, dco2out0    ; Multipl signed DCO1_out * DCO2_out
5
6
  ; shift 3 times left
7
  lsr   r1
8
  ror   r0
9
  lsr   r1
10
  ror   r0
11
  lsr   r1
12
  ror   r0
13
  
14
  ; make unsigned 12bit
15
  //subi  r1, 0x88
16
17
  ; save Result
18
  //movw dco1out1:dco1out0, r1:r0      
19
  
20
  movw  dco1out0,r0
21
  subi  dco1out1,0x88
22
  sts   0x0318, dco1out0          ; 2   L-Byte to DAC-Register (CH0DATAL Adr. 0x0318)
23
  sts   0x0319, dco1out1          ; 2   H-Byte to DAC Register (CH0DATAH Adr. 0x0319)

von Maik M. (myco)


Lesenswert?

aja... wohl weil das nicht auf dem Register erlaubt ist. Vielleicht so:
1
//-------------------------------------------------------------------
2
// Ringmodulation Sample1 (dco1out0 8Bit) * Sample2 (dco2out0 8Bit)
3
// Multiplikationsergebnis durch 16 teilen für 12Bit Ausgabe auf DAC
4
//-------------------------------------------------------------------
5
  Ringmod:
6
  subi  dco1out0,0x80      ; make signed Wave
7
  subi  dco2out0,0x80
8
  muls  dco1out0, dco2out0    ; Multipl signed DCO1_out * DCO2_out
9
10
  ; shift 3 times left
11
  lsr   r1
12
  ror   r0
13
  lsr   r1
14
  ror   r0
15
  lsr   r1
16
  ror   r0
17
  
18
  ; save Result
19
  movw dco1out1:dco1out0, r1:r0   
20
21
  ; make unsigned 12bit
22
  subi  dco1out1, 0x88   
23
  
24
  sts   0x0318, dco1out0          ; 2   L-Byte to DAC-Register (CH0DATAL Adr. 0x0318)
25
  sts   0x0319, dco1out1          ; 2   H-Byte to DAC Register (CH0DATAH Adr. 0x0319)

Edit: ja, so wie du's jetzt ergänzt hast, ist's genau wie ich es hier 
vorgeschlagen habe.

von Rolf D. (rolfdegen)


Lesenswert?

Danke Maik für deine tolle Unterstützung. Ich sach mal Schüß bis zum 
nächsten Problem :) und schönen Sonntag noch.

Gruß Rolf

von Rolf D. (rolfdegen)


Lesenswert?

Hallo

Ich komme einfach nicht weiter in Bezug auf die Tune-Funktion des 
Oszillators in meinem AVR-Synthi.

Um die Midi-Note 1-127 in eine Tonfrequenz umzuwandeln greife ich auf 
eine 16Bit Tabelle mit 127 Frequenzwerten im Flash-Speicher des Xmegas 
zu. Wenn ich zB Oszillator2 eine Oktave tiefer stellen möchte, dann 
teile ich einfach den Frequenzwert der Midi-Note. Funktioniert auch 
problemlos und über alle Oktaven sehr genau. Aber wie kann ich den 
Oszillator um einen Halbton verstellen. Ich möchte später mit der 
Tune-Funktion den Oszillator um +-24 Halbtöne verstellen können. Alle 
meine Berechnungen liefern "Krumme Werte" so das eine genaue Einstellung 
nicht möglich ist.

1
//***********************************************
2
// Frequenz_Tune
3
// Wiedergabefrequenz mit Tune-Werten berechnen
4
// 
5
// noten_nr = Midi-Key_Nr.
6
//***********************************************
7
void frequency_tune()
8
{
9
     frequenz = pgm_read_word (&(midi_frequenz[noten_nr]));
10
     schrittweite1 = (__uint24) 411 * frequenz;  // Phasendelta für Oszillator1
11
     
12
     schrittweite2 = (__uint24) 411 * frequenz >> 1; Phasendelta für Oszillator2 (eine Oktave niedriger)



Gruß Rolf

von Rolf D. (rolfdegen)


Lesenswert?

Hallo

Ich hab das jetzt mal so gelöst:
1
//***********************************************
2
// Frequenz_Tune
3
// Wiedergabefrequenz mit Tune-Werten berechnen
4
//***********************************************
5
void frequency_tune()
6
{
7
     frequenz = pgm_read_word (&(midi_frequenz[noten_nr]));
8
     schrittweite1 = (__uint24) 411 * frequenz;
9
     print_16bit_number(frequenz,20,20,3);
10
  
11
  uint8_t halbton = 8;
12
  uint32_t temp_schrittweite2 = 0;   
13
     temp_schrittweite2 = (uint32_t)schrittweite1 >> 1;
14
     temp_schrittweite2 = (uint32_t)temp_schrittweite2 / 12;
15
     temp_schrittweite2 = (uint32_t)temp_schrittweite2 * halbton;
16
     schrittweite2 = (__uint24) schrittweite1 + temp_schrittweite2;

Um die Rundungen bei der Berechnung der Schrittweite2 (Phasendelta) für 
Oszillator2 so gering wie möglich zu halten, habe ich den Zahlenwert auf 
32Bit erhöht.

Gruß Rolf

von Maik M. (myco)


Lesenswert?

Du gehst die Sache sehr Ressourcenfressend an. Ich habe diesen Teil bei 
meinem Synth auch in Assembler gemacht. Es ist nicht kompliziert, und 
man kann mit der richtigen Technik auch sehr viel Ressourcen sparen. Bei 
mir schaut das ungefähr so aus:

Ich habe 1 Table, der enthält 14 Akkumulatoren-Deltas, nennen wir ihn 
mal "PitchTable". Der erste Wert ist das Delta für B-1, der 14. Wert ist 
für das C+1, dazwischen sind endsprechend die anderen Noten-Werte.

Eingänge sind: inNote(0..127), inSemi(-24..24), inFine(-128..127)
Ausgang ist outPitch
1
fNote = inNote + inSemi;
2
if (fNote & 0x80)fNote = 0;// <-- Überschlag vermeiden
3
fOctave = fNote / 12;      // <-- Ganzzahlig
4
fPitchIndex = fNote % 12;  // <-- Ganzzahlig
5
fPitch1 = PitchTable[fPitchIndex+1] << fOctave;
6
fPitch2 = PitchTable[fPitchIndex+(inFine>0) ? 2 : 0] << fOctave;
7
fFineByte = abs(inFine) << 1;
8
outPitch = ( fPitch1 * (255-fFineByte) ) >> 8 + ( fPitch2 * (fFineByte) ) >> 8;

Das is Pseudocode. Aber die Voregehensweise ist im Groben: Oktave werden 
geschiftet, Semi einfach ganz am Anfang auf die Notennummer addiert, 
Fine wird linear zwischen zwei benachbarten Akkumulatoren-Werten 
interpoliert.

von Maik M. (myco)


Lesenswert?

Ah Käse, geht noch fixer:
1
fNote = inNote + inSemi;
2
if (fNote & 0x80)fNote = 0;// <-- Überschlag vermeiden
3
fOctave = fNote / 12;      // <-- Ganzzahlig
4
fPitchIndex = (fNote % 12) + 1;  // <-- Ganzzahlig
5
fPitch1 = PitchTable[fPitchIndex];
6
fPitch2 = PitchTable[fPitchIndex+(inFine>0) ? 1 : -1];
7
fFineByte = abs(inFine) << 1;
8
outPitch = ( fPitch1 * (255-fFineByte) ) >> 8 + ( fPitch2 * (fFineByte) ) >> 8;
9
outPitch <<=fOctave;

von Rolf D. (rolfdegen)


Lesenswert?

Danke für deine Hilfe. Werde deinen Vorschlag ausprobieren und wenns 
klappt oder auch nicht noch einmal hier melden.

Gruß Rolf

von Rolf D. (rolfdegen)


Lesenswert?

Hallo Maik

Die Routine funktioniert leider nicht so wie sie soll. Als Ergebnis 
erhalte ich immer Frequenzen im Bereich von 0-5Hz über die spielbaren 
Oktaven.


Mein Code:
1
//*********************************************
2
//
3
//   PhaccuDelta-Value from Midi Note-Nr. 0-11
4
//
5
//   Phaccu_Delta = 411 * Midi-Note Frequenz
6
//
7
//*********************************************
8
uint16_t phaccu_delta [14] = { 
9
  3171,    // B-1
10
  3360,    // C1  // Midi-Note 0-11
11
  3560,
12
  3771,
13
  3996,
14
  4233,
15
  4485,
16
  4752,
17
  5034,
18
  5334,
19
  5651,
20
  5987,
21
  6343,
22
  6720      // B+1            
23
};
24
25
26
//***********************************************
27
// Frequenz_Tune
28
// Wiedergabefrequenz mit Tune-Werten berechnen
29
//***********************************************
30
void frequency_tune()
31
{
32
    uint8_t fNote;
33
    uint8_t inNote = noten_nr;
34
    char inSemi = 0;
35
    char inFine = 0;
36
    uint8_t fFineByte = 0;
37
    char fOctave = 0;
38
    uint8_t fPitchIndex;
39
    __uint24 fPitch1;
40
    __uint24 fPitch2;
41
    __uint24 outPitch;
42
         
43
    fNote = inNote + inSemi;
44
    if(fNote & 0x80) fNote = 0;
45
    fOctave = fNote / 12;
46
    fPitchIndex = (fNote % 12) + 1;
47
    fPitch1 = phaccu_delta[fPitchIndex];
48
    fPitch2 = phaccu_delta[fPitchIndex + (inFine > 0) ? 1 : -1];
49
    fFineByte = abs(inFine) << 1;
50
    outPitch = ( fPitch1 * (255-fFineByte) ) >> 8 + ( fPitch2 * (fFineByte) ) >> 8;
51
    outPitch <<=fOctave;
52
    schrittweite1 = (__uint24)outPitch;

Änder ich die Zeile mit Null shift so ab:
1
outPitch = ( fPitch1 * (255-fFineByte) ) >> 0 + ( fPitch2 * (fFineByte) ) >> 8;
erhalte ich fast die richtige Frequenzen (A = 438.5 Hz).
Der Wert für die Oktavierung funktioniert leider auch nicht und hat 
keine Auswirkungen auf die Wiedergabefrequenz.

Gruß Rolf

von Rolf D. (rolfdegen)


Lesenswert?

Das mit der Oktavierung war eine falsche Aussage von mir. Ich sehe 
gerade das eine Oktavierung nur über den Semiton-Werte funktioniert. 
Also Semiton-Wert zb "12" wäre eine Oktave höher und "-12" eine Oktave 
niedriger.

von Maik M. (myco)


Lesenswert?

Ich habe so meine Probleme mit C/C++. Da muss man sehr auf die 
Typenbreite achten, besonders bei dieser Zeile:
1
outPitch = ( fPitch1 * (255-fFineByte) ) >> 8 + ( fPitch2 * (fFineByte) ) >> 8;

Ich glaube C/C++ rechnet hier in 24bit, die Multiplikation aus 24bit mit 
8bit ergibt allerdings 32bit und der Compiler convertiert das runter 
indem er die höheren (wichtigeren) 8bit weglässt. Das müsste irgendwie 
mit Typecasting lösbar sein, allerdings bin ich da nicht so Fit. Evtl. 
geht es so:
1
outPitch = uint24_t(( (uint32_t)fPitch1 * (255-fFineByte) ) >> 8 + ((uint32_t)fPitch2 * (fFineByte) ) >> 8);

von Rolf D. (rolfdegen)


Lesenswert?

Ich teste es jetzt.

von Rolf D. (rolfdegen)


Lesenswert?

Ne.. leider kein Erfolg. Frequenz immer noch auf 0-32Hz

von Rene B. (themason) Benutzerseite


Lesenswert?

@Maik,

aber das lineare Interpolieren zwischen zwei Halbton-Pitches müsste doch 
eigentlich fehlerhaft sein da es ja eine Exponentialfunktion ist die die 
Frequenzen zwischen den Oktaven und auch entsprechend den Halbtönen 
bestimmt. Wie genau das Ohr diese unterschiede wahrnehmen kann weiß ich 
nicht, aber ich denke das man bei ner abweichung von 50cent schon 
hörbare Frequenzunterschiede wahrnehmen müsste zwischen tatsächlicher 
Frequenz und interpolierter Frequenz.
Ansonsten ist das Verfahren ja richtig und auch recht platzsparend. Ich 
wär bzw bin ganz bruteral mit ner Tabelle über 120 Halbtöne und 8/16/32 
Abstufungen drangegangen, aber das ist im Prinzip ja quatsch, da sich 
die Oktaven ja durch Shiften bilden lassen, und man dann von den 
höchsten Inkrementen bzw Schrittweiten ausgeht und runtershiftet.

von Maik M. (myco)


Lesenswert?

Was passiert, wenn du die Zeile:
1
outPitch = ( fPitch1 * (255-fFineByte) ) >> 8 + ( fPitch2 * (fFineByte) ) >> 8;

durch diese ersetzt:
1
outPitch = fPitch1;

bzw:
1
outPitch = fPitch2;

von Rolf D. (rolfdegen)


Lesenswert?

Ja das wollte ich gerade machen.

 outPitch = (__uint24)fPitch1 << fOctave; = 440,1Hz
 outPitch = (__uint24)fPitch2 << fOctave; = 261,6Hz

von Maik M. (myco)


Lesenswert?

Rene B. schrieb:
> @Maik,
>
> aber das lineare Interpolieren zwischen zwei Halbton-Pitches müsste doch
> eigentlich fehlerhaft sein da es ja eine Exponentialfunktion ist die die
> Frequenzen zwischen den Oktaven und auch entsprechend den Halbtönen
> bestimmt.

Ja im Prinzip ist es falsch, aber man muss halt Kompromisse eingehen. 
Zur Not könnte man einen Exponential-Tabelle nutzen, aber richtig 
berechnen kann man das auf einem AVR nicht.

von Maik M. (myco)


Lesenswert?

Rolf Degen schrieb:
> Ja das wollte ich gerade machen.
>
>  outPitch = (__uint24)fPitch1 << fOctave; = 440,1Hz
>  outPitch = (__uint24)fPitch2 << fOctave; = 261,6Hz

ja, dann hat es irgendwas mit der Typenkonvertierung in der Zeile zu 
tun. Wie gesagt, das war nie mein Ding :)

von Maik M. (myco)


Lesenswert?

Ohne Typkonvertierung könnte es so gehen:
outPitch = ( (fPitch1>>8) * (255-fFineByte) ) + ( (fPitch2>>8) * 
(fFineByte) );

Allerdings produziert das massive Rundungsfehler

von Rolf D. (rolfdegen)


Lesenswert?

Damit ändern sich die Notenwerte in einer Oktave nicht mehr. Sie bleiben 
gleich und verdoppeln sich bei der nächsten Oktave.

von Maik M. (myco)


Lesenswert?

Vielleicht geht's ja so:
1
//*********************************************
2
//
3
//   PhaccuDelta-Value from Midi Note-Nr. 0-11
4
//
5
//   Phaccu_Delta = 411 * Midi-Note Frequenz
6
//
7
//*********************************************
8
uint16_t phaccu_delta [14] = { 
9
  3171,    // B-1
10
  3360,    // C1  // Midi-Note 0-11
11
  3560,
12
  3771,
13
  3996,
14
  4233,
15
  4485,
16
  4752,
17
  5034,
18
  5334,
19
  5651,
20
  5987,
21
  6343,
22
  6720      // B+1            
23
};
24
25
26
//***********************************************
27
// Frequenz_Tune
28
// Wiedergabefrequenz mit Tune-Werten berechnen
29
//***********************************************
30
void frequency_tune()
31
{
32
    uint8_t fNote;
33
    uint8_t inNote = noten_nr;
34
    char inSemi = 0;
35
    char inFine = 0;
36
    uint8_t fFineByte;
37
    uint8_t fOctave;
38
    uint8_t fPitchIndex;
39
    uint16_t fPitch1;
40
    uint16_t fPitch2;
41
    int16_t fPitchDiff;
42
    __uint24 outPitch;
43
         
44
    fNote = inNote + inSemi;
45
    if(fNote & 0x80) fNote = 0;
46
    fOctave = fNote / 12;
47
    fPitchIndex = (fNote % 12) + 1;
48
    fPitch1 = phaccu_delta[fPitchIndex];
49
    fPitch2 = phaccu_delta[fPitchIndex + (inFine > 0) ? 1 : -1];
50
    fPitchDiff = (int16_t) (fPitch2-fPitch1);
51
    fFineByte = ((uint8_t)(abs(inFine))) << 1;
52
    outPitch = (__uint24)(((((int32_t)fPitch1)<<8) + ((int32_t)fPitchDiff*(int32_t)fFineByte)) >> 8);
53
    outPitch <<=fOctave;
54
    schrittweite1 = outPitch;
55
...

von Rolf D. (rolfdegen)


Lesenswert?

Ich habe das Programm etwas abgeändert und es kommen jetzt zumindest die 
richtigen Frequenzen heraus. Allerdings funktioniert es mit 
"inFine"-Werten noch nicht.
1
//***********************************************
2
// Frequenz_Tune
3
// Wiedergabefrequenz mit Tune-Werten berechnen
4
//***********************************************
5
void frequency_tune()
6
{
7
    uint8_t fNote;
8
    uint8_t inNote = noten_nr;
9
    char inSemi = 0;
10
    char inFine = 10;
11
    uint8_t fFineByte = 0;
12
    uint8_t fOctave = 0;
13
    uint8_t fPitchIndex;
14
    uint16_t fPitch1;
15
    uint16_t fPitch2;
16
    __uint24 outPitch;
17
         
18
    fNote = inNote + inSemi;
19
    if(fNote & 0x80) fNote = 0;
20
    fOctave = fNote / 12;
21
    fPitchIndex = (fNote % 12) + 1;
22
    fPitch1 = phaccu_delta[fPitchIndex];
23
    fPitch2 = phaccu_delta[fPitchIndex + 1];// (inFine > 0) ? 1 : -1];
24
    fFineByte = abs(inFine) << 1;
25
    outPitch = (__uint24)( fPitch1 * (255-fFineByte) ) >> 8 + ( fPitch2 * (fFineByte) ) >> 8;    
26
    outPitch = (__uint24)fPitch2 << fOctave;
27
    schrittweite1 = outPitch;


Ups.. du warst schneller. Ich werde deine Programm jetzt testen.

von Rolf D. (rolfdegen)


Lesenswert?

Dein Programm funktioniert soweit. Nur bei den inFine-Werten stimmt was 
nicht. Ich bekomm bei positiven und negativen Werten immer eine niedrige 
Frequenzen heraus.

Zum Beispiel:
inFine =  0  = 440Hz
inFine =  10 = 426Hz
inFine = -10 = 426Hz

von Maik M. (myco)


Lesenswert?

ups ... Auskommtierung vergessen

ersetze
1
fPitch2 = phaccu_delta[fPitchIndex + 1];// (inFine > 0) ? 1 : -1];

durch
1
fPitch2 = phaccu_delta[fPitchIndex + (inFine > 0) ? 1 : -1];

von Rolf D. (rolfdegen)


Lesenswert?

Die Programmzeile hatte ich bereits wieder geändert. Ich habe mir den 
fPitch2 Wert mal angesehen. Der Wert (3360) ist bei inFine = +10 bei 
jeder Midi-Note gleich groß.

von Maik M. (myco)


Lesenswert?

Das kann nicht sein, der müsste sich bei jeder Note um 1 Index 
verschoben zu Pitch1 bewegen.

von Rolf D. (rolfdegen)


Lesenswert?

Midi-Note = 440Hz (Messung: 440Hz)
inFine = 0
fPitch1 = 5651
fPitch2 = 3360
fPitchDiff = 63245


Midi-Note = 440Hz (Messung: 426.3Hz)
inFine = 10
fPitch1 = 5651
fPitch2 = 3360
fPitchDiff = 63245


Midi-Note = 440Hz (Messung: 426.1Hz)
inFine = -10
fPitch1 = 5651
fPitch2 = 3360
fPitchDiff = 63245

von Rene B. (themason) Benutzerseite


Lesenswert?

Also das die beiden fPitch's bei +10 und -10 cent diesselben sind kann 
ja nicht sein. fPitch2 müsste ja ggü fPitch1 den Pitch für einen Halbton 
unter fPitch1 liegen, oder ?

von Maik M. (myco)


Lesenswert?

argh... auf ein Neues...
1
//*********************************************
2
//
3
//   PhaccuDelta-Value from Midi Note-Nr. 0-11
4
//
5
//   Phaccu_Delta = 411 * Midi-Note Frequenz
6
//
7
//*********************************************
8
uint16_t phaccu_delta [14] = { 
9
  3171,    // B-1
10
  3360,    // C1  // Midi-Note 0-11
11
  3560,
12
  3771,
13
  3996,
14
  4233,
15
  4485,
16
  4752,
17
  5034,
18
  5334,
19
  5651,
20
  5987,
21
  6343,
22
  6720      // B+1            
23
};
24
25
26
//***********************************************
27
// Frequenz_Tune
28
// Wiedergabefrequenz mit Tune-Werten berechnen
29
//***********************************************
30
void frequency_tune()
31
{
32
    uint8_t fNote;
33
    uint8_t inNote = noten_nr;
34
    char inSemi = 0;
35
    char inFine = 0;
36
    uint8_t fFineByte;
37
    uint8_t fOctave;
38
    uint8_t fPitchIndex;
39
    uint16_t fPitch1;
40
    uint16_t fPitch2;
41
    int16_t fPitchDiff;
42
    __uint24 outPitch;
43
         
44
    fNote = inNote + inSemi;
45
    if(fNote & 0x80) fNote = 0;
46
    fOctave = fNote / 12;
47
    fPitchIndex = (fNote % 12);
48
    fPitch1 = phaccu_delta[fPitchIndex+1];
49
    if (inFine > 0)fPitchIndex+=2;
50
    fPitch2 = phaccu_delta[fPitchIndex];
51
    fPitchDiff = (int16_t) (fPitch2-fPitch1);
52
    fFineByte = ((uint8_t)(abs(inFine))) << 1;
53
    outPitch = (__uint24)(((((int32_t)fPitch1)<<8) + ((int32_t)fPitchDiff*(int32_t)fFineByte)) >> 8);
54
    outPitch <<=fOctave;
55
    schrittweite1 = outPitch;
56
...

von Rolf D. (rolfdegen)


Lesenswert?

Ja es funktioniert jetzt. Prima :) Der Notenwerte liegen zwar jetzt 
etwas höher A 440Hz = gemessen 440,5 aber das ist ok. Kann man mit dem 
inFine-Wert -1 aber korregieren.

Danke für deine großartige Hilfe. Das Ganze bringt mich in meinem 
Projekt wieder einen großen Schritt nach vorne.

Schönen Tag noch und lieben Gruß Rolf

von Rolf D. (rolfdegen)


Angehängte Dateien:

Lesenswert?

Hallo

Ich habe Heute die Menü-Seite für die DCO-Einstellung fertig gestellt 
(siehe Bild). Jeder Oszillator kann in +-24 Semitone-Schritten 
(entspricht +- 2 Oktaven) verstellt werden. Zusätzlich kan man mit der 
Fine-Einstellung (Schrittweite +-100 Cent) den Oszillator zwischen zwei 
Halbtönen verstimmen. Mit den Tasten "+" und "-" ist außerdem eine 
genauere Einstellung der Parameter möglich.

Nochmals besten Dank an Maik. Ohne seine großartige Unterstützung hätte 
es nicht funktioniert.


Bild: DCO-Einstellung im AVR-Synthi

MfG Rolf

von Rolf D. (rolfdegen)


Lesenswert?

Hallöchen..

Damits hier nicht zu langweilig wird, gibts zur Abwechslung ein paar 
Demo-Sound. Was in den Sounds noch fehlt, ist die Filter und 
Amplituden-Modulation sowie FM und Synchronisation der Oszillatoren.

AVR-Synthi Demo-Sounds
Link: http://soundcloud.com/rolfdegen/avr-demo-31102012

von Rene B. (themason) Benutzerseite


Lesenswert?

Sehr schönes Demo.

Vor allem gefällt mir der Sound ab 11:30.
Aber ist der Filter wirklich noch nicht mir drin ? Tlw klingt es ja 
schon so :-)
Aber echt sehr schön gemacht. Weiter so. Bin mal gespannt auf die 
restlichen Features wie Ringmod/Sync/FM und eben alles noch mit Filtern 
zusammen :-)

von Rolf D. (rolfdegen)


Lesenswert?

Ne der Filter wird z.Zt. mit dem ADSR-Werten gesteuert.

von Rolf Degen (Gast)


Lesenswert?

Hallo

Ich bin gerade dabei die Sync-Funktion für die Oszillatoren zu 
programmieren. Ich weis allerdings nicht ob mein Ansatz richtig ist 
(siehe Code).
1
//===============================================================
2
// SUBTRAKTIVE KLANGSYSYNTHESE
3
//===============================================================
4
5
SubSynthese:
6
7
//---------------------------------------------------------------
8
// DCO-1 
9
//   * 24-Bit Akku-Breite
10
//   * 24-Bit Phasen-Delta (2,384185mHz/Unit)
11
//   * 8-Bit Sample
12
//---------------------------------------------------------------
13
14
  // Phasen-Akku1 incrementieren
15
  // ----------------------------
16
17
  LDS   delta0, schrittweite1+0  ; 2   Phasen-Delta1 aus SRAM laden
18
  LDS   delta1, schrittweite1+1  ; 2
19
  LDS   delta2, schrittweite1+2  ; 2
20
21
  LDS   phakku0, phaccu1+0       ; 2   Phasen-Akku1 aus SRAM laden
22
  LDS   phakku1, phaccu1+1       ; 2
23
  LDS   phakku2, phaccu1+2       ; 2
24
25
  ADD   phakku0, delta0          ; 1   Phasen-Akku1 + Phasen-Delta1
26
  ADC   phakku1, delta1          ; 1
27
  ADC   phakku2, delta2          ; 1
28
29
  // DCO2 sync
30
  BRCC  test_01           ;     setze Phasen-Akku2 auf null wenn Phasen-Akku1 Überlauf
31
  CLR   r21
32
  STS   phaccu2+0, r21
33
  STS   phaccu2+1, r21
34
  STS   phaccu2+2, r21 
35
   
36
  test_01:
37
  STS   phaccu1+0, phakku0       ; 2   Phasen-Akku1 in SRAM zurückschreiben
38
  STS   phaccu1+1, phakku1       ; 2
39
  STS   phaccu1+2, phakku2       ; 2
40
  
41
42
43
  // Basis-Adresse für Wavetable laden
44
  // -------------------------------------------------
45
  LDS   R30, wavetable1+0        ; 1   Startadr. Wave-Table (Low-Byte)
46
  LDS   R31, wavetable1+1        ; 1                        (High-Byte)
47
48
  // Sample1 aus Wavetable laden (8Bit Auflösung)
49
  // ------------------------------------------------------------------
50
  BL_Wave1:
51
  clr   R21
52
  ADD   R30, phakku2       ; 1   Adresse Sample-Nr.
53
  ADC   R31, r21         ; 1
54
  LPM   dco1out0, Z+             ; 3   Sample aus Wavetable laden (8-Bit)
55
  LPM   dco1out1, Z              ; 3 
56
  clr   dco1out1
57
58
59
  // Phasen-Akku2 incrementieren
60
  // ----------------------------
61
  DCO2:
62
  LDS   delta0, schrittweite2+0  ; 2   Phasen-Delta aus SRAM laden
63
  LDS   delta1, schrittweite2+1  ; 2
64
  LDS   delta2, schrittweite2+2  ; 2
65
66
  LDS   phakku0, phaccu2+0       ; 2   Phasen-Akku aus SRAM laden
67
  LDS   phakku1, phaccu2+1       ; 2
68
  LDS   phakku2, phaccu2+2       ; 2
69
70
  ADD   phakku0, delta0          ; 1   Phasen-Akku + Phasen-Delta
71
  ADC   phakku1, delta1          ; 1
72
  ADC   phakku2, delta2          ; 1
73
    
74
  STS   phaccu2+0, phakku0       ; 2   Phasen-Akku in SRAM zurückschreiben
75
  STS   phaccu2+1, phakku1       ; 2
76
  STS   phaccu2+2, phakku2       ; 2
77
78
79
  // Basis-Adresse für Wavetable laden
80
  // -------------------------------------------------
81
  LDS   R30, wavetable2+0        ; 1   Basis-Adresse Saw-Tabelle (Low-Byte)
82
  LDS   R31, wavetable2+1        ; 1                             (High-Byte)
83
84
  // Sample1 aus Wavetable laden
85
  // -------------------------------------------------
86
  BL_Wave2:
87
  clr   R21
88
  ADD   R30, phakku2       ; 1   Adresse Sample-Nr. 
89
  ADC   R31, r21                 ; 1
90
  LPM   dco2out0, Z+             ; 3   Sample aus Wavetable laden (8-Bit)
91
  LPM   dco2out1, Z              ; 3 
92
  clr   dco2out1
93
94
   //-----------------------------------------------------------
95
  // Addition Sample1 + Sample2
96
  //-----------------------------------------------------------
97
  Wave_out:
98
  ADD   dco1out0,dco2out0    ; DCO1_out + DCO2_out
99
  ADC   dco1out1,dco2out1
100
  LSL   dco1out0        ; Ausgabewert auf 12Bit (mul 3) 
101
  ROL   dco1out1
102
  LSL   dco1out0
103
  ROL   dco1out1
104
  LSL   dco1out0
105
  ROL   dco1out1
106
107
  // --------------------------------------------------------------
108
  // 12Bit Ergebnis zum DAC-Converter (DACA Chanal 0)
109
  // --------------------------------------------------------------
110
  DAC_out:
111
  STS   0x0318, dco1out0          ; 2   L-Byte to DAC-Register (CH0DATAL Adr. 0x0318)
112
  STS   0x0319, dco1out1          ; 2   H-Byte to DAC Register (CH0DATAH Adr. 0x0319)

Für eine Info wäre ich dankbar.

von Rene B. (themason) Benutzerseite


Lesenswert?

Meine AVR-Assembler sind zwar recht begrenzt, aber im Prinzip müsste es 
so gehen. Sobald ein Überlauf im Phasenakku 1 festegestellt wird, wird 
Phasenakku 2 genullt. Damit wird OSC2 durch OSC1 synchronisiert (was ja 
auch angestrebt ist :-)).

von Rolf D. (rolfdegen)


Lesenswert?

Ja super. Dann werde ich das im Prinzip so und zusätzlich mit einer 
Abfrage  für die Sync-Funktion in meinem Programm implementieren.

Gruß Rolf und schönes Wochenende.

von Rolf D. (rolfdegen)


Lesenswert?

Hallo

Ich brauch mal wieder Hilfe. Ich möchte in Assembler auf einen 256 Byte 
großen Ringbuffer in meinem Main-Programm (in C) zugreifen. So wie ich 
es versuche funktioniert es leider nicht. Ein Rat wäre sehr hilfreich. 
Schon mal Danke im Vorraus. LG Rolf


Ring-Buffer (FIFO) in C:
1
//******************************
2
// write data in to FIFO-Buffer
3
//******************************
4
   buf_data = 170;
5
   test_buffer[wrbuf_pointer] = buf_data;      // save data in to buffer
6
   wrbuf_pointer++;                // inc Buffer-Pointer
7
   buf_data = 0;
8
   buf_status = 1;
9
   _delay_ms(10);
10
   print_8bit_number(buf_data,120,70,2);


Lesen eines Bytes aus dem Ringbuffer in der Assembler-Routine und 
anschließend zurückschreiben in buf_data. buf_dat wird im Main-Programm 
ausgewertet.
1
// Buffer Testfunktion
2
  lds  r20, buf_status;      ;    Bufferstatus prüfen (ist 1 wenn Byte vorhanden)
3
  cpi  r20, 1          ;   
4
  brne next            ;    wenn ungleich Sprung zu next
5
  ldi  r20, lo8(rdbuf_pointer)  ;   ein Datenbyte aus dem Buffer lesen 
6
  sts  buf_data, r20      ;    und wieder abspeichern
7
  
8
  next:

von Maik M. (myco)


Lesenswert?

habe zwar von dem gemixe von C und ASM nicht soviel Ahnung, aber die 
Zeile
1
ldi  r20, lo8(rdbuf_pointer)  ;   ein Datenbyte aus dem Buffer lesen

schaut schon merkwürdig aus. Wenn der Ringbuffer im SRAM liegt, müsste 
die Adressierung 16Bit haben. Keine Ahnung was rdbuf_pointer überhaupt 
ist, aber wäre es ein Pointer in den SRAM (Elementindex eingeschlossen 
), dann würdest du mit ldi nur die Hälfte der Addresse bekommen.

Das ist jetzt nur wilde Spekulation, aber vielleicht geht es so:
1
ldi XL, low(rdbuf_pointer)
2
ldi XH, high(rdbuf_pointer) 
3
ld  r20, X

von Rolf D. (rolfdegen)


Lesenswert?

Danke. Werde deinen Vorschlag gleich testen. rdbuf_pointer ist der 
Adresszeiger bein lesen des Buffers und wrbuf_pointer beim schreiben in 
den Buffer.

von Rolf D. (rolfdegen)


Lesenswert?

Funktioniert leider nicht. Bekomme die Fehlermeldung :"Error  1  garbage 
at end of line"


Die Dekleration der Buffer-Variablen sieht bei mir im Main-Programm so 
aus:

// Test-Buffer (Lesezugriff erfolgt in ASM-Teil)
volatile uint8_t test_buffer[256];       // Receive Buffer for Wave-Data
uint8_t wrbuf_pointer = 0;               // Write_Pointer
volatile uint8_t rdbuf_pointer = 0;      // Read_Pointer
volatile uint8_t buf_data = 0;           // Bufferdaten
uint8_t buf_status = 0;                  // buf_status = 1 wenn Daten im 
Buffer


Die Buffer-Funktion soll später für die Soundausgabe in der ASM-Routine 
genutz werden. Die Sounddaten werden alle im Main-Programm vorberechnet 
und im Buffer gespeichert, wo sie dann im ASM-Teil ausgegeben werden.

von Rolf D. (rolfdegen)


Lesenswert?

Hallo

Ich konnte es jetzt so lösen:
1
// Buffer Testfunktion
2
  lds  r20, buf_status;      ;    Bufferstatus prüfen (ist 1 wenn Byte vorhanden)
3
  cpi  r20, 1                ;   
4
  brne next                  ;    wenn ungleich Sprung zu next
5
  ldi ZL, lo8(test_buffer)   ;    Startadresse des Test-Buffers laden
6
  ldi ZH, hi8(test_buffer)
7
  lds r17,(rdbuf_pointer)    ;    Position des Lesezeigers laden
8
  add ZL, r17                ;    und zur Startadresse hinzu addieren
9
  ldi r16,0
10
  adc ZH, r16
11
  ld  r20, Z                 ;    ein Byte aus dem Test-Buffer laden
12
  sts  buf_data, r20         ;    und für Testzweck speichern
13
  next:

In ZL+ZH befinden sich die Start-Adresse des Test-Buffer. In r17 die 
Position des Lesezeiger der auf das aktuelle Datenbyte zeigt.

Gruß Rolf

von Maik M. (myco)


Lesenswert?

da war ich doch nah dran... würde mich interessieren, was bei meinem 
falsch war. Das low/high?

von rolf degen (Gast)


Lesenswert?

Ich programmiere unter atmel Studio 6 und der Assembler ist da etwas 
eigen. Statt low/high muss lo8/Hi im Quellcode stehen.

LG Rolf

von Rolf D. (rolfdegen)


Lesenswert?

rolf degen schrieb:
> Ich programmiere unter atmel Studio 6 und der Assembler ist da etwas
> eigen. Statt low/high muss lo8/Hi im Quellcode stehen.
>
> LG Rolf

Ich meine lo8 und hi8

von Rolf D. (rolfdegen)


Lesenswert?

Hallo

Ich habe die Soundausgabe des AVR-Synthis testweise über einen 256 Byte 
großen Ring-Puffer geleitet (siehe Code). Der Puffer wird im 
Main-Programm (siehe Code) ständig mit Sample-Daten aus einer 8Bit 
Sinus-Tabelle befüllt und in der Interruptroutine für die Soundausgabe 
geleert.

Soweit funktioniert das auch ganz nett. Aber sobald ich Eingaben und 
andere Dinge auf dem Touch-Display mache, bekommt der Puffer nicht mehr 
genug Daten und es treten Fehler in der Soundausgabe auf.

Viele Funktionen für das Touch-Display benötigen mehr Zeit als der 
Puffer es erlaubt. Die maximale Pufferzeit beträgt 6.4 msec bis der 
Puffer leer ist. In der Main-Schleife wird ständig der Füllstand geprüft 
und nach Bedarf wieder gefüllt. Wenn der Prozessor aber z.Zt. mit 
anderen Dingen wzB mit der Display-Ausgabe und Auswertung der 
Touch-Eingaben beschäftigt ist, läuft der Puffer leer und kann nicht 
rechtzeitig wieder befüllt werden.

Man müsste jetzt also hergehen und in den Display-Routinen eine 
Füllstandsabfrage des Puffers integrieren. Sobald die Füllstandsanzeige 
den "Beinahe"-Leerstand signalisiert, wird die Display-Routine 
unterbrochen und der Puffer wieder befüllt. Mal schaun wie ich das 
umsetze. Das Ganze müsste dann später genauso mit den Routinen für die 
Soundberechnung gemacht werden. Da kommt Freude auf..


ASM-Code: Ring-Puffer für die Soundausgabe
1
//**************************************************************
2
//             ATMEL Studio 6 Assembler-Routine 
3
//              Soudausgabe auf DACA Chanal 0
4
// Timer1 Interruptroutine: alle 25usec = 40.0 KHz Samplerate
5
//      
6
//             (CPU ATxmega128A1 Clock 32MHz)
7
// 
8
// Testversion Sound-Puffer  66 Prozessorzyklen = 2,062 µsec
9
//
10
//**************************************************************
11
12
  #include "avr/io.h"
13
14
  .extern sound_out               // Name der Assembler-Funktion
15
  .global TCC1_OVF_vect           // Timer1 Interrupt-Vektor
16
17
//---------------------------------------------------------------
18
// Definition und verwendete Prozessor-Register
19
//---------------------------------------------------------------
20
21
temp     = 16   ; R15 Temp-Register für Berechnungen  
22
sample_low   = 17  ; R16 Sample Low-Byte
23
sample_high  = 18   ; R17 Sample High-Byte    
24
25
//---------------------------------------------------------------
26
// Prozessor-Register inkl. Status-Register sichern
27
// Interrupt-Routine Timer1-Overflow (40.000Hz)
28
//---------------------------------------------------------------
29
30
TCC1_OVF_vect:           
31
  PUSH  R0              ; 2   R0  auf Stack schieben
32
  IN    R0, SREG          ; 1   Status-Register über bereits gesichertes
33
  PUSH  R0              ; 2   R0 auf Stack schieben
34
  PUSH  R1              ; 2   R1  auf Stack schieben
35
  PUSH  R16              ; 2   R16 auf Stack schieben 
36
  PUSH  R17              ; 2   R17 auf Stack schieben
37
  PUSH  R18              ; 2   R18 auf Stack schieben
38
  PUSH  R30              ; 2   R30 auf Stack schieben (ZL)
39
  PUSH  R31              ; 2   R31 auf Stack schieben (ZH)
40
                  ; 17  Zyklen
41
  //------------------------------------------------------------
42
  // Sample aus dem Ring-Puffer laden 
43
  //------------------------------------------------------------
44
  ldi  ZL, lo8(sample_puffer)    ; 1  Puffer-Startadr. laden
45
  ldi  ZH, hi8(sample_puffer)    ; 1  
46
  lds  temp,(rd_pointer)      ; 2  Puffer-Lesezeiger laden
47
  add  ZL, temp            ; 1  und als Offset zur Puffer-Startadr. addieren
48
  ldi  temp,0            ; 1
49
  adc  ZH, temp            ; 1
50
  ld   sample_low, Z        ; 3  lade Datenwert in R16
51
  ldi  sample_high, 0        ; 1  lösche High-Byte im Sample-Word
52
                  ; 11 Zyklen
53
  //-----------------------------------
54
  // 8Bit Sample auf 12Bit erhöhen
55
  // Multiplikation mit 16
56
  //-----------------------------------
57
  
58
  LSL   sample_low          ; 1  Ausgabewert auf 12Bit (mul 16) 
59
  ROL   sample_high          ; 1
60
  LSL   sample_low          ; 1
61
  ROL   sample_high          ; 1
62
  LSL   sample_low          ; 1
63
  ROL   sample_high          ; 1
64
  LSL   sample_low          ; 1
65
  ROL   sample_high          ; 1
66
                  ; 8 Zyklen
67
  //------------------------------------
68
  // Sample-Ausgabe auf 12Bit DAC
69
  //------------------------------------
70
71
  STS   0x0318, sample_low          ; 2  L-Byte to DAC-Register (CH0DATAL Adr. 0x0318)
72
  STS   0x0319, sample_high         ; 2  H-Byte to DAC Register (CH0DATAH Adr. 0x0319)
73
                  ; 4  Zyklen
74
  //-------------------------------------
75
  // Puffer-Lesezeiger inc
76
  //-------------------------------------
77
78
  lds temp,rd_pointer        ; 2  Puffer-Lesezeiger inc
79
  inc temp              ; 1
80
  sts rd_pointer, temp        ; 2  und sichern
81
                  ; 5  Zyklen
82
  
83
   // --------------------------------------------------------------
84
  // Prozessor-Register inkl. Status-Register wiederherstellen
85
  // --------------------------------------------------------------
86
  POP   R31              ; 2   R31 von Stack wiederherstellen (ZH)
87
  POP   R30              ; 2   R30 von Stack wiederherstellen (ZL)
88
  POP   R18              ; 2   R18 von Stack wiederherstellen
89
  POP   R17              ; 2   R17 von Stack wiederherstellen
90
  POP   R16              ; 2   R16 von Stack wiederherstellen
91
  POP   R1              ; 2   R1  von Stack wiederherstellen
92
  POP   R0              ; 2   Status-Register über R0 wieder
93
  OUT   SREG, R0          ; 1   herstellen
94
  POP   R0              ; 2   R0 von Stack wiederherstellen
95
  RETI                ; 4   Return Interrupt und I-Flag quittieren
96
                  ; 21  Zyklen
97
// --------------------------------------------------------------
98
.end
99
//



C-Code: Puffer-Funktion in der Main-Programm Schleife
1
//------------------------------------ Main-Loop ---------------------------------------
2
3
  while(1)
4
  {    
5
    
6
    //-----------------------------------------------------
7
    // Wenn sich Sample-Puffer leert, dann 
8
    // Daten aus der Sinus-Tabelle nach schieben
9
    //-----------------------------------------------------
10
      
11
    while (rd_pointer != wr_pointer)              // Wenn beide Puffer-Zeiger ungleich           
12
    {                              // dann ist freier Platz im Puffer 
13
      buf_data = pgm_read_byte (&(sinus_8Bit[sample_index])); // Sample aus Sinus-Tabelle laden
14
      sample_puffer[wr_pointer] = buf_data;          // Sample in Puffer schreiben
15
      wr_pointer++;
16
      if (sample_index < 255)
17
      {
18
        sample_index++;
19
      }
20
      else sample_index = 0;
21
    }
22
    
23
    //_delay_ms(6);      // maximale Wartezeit bis Puffer leer ( ein Sample 25µsec * 256 = 6,4msec)
24
    
25
    get_encoder();      // Drehgeber abfragen
26
    touch_panel();      // Touch-Panel abfragen    
27
    mididata_avail();    // Prüfe ob Midi-Daten im Empfangspuffer
28
    midi_play();            // Midi-Daten spielen
29
    
30
  }
31
}
32
 
33
//*********************************** END Program **************************************



Noch ein Tip. Wer eine sehr gute Befehlsbeschreibung der AVR-Befehle für 
die Assembler-Programmierung benötig findet diese hier: 
http://www.avr-roboter.de/controller/befehle/beschreibung/beschreibung.html

Gruß Rolf

von Rene B. (themason) Benutzerseite


Lesenswert?

@Rolf

Wie wäre es wenn du in der Main Loop das Funktionskonglomerat 
"get_encoder, touch_panel, mididata_available, midi_play" immer nur pro 
durchlauf einmal aufrufst.
Quasi eine Statemachine die erst get_encoder, dann touch_panel, dann 
mididata_available und dann midi_play aufruft ?

also etwa so :

  char state = 1;

  while (1)
  {

    while (rd_pointer != wr_pointer)
    {
      ...
    }

    switch (state)
    {
      case 1 : state = 2; get_encoder (); break;
      case 2 : state = 3; touch_panel (); break;
      case 3 : state = 4; mididata_available (); break;
      case 4 : state = 1; midi_play (); break;
    }

  }

Ich weiß zwar nicht wie lange die einzelnen Funktionen brauchen, aber 
vllt bietet es sich an längere Funktionen (vor allem wenn Verzögerungen 
im Spiel sind) ebenfalls über eine Statemachine zu realisieren. Der 
Vorteil ist das du dann nur dann was machen mußt, wenn auch wirklich was 
zu tun ist, und du ersparst dir so Wartezeiten die den Prozi dann 
komplett blockieren. Ein weiterer Nebeneffekt ist das solche 
"Salami"-Funktionen eine gewisse Nebenläufigkeit mitbringen. Sprich : Es 
wird quasi ein eigener "Task" daraus, der je nachdem wie er programmiert 
ist eben keine anderen Aufgaben blockiert.

von Rolf D. (rolfdegen)


Angehängte Dateien:

Lesenswert?

Hallo Rene

Ich hab das vorläufig etwas anders gelöst. Der Hinweis von Franz und 
Wolfgang aus dem CC2-Forum war die Lösung. Ich habe in die Routine für 
das Senden der Daten zum Display eine Abfrage für den Soundpuffer 
integriert (siehe Code). Jetzt funktioniert die gleichzeitige 
Display-Steuerung und Soundausgabe ohne Probleme. Verzögerungen bei der 
Ansteuerung des Displays sind mir kaum aufgefallen.


Code: Routine zum Senden der Daten ans Display
1
//**************************************
2
// Ein Byte über SPI zum Display senden
3
// SPI Datenrate 2MHz
4
// *************************************
5
void spi_send (unsigned char data)
6
{
7
    fuellstand = wr_pointer - rd_pointer;        // Füllstand Soundpuffer prüfen
8
    if (fuellstand < 50)                        // wenn < 50 Soundpuffer füllen
9
    {
10
        sound_puffer();
11
    }
12
    else _delay_us(100);                        // wenn Soundpuffer nicht befüllt wird,
13
                                                // dann warte 100µsec bis Display bereit für den Datenempfang
14
                                                // (nur notwendig wenn SPI-Datentransfer schneller als 100KHz)
15
    
16
    SPIC.DATA = data;                            // ein Byte zum Display senden
17
    while(!(SPIC.STATUS & (1 << SPI_IF_bp))){}    // wartet ca. 4µsec bis Byte gesendet wurde
18
}


Soundpuffer Routine
1
//---------------------------------------------
2
// Sound-Puffer füllen
3
//---------------------------------------------
4
void sound_puffer()
5
{ 
6
  uint8_t i;            // Anzahl der Samples für die Befüllung
7
  uint8_t sample;        // Sample aus der Sinus-Tabelle 
8
  
9
  for (i=0;i<200;i++)
10
  {
11
      sample = pgm_read_byte (&(sinus_8Bit[sample_index]));    // Sample aus Sinus-Tabelle laden
12
      sample_puffer[wr_pointer] = sample;                    // Sample in Puffer schreiben
13
      wr_pointer++;                                            // Schreibzeiger des Soundpuffers +1
14
      sample_index++;                                        // Sample-Index für Datenposition in Sinustabelle inc
15
      PORTA.OUT |= (1<<PIN0);                                // Für Testzweck: Portbit High
16
      PORTA.OUT &= ~ (1<<PIN0);                                //                Portbit Low
17
  }
18
}

Bild 1:
Kanal 1 (Oben) Portbit signalisiert die Übertragung von 200 Bytes an den 
Soundbuffer in ca. 230µsec
Kanal 2 (Unten) Sinus-Wave aus dem Soundpuffer

Gruß Rolf

von Rolf D. (rolfdegen)


Lesenswert?

Hallo

Ich verkaufe meinen beiden Original CEM3320 Filterbausteine. Geprüft und 
kaum benutzt. Lagen bei mir in der Schublade auf einer Iso-Matte. Alter 
ca. 10 Jahre. Da ich in meinem Synthi-Projekt die SSM2044 einsetze sind 
die CEM3320 überflüssig. Preis ist Verhandlungssache.

Gruß Rolf

Achso.. hät ich beinahe vergessen. Zur Zeit teste ich die 
Speicheranbindung des 8MByte großen SDRAM auf Xplained-Board das ich in 
meinem AVR-Synthi als Wave-Speicher verwenden will. Die Transverleistung 
des Speicherinterface im Xmega zum 8MByte SDRAM liegt bei beachtlichen 
1.4Mbyte in der Sekunde. Für so einen 8Bitter eine stolze Leistung.

Falls alles nach meinen Vorstellungen funktioniert, soll der 
XMega-Prozessor verschiedene 16Bit Waveforms nach dem Systemstart 
vorrausberechnen und in dem 8MByte SDRAM zwischen speichern. Per 
Timer-Interrupt werden die Wave-Daten aus dem Speicher gelesen und an 
als 12Bit Wert an den DAC gesendet. Das ganze funktioniert per Direct 
Digital Synthese und Waveform-Tabellenzugriff.

Gruß Rolf

von Rolf D. (rolfdegen)


Angehängte Dateien:

Lesenswert?

Hallo

Ich will noch einmal auf das Thema Interpolation zu sprechen kommen. 
Wolfgang aus dem CC2-Forum hatte ja bereits in seinem Beitrag 
(http://www.cczwei-forum.de/cc2/thread.php?postid=79168#post79168) viel 
erklärt und gute Tips für die Berechnung geliefert.

Da mir jetzt "Unbegrenzter" Speicherplatz in meinem Synthesizer zur 
Verfügung steht (ganze 8 MegaByte wow..) habe ich einige Test mit der 
Interpolation von Soundsamples gemacht (siehe Bild 1+2) . Die 
Original-Samples hatten jeweils eine Auflösung von 7 Bit.

Bild 1: Links Original Waveform 7Bit, Rechts interpolierte Waveform
Bild 2: Links Original 7Bit Waveform, Mitte interpolierte Waveform

Das Ergebnis ist wirklich gut. Kein Rauschen kein nix. Die Original 7Bit 
Soundsamples klangen im unteren Frequenzbereich (< 500Hz) sehr 
verrauscht. Nach der Interpolation klingt das ganze schon super finde 
ich.


Mein Code für die Interpolation
1
//--------------------------------------------------------------------------
2
// Wave-Interpolation
3
// ------------------ 
4
// Zwei Sample aus Wave-Tabelle laden und drei neue Zwischenwerte berechnen.
5
// Dann 1.Sample und die drei neuen Zwischenwerte für die Soundausgabe ins
6
// SDRAM schreiben.
7
//---------------------------------------------------------------------------
8
void init_wave2()
9
{
10
    // schreiben
11
    uint8_t sample_index = 0;
12
    uint16_t wr_pointer = 0;
13
    uint32_t sample_a = 0;
14
    uint32_t sample_b = 0;
15
    uint32_t sample_xa = 0;
16
    uint32_t sample_xb = 0;
17
    uint32_t sample_xc = 0;
18
    
19
    for (sample_index = 0; sample_index < 127; sample_index++)
20
    {
21
        sample_a = pgm_read_byte (&(wave_7Bit[sample_index]));                // 1.Sample aus Sinus-Tabelle laden
22
        sample_b = pgm_read_byte (&(wave_7Bit[sample_index+1]));            // 2.Sample aus Sinus-Tabelle laden
23
        sample_a = sample_a << 8;                                            // 1.Sample to 16Bit
24
        sample_b = sample_b << 8;                                            // 2. Sample to 16Bit
25
        sample_xb = (sample_a + sample_b) >> 1;                                // 1. - 3.Zwischenwerte berechnen        
26
        sample_xa = (sample_a + sample_xb) >> 1;
27
        sample_xc = (sample_b + sample_xb) >> 1;
28
        hugemem_write16(BOARD_EBI_SDRAM_BASE + 512 + wr_pointer,sample_a);    // write 1.Sample to Puffer
29
        wr_pointer = wr_pointer + 2;                                        // inc Sample-Puffer (+2 wegen 16Bit)
30
        hugemem_write16(BOARD_EBI_SDRAM_BASE + 512 + wr_pointer,sample_xa);    // write 1.Zwischenwert to Puffer
31
        wr_pointer = wr_pointer + 2;
32
        hugemem_write16(BOARD_EBI_SDRAM_BASE + 512 + wr_pointer,sample_xb);    // write 2.Zwischenwert to Puffer
33
        wr_pointer = wr_pointer + 2;
34
        hugemem_write16(BOARD_EBI_SDRAM_BASE + 512 + wr_pointer,sample_xc);    // write 3.Zwischen to Puffer
35
        wr_pointer = wr_pointer + 2;                                        // inc Sample-Puffer (+2 wegen 16Bit)
36
    }
37
}

Neue (interpolierte) Wave aus SDRAM abspielen
1
void play_wave2()
2
{
3
    uint16_t sample_index = 0;
4
    uint16_t rd_pointer = 0;
5
    uint16_t sample = 0;
6
    for (sample_index = 0; sample_index < 508; sample_index++)
7
    {
8
        sample = hugemem_read16(BOARD_EBI_SDRAM_BASE + 512 + rd_pointer);
9
        DACA.CH0DATA= sample >> 4;                    // 16Bit Sample to 12Bit
10
        _delay_us(1);
11
        rd_pointer = rd_pointer + 2;        
12
    }
13
}

Mir geht es bei der Interpolation in erster Linie darum, die niedrige 
Auflösung der Wavefiles (8Bit/256Byte Länge und 7Bit/129Byte Länge) die 
sich im Flashspeicher des Xmega-Prozessors befinden, wärend des 
Systemstarts auf 12Bit/1024Byte Länge zu erhöhen und in das SDRAM 
abzulegen. Von da aus soll nach Bedarf jeweils ein Wavefiles für 
Oszillator1+2 in einen Soundpuffer (SRAM im XMega) kopiert, gemixt und 
auf den DAC gesendet werden. Das gleiche passiert mit Berechnungen von 
Wellenformen.

In Bezug auf die SDRAM-Steuerung vielleicht noch ein paar interessante 
Infos. Ein 8Bit Lesezugriff auf das SDRAM dauert mit der "hugemem" 
Routine komplett ca. 1µsec (inlkusiv Adressierung und Rückgabe des 
Speicherwertes). Die ganze Adressierung und Rückgabe des 8Bit 
Speicherwertes wird also in ca. 32 CPU-Takten erledigt. Für einen 16Bit 
Lesezugriff ist die Zeit doppelt so lang. Das interne Steuerinterface 
(EBI) für das SDRAM wird im Xmega-Prozessor mit dem doppelten CPU-Takt 
von 64MHz betrieben. Die schnellste Zugriffszeit für das SDRAM auf 
meinem Xplained Board liegt laut Datenblatt bei 7.5nsec.

Gruß Rolf

von Rolf D. (rolfdegen)


Lesenswert?

Hallo

Bin jetzt etwas verärgert  . Habe das neue SP2 für Atmel Studio6 
installiert und jetzt kann ich kein Programm mit meinem AVRISP MKII mehr 
flashen. Kann mir da einer weiterhelfen ?

Liegt es vielleicht an Win8 ? Wenn ja fliegt Win8 im hohen Bogen von 
meiner Festplatte. Mit Win8 bin ich wieso nicht ganz zufrieden !

von Rolf D. (rolfdegen)


Lesenswert?

Irgendetwas stimmt da nicht mit dem Programmiergerät. Die ausgelesenen 
Hex-Daten aus dem Flashpeicher des Xmegas sehen im unteren Teilbereich 
anders aus als die erzeugten Hex-Datei in Atmel Studio6 ???

von Rolf D. (rolfdegen)


Lesenswert?

Hallo.. ich bins noch einmal

Ich habe Win8 noch einmal neu installiert und danach Atmel Studio 6 SP2. 
Leider ohne Erfolg. Der Xmega wird vom AVRISP MKII nicht richtig 
programmiert. Ein manuelles Verify über die Tool-Funktion in 
Atmel-Studio zeigt Fehler am Ende der Programmdaten im XMega.

Ich habe jetzt unter Windows8 die Treibersignierung deaktiviert und 
Atmel Studio 5.1 installiert. Das funktioniert auf Anhieb und ohne 
Probleme

Atmel: "Don't change a running system" :(

Ich werde jetzt erst einmal bei Atmel Studio 5.1 bleiben und mein 
Projekt unter Windows 8 weiter entwickeln. Aber ich wunder mich schon 
ein wenig, das Atmel die Version 6 mit dem SP2 für Windows8 kompatilen 
hält und keiner meckert ??

LG Rolf

von Rolf D. (rolfdegen)


Angehängte Dateien:

Lesenswert?

Hallo ihr Lieben

Nachdem sich nun die Fachleute von Atmel um mein Problem mit dem 
Programmieradapter AVRISP MKII und ATMEL Studio 6 kümmern, kann ich mich 
wieder voll meinem Synthi-Projekt widmen.

Ich möchte den AVR-Synthi mit zwei VCA-Chips (spannungsgesteuerter 
Verstärker) vom Typ SSM2164 austatten, weil ich mit der Soundqualität 
nicht zu frieden bin. Bei leisen Klängen ist ein Hintergrundrauschen vom 
DA-Wandler und dem Filterbaustein SSM2044 zu hören. Da der VCA-Chip am 
Ende der Übertragungskette, also hinter den Filtern sitzt, wird das 
Rauschen vom DAC und Filterbaustein mit abnehmbarer Lautstärke des VCAs 
auch leiser und ist dann kaum noch warnehmbar. Gesteuert werden die 
beiden VCA-Chips wie auch die Filter durch die PWM-Anschlüsse am 
Xmega-Prozessor.

Das Hardwarekonzept für die Klangerzeugung sieht jetzt so wie in Bild 1 
aus. Allerdings sind VCA-Chips nicht gerade billig. Ich habe sie bei 
einem Münchner Lieferanten (Sahin Electronic) für einen Stückpreis von 
18,- Euro bekommen. Auf Ebay sind die Chips preisgünstiger zu bekommen, 
haben aber sehr lange Lieferzeiten (5-7 Wochen) weil aus Übersee.


Bild 1: AVR-Synthi "WAVE 1"

LG Rolf

von Knut B. (Firma: TravelRec.) (travelrec) Benutzerseite


Lesenswert?

Rolf Degen schrieb:
> Ich möchte den AVR-Synthi mit zwei VCA-Chips (spannungsgesteuerter
> Verstärker) vom Typ SSM2164 austatten, weil ich mit der Soundqualität
> nicht zu frieden bin.

Hast Du schon mal darüber nachgedacht, einen externen I2S-DAC 
(16...24Bit) zu verwenden? Mittels UART im SPI-Mode und per Event-System 
synchronisiertem Timer für die WordClock bekommt man ein astreines, 
komplett in Hardware laufendes I2S-Interface hin. Nachladen kann man die 
Daten per DMA.

I2S-DACs so in Richtung CS4344 kosten zum Beispiel bei CSD-electronics 
keine 3 EUR. I2S kann man auch wunderbar auf SPDIF umwandeln :-)

von Rolf D. (rolfdegen)


Lesenswert?

Hallo Knut

Das wäre auf jeden Fall eine Option bzw Verbesserung über die man später 
nachdenken kann. Aber erst einmal muss das ganze Projekt funktionieren. 
Es sind momentan noch zu viele Baustellen an denen ich arbeite.

Gruß Rolf

von Rolf D. (rolfdegen)


Angehängte Dateien:

Lesenswert?

Hallöchen..

Heute sind meine zwei bestellten VCA-Chips (SSM2164D) angekommen. Die 
werde jetzt mal schnell in die Synthi-Schaltung geklebt und dann die 
Ansteuerung über die Software programmieren. Mal schaun wies klingen 
wird.

Softwaremäßig sieht es zur Zeit etwas schwierig aus. Der Programmcode 
ist mittlerweile sehr groß und unübersichtlich geworden. Aus diesem 
Grund, habe ich damit angefangen, die vielen Funktionen in 
Funktionsblöcke zusammenzufassen und in sogenannten Include-Files 
abzulegen.

Das ganze Code wird dadurch übersichtlicher und verständlicher.

Das Projekt sieht zum Beispiel jetzt so aus wie im Bild 1. Auf der 
linken Seite steht eine ausgewählte Funktion, die zB. Daten über die 
SPI-Schnittselle des XMega-Prozessor zum Display sendet. Auf der rechten 
Seite sieht man die Ordnerstruktur und die ganzen Include-Files mit den 
darin befindlichen Funktionen.

Bild 1: AVR-Synthi Projekt mit ATMEL Studio6


Gruß Rolf

von eProfi (Gast)


Lesenswert?

Hallo Rolf,
Hut ab vor Deinen Experimenten.

Aber ein paar Dinge sind mir noch unklar:
>    uint32_t sample_a = 0;
>    uint32_t sample_b = 0;
>    uint32_t sample_xa = 0;
>    uint32_t sample_xb = 0;
>    uint32_t sample_xc = 0;
Warum 32?  16 sollte da voll ausreichen.

>    for (sample_index = 0; sample_index < 127; sample_index++)
Zeitkritische for-Schleifen lasse ich immer bis 0 laufen, da der 
Vergleich wegfällt, er wird implizit im Dec-Befehl ausgeführt 
(Zero-Flag).
Natürlich muss es auch beim Schreiben in dieser Reihenfolge geschehen.

Außerdem: muss es nicht <128 (oder <=127) heißen für 0..127 ?


also:  sample_index=127;do{...}while(--sample_index!=0);
wichtig ist vorher decrement, dann vergleich, sonst vergleicht er gern 
mit 0xff.

Außerdem würde ich hier ebenfalls mit einem Pointer arbeiten.

Du liest jedes Sample 2 mal aus, zuerst als b, dann als a.
Du kannst ja statt des 1. Reads  sample_a=sample_b schreiben.


Mein Vorschlag (aus dem Stegreif, alles ungetestet) dürfte doppelt so 
schnell laufen:
  uint8_t   cnt;
  uint16_t  sample_a,sample_b;
  uint16_t* sample_ptr,wrt_ptr;

  sample_ptr = .....;wrt_ptr = ....;
  sample_b = pgm_read_byte (&(wave_7Bit[sample_ptr++]));
  cnt=128;do{
    sample_a=sample_b;
    sample_b=pgm_read_byte (&(wave_7Bit[sample_ptr++]));
    delta=sample_b - sample_a;
    sample_a*=4;
    hugemem_write16(BOARD_EBI_SDRAM_BASE + 512 + wr_ptr   ,sample_a   );
    hugemem_write16(BOARD_EBI_SDRAM_BASE + 512 + wr_ptr+=2,sample_a+=d);
    hugemem_write16(BOARD_EBI_SDRAM_BASE + 512 + wr_ptr+=2,sample_a+=d);
    hugemem_write16(BOARD_EBI_SDRAM_BASE + 512 + wr_ptr+=2,sample_a+=d);
    }while(--cnt);

von Rolf D. (rolfdegen)


Lesenswert?

Hallo eProfi

Danke für deine guten Vorschläge. Ich weis, das ganze ist von mir noch 
nicht optimal programmiert. Es war auch mein erster Versuch mit dem 
Zugriff aufs SDRAM. Ich werde deine Vorschläge auf jeden Fall 
berücksichtigen.

Danke. Gruß Rolf

von eProfi (Gast)


Lesenswert?

Nochwas:
    hugemem_write16(BOARD_EBI_SDRAM_BASE + 512 + wr_ptr   ,sample_a   );
    hugemem_write16(BOARD_EBI_SDRAM_BASE + 512 + wr_ptr+=2,sample_a+=d);
    hugemem_write16(BOARD_EBI_SDRAM_BASE + 512 + wr_ptr+=2,sample_a+=d);
    hugemem_write16(BOARD_EBI_SDRAM_BASE + 512 + wr_ptr+=2,sample_a+=d);

geht so schneller, da er die beiden Adress-Additionen nicht jedes mal 
machen muss:
vor der do..while:
    uint16_t wrt_ptr; //war oben fälschlich ein Pointer uint16_t*
    wr_ptr = BOARD_EBI_SDRAM_BASE + 512;

in der do..while:
    hugemem_write16(wr_ptr   ,sample_a   );
    hugemem_write16(wr_ptr+=2,sample_a+=d);
    hugemem_write16(wr_ptr+=2,sample_a+=d);
    hugemem_write16(wr_ptr+=2,sample_a+=d);


Ausgabe würde ich unbedingt mit timergesteuerter DMA machen.
Habe ich mal mit einem Renesas M32C (interner DAC als DMA-Ziel) so 
gemacht, keine Belastung durch die Ausgabe.

von Rolf D. (rolfdegen)


Lesenswert?

eProfi schrieb:

> Ausgabe würde ich unbedingt mit timergesteuerter DMA machen.
> Habe ich mal mit einem Renesas M32C (interner DAC als DMA-Ziel) so
> gemacht, keine Belastung durch die Ausgabe.


Ich weis nicht recht, ob meine Methode (timergesteurte Soundausgabe über 
Assembler-Routine) im Vergleich zur DMA-Funktion doch die bessere ist. 
In dem Assembler-Teil für die Soundausgabe wird der 12Bit Wert aus dem 
Soundpuffer direkt in das Ausgaberegister des DA-Wandler geschriebe.

Gruß Rolf

von Rolf D. (rolfdegen)


Lesenswert?

Hallo

Zur Abwechslung mal ein geiler Demo-Sound aus dem AVR-Synthi. Dabei 
wurde die Filterfrequenz und die Oszillatorfrequenz moduliert.

https://soundcloud.com/rolfdegen/lfovcfmod

Gruß Rolf

von Knut B. (Firma: TravelRec.) (travelrec) Benutzerseite


Lesenswert?

Klingt doch ganz manierlich ;-)

von Rene B. (themason) Benutzerseite


Lesenswert?

Klingt echt toll dein AVR Synthi :-) Nur weiter so.

von egonotto (Gast)


Lesenswert?

Hallo,

klingt wirklich gut.

MfG
egonotto

von Rolf D. (rolfdegen)


Angehängte Dateien:

Lesenswert?

Hallo

Ich habe ein kleines Verständnis Problem mit der abgebildeten 
VCA-Schaltung von Oliver Gillert (Entwickler des Shruthi Synthis). Wie 
man erkennen kann, ist im Gegenkopplungszweig von Operationsverstärker 
(IC3A) ein VCA (IC7C) geschaltet (rotes Rechteck). Ich bin mir einfach 
nicht genau im klaren darüber, wozu der VCA (IC7C) notwendig ist. 
Vielleicht dient er zur Anpassung an eine lineare 
Steuerspannungskennlinie für den eigentlichen VCA in IC9D.

Bild: VCA-Schaltung


Gruß Rolf

von Rolf D. (rolfdegen)


Lesenswert?

Hallo zusammen..

Wolfgang aus dem CC2-Forum meint, das es sich hierbei um ein 
Lin-Log-Wandler als Multiplizierer handelt.

Gruß Rolf

von Rolf D. (rolfdegen)


Angehängte Dateien:

Lesenswert?

Hallo zusammen

Die Weihnachtstage habe ich dazu genutzt, um die VCA-Schaltung für 
meinen AVR-Synthesizer zu bauen und zu testen. Herausgekommen ist die im 
Bild1 gezeigte Schaltung. Grundlage war der Schaltungsentwurf von Oliver 
Gillert 
[https://ox1aha.bay.livefilestore.com/y1psza5s_RUtuXITmcuMA58lD5eyOBqyuXUQMaaMn7uBGqJqOuzvw-yzL8Q-P8wOXyN1doNAyF2V1vleuuO1fYgZ7lN4EPhcD2v/VCA_01.png].

Ich musste allerdings ein paar Änderungen für meine AVR-Synthi machen, 
da Oliver in seinem Shruthi-Synthesizer mit einem anderen Spannungspegel 
(+5Volt) am PWM-Eingang arbeitet.

Bild 1: AVR-Synthesizer VCA-Schaltung

Mit dem Trimmer R5 wird die Steuerkennlinie für den VCA angepasst. Mit 
R3 wird das Steuersignal für den VCA verstärkt.

Bei der Beschaltung des VCA's gibt es noch eine Besonderheit. Der VCA 
besitzt einen Mode-Pin (Pin1). Der Mode-Pin ermöglicht die Einstellung 
des Ruhestroms durch den Verstärker im VCA. Ist er der Mode-Pin nicht 
beschaltet, dann treten am VCA-Ausgang geringere Verzerrungen aber 
höheres Rauschen auf. Ist der Mode-Pin mit einem Pullup-Widerstand (hier 
R12) beschaltet, dann nehmen die Verzerrungen zu und das Rausches ist 
geringer.

Gruß Rolf

von Rolf D. (rolfdegen)


Angehängte Dateien:

Lesenswert?

Hallo zusammen..

Ich hoffe, ihr seit alle gut in das neue Jahr gerutscht. Ich habe mit 
meinen Freunden aus China gefeiert und das Neujahr begrüsst. Zur Party 
hatte ich extra Luftschlangen, Konfetti und Bleigießen mitgebracht, um 
zu zeigen, wie hier das neue Jahr gefeiert wird. Es war wunderschön.. :)

Auch diese Tage hatte ich etwas Zeit für mein Projekt und habe die 
Filterschaltung für den AVR-Synthi etwas verbessert und getestet (siehe 
Bild 1).

Meine Annahme, das der Steuereingang des Filter-ICs SSM2044 die gleiche 
logarithmische Steuercharakteristik besitzt wie der VCA-Chip SSM2164D 
war ein Irrtum. Da der Filter-Chip eine lineare Steuerkennlinie besitzt, 
ist der Bauteilaufwand dafür sogar etwas geringer. Das IC1d dient hier 
als Filter für das PWM-Steuersignal. Durch R1 wird das Steuersignal am 
Ausgang des Operationsverstärkers auf +0.90V angehoben und über den 
Spannungsteiler an den Steuereingang für die Filterfrequenz geführt. Die 
Steuerspannung für den Filter liegt hier, wie im Datenblatt genannt, 
zwischen -90mV und +90mV.

Das Audiosignal vom XMega-DAC wird über einen LP-Filter IC2c an den 
Eingang des Filter-ICs geführt. Über den Trimmer P1 kann das 
Filter-Eingangssignal abgeschwächt werden, um Übersteuerungen am 
Filterausgang zu vermeiden. Ich habe den Audiopegel am Filterausgang auf 
6.0 V~ eingestellt um etwas Reserven nach oben zu haben. Maximal ist ein 
Ausgangspegel von 8.0 V~ am Filterausgang möglich. Bei eingestellter 
Resonanz kann es schon mal vorkommen, das der maximale Audiopegel 
überschritten wird und es zu Verzerrungen kommt. Die Lösung ist entweder 
den Filtereingangspegel mit P1 zu reduzieren, oder die Steuerspannung 
für die Resonanz zu verringern.

Gruß Rolf

von Rolf D. (rolfdegen)


Angehängte Dateien:

Lesenswert?

Hallo

Hab mal ein kleines Video gedreht und auf Youtube hochgeladen. Es zeigt 
die VCA-Schaltung im AVR-Synthi in Aktion.

Link zum Youtube-Video: http://www.youtube.com/watch?v=nTUUqwcewA8


Gruß Rolf

von Rolf D. (rolfdegen)


Angehängte Dateien:

Lesenswert?

Hallo

VCF und VCA-Schaltung sind jetzt fertig. Hier ein kleines Youtube 
Demo-Video. Es zeigt die LFO-Modulation von VCF und VCA.

http://www.youtube.com/watch?v=iwUUhr8rD48

Achtung! Sound ist sehr laut.

Hier noch schnell ein Bild mit dem kompletten Hardwareaufbau meines 
AVR-Synthis in der Stereo-Version.

Bild 1: AVR-Synthi Stereo-Version

Gruß Rolf

von Knut B. (Firma: TravelRec.) (travelrec) Benutzerseite


Lesenswert?

Na das geht doch schon ganz ordentlich. Ich stell mir das Teil jetzt 
schon mal an einem Sequencer vor :-)

von Rolf D. (rolfdegen)


Lesenswert?

Hallo Knut

Danke. Ich bin jetzt dabei, die LFO's zu programmiere. Insgesammt sollen 
es einmal sechs Stück werden für 2x VCA, 2x VCF, DCO, PAN. Davon sind 
jeweils zwei für die Stereo-Kanäle vorgesehen.

Gruß Rolf

von Knut B. (Firma: TravelRec.) (travelrec) Benutzerseite


Lesenswert?

Wenn Du das Ding fertig hast und ordentlich dokumentierst, baue ich mir 
vielleicht auch einen :-)

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.