AVR Synthesizer "WAVE 1" / De-Generator

Dieses Thema im Forum "Lötkunst" wurde erstellt von rolfdegen, 10. Januar 2014.

  1. Moogulator

    Moogulator Admin

    Cooler Scheiß, wann kommt der finale auf den Ladentisch?
     
  2. rolfdegen

    rolfdegen ....

    Hi Mick. Das wüste ich auch gerne. Überall sind noch Baustellen. Aber ich bin "hartnäckich" und kämpfe mich halt durch. Hab jetzt zwei Wochen Urlaub und viel Zeit für meine Kiste.

    Hier noch mehr "Cooler Scheiß" :geige: Gruß Rolf

     
  3. rolfdegen

    rolfdegen ....

    Hallo und guten Morgen..

    Ich habe ein kleines Sound Problem mit der "Quad Saw" im DE-GENERATOR. Im Vergleich mit dem
    Shruthi Synthesizer klingt der Sound im DE-GENERATOR sehr statisch. Ich habe den Quellcode
    aus dem Shruthi für den DE-GENERATOR etwas angepasst. Der Parameter-Wert steht auf beiden
    Synthesizern auf 0. Ich habe ein kleines Video gedreht um es zu zeigen und zu hören.
    Damit die QuadSaw funktioniert muss der "phase_spread" immer >0 sein.



    Shruthi Quellcode
    Code:
    .
    // ------- Quad saw or pwm (mit aliasing) ------------------------------------
    void Oscillator::RenderQuad(uint8_t* buffer) {
      uint16_t phase_spread = (
          static_cast<uint32_t>(phase_increment_.integral) * parameter_) >> 13;
      ++phase_spread;
      uint16_t phase_increment = phase_increment_.integral;
      uint16_t increments[3];
      for (uint8_t i = 0; i < 3; ++i) {
        phase_increment += phase_spread;
        increments[i] = phase_increment;
      }
     
      if (shape_ == WAVEFORM_QUAD_SAW_PAD) {
        BEGIN_SAMPLE_LOOP
          UPDATE_PHASE
          data_.qs.phase[0] += increments[0];
          data_.qs.phase[1] += increments[1];
          data_.qs.phase[2] += increments[2];
          uint8_t value = (phase.integral >> 10);
          value += (data_.qs.phase[0] >> 10);
          value += (data_.qs.phase[1] >> 10);
          value += (data_.qs.phase[2] >> 10);
          *buffer++ = value;
        END_SAMPLE_LOOP
      }
      else { //WAVEFORM_QUAD_PWM
        uint16_t pwm_phase = static_cast<uint16_t>(127 + parameter_) << 8;
        BEGIN_SAMPLE_LOOP
          UPDATE_PHASE
          data_.qs.phase[0] += increments[0];
          data_.qs.phase[1] += increments[1];
          data_.qs.phase[2] += increments[2];
          uint8_t value = phase.integral < pwm_phase ? 0 : 63;
          value += data_.qs.phase[0] < pwm_phase ? 0 : 63;
          value += data_.qs.phase[1] < pwm_phase ? 0 : 63;
          value += data_.qs.phase[2] < pwm_phase ? 0 : 63;
          *buffer++ = value;
        END_SAMPLE_LOOP
      }
    }
    
    
    
    DE-GENERATOR Quellcode
    Code:
    .
    //*************************************************************************
    // OscRender: Quad saw or pwm (mit aliasing)
    //*************************************************************************
    void OscRender_PAD(uint8_t osc_nr)
    {
       uint8_t block_size = 40;
       uint8_t shape = Osc.shape[osc_nr];
       uint8_t parameter = Osc.prm[osc_nr];
       uint8_t *buffer;   // pointer name
       uint8_t i = 0;
           
       if (osc_nr == 0)
       {
           if (Voice.buffer_nr == 0)
           {
               buffer = Voice.Buffer2a;
           }
           else {buffer = Voice.Buffer1a;}
       }
       else
       {
           if (Voice.buffer_nr == 0)
           {
               buffer = Voice.Buffer2b;
           }
           else {buffer = Voice.Buffer1b;}
       }
       
       uint16_t phase_increment_ = (Osc.phase_increment[osc_nr] >> 8);
       uint16_t phase_spread = (uint32_t)(phase_increment_ * parameter) >> 13;
       ++phase_spread;
       
       uint16_t increments[3];
       for (uint8_t i = 0; i < 3; ++i) {
           phase_increment_ += phase_spread;
           increments[i] = phase_increment_;
       }
    
       //BEGIN_SAMPLE_LOOP ---------------------------------------------------
       if (shape == OSC_SAW_PAD) {
           do
           {
               // update Phase
               Osc.phase[osc_nr] += Osc.phase_increment[osc_nr];
               
               if (SREG & 0b00000001) // test overflow
               {
                   Osc.phase[osc_nr] = 0;
               }
                   
               uint16_t phase_intergral = Osc.phase[osc_nr] >> 8;
               uint8_t value = (phase_intergral >> 10);
               
               // Osc1
               if (osc_nr == 0)
               {
                   Osc1.data_qs_phase[0] += increments[0];
                   Osc1.data_qs_phase[1] += increments[1];
                   Osc1.data_qs_phase[2] += increments[2];
                   value += (Osc1.data_qs_phase[0] >> 10);
                   value += (Osc1.data_qs_phase[1] >> 10);
                   value += (Osc1.data_qs_phase[2] >> 10);
               }
               else // Osc2
               {
                   Osc2.data_qs_phase[0] += increments[0];
                   Osc2.data_qs_phase[1] += increments[1];
                   Osc2.data_qs_phase[2] += increments[2];
                   value += (Osc2.data_qs_phase[0] >> 10);
                   value += (Osc2.data_qs_phase[1] >> 10);
                   value += (Osc2.data_qs_phase[2] >> 10);
               }
               
               // write sample in sound-buffer
               buffer[i++] = ~value;
               
           } while (--block_size);
       }
    }
    
    Vielleicht hat jemand eine Idee, wie ich das im DE-GENERATOR verbessern kann.

    Gruß Rolf
     
    Zuletzt bearbeitet: 30. Dezember 2017
  4. Cyclotron

    Cyclotron |||||

    Hab nur kurz ins Video gesehen, da grad auf dem Sprung: Der zweite Sound klingt so, als ob da die Frequenzen näher beieinander lägen (also phase_spread und das daraus folgende Delta zwischen den increments deutlich kleiner ist).
     
  5. rolfdegen

    rolfdegen ....

    Hi Cyclotron

    Ich hab ein wenig experimentiert und einen kleinen Zufallswert zu dem phase_spread addiert. Klingt schon besser aber halt noch anders wie im Shruthi.

    Mein Code
    Code:
    //*************************************************************************
    // OscRender: Quad saw or pwm (mit aliasing)
    //*************************************************************************
    void OscRender_PAD(uint8_t osc_nr)
    {
        uint8_t block_size = 40;
        uint8_t shape = Osc.shape[osc_nr];
        uint8_t parameter = Osc.prm[osc_nr];
        uint8_t *buffer;    // pointer name
        uint8_t i = 0;
           
        if (osc_nr == 0)
        {
            if (Voice.buffer_nr == 0)
            {
                buffer = Voice.Buffer2a;
            }
            else {buffer = Voice.Buffer1a;}
        }
        else
        {
            if (Voice.buffer_nr == 0)
            {
                buffer = Voice.Buffer2b;
            }
            else {buffer = Voice.Buffer1b;}
        }
       
        uint16_t phase_increment_ = (Osc.phase_increment[osc_nr] >> 8);
        uint16_t phase_spread = (uint32_t)(phase_increment_ * parameter) >> 13;
       
        // Test phase_spread with/without random value
        if (Osc.Test_flag == 0)
        {
            ++phase_spread;
        }
        else {phase_spread += (Noise.sample >> 7);}
       
       
        uint16_t increments[3];
        for (uint8_t i = 0; i < 3; ++i) {
            phase_increment_ += phase_spread;
            increments[i] = phase_increment_;
        }
    
        //BEGIN_SAMPLE_LOOP ---------------------------------------------------
        if (shape == OSC_SAW_PAD) {
            do
            {
                // update Phase
                Osc.phase[osc_nr] += Osc.phase_increment[osc_nr];
               
                if (SREG & 0b00000001) // test overflow
                {
                    Osc.phase[osc_nr] = 0;
                }
                   
                uint16_t phase_intergral = Osc.phase[osc_nr] >> 8;
                uint8_t value = (phase_intergral >> 10);
               
                // Osc1
                if (osc_nr == 0)
                {
                    Osc1.data_qs_phase[0] += increments[0];
                    Osc1.data_qs_phase[1] += increments[1];
                    Osc1.data_qs_phase[2] += increments[2];
                    value += (Osc1.data_qs_phase[0] >> 10);
                    value += (Osc1.data_qs_phase[1] >> 10);
                    value += (Osc1.data_qs_phase[2] >> 10);
                }
                else // Osc2
                {
                    Osc2.data_qs_phase[0] += increments[0];
                    Osc2.data_qs_phase[1] += increments[1];
                    Osc2.data_qs_phase[2] += increments[2];
                    value += (Osc2.data_qs_phase[0] >> 10);
                    value += (Osc2.data_qs_phase[1] >> 10);
                    value += (Osc2.data_qs_phase[2] >> 10);
                }
               
                // write sample in sound-buffer
                buffer[i++] = ~value;
               
            } while (--block_size);
        }
    }
    
    
    Im Video schalte ich zwischen einer Addition mit random Wert und ohne um.



    Gruß Rolf
     
  6. rolfdegen

    rolfdegen ....

    Ich glaube den Fehler gefunden zu haben. Die Osc.phase_increment Werte in meiner DDS sind höher im Vergleich zum Shruthi. Deshalb gabs bei der Berechnung der increments einen Überlauf.
     
    Cyclotron gefällt das.
  7. humax5600

    humax5600 .....

  8. rolfdegen

    rolfdegen ....

    Danke. Euch auch allen.. Obwohl ich noch an dem Problem brüte. Aber heut wird dat nix mehr :)
     
  9. rolfdegen

    rolfdegen ....

    Hallöchen miteinander und allen ein schönes Neues Jahr

    Hab den code jetzt wie im Shruthi Synth angepasst (Osc.phase_increment und buffer Größe). Aber leide keine Besserung. Beim erhöhen des parameter Wertes (0-127) klingt der Sound ab der 21.Sek (parameter Wert = 103) völlig anders im Vergleich zum Shruthi. Als ob da ein Wert überläuft ????

    Hab alles geprüft. Ich seh da keinen Fehler. Der einzige Unterschied in den Codezeile ist der Cast im Shruthi:

    Code:
    uint16_t phase_spread = (
          static_cast<uint32_t>(phase_increment_.integral) * parameter_) >> 13;
      ++phase_spread;
      uint16_t phase_increment = phase_increment_.integral;
      uint16_t increments[3];
    Soundtrack:



    Code:
    //*************************************************************************
    // OscRender: Quad saw or pwm (mit aliasing)
    //*************************************************************************
    void OscRender_PAD(uint8_t osc_nr)
    {
        if (osc_nr == 1)
        {
            return;
        }
     
        uint8_t block_size = 128;
        uint8_t shape = Osc.shape[osc_nr];
        uint8_t parameter = Osc.prm[osc_nr];
        uint8_t *buffer;    // pointer name
        uint16_t increments[3];
     
        // Double buffering
        if (Voice.buffer_nr == 0)
        {
            buffer = Voice.Buffer2a;
        }
        else {buffer = Voice.Buffer1a;}
     
        uint16_t phase_increment_ = (Osc.phase_increment[osc_nr] >> 8);
        uint16_t phase_spread = (uint32_t)(phase_increment_ * parameter) >> 13;
        ++phase_spread;
     
        for (uint8_t i = 0; i < 3; ++i) {
            phase_increment_ += phase_spread;
            increments[i] = phase_increment_;
        }
     
    
        //BEGIN_SAMPLE_LOOP ---------------------------------------------------
        do
        {
            // update Phase
            Osc.phase[osc_nr] += Osc.phase_increment[osc_nr];
       
            if (SREG & 0b00000001) // test overflow
            {
                Osc.phase[osc_nr] = 0;
            }
       
            uint16_t phase_intergral = Osc.phase[osc_nr] >> 8;
            uint8_t value = (phase_intergral >> 10);
       
            Osc1.data_qs_phase[0] += increments[0];
            Osc1.data_qs_phase[1] += increments[1];
            Osc1.data_qs_phase[2] += increments[2];
            value += (Osc1.data_qs_phase[0] >> 10);
            value += (Osc1.data_qs_phase[1] >> 10);
            value += (Osc1.data_qs_phase[2] >> 10);
       
            // write sample in sound-buffer
            *buffer++ = ~value;
       
        } while (--block_size);
    }
     
    Zuletzt bearbeitet: 1. Januar 2018
  10. rolfdegen

    rolfdegen ....

    OK. Konnte den Überlauf Fehler jetzt beseitgt indem ich "phase_increment" auf 24Bit gesetzt habe. Aber der Sound im DE-GENERATOR klingt immer noch statisch:

    QuadSaw from DE-GENERATOR
     
  11. Cyclotron

    Cyclotron |||||

    Wie gesagt, im Vergleich mit dem Shruti kommt es mir so vor, als ob die errechnete Spreizung bei Dir einen höheren Wert hat und die Schwebungen dadurch viel schneller laufen (d.h., falls das der problematische Effekt ist - habe hier grad nur mittelmäßige Boxen am Rechner und höre nur diesen Unterschied).

    Was passiert denn, wenn du hier

    Code:
    uint16_t phase_spread = (uint32_t)(phase_increment_ * parameter) >> 13;
    
    mal testweise mit einem größeren Exponenten nach rechts shiftest?
     
  12. rolfdegen

    rolfdegen ....

    Hat keine Auswirkung da der parameter immer 0 ist.

    Der parameter ist bei allen Testsounds immer auf 0 eingestellt außer bei der letzten Demo.

    Deshalb auch die Zeile ++phase_spread; die den phasen_spread bei parametereinstellung von 0 immer zu 1 macht.
     
    Zuletzt bearbeitet: 1. Januar 2018
  13. rolfdegen

    rolfdegen ....

    Also das beste Soundergebnis habe ich jetzt mit einer Addition eines Zufallwertes (8Bit) zum phase_spread gemacht.

    Beispiel:

    Code:
    __uint24 phase_increment_ = (Osc.phase_increment[osc_nr] >> 8);
        uint16_t phase_spread = (uint32_t)(phase_increment_ * parameter) >> 13;
        //++phase_spread;
        phase_spread += (Noise.sample >> 7);
     
        for (uint8_t i = 0; i < 3; ++i) {
            phase_increment_ += phase_spread;
            increments[i] = phase_increment_;
        }
    Sound: 1.DE-GENERATOR 2.Shruthi


    Gruß Rolf
     
    Zuletzt bearbeitet: 1. Januar 2018
  14. Cyclotron

    Cyclotron |||||

    Bei Dir scheint aber selbst der minimale phase_spread von "1" einen viel größeren Detune - Effekt zur Folge zu haben. Hast Du bei Deiner Anpassung die Gewichtung von Integer / Fractional - Part im Gegensatz zum Shruti geändert?

    Meine Überlegung: Wenn sich die Bitbreite des Integerparts verringert, hätte phase_spread = 1 auch eine höhere relative Wirkung. Damit gibt es mehr Detuning und die Schwebungen laufen schneller.

    Gleiches würde auch passieren, wenn Du den Integerpart mit einem zu kleinen Exponenten (hier 8 ) extrahierst. Denn auf dessen Basis wird ja der Spread berechnet. Sowas könnte btw. auch zum dem Überlauf in phase_increment_ führen, so dass Du ab einem bestimmten Parameterwert plötzlich wieder einen sehr kleinen Spread hättest (oder eben auf 24 Bit gehen musst, bevor Du mit 13 shiftest).

    Ich kann Deinen Code mangels Gerät leider nur im Kopf durchlaufen lassen. Sorry, wenn das alles nix bringt oder ich selber hier Denkfehler kultiviere. Will die Fehlersuche für andere nicht mit falschen Fährten zukleistern.

    Aber: Die Variante mit Zufall klingt nicht schlecht. Finde ich besser als beim Shruti - Beispiel, weil sich die Frequenzen statt eines statischen Detunings immer ein wenig ändern.
     
  15. rolfdegen

    rolfdegen ....

    Hi..

    Erst einmal vielen Dank für deine Hilfe. Weis meinst du mit folgendem Satz genau:

    "Hast Du bei Deiner Anpassung die Gewichtung von Integer / Fractional - Part im Gegensatz zum Shruti geändert?"

    Versteh nicht ganz was du mit Integer / Fractional meinst.

    Osc.phase_increment und Osc.phase sind jeweils 24Bit breit.
    Die vorberechneten Phasen increments (16Bit Integer) lade ich aus einer Tabelle. Dazu werden noch Pitch und Modulationswerte addiert. Das Ergebnis steht als 24Bit Wert in Osc.phase_increment.
     
    Zuletzt bearbeitet: 1. Januar 2018
  16. Cyclotron

    Cyclotron |||||

    Besser gesagt: Integral / Fractional - also Fixed Point Math. Danach sieht es zumindest beim Shruti aus, wenn dort beispielsweise von phase_increment_.integral die rede ist (es müsste also auch ein phase_increment_.fractional geben - oder was soll das sein).

    Wenn man das - wie in Deiner Variante - mit nur einer Variable macht, muss man halt rechts shiften, um den Integralteil zu erhalten (bzw. maskieren, um den fraktionalen Teil zu bekommen). Das ist hauptsächlich beim Table - Lookup relevant für die Adressierung (.integral) und die Interpolation (.fractional). Oder eben wie hier, wenn nur mit einem Teil (nämlich .integral) gerechnet werden soll.

    Wenn man an der Integral / Fractional Aufteilung was ändert (weil man vielleicht andernorts die Größe einer Lookuptable verändert hat), ändert sich auch der Einfluss von phase_spread. Und es kommt dann genau zu diesem Effekt.

    Und wenn man den Integralteil nicht korrekt isoliert (also mit zu kleinem Exponenten shifted und der resultierende Wert zu groß ist), kann es zu Überläufen kommen. Beide Probleme hatte ich mit meinem Synth auch schon mal und da lag es an der Änderung der Integral / Fractional Gewichtung. Weshalb ich alle "magic numbers" durch statische Konstanten ersetzt habe, die abhängig voneinander errechnet werden.

    Wie gesagt, ich kenne das ganze Ding nicht und kann nur raten, was im Shruti was bedeuten soll. Die daraus resultierenden Folgerungen für dein Gerät mögen daher falsch sein.
     
  17. rolfdegen

    rolfdegen ....

    OK. Vielen Dank für deine ausgiebige Erklärung.

    Die Lookup Tabels sind nicht gleich, da ich mit 40.000 Hz abtaste. Im Shruthi sind es 39.215 Hz.
    Ich werde mich jetzt noch einmal auf den Shruthi code stürzen.

    Vielen Dank für deine Hilfe. Gruß Rolf
     
  18. rolfdegen

    rolfdegen ....

    DE-GENERATOR Build 3.61
    - Scope Darstellung: Auflösung wurde von 150 auf 64 Pixel reduziert. Dadurch wird die Systemlast verringert. Zeitbasis passt sich jetzt automatisch an Audiofrequenz an.
    - Fehler in Ringmodulation auf der Oszillator Page beseitigt
    - Vowel Sound implementiert (Shape: VOW)
    - SoundBuffer Größe von 40 auf 128 erhöt. Menüdarstellung und Bedienung wird dadurch etwas schneller und flüssiger
    - Für eine geringere Systemlast wurden einige Funktionen in der Soundengine verbessert.
    - Berechnung der Notenfrequenz verbessert. Tabellenwerte jetzt 16Bit

    [​IMG]

    Gruß Rolf
     
  19. rolfdegen

    rolfdegen ....

    Mein neuer "Workplace" und einen Platz für die leckeren Kekse gibts auch noch [​IMG]

    [​IMG]

    Gruß Rolf
     
    Cyclotron gefällt das.
  20. Wann ist das Release Date ?
    Es wurde ja nun schon um mehr als 1 Jahr angekündigt.
    Die Software kannst und wirst Du doch eh noch mehrfach anpassen, wichtiger ist doch das wir die Hardware haben bevor End of Life von diversen Bauteilen erreicht wird.

    Es hilft Dir und Andre bestimmt weiter wenn Ihr mal Geld für eure Arbeit seht.
     
    Saartekk, pulse und humax5600 gefällt das.
  21. DanReed

    DanReed ..

    Hallo Rolf,

    wir hatten schon vor langer Zeit bei einem Ditherproblem in Deinem Code das Vergnügen. Nun zeigst Du wieder ein bischen Code und ich frage mich, ob Du schon lange programmierst und ob Du die Entwicklungsumgebung von Arduino benutzt?

    Bitte versteh das nicht als Klugscheissen, sondern als Tipp, um mit ganz einfachen Mitteln weniger Bugs zu erzielen.

    Vielleicht würdest Du nämlich weniger Bugs übersehen, wenn Du den Sourcecode einmal automatisch reformatierst (und auf äquivalente Schreibweisen, die eine gute Entwicklungsumgebung wie Eclipse vorschlagen kann, eingehst)?

    Dann würde folgendes Beispiel aus Deinem Code zur Zeile darunter:
    buffer = (Voice.buffer_nr == 0) ? Voice.Buffer2a : Voice.Buffer1a;

    Oder die fehlende Einrückung würde verschwinden und klarer machen, dass die beiden Schleifen auch nur bedingt ausgeführt werden:
     
    Zuletzt bearbeitet: 13. Januar 2018
  22. DanReed

    DanReed ..

    Es gibt natürlich auch Fehler, die darauf basieren, dass die Cast-Regeln in C nicht verinnerlicht sind: Bei einer Multiplikation wird als Akkumulatorwortlänge immer die größere der Wortlängen der beiden Multiplikatoren genommen. Beim Shruthi wird vor der Multiplikation auf 32 Bit gecastet, damit der Akku 32 Bit hat und es nicht zu einem Überlauf kommt. Du dagegen castest erst hinterher, so dass Dein Akku immer nur die Wortlänge von phase_increment_ hat, also 16 Bit, so dass es zu Überläufen kommen kann. Dein Casten auf 32 Bit ist performancemäßig sogar kontraproduktiv, da der übergelaufene Wert dann auf 32 Bit erweitert wird, um dann aufwändig doch nur nach rechts geshiftet zu werden und sofort danach in 16 Bit gecastet zu werden.

    Lösung:
    Code:
    uint16_t phase_spread = ( (uint32_t)phase_increment_ * parameter ) >> 13;
     
    Zuletzt bearbeitet: 13. Januar 2018
    Cyclotron gefällt das.
  23. rolfdegen

    rolfdegen ....

    Vielen Dank für die Hinweise. Man lernt nie aus :bravo:
     
  24. DanReed

    DanReed ..

    Gerne, ich kann ja mehr nicht dazu beitragen, dass Dein Projekt vorangeht. Was mir gerade noch einfällt: wenn Du einen 16 Bit-Wert um 13 Bit rechts shiftest, bleiben nur 3 Bit, also 7 Stufen von Deinen Parameter-Werten 0 bis 127 übrig. Und genau diese Sprünge bei der Zunahme der Spreizung habe ich (neben dem Überlauf) in Deinem Beispiel gehört:
    Insofern ist das, was Cyclotron über "Integral / Fractional" geschrieben hat, äußerst wichtig für die Vergleichbarkeit mit Shruthi!
     
  25. rolfdegen

    rolfdegen ....

    Der singende DE-GENERATOR


    Parametereinstellungen
    Osc1: Vowel
    Osc2: Vowel -7 semitones
    Env1: CUTOFF + Osc1+2 vowel parameter
    Env2: VCA
    LFO1: Osc1+2 fine tune
    Portamento: 14
    Delay: Time 114 / Feedback 79 / Volume 127
     
    Zuletzt bearbeitet: 21. Januar 2018
    pulse, Zeitproblem und yaledbrever gefällt das.
  26. rolfdegen

    rolfdegen ....

    I love vowel sounds :frolic:



     
  27. rolfdegen

    rolfdegen ....

  28. humax5600

    humax5600 .....

    Gibt´s was neues wg. Verfügbarkeit eures Gerätes ?
    ( Lötstation oxidiert so vor sich hin...........)
     
    LED-man gefällt das.
  29. lilak

    lilak ||

    ich basel ja seit geraumer zeit auch an einem wavetable synth auf sample basis, aber der name "degenerator" schlägt alles was vor und nach ihm kommen wird :) der klingt jetzt schon wesentlich interessanter als ein schruti.
     
  30. 4 Jahre sind so schnell vergangen,
    Hoffentlich sind die Bauteile noch nicht angekündigt.
    Schade das es so lang dauert.
     
    humax5600 und aven gefällt das.