Encoder Master Out an Midi Controller

Hallo zusammen!

gerne möchte ich den Encoder Master Out (Peak Meter) per Script an meinen Midi Controller CMD MM-1 übertragen und durch die Peak Meter anzeigen lassen.

Mein momentaner Lösungsansatz sieht so aus.

[code]procedure OnLoad;
begin
EnableTimer(1);
end;

procedure OnTimer;
var
EncoderMasterOutLeft, EncoderMasterOutRight: Integer;
begin
// EncoderMasterOutLeft := ?;
// EncoderMasterOutRight := ?;
MidiOut(4, 180, 80, EncoderMasterOutLeft);
MidiOut(4, 180, 81, EncoderMasterOutRight);
end;

procedure OnUnload;
begin
DisableTimer;
MidiOut(4, 180, 80, 48);
MidiOut(4, 180, 81, 48);
end;[/code]

Hat jemand eine Idee, vieleicht Torben, wie ich das realisieren kann?

LG, Holger

Theoretisch hinzukriegen, aber im Moment etwas kompliziert… Welche Version verwendest du?

Davon ab, ich glaube 1ms ist etwas zu oft. Das wird dir deinen MIDI-Port lahmlegen. Probier es mal mit 50ms oder so, das sollte für das menschliche Auge reichen :wink:

Hallo Torben!

Habe die ms wie empfohlen auf 50 hochgesetzt. Verwende momentan die Version 5.1.5. :slight_smile:

Ok, dann steig doch bitte schonmal auf die v5.2 um, und insbesondere auf den aktuellen Snapshot 2948!

Ich schreibe später mehr, was sich damit anfangen lässt. Ganz trivial ist es noch immer nicht, damit damit sehr viel leichter :slight_smile:

Ok, bin auf den aktuellen Snapshot umgestiegen. Warte schon gespannt auf deine Beschreibungen. :slight_smile:

Ok, das Zauberwort heißt: ILevelInfo. Damit kannst du dir die aktuellen Pegelwerte für alle Kanäle (normalerweise 0 - links, 1 - rechts) als Array abholen.

Hier mal ein Beispielscript, das die Pegel, auf einen Bereich von 0 bis 127 skaliert, einmal pro Sekunde ins SystemLog schreibt (ich besitze kein passendes MIDI-Gerät):

var
  levelinfo: ILevelInfo;

procedure OnLoad;
begin
  levelinfo := Encoder.GetMixerLevelInfo(smMaster);
  EnableTimer(1000);
end;

procedure OnTimer;
var
  levels: TLevelArray;
  l, r: integer;
begin
  if levelinfo.GetLevels(levels) then begin
    l := trunc(levels[0] * 127);
    r := trunc(levels[1] * 127);
    SystemLog(IntToStr(l));
    SystemLog(IntToStr(r));
  end;
end;

So weit, so gut.

Wenn du das jetzt aber einfach an deinen Controller schickst, wirst du zwei Dinge bemerken:

  1. Der angezeigte Pegel bewegt sich im sehr niedrigen Bereich, obwohl er sehr viel höher sein müsste. Das liegt daran, dass ILevelInfo lineare Werte (zwischen 0 und 1) zurückgibt, ein Peakmeter aber üblicherweise eine logarithmische Skala hat - meistens sogar eine, die nur so “ungefähr” logarithmisch ist. mAirList bietet dafür zwei Funktionen: LinearToDB (wandelt linearen Wert in dBFS-Wert um) und DBToScale (wandelt den dB-Wert in einen Wert 0…1 um, der dann “ganz gut” der Anzeige auf einem Levelmeter entspricht).

  2. Der Pegel flackert wie wild hin und her. Das wiederum liegt darin begründet, dass ein echtes Peakmeter nicht einfach nur den aktuellen Wert anzeigt, sondern eine Rücklaufzeit besitzt: Wenn der Pegel schlagartig von voll auf null zurückgeht, dann geht die Anzeige nicht sofort auf 0 zurück, sondern sie läuft langsam zurück. Ich glaube, in der entsprechenden DIN-Norm ist von 1,7 Sekunden pro 20dB die Rede, oder etwas in der Größenordnung. mAirList verwendet 1,5s pro 20dB, wenn ich mich nicht irre. Ist also nicht ganz nach Norm, aber so ungefähr.

Was du also tun must:

  • Den in der letzten Runde angezeigten “aktuellen” Wert entsprechend der Rücklaufzeit anteilig verringern (bei 20dB pro 1,5 Sekunden und einem Intervall von 50ms also 20/30 = 0,667 dB abziehen).
  • Den neuen Wert vom ILevelInfo abholen und mit LinearToDB nach dBFS wandeln.
  • Von diesen beiden Werten das Maximum nehmen und als neuen “aktuellen Wert” merken.
  • Mit DBToScale in einen passenden Anzeigewert umwandeln.
  • Diesen dann mit 127 multiplizieren und an den MIDI-Controller schicken.
  • Das alles für beide Kanäle, links und rechts.

Für die “aktuellen” Werte machst du dir einfach zwei globale Variablen vom Typ single im Script, die du in OnLoad mit -200 o.ä. initialisierst.

Und jetzt viel Spaß beim Programmieren :slight_smile:

Ok, ich will nicht so sein, probier es mal so:

const
  INTERVAL = 50;

var
  levelinfo: ILevelInfo;
  cur_l, cur_r: single;

procedure OnLoad;
begin
  levelinfo := Encoder.GetMixerLevelInfo(smMaster);
  cur_l := MIN_DB;
  cur_r := MIN_DB;
  EnableTimer(INTERVAL);
end;

procedure OnTimer;
var
  levels: TLevelArray;
  new_l, new_r: single;
begin
  if levelinfo.GetLevels(levels) then begin

    if cur_l > MIN_DB then
      cur_l := cur_l - (20/(1500/INTERVAL));
    if cur_r > MIN_DB then
      cur_r := cur_r - (20/(1500/INTERVAL));

    new_l := LinearToDB(levels[0]);
    new_r := LinearToDB(levels[1]);

    if new_l > cur_l then
      cur_l := new_l;
    if new_r > cur_r then
      cur_r := new_r;

    MidiOut(4, 180, 80, trunc(127 * DBToScale(cur_l)));
    MidiOut(4, 180, 81, trunc(127 * DBToScale(cur_r)));
  end;
end;

MIN_DB ist eine Konstante, die den niedrigsten “sinnvollen” dB-Wert repräsentiert. Ich meine es ist -200.

Hallo Torben!

Danke für den Sourcecode und die tolle Erklärung.
Leider funktioniert es so noch nicht.
Ich vermute es liegt an der Range des Controllers. Die Range der Peak Meter am Controller ist 48 bis 63.

Dann musst du das entsprechend skalieren:

 MidiOut(4, 180, 80, 48 + trunc(15 * DBToScale(cur_l)));

(48 + 15 = 63)

Hier meine momentane Lösung. Sieht ganz gut aus.

[code]var
LevelInfo: ILevelInfo;
CurrentLevelLeft, CurrentLevelRight: Single;

procedure OnLoad;
begin
LevelInfo := Encoder.GetMixerLevelInfo(smMaster);
CurrentLevelLeft := -200;
CurrentLevelRight := -200;
EnableTimer(50);
end;

procedure OnTimer;
var
Levels: TLevelArray;
NewLevelLeft, NewLevelRight: Single;
begin
if LevelInfo.GetLevels(Levels) then
begin
if CurrentLevelLeft > -200 then CurrentLevelLeft := CurrentLevelLeft - 1.5;
if CurrentLevelRight > -200 then CurrentLevelRight := CurrentLevelRight - 1.5;
NewLevelLeft := Trunc(Levels[0] * 15);
NewLevelRight := Trunc(Levels[1] * 15);
if NewLevelLeft > CurrentLevelLeft then CurrentLevelLeft := NewLevelLeft;
if NewLevelRight > CurrentLevelRight then CurrentLevelRight := NewLevelRight;
MidiOut(4, 180, 80, 48 + Trunc(CurrentLevelLeft));
MidiOut(4, 180, 81, 48 + Trunc(CurrentLevelRight));
end;
end;

procedure OnUnload;
begin
DisableTimer;
end;[/code]

Super :slight_smile:

Das DisableTimer kannst du dir sparen, der Timer wird natürlich automatisch gestoppt, wenn das Script angehalten wird.