Neu

Interessiert an einem Austausch zum Thema DSP-Entwicklung?

Interessiert an einem Austausch zum Thema DSP-Entwicklung?

  • Gar kein Interesse, ich mach lieber Musik

    Stimmen: 3 14,3%
  • Interesse schon, aber kein interesse an einem Austausch

    Stimmen: 2 9,5%
  • Spannendes Thema, auf jeden Fall

    Stimmen: 16 76,2%

  • Umfrageteilnehmer
    21
  • Diese Umfrage wird geschlossen: .
  • #31
Aktuell fade ich bei einer problematischen Änderung den Output kurz aus, führe die Änderung durch und fade dann wieder ein. Aber so wirklich zufriedenstellend ist das auch nicht, da man in diesem Zeitraum natürlich auch nichts mehr hört.

Aber jedes Audiosignal hat ja ohne Ende Nulldurchgänge. Anstatt also selbst einen mit Fade-In und -Out zu erzwingen, könntest du auch einfach einen solchen abwarten.
 
  • #32
Eine Frage ist ja, wie oder worüber man sich überhaupt sinnvoll austauschen kann.
Ich hatte ja mal an einem Softsynth gearbeitet:
Der basierte auf spektraler Synthese einschließlich Spectrum Squash/Stretch und bot verschiedene Verfahren zur Erzeugung von Unisono-Effekten/Texturen.
Dafür war zwar die MPE-Implementierung von JUCE die Basis, aber ich habe eine Menge eigenen Code implementiert wie z.B. spektrale Oszillatoren, diverse Verfahren zur Textur-Generierung, dazu Basis-Funktionalität wie kubische Spline-Interpolation, einen counter-basierten Pseudozufallszahlen-Generator uvm.
Wegen der mir eigenen Sprunghaftigkeit ruht das Ganze allerdings im Moment.
Frustriert hatten mich bei der Entwicklung letztlich die folgenden Punkte:
  • Ich wollte als Erweiterung der bestehenden Funktionalität auch noch spektrale Transformationen in Realtime einbauen, diese waren dann aber so rechenaufwendig, dass am Ende nur noch eine Stimme gleichzeitig gerendert werden konnte - also leider nicht praktisch einsetzbar.
  • Es sollte zur Ergänzung noch ein normales subtraktives Filter dazu, wofür ich das Ladder-Filter von JUCE genommen habe. Mit dieser Entscheidung war ich dann aber unglücklich, weil mich die Qualität des Filters nicht überzeugt hat.
  • Ich wollte bei der FFT auf double precision gehen, habe es aber bisher nicht geschafft.
    Dazu wollte ich den double precision PFFFT-Fork nehmen, habe ihn aber mit der LLVM/MSVC Toolchain nicht compilieren können.
    Mir fehlte dann leider die Kraft für den Versuch, komplett auf die MingGW-Toolchain zu gehen (also mit PFFFT, JUCE und meinem eigenen Projekt) und auch für CMake alles entsprechend anzupassen, oder aber den PFFFT-Fork mit LLVM/MSVC compilierbar zu machen.
  • Ich hatte es versäumt, frühzeitig ein Modulationssystem einzubauen, und nachdem ich dann schon zig Parameter hatte, wurde es schwierig, das nachträglich noch hinzuzufügen.
  • Trotz der in JUCE schon vorhandenen Widgets ist es mir nicht gelungen, eine vernünftige GUI aufzubauen.
Falls ich noch einmal in dem Vorhaben einsteige, würde ich von null anfangen und alles aus dem alten Projekt schrittweise übernehmen.
Zuerst müsste ich aber das Problem mit dem Umstieg auf double precision FFT lösen (davon verspreche ich mir viel, weil sehr viel durch FFT vorberechnet und bzw. aus den FFT-Koeffizienten abgeleitet und neu synthetisiert wird, und weil durch Oversampling die benötigten FFT-Frames noch einmal größer werden).
Diesmal würde ich auch von Anfang an das Modulationssystem anlegen und diesen Punkt nicht aufschieben.
Außerdem würde ich das JUCE Ladder Filter durch ein selbst implementiertes Zero-Delay Feedback-Filter ersetzen, müsste mich dafür aber erst in die Materie einlesen.
Bedenkenswert wäre auch, sich von der Idee zu lösen, ein komplettes Plugin zu realisieren, und stattdessen nur einen Oszillator für VCV Rack zu schaffen, wie schon mal jemand angeregt hatte, das würde die Aufgabe deutlich vereinfachen. Allerdings hat allein die Kombi aus Oszillator und Texturgenerator bereits zig Parameter, das lässt sich in einem herkömmlichen Modul kaum unterbringen.

Sollte jemand hier ähnliche Interessen haben oder mit ähnlichen Problemen kämpfen, dann wäre ich an einem Austausch sehr interessiert.
Das betrifft vor allem die Nutzung des PFFFT Double-Precision-Forks mit LLVM/MSVC und evtl. auch die anderen genannten Themen.
 
Zuletzt bearbeitet:
  • #33
Das könnte für manche Fälle tatsächlich ausreichen. Bei Puffern hast du allerdings die Gefahr einer Diskontinuität.

Beispiel Ringpuffer (Delay):
1767104450413.webp
Du stellt die Zeit von 2 auf 3 Sekunden ein. Die zwei Sekunden wurden bisher kontinuierlich beschrieben und wiedergegeben. Je nachdem, wo sich Write- und Read-Head gerade befinden, kann es zu Knacksern an Anfang und Ende des neuen roten Bereichs kommen. Nämlich dann, wenn der Read-Head sich vor dem Write-Head befinden. Welche Werte sich im roten Bereich befinden weiß man nicht. Extrem gesprochen, hast du im letzen Sample Vollausschlag und im ersten Sample des roten Bereichs eine 0, weil der Write-Head dort noch nicht war um das kontinuierliche Signal dort hineinzuschreiben. Das führt unweigerlich zu einem Knackser. Genau so am Ende.

Ich denke, dass es in diesem Fall Eingangs auf einen Fade-out auf 0 und am Ende auf den Wert des ersten Samples im Puffer hinauslaufen wird. Änderungen von wenigen Millisekunden sind aber trotzdem kritisch, weil man dann zu wenige Sample fürs Fade-in/-out hat. Für den Fall interpoliert man wohl am besten das letzte bekannte Sample auf das erste Sample im Puffer und schreibt das dann in den roten Bereich.

Oder man bewegt den Read-Head erst weiter, wenn der Write-Head vor dem Read-Head ist und stellt den Output mit Kurve auf 0. Das ist wahrscheinlich die einfachste Lösung.
 
  • #34
Aber jedes Audiosignal hat ja ohne Ende Nulldurchgänge. Anstatt also selbst einen mit Fade-In und -Out zu erzwingen, könntest du auch einfach einen solchen abwarten.
Das Probem bei diesem "Trick" ist, dass das Knacken nur scheinbar weg ist: Das Signal der neuen Parameter könnte z.B. im Umfeld dieser Stelle Werte weit vom Nulldurchgang entfernt haben.

Nicht nur ein Sprung im Zeitsignal wird zu einem Knack, sondern auch eine Diskontinuität in der 1. Ableitung (z.B. wenn ein Sinus stufenartig seine Frequenz ändert).

Diese scheinbar beiläufige "Problemchen" macht bei vielen PlugIns einen der großen Unterschiede zwischen analogem Vorbild und digitaler Simulation.

Nicht dass ich jetzt dafür eine gute Lösung anbieten könnte ...
(Cross-fading über z.B. 5ms ist weitgehend unhörbar, aber benötigt über 5ms das Berechnen von beiden Signalen gleichzeitig, also doppelten Rechenaufwand)
 
Zuletzt bearbeitet:
  • #35
Wofür ich allerdings noch keine elegante Lösung habe ist die Verzögerung von Parameter-Änderungen durch den Nutzer (also das Drehen am Poti). Gerade wenn es um Puffer geht (Größenänderung z. B.) kann man davon ausgehen, dass unerwünschtes Knistern gibt. Aktuell fade ich bei einer problematischen Änderung den Output kurz aus, führe die Änderung durch und fade dann wieder ein. Aber so wirklich zufriedenstellend ist das auch nicht, da man in diesem Zeitraum natürlich auch nichts mehr hört.
Kannst Du nicht in einem solchen Fall für eine gewisse Zeit vom alten Wert auf den neuen Zielwert des Parameters linear überblenden?
Und, solange dieses Überblenden läuft, evtl. nicht auf neue Parameter-Änderungen des Nutzers reagieren, sondern erst, wenn der letzte Zielwert erreicht ist.
(Kontinuierliches Nachführen mit Smoothing geht natürlich auch).
 
  • #36
Mir fehlte dann leider die Kraft für den Versuch, komplett auf die MingGW-Toolchain zu gehen
Ich vermute mal, dass du auf Windows arbeitest. UCRT64 ist eigentlich eine ziemlich einfache Umgebung.


Darauf baue ich auch für Windows. Ansonsten verstehe ich das. Architekturfehler rächen sich früher oder später immer. Und je später man anfängt, desto schwieriger wird's. Deswegen war meine erste Zeit auch geprägt von Nächten, in denen ich immer wieder alles gerade gezogen habe. Das Repo dazu willst du nicht sehen. Dafür bin ich jetzt allerdings wirklich sehr zufrieden.
 
  • #37
Zielwert des Parameters linear überblenden
Das ist in der Parameter-Klasse sogar implementiert. Sowohl Werte- als auch Puffer-basiert. Aber das funktioniert halt nicht für alles. Zumal das auch noch maximal ineffizient ist. Was aber so lange keine Rolle spielt, wie man ausreichend CPU-Ressourcen hat. Bei meinem Supersaw-Synth für den Raspi3 kam es am Schluss allerdings wirklich auf jeden CPU-Zyklus an, so am Anschlag war der Raspi.

Aber vielleicht muss man das einfach auch akzeptieren. Ist halt so. Dann fallen halt Zyklen für Feature weg. Fällt mir aber häufig schwer.
 
  • #39
Oder man bewegt den Read-Head erst weiter, wenn der Write-Head vor dem Read-Head ist und stellt den Output mit Kurve auf 0. Das ist wahrscheinlich die einfachste Lösung.

Hm, spricht etwas gegen double buffering? Dann musst du dich nur um das richtige Timing beim Umschalten zwischen den beiden Puffern kümmern. Und gegebenenfalls Hysterese implementieren, um Parameter-Dauerfeuer durch Usereingaben abzufangen.
 
  • #40
Fies knacksende Boxenkiller sind es dagegen nicht
Diskontinuität kann halt bei allem auftreten, was in den Ausgabepuffer schreibt. So lange es nur ein Sample ist, ist es zwar unschön, aber nicht so gefährlich, denke ich.

Ich frage mich immer, was das in den 80/90ern bei der verfügbaren Hardware für krasse Hacker gewesen sein müssen, um dermaßen sauber zu implementieren.
Hm, spricht etwas gegen double buffering?
Du hast exakt dasselbe Problem. Mögliche Diskontinuität beim Switchen auf den anderen Puffer mit anderem Inhalt. Kannst du drehen und wenden wie du magst. ;-) Oder du erklärst das mit dem Timing etwas genauer. Vielleicht täusche ich mich ja.

Hysterese ist ja im Grunde nichts anderes als ein Filter. Da hast du unter Umständen dann zwar kleinere Sprünge, aber immer noch Sprünge. Kann gut gehen, muss es aber nicht. Was noch machbar wäre ist, dass der Parameterwechsel tatsächlich erst nach einer definierten Zeit durchgeführt wird. Z. B., wenn 100ms lang keine Werteänderung mehr stattgefunden hat. Für die Zeitmessung muss man sich aber wieder in den DSP-Prozess hängen. Alles andere ist ziemlich umständlich. Das wird wahrscheinlich auch der Weg sein, den ich wählen werden.

Kleine, lästige, aber leider wichtige Details bei Plugins. Eigentlich ja unwichtig, da ich niemals Geld damit verdienen will und werde.
 
  • #41
ich nutze auch JUCE klappt wunderbar mit Visual Studio 26 und Copilot mit Chatgpt
 
  • Daumen hoch
M.i.a.u.: _thomas_
  • #42
Oh, wir sind jetzt in der Nerdzone, also offiziell damit wohl Nerds. ;-)
Ich begnüge mich mit VS Codium auf Ubuntu. Claude nutze ich sehr viel als Dokumentationsgenerator, als mit Vorsicht zu genießenden Erklärbär und hauptsächlich für die Fehlersuche. Claude erkennt die Standardfehler sofort. Das ist ganz cool und hat mir schon viel Zeit und Mühe gespart. Erst vorhin wieder mit meinem Zufallszahlen-Generator, der leider aufgrund einer fehlerhaften Initialisierung statistisch zu Negativität tendierte.
 
  • #43
Du hast exakt dasselbe Problem. Mögliche Diskontinuität beim Switchen auf den anderen Puffer mit anderem Inhalt. Kannst du drehen und wenden wie du magst. ;-) Oder du erklärst das mit dem Timing etwas genauer. Vielleicht täusche ich mich ja.

Mir fallen da ein paar Sachen ein, aber die Universalstrategie gibt es nicht. Ist immer vom Kontext abhängig.

Ich geh mal davon aus, dass dein Ringpuffer von maximal 10 Sekunden irgendeinen tatsächlich nutzbaren Inhalt/Bereich mit Offset(Anfang) und Länge(Ende) per Zeiger kleinergleich 10 Sekunden beschreibt.

Da fiele mir dann ein, die zehn Sekunden grundsätzlich während einer Initialisierung bereits komplett voll zu schreiben und hinterher nur noch die beiden Zeiger zu ändern. Die neuen Zeigerpositionen müssen dann immer auf Nulldurchgänge zeigen. Damit kann man dann schon mal die länge des Buffers sehr schnell ändern, ohne in den Puffer schreiben zu müssen. So könnte man quasi eine Wavetable erzeugen und dann durchmorphen, indem einfach nur die Zeiger geändert werden.

Will man nun den Inhalt des Ringpuffers ändern, zum Beispiel mit einem identischen Signal anderer Frequenz, dann holt man einen zweiten Ringpuffer dazu, füllt den komplett mit dem neuen Signal, sucht die Nulldurchgänge* und setzt die Pointer auf Offset(Anfang) und Länge(Ende) und schaltet dann um, sobald der alte Puffer sein Ende erreicht hat.

*Wenn die Frequenz des neuen Signals bekannt ist, kann man die Nulldurchgänge auch algorithmisch bestimmen, ohne den Pufferinhalt analysieren zu müssen.

Die Hysterese müsste dann halt der Leistungsfähigkeit der CPU entsprechend gewählt werden. Je schneller der Puffer im Hintergrund gefüllt werden kann, umso eher kann wieder getauscht werden.
 

News

Zurück
Oben