AX8-Parameter über Midi-Controller steuern

Felix

Active member
Mitglied seit
Aug 6, 2019
Beiträge
252
Moin.
Wenn ich das Handbuch richtig verstehe, kann ich praktisch jeden Parameter des AX8 auch per Midi steuern. Ohne Ausprobieren in der Praxis fällt es mir allerdings schwer, die Lösung technisch wirklich zu begreifen.

Im Grunde möchte ich die Fußschalterzahl erhöhen durch einen externen Controller und auf diesem Weg im Stompbox-Mode mehr Optionen gewinnen.
UND ich möchte die Paramter der einzelnen Bodentreter steuern, also Distortion, Level, Tone, Speed, Depth etc.

Würde das gehen? Und wäre das mit einem einmaligen (großen) Programmieraufwand erledigt oder müsste das doch für jedes Preset komplett neu gebastelt werden?

Die Anschlussfrage betrifft die Hardware: Ist euch ein Controller mit entsprechendem Layout bekannt? Der Behringer 1010 zum Beispiel hat ja nur Fußtaster. Ich kenne den Markt nicht so gut. Der Boss ME70 kam mit solch einer Aufteilung, aber der ist freilich kein Midi-Controller.
Ein Controller mit meinetwegen 8 Fußtastern und darüber jeweils drei Encodern - das muss es doch geben.
 

axifist

Well-known member
Mitglied seit
Nov 14, 2013
Beiträge
1.713
[...] UND ich möchte die Paramter der einzelnen Bodentreter steuern, also Distortion, Level, Tone, Speed, Depth etc.
Ja das geht auch. Das Stichwort dafür ist SysEx.

Der Programmieraufwand hält sich in Grenzen, wenn du mit einem selbstgebauten Controller immer nur einen Parameter steuern möchtest. Sobald du davon abweichst, wird es aufwändiger. Dann müssen die verschiedenen Dinge ja gespeichert und zum richtigen Zeitpunkt aktiviert werden.

Um diese von dir gewünschten Parameter steuern zu können, kannst du -- soweit ich weiß -- auf keinen kommerziellen Controller zurückgreifen. Die nutzen in der Regel nur "normales" Midi. Außer natürlich, du baust ein Gerät, das die Daten von so einem Controller empfängt, in SysEx-Daten umwandelt und an das AX8 weiterleitet. Das wäre prinzipiell denkbar, aber auch nicht einfach mal so machbar, außer man ist geübt im Programmieren.
 

Andy

Well-known member
Axe-Fest 2017 Teilnehmer
Axe-Fest 2020 Online Teilnehmer
Mitglied seit
Okt 21, 2012
Beiträge
6.824
Für das Aktivieren / Deaktivieren sollte es ausreichen die entsprechenden CCs zu kennen und auf deinen Taster zu legen. Dazu wird es im Manual hoffentlich eine Tabelle geben. So wie es das MFC eben auch tut. Regeln einzelner Parameter, aber da kenn ich es nur vom AxeFx II, indem du einen Controller auf den jeweiligen Parameter legst und dann bspw. mittels eines Pedals diesen beeinflusst. Generell müsste man, wie axifist schreibt, alles regeln können, denn der Editor schafft das ja auch. Aber da bin ich raus.
 

axifist

Well-known member
Mitglied seit
Nov 14, 2013
Beiträge
1.713
Generell müsste man, wie axifist schreibt, alles regeln können, denn der Editor schafft das ja auch. Aber da bin ich raus.
Zumindest beim Standard/Ultra geht das, da habe ich das beim Schreiben des Codes meines Midi-Controllers mal implementiert und testweise am Laufen gehabt, aber letztlich nicht verwendet, weil ich dafür keinen Bedarf hab und es viel zu viel Aufwand wäre.
Bin mir sehr sicher, dass das zumindest beim Axe II auch geht, entsprechend auch beim AX8.

edit: Also letztlich verhält es sich dann tatsächlich so, wie wenn man mit dem Editor Parameter regelt. Der schickt auch SysEx-Daten raus. Die kann man auch abfangen, lesen und analysieren. Das funktioniert auch wirklich gut!
 

axifist

Well-known member
Mitglied seit
Nov 14, 2013
Beiträge
1.713
Mal ein kleiner Auszug aus meinem Code. Wie gesagt, ist fürs Ultra (funktioniert mit dem Standard sicherlich auch).

Mit SysEx hat man bis zu 255 "Stufen", die aus zwei Komponenten bestehen. (Eigentlich wären bis zu 256 Stufen möglich. Genutzt werden im Standard/Ultra aber nur 255. Warum? Kein Plan...)
Code:
const uint8_t PARAMETERVALUES1[255] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
                                       0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
                                       0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
                                       0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
                                       0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
                                       0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
                                       0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
                                       0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
                                       0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
                                       0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
                                       0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
                                       0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
                                       0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
                                       0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
                                       0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
                                       0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E
                                      };
const uint8_t PARAMETERVALUES2[255] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                                       0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
                                       0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
                                       0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
                                       0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
                                       0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
                                       0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
                                       0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
                                       0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
                                       0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
                                       0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A,
                                       0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B,
                                       0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C,
                                       0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D,
                                       0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E,
                                       0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F
                                      };
Möchte man dann beispielsweise den Threshold für Compressor 1 ändern (warum ich das Beispiel genommen hab, fragt mich net, das ist ja relativ sinnfrei, war ja nur für Tests), dann muss man das so raussenden:
Code:
uint8_t Compressor1Threshold[12] = {0x00, 0x01, 0x74, AXEVERSION, 0x02, 0x04, 0x06, 0x00, 0x00, PARAMETERVALUES1[controllerValue[loopThroughPotis]], PARAMETERVALUES2[controllerValue[loopThroughPotis]], 0x01};
MIDI.sendSysEx(12, Compressor1Threshold);
Infos dazu:
Die erste Zeile des Codes "generiert" sozusagen den SysEx-Befehl in Abhängigkeit von der Pedalstellung, die zweite Zeile schickt den Befehl raus.

Die beiden Zeilen müssen dann natürlich in eine Schleife, wobei dann bei jedem Durchlauf
  1. die Stellung des Pedals gemessen (im Arduino dann ein Wert zwischen 0 und 1023),
  2. dieses Intervall von 0 bis 1023 mit z.B. dem map-Befehl auf das Intervall, das für den gewünschten Parameter vorliegt, umgerechnet (jeder Parameter hat seinen eigenen, individuellen Bereich, geht also nicht immer über die vollen 0 bis 254),
  3. damit dann aus den obigen Tabellen das korrekte entsprechende Wertepaar rausgeholt,
  4. der SysEx-Befehl generiert und
  5. der Befehl rausgeschickt werden muss.
Dann wird in diesem Fall, wenn man das Expressionspedal von der Null-Stellung aus nach oben bewegt, folgendes gemacht:
Code:
uint8_t Compressor1Threshold[12] = {0x00, 0x01, 0x74, AXEVERSION, 0x02, 0x04, 0x06, 0x00, 0x00, 0x00, 0x00, 0x01};
MIDI.sendSysEx(12, Compressor1Threshold);
uint8_t Compressor1Threshold[12] = {0x00, 0x01, 0x74, AXEVERSION, 0x02, 0x04, 0x06, 0x00, 0x00, 0x01, 0x00, 0x01};
MIDI.sendSysEx(12, Compressor1Threshold);
uint8_t Compressor1Threshold[12] = {0x00, 0x01, 0x74, AXEVERSION, 0x02, 0x04, 0x06, 0x00, 0x00, 0x02, 0x00, 0x01};
MIDI.sendSysEx(12, Compressor1Threshold);
uint8_t Compressor1Threshold[12] = {0x00, 0x01, 0x74, AXEVERSION, 0x02, 0x04, 0x06, 0x00, 0x00, 0x03, 0x00, 0x01};
MIDI.sendSysEx(12, Compressor1Threshold);
uint8_t Compressor1Threshold[12] = {0x00, 0x01, 0x74, AXEVERSION, 0x02, 0x04, 0x06, 0x00, 0x00, 0x04, 0x00, 0x01};
MIDI.sendSysEx(12, Compressor1Threshold);
...
uint8_t Compressor1Threshold[12] = {0x00, 0x01, 0x74, AXEVERSION, 0x02, 0x04, 0x06, 0x00, 0x00, 0x00, 0x01, 0x01};
MIDI.sendSysEx(12, Compressor1Threshold);
uint8_t Compressor1Threshold[12] = {0x00, 0x01, 0x74, AXEVERSION, 0x02, 0x04, 0x06, 0x00, 0x00, 0x01, 0x01, 0x01};
MIDI.sendSysEx(12, Compressor1Threshold);
uint8_t Compressor1Threshold[12] = {0x00, 0x01, 0x74, AXEVERSION, 0x02, 0x04, 0x06, 0x00, 0x00, 0x02, 0x01, 0x01};
MIDI.sendSysEx(12, Compressor1Threshold);
uint8_t Compressor1Threshold[12] = {0x00, 0x01, 0x74, AXEVERSION, 0x02, 0x04, 0x06, 0x00, 0x00, 0x03, 0x01, 0x01};
MIDI.sendSysEx(12, Compressor1Threshold);
uint8_t Compressor1Threshold[12] = {0x00, 0x01, 0x74, AXEVERSION, 0x02, 0x04, 0x06, 0x00, 0x00, 0x04, 0x01, 0x01};
MIDI.sendSysEx(12, Compressor1Threshold);
...
...
...
uint8_t Compressor1Threshold[12] = {0x00, 0x01, 0x74, AXEVERSION, 0x02, 0x04, 0x06, 0x00, 0x00, 0x0A, 0x0F, 0x01};
MIDI.sendSysEx(12, Compressor1Threshold);
uint8_t Compressor1Threshold[12] = {0x00, 0x01, 0x74, AXEVERSION, 0x02, 0x04, 0x06, 0x00, 0x00, 0x0B, 0x0F, 0x01};
MIDI.sendSysEx(12, Compressor1Threshold);
uint8_t Compressor1Threshold[12] = {0x00, 0x01, 0x74, AXEVERSION, 0x02, 0x04, 0x06, 0x00, 0x00, 0x0C, 0x0F, 0x01};
MIDI.sendSysEx(12, Compressor1Threshold);
uint8_t Compressor1Threshold[12] = {0x00, 0x01, 0x74, AXEVERSION, 0x02, 0x04, 0x06, 0x00, 0x00, 0x0D, 0x0F, 0x01};
MIDI.sendSysEx(12, Compressor1Threshold);
uint8_t Compressor1Threshold[12] = {0x00, 0x01, 0x74, AXEVERSION, 0x02, 0x04, 0x06, 0x00, 0x00, 0x0E, 0x0F, 0x01};
MIDI.sendSysEx(12, Compressor1Threshold);
Es ist bestimmt machbar, aber ich hatte da sehr schnell die Lust verloren, auch, weil ich das alles nicht verallgemeinern konnte, sondern für jeden Parameter wahrscheinlich eine eigene Schleife gebraucht hätte.
Wenn man nur ganz wenige Parameter so steuern will, hält sich das in Grenzen. Meine Idee war damals, das für alle Parameter zu ermöglichen. Das wäre schlicht zu viel gewesen und hätte vielleicht auch zu Speicherproblemen geführt usw.


edit:
Oh, mir fällt gerade ein... Im AxeFX II (im AX8 entsprechend bestimmt auch) kann man nicht, so wie hier, einfach diese Daten raussenden. Da muss noch eine Prüfsumme mitgeschickt werden, damit eben Übertragungsfehler nicht zu merkwürdigem Verhalten des AxeFX II / AX8 führen. Wie das genau funktioniert, weiß ich nicht, hatte ich nie ausprobiert. Im Wiki findet man dazu aber zumindest Infos: https://wiki.fractalaudio.com/wiki/index.php?title=MIDI_SysEx#MIDI_SysEx:_calculating_the_SysEx_Checksum
 
Zuletzt bearbeitet:

Andy

Well-known member
Axe-Fest 2017 Teilnehmer
Axe-Fest 2020 Online Teilnehmer
Mitglied seit
Okt 21, 2012
Beiträge
6.824
ich hab noch nicht ganz durchgeblickt, was die einzelnen Einträge bedeuten aber wenn du da ein bisschen rechnen würdest, würde sich der Speicherbedarf ganz schnell reduzieren lassen. Jedes einzelne Datenpaket hinschreiben ist in der Abarbeitung dann sehr schnell, weil keine Rechenoperationen notwendig sind, kostet aber irre viel Speicher.

Da ist es besser den fixen Teil am Anfang als Header einmal hinzuschreiben, sogar als const, also Konstante, wenn sich da nix ändert und dann den dynamischen Teil berechnen und wenn am Schluss oder mittendrin etwas fix ist, wieder als const. ... DA kann man dann auch vier fünf Varianten machen, wenn es da mehrere gibt. Gäbe aber mehrere Varianten, müsste man mal genau wissen. Man kann auch beim Start den Frame in einen Puffer kopieren und dann den Wert überschreiben der anders ist. ... Birgt dann aber eben die Gefahr, dass bei nem Fehler möglichweise der Puffer überschrieben wird.... und die Header falsch sind, also der fixe Teil. Sollte aber nicht vorkommen, wenn man gut programmiert ;) Kann man ja auch zylisch wiederholen... Immer zum Scheifenbeginn.

Eine FCS frame-check-sum von der Art rechnet man dann besser aus. Weil die eben für jeden Frame anders ist. Ist wohl nur ein bisschen XOR und eine bitweise Ver-UND-ung. Das rechnet ein µC im Schlaf auf. Die Rechenvorschrift steht sogar im wiki. Wobei man sich mal die
MIDI.sendSysEx näher ansehen müsste, ob die die Daten in einen Send-Buffer kopiert usw.... Eigentlich würde ich die FCS dort mit hineinschreiben...
 
Zuletzt bearbeitet:

Felix

Active member
Mitglied seit
Aug 6, 2019
Beiträge
252
Cool, ganz herzlichen Dank. Dann fehlt ja nur noch der passende Controller. Also entweder eine Kombination aus Tretboard und Drehboard. Oder etwas selbstgebautes. Für die SysEx-Sachen scheint Arduino geeignet zu sein, sofern die Leistung genügt.

Wenn der Sommer früh endet und es im Herbst genug regnet, bastel ich sowas. Oder sagen wir's so: Ich verschaffe mir einen Überblick über die Anleitungen und überlege mir sehr gut, ob ich das auf mich nehme oder doch lieber die einzelnen Bodentreter selbst baue. Das hat dann natürlich mit Midi nichts zu tun.

Oder ich schalte erstmal nur mit einem externen Midi-Brett die Effekte/Looper etc um und schaue dann, ob das nicht doch genügt. Vermutlich wird (und bleibt) es das.

Ich danke euch herzlich für die Erklärungen.
 

axifist

Well-known member
Mitglied seit
Nov 14, 2013
Beiträge
1.713
ich hab noch nicht ganz durchgeblickt, was die einzelnen Einträge bedeuten
Hab es editiert, steht jetzt dran. Steht auch in den jeweils verlinkten Seiten.
aber wenn du da ein bisschen rechnen würdest, würde sich der Speicherbedarf ganz schnell reduzieren lassen. Jedes einzelne Datenpaket hinschreiben ist in der Abarbeitung dann sehr schnell, weil keine Rechenoperationen notwendig sind, kostet aber irre viel Speicher.
Das hab ich nicht hinbekommen und weiß auch heute noch nicht, wie ich das machen würde. Ich hab mir während des Projekts ja eigentlich erst das Programmieren draufgearbeitet. Ich kann quasi nur das, was ich eingebaut hab ;)

[...] sogar als const, also Konstante [...]
Stimmt, klar, danke! Da merkt man, dass ich den Teil noch ganz am Anfang geschrieben hab, wo ich von Konstanten noch gar nichts wusste. Mittlerweile ist alles als Konstante drin, was eben verwendet wird und sich nie ändert.

Sollte aber nicht vorkommen, wenn man gut programmiert ;)
😜 Das ist halt immer das Ding!

Wenn ich an dem Ding mal weiterarbeite, will ich das auf jeden Fall nochmal angehen. Aber eigentlich sollte ich es erstmal nutzen, bisher ist es noch immer ein reines Bastelobjekt, das noch keine Bühne, nicht mal einen Proberaum gesehen hat.

Für die SysEx-Sachen scheint Arduino geeignet zu sein, sofern die Leistung genügt.
Definitiv! Da würde ein Arduino Uno für wenige Euronen völlig reichen.
 

Andy

Well-known member
Axe-Fest 2017 Teilnehmer
Axe-Fest 2020 Online Teilnehmer
Mitglied seit
Okt 21, 2012
Beiträge
6.824
Kein Problem. Dafür dass du dir dabei das Programmieren beigebracht hast, ist es doch gut. Wie gesagt, wenn der Fokus auf Geschwindigkeit liegt kann man das auch so machen. Kostet dann halt Speicher. Es hängt auch davon ab was man alles machen will. Wenn ich weiss ich nutze 10 oder 20 Befehle dann ist es keine Schande alles hinzuschreiben, wie es sein soll. Wenn man mehr haben will geht man ab einem bestimmten Punkt einen anderen Weg.

Konkret, wenn ich 12 Taster hätte und alle haben eine feste Zuweisung was sie tun sollen, würd ich das vielleicht auch so machen. Wenn ich die 12 Taster konfigurierbar mache und es werden auf einmal 200 Befehle draus, würde ich die Datenpakete "zusammenbauen". Der Punkt ab dem man vom einen zum anderen wechselt ist dann irgendwo dazwischen.

Bei 1024 Werten würde man aber nicht mehr alles hinschreiben. Und dynamische Änderungen, also nicht etwas einstellen, sondern bspw. ein Wha würde ich nicht über Syx schicken. Ich glaub da ist man dann schon wieder in der Nullposition bis die letzte Nachricht in Richtung Voll-Durchtreten am Gerät ankommt :)
 
Zuletzt bearbeitet:
Oben