Anschlagdynamik Kurven berechnen

  • Ersteller MagicTobi
  • Erstellt am
MagicTobi
MagicTobi
Registrierter Benutzer
Zuletzt hier
09.12.17
Registriert
06.02.05
Beiträge
245
Kekse
34
Hallo!

Ich schreibe eine Software zur MIDI-Steuerung, wo auch Anschlagdynamik vorkommen soll. Mein Stand ist so, dass eine einfache Anschlagdynamik funktioniert, die linear arbeitet. Allerdings würde ich gerne auch exponentielle Kurven einsetzen, nur, wie berechne ich die?

Kennt jemand eine Seite, die das beschreibt, oder hat jemand in Mathematik aufgepasst?

Gruß

Tobias
 
Eigenschaft
 
Man nehme eine log oder exp Funktion und drehe an den Parametern bis es paßt! :great:
 

Anhänge

  • Image1.png
    Image1.png
    107,1 KB · Aufrufe: 325
Hi
Im Detail muss man auf einiges achten, damit sich auch sinnvolle Werte ergeben.
Wie dreht man sinnvoll an den Parametern ?
Soll es tatsaechlich die Anschlagscharakteristik sein wie sie Leefs Beispiel zeigt ?
Nehmen wir mal an deren Eingansgroesse sei x und deren Werte 0..127
Die Ausgangsgroesse sei y.
Die Aufgabe waere es dann eine nichtlineare Uebertragungsfunktion f() zu konstruieren :
y=f(x). Eventuell mit einem einstellbaren Parameter p :
y=f(x,p)
Am besten verwendest du fuer f() eine Variante der e-Funktion die fuer x=0..127 auf die Werte 0..1 normiert ist.
f(0)=0, f(127)=1
Dann bildest du y=127*f(x) und die Aufgabe ist geloest.
Ich wuerde fuer die Konstruktion, Normierung ein Grafikprogramm wie gnuplot zur Kontrolle verwenden. Zudem daran denken :

exp(0)=1
exp(1)=2.718
exp(127)=eine Zahl mit 55 Nullen :)

y=exp(x) funktioniert somit natuerlich nicht
Du musst das Argument z.b auf 1 begrenzen
y=exp(x/127)
Jetzt ist aber exp(127/127) gleich exp(1) und nicht 1
Also teilen wir das ganze durch exp(1)
y=exp(x/127)/exp(1)

Fast perfekt. Nur gilt exp(0)=1 und unsere Kurve staret daher nicht bei 0. f(0)<>0
Das koennte man noch spaeter korrigieren. Jetzt bauen wir zunaechst einen Parameter p ein um die Empfindlichkeit der Tastatur einzustellen.
Das muessen wir auch im Nenner ueber exp(p) beruecksichtigen :
Und damit waere dies schon die erste Versuchskonstruktion :
y=127*exp(p*x/127)/exp(p), p=1..6
**************************
Wegen f(0)<>0 ist es aber besonders fuer p<1 noch eine Fehlkonstruktion
exp Funktionen sind rechenintensiv. In der Praxis wird man somit mittels einem kleinen Prograemmchen oder beim Programmstart, Einstellen eine Tabelle f[x] anlegen und diese dann einfach auslesen. f[originalwert]=neuer Wert
Gruesse
 
Zuletzt bearbeitet:
Teil 2
****
Beheben der Einschraenkung f(0)<>0
Es gibt mehrere Loesungswege hier mal einer davon :
Am besten stellt man sich den Loesungsweg zunaehst grafisch vor.
Zunaechst ziehen wir den Offset f(0) von der Funktion ab.
f(x)-f(0)
Das waere (exp(p*x/127)-1)/exp(p) (Die minus eins)
Klar jetzt fehlt der Offset nun beim Maximalwert. Um f(127)=1 wiederherzustellen multiplizieren wir die Funktion mit einem Faktor k. y=k*(f(x)-f(0)) In der Form, das gilt y=k*(f(x_max)-f(0))=1
Wir erhalten aus der Bestimmungsgleichung fuer k :
k=exp(p)/(exp(p)-1)

Und nun als fehlerfreie Funktion :
y=127*(exp(p*x/127)-1)/exp(p)*exp(p)/(exp(p)-1)
Wir koennen exp(p) noch kuerzen :

y(x)=127*(exp(p*x/127)-1)/(exp(p)-1)
****************************
velocity.gif

Ahhh :)
Waehlt man z.B p=0.1 geht die Exp-Funktion ueber in eine lineaere Funktion.
Und fuer negative Werte von p erhalt man die "Easy" Einstellungen.
Fuer k=-1000 waere die Anschlagsynamik ausgeschaltet. Dann wuerde ich aber y_max begrenzen.
Man sollte dies in einer Tabelle implementieren. Fuer jeden Anschlagwert neu auszurechnen waere Unfug.
Die Umrechnung Usereingaebe PU zu p wuerde ich auch in einer Tabelle vorgeben.
Gruesse
 
Zuletzt bearbeitet:
Hallo!

Vielen Dank für die Antworten!

Zu Leef:
Also, ich habe schon experimentiert, wie Du sagtest, allerdings gelang es mir nicht, eine Kurve zu generieren, die sauber durch 0 und den Maximalwert geht

Zu Richy:
Du hast in Mathe aufgepasst ;) Genial, genau, was ich gesucht habe. Muss mich jetzt mal mit auseinandersetzen. Verstehe, was gemeint ist, aber ich muss es für mich halt noch begreifen.

Mit der Tabelle hatte ich auch vor. Was ich nur überlege, ob ich die Tabelle bei Bedarf erstelle zur Laufzeit, dass beim Auswählen der Kurve die Werte berechnet werden, oder ob ich für alle Kurven die Tabellen von vorn herein lade. Das Programm soll jetzt nicht mit einem 486er zurechtkommen, sondern reicht, wenns auf Netbooks läuft ;)

Wie dem auch sei, ich bin einen großen Schritt weiter. Vielen Dank

Gruß

Tobias
 
Hi Tobi
Was ich nur überlege, ob ich die Tabelle bei Bedarf erstelle zur Laufzeit, dass beim Auswählen der Kurve die Werte berechnet werden, oder ob ich für alle Kurven die Tabellen von vorn herein lade
Es reicht eine Tabelle und fuer deren Beschreibung der Parameter p.
Entweder initialisierst du die Tabelle beim Programmstart z.B. mit
for i=0..127 velo=i ; next
oder mit der letzten Usereinstellung fuer die du lediglich den Wert von p speicherst.
lese aus Datei p
for i=0..127 velo:=127*(exp(p*i/127)-1)/(exp(p)-1); next

Anwenden im Programm : velo_neu=velo[velo_original]
Die Tabellenwerte selbst musst du nicht auf Platte speichern. Du berechnst die Werte ueber p.
Aendert der User den Wert p erzeugst du einfach sofort die neue Tabelle und speicherst p fuer den naechsten Programmstart. Benutzerfreundich ueber Usereingabe PU=0.1.2...etc
pt[0]:=0.1; pt[1]:=-2: pt[2]:=-1; pt[3]:=1 e.t.c
eingabe PU; p=pt[PU]
for i=0..127 velo:=127*(exp(p*i/127)-1)/(exp(p)-1); next;
speichere p in Datei

Das halte ich fuer die einfachste Methode. Eine Tabelle ist ausreichend, denn du benoetigst zur Laufzeit auch immer nur eine Tabelle. Mehrere Tabellen waere speicherintensiver schechter Programmierstil.
... aber ich muss es für mich halt noch begreifen.
Kurzform wie beim Baecker :
y=exp(x)
Vertikal stauchen um erstmal vernuenftige Ausgangswerte zu erhalten
y=exp(x/127)
y_max auf eins normieren
y=exp(x/127)/exp(1), HILFE : exp(127/127)/exp(1)=?
Parameter p einbauen (dabei neu normieren)
y=exp(p*x/127)/exp(p) HILFE : exp(p*127/127)/exp(p)=?
y_min auf Null ziehen (Offset entfernen)
y=(exp(p*x/127)-1))/exp(p) HILFE : exp(p*0/127)-1=?
y_max durch "Strecken" ueber k erneut auf eins normieren,f(0)=0 bleibt dabei erhalten ! :
y=k*(exp(p*x/127)-1))/exp(p)
k bestimmen ueber
1=k*(exp(p*127/127)-1))/exp(p) =>
k=exp(p)/(exp(p)-1) => (k einsetzen)
y(x)=(exp(p*x/127)-1)/(exp(p)-1)

Die nun von 0..1 normierte Funktion mit Maximalwert (127) multiplizieren.
Fertig :
y(x)=127*(exp(p*x/127)-1)/(exp(p)-1)
Ergibt die oben angezeigten Grafiken fuer verschiedene p
Vorsicht x sollte Fliesskomma sein, sonst gibt es evtl. cast-Rundungsfehler und das Ergebnis ist 0 oder 127
Gruesse
 
Zuletzt bearbeitet:
Hi Richy,

nochmal vielen Dank :)

Ich kann die Formel einsetzen, obwohl ich mathematisch nicht nachvollziehen kann, was die exp Funktion genau rechnet. Für das Programm ist es nicht wichtig, aber ich verstehe gerne die angewendete Mathematik ;) Vielleicht befass ich mich mal damit irgendwann, aber erstmal weiterprogrammieren...

Wo Du von Programmierstil sprichst... Hm, ob mein Programm guter Stil ist...:gruebel:? Ich verwende recht viele Benutzersteuerelemente (ich programmiere Visual Basic .net 2008) und frage mich immer wieder, ob es günstig ist, so zu arbeiten. Der Vorteil von Steuerelementen ist, dass es so leicht ist, komplette Einheiten zu haben, die einfach zu vervielfachen sind. Es kann sein, dass ich z.B. mehrmals eine Instrument Auswahl brauche, etc. Und ich brauch mich nicht um die grafische Darstellung kümmern, ausser dass angegeben wird, wo das Steuerelement platziert wird.

Gruß

Tobias
 
Als ich vor ein paar Jahren in Visual C++ eine Software mit Masterkeyboard-Funktionen geschrieben habe, habe ich auch verschiedene Velocity-Kurven über Tabellen realisiert...allerdings hatte ich nur verschiedene Kurven vorprogrammiert, und da war es einfacher, die fertigen Tabellen von Hand einzutippen, als das Programm die berechnen zu lassen ;-) bis ich die Mathematik verstanden hätte, wären Monate vergangen...

Die schickste Methode wäre natürlich, grafisch editierbare Bezier-Kurven zur Editierung von Velocitykurven zu programmieren.

Pass aber auf mit dem Nullwert, damit der Eingangswert 0 in jedem Fall auf den Ausgangswert 0 abgebildet wird, denn Nullwerte bei Note-Messages bedeuten ja "Note off" und das muß auf jeden Fall gleich bleiben, sonst stoppt der Ton nicht.

Harald
 
Als ich vor ein paar Jahren in Visual C++ eine Software mit Masterkeyboard-Funktionen geschrieben habe, habe ich auch verschiedene Velocity-Kurven über Tabellen realisiert...
Apropo, ich werde irgendwann auch nochmal auf eure Hilfe zurückgreifen. Ich schreibe ja auch ein wenig an meiner Software rum und bin im Moment dabei das ganze so aufzubereiten, daß es einfach erweiterbar wird.

Sobald ich in dem Status bin wo es an den Inhalt, also weg von technischen Problemen und hin zum Nutzen / musikalischen geht, werde ich bestimmt noch die eine oder andere Frage haben. Die ausführlichen Beiträge zu den Dynamikkurven von Richy haben mir hier bereits ebenfalls sehr geholfen. Danke! :great:

Eine alte Version meiner Software gibt's hier https://www.musiker-board.de/vb/synthesizer-workstations/323215-midi-effekte.html#post3729788
 
Auch von mir schonmal vielen Dank an Richy. Ich bastel ebenfalls an einer Software zur MIDI-Verarbeitung (zu finden hier), und merke gerade, daß meine bisherige Formel für Velocity-Kurven wohl doch noch nicht das Gelbe vom Ei war... Dafür ist sie aber einfacher ;)

Bisher benutze ich in etwa das hier:
y = 127 * (x/127)**p

Werte von p < 1 machen den Anschlag weicher, p > 1 härter:
attachment.php


Wie man sieht ist die Kurve im Bereich niedriger Velocity-Werte besonders stark gekrümmt, während sie sich im oberen Bereich schon fast einer Gerade annähert.
Über das Spielgefühl sagt das natürlich noch nicht viel aus. Ich werde Richy's Variante einfach auch mal implementieren, und dann gucken, welche sich "richtiger" anfühlt...
 

Anhänge

  • velocity.png
    velocity.png
    17,8 KB · Aufrufe: 583
Hi fubar
Prima einfache Variante mit aehnlichem Ergebnis.
( Anmerkung ** bedeutet "hoch", potenzieren )
Und null hoch p ist null. Und 1 hoch p ist 1 fuer alle p. Wie es die Grafik auch zeigt.
Prima Idee ! Muss ja nicht exponentiell sein. Koennte mir vorstelen dass sich das sogar besser anfuehlt.
Ein kurzer Erfahrungsbericht waere fuer alle sicherlich hilfreich.
Und wenn man wie Matmann nicht gerade von Hand die Tabellen erstellt, kann man beide Versionen implementieren. (Eine Tabelle reicht wie erwaehnt)
Die schickste Methode wäre natürlich, grafisch editierbare Bezier-Kurven zur Editierung von Velocitykurven zu programmieren.
Na dann muss der Benutzer sich wieder verkuensteln. Und Bezier Kurven sind sicherlich nicht die einfachste Mathematik :) Sind schoen abgerundet. Aber im Grunde braucht man die hier nicht.
http://www.stefan-koegler.de/studienarbeit_online-tutorium/node37.html
Gruesse
richy
 
Zuletzt bearbeitet:
Respekt, Richy, dass du den Kurven schon ansiehst, wie sie sich anfühlen :D
Wenn man Punkte vorgibt, sollte es ein simpler kubischer Spline doch auch schon tun.
Dass die nicht perfekt glatt sind, ist ja vollkommen wurscht, wenn uns nur diskrete 127 Punkte interessieren.
Mit bestem Gruß
Oli
 
Hi Di Box
Was gibt es einfacheres als z.B. ueber y = 127*(x/127)**p Tabellen zu erstellen ?
So machen es auch die Hersteller. Und letztendlich kommt es noch darauf an wie die Sounds die Velocity verarbeiten. Die sind sicherlich auch nicht linear.
Zu den Varianten.
Wenn du Fubas oder die rote exp easy Kurve betrachtetst. Die erreicht bei 40 schon knapp Velo 100. Das ist wohl zu empfindlich. Die gruene Kurve duerfte fuer Anfaenger recht gut sein.
Bei fubas Kurve koennte es vorteilhaft sein, dass sie nur den unteren Teil komprimieren, expandieren und dann eher linear verlaufen. Die Expo Kurven sind durchgehnd gekruemmt.
Man muesste das wirklich ausprobieren.
Bin gespannt ob jemand davon berichtet.
Gruesse
 

Ähnliche Themen


Unser weiteres Online-Angebot:
Bassic.de · Deejayforum.de · Sequencer.de · Clavio.de · Guitarworld.de · Recording.de

Musiker-Board Logo
Zurück
Oben