Audio Pegel-Anzeige mit 8×8 LED Matrix

Titelbild zum Artikel 8x8 LED Matrix

Wenn Sie mit dem Raspberry Pi eine Audio-Anwendung realisieren möchten, finden Sie mit der BASS-Bibliothek von un4seen.com eine praktische Unterstützung. In Kombination mit der TMS-Komponente für eine 8×8-Matrix lässt sich daraus eine interessante Pegel-Anzeige bauen.

Für meinen Mediaplayer Tactbox habe ich schon eine Audio-Spektrum-Anzeige in der Software programmiert. Die Software dafür ist mit Lazarus entwickelt, deshalb ist auch die Visualisierung auf dem 7“ Display gut realisierbar.

Mediaplayer für Raspberry Pi
Mediaplayer TACTbox+ mit Lautsprechern – ohne Pegelanzeige

Nach dem Ausprobieren und Testen einer I2C-LED-Matrix, wollte ich diese Technik auch für die Pegel-Anzeige verwenden.

In meinem Fall passt alles perfekt zusammen:

  • Lazarus als Programmierbasis für das gesamte Projekt
  • BASS-Bibliothek zum Abspielen von MP3-Files oder Internet-Radio
  • BASS-Funktion für Pegel-Anzeige und Spektrum ist vorhanden
  • Ansteuerung der LED-Matrix über die Lazarus Komponente von TMS-Software

Wenn Sie eine ähnliche Voraussetzung haben oder selber einen eigenen Mediaplayer mit Pegel-Anzeige entwickeln möchten, erfahren Sie in diesem Artikel, wie das realisierbar ist.

Mir schwebte dabei die nostalgische 80er Variante Knight Rider aus dem Titelbild vor. Dafür konnte ich aber meine Tochter nicht begeistern! Ist ja auch kein Wunder, sprechende Computer, wie KITT, sind für Sie ja heute ziemlich normal und nichts Besonderes mehr.

Eine MP3-Datei mit BASS abspielen

Bevor Sie starten, fügen Sie Ihrem Lazarus-Projekt die BASS-Library hinzu. Das funktioniert über den Projektinspektor. In der Unit-Anweisung brauchen Sie nur die lazdynamic_bass einzufügen.

uses
LCLIntf, LCLType, LMessages, Messages, SysUtils, Classes, ...lazdynamic_bass;

Als nächstes müssen die Funktionen von BASS geladen werden. Das bringen wir am besten in der FormCreate-Prozedure unter.

Jedes Betriebssystem braucht dafür eine passende Datei. Für den Raspberry Pi mit Raspbian heißt diese Datei libbass.so. Sie finden Sie in der Beispiel App am Ende des Artikels unter Downloads.

Ich kopiere die Datei am liebsten in das Projektverzeichnis. Der Befehl zum Laden sieht dann so aus:

 Load_BASSDLL(ExtractFilePath(paramstr(0))+'libbass.so');

Dann wird BASS initalisiert und falls es nicht klappt eine Meldung ausgegeben:

 if not BASS_Init(-1, 44100, 0, Handle, nil) then
  MessageDlg('Achtung','BassInitError',mtWarning,[mbOk],0);

Die Parameter bedeuten:

  • -1: das Standard-Ausgabegerät
  • 44100 Hz,
  • 0: Stereo, 16 bit

Jetzt können wir mit dem Abspielen eines MP3-Files beginnen.

channel:=BASS_StreamCreateFile(FALSE,
         PChar(Filename),0,0,0 {$IFDEF UNICODE} or BASS_UNICODE {$ENDIF});
BASS_ChannelPlay(channel,false);

Vor- und Zurückspulen

Zu jedem MP3 Player gehört auch die Möglichkeit vor- und zurückzuspulen. Für uns ist das noch wichtiger, denn um die Pegel-Anzeige zu testen, springen wir zwischen lauten und leisen Stellen hin und her.

Für die Umsetzung brauchen wir irgendeine Komponente, die sich für diesen Zweck eignet. In der Standardpalette passt dafür beispielsweise eine Trackbar.

Bei einem Klick auf die Trackbar wird die Position in der Stream-Ausgabe von BASS neu positioniert. BASS stellt dafür die Funktion ChannelSetPosition bereit. Die Position wird dabei als Double erwartet. Aber auch für diese Umrechnung gibt es schon eine Funktion mit ChannelSeconds2Bytes.

procedure TFormPlayer.TrackBar1Click(Sender: TObject);
begin
  BASS_ChannelSetPosition(channel,
    BASS_ChannelSeconds2Bytes(channel,TrackBar1.Position),BASS_POS_BYTE);
end;

Damit auch die Auflösung der Trackbar korrekt ist, müssen wir den maximalen Wert der Trackbar richtig setzen. Das machen wir beim Öffnen einer MP3-Datei.

TrackBar1.Max:=Trunc(BASS_ChannelBytes2Seconds(
channel,BASS_ChannelGetLength(channel,BASS_POS_BYTE)));
TrackBar1.Position:=0;

Eine Pegel-Anzeige ermitteln

BASS bietet zur Ermittlung des aktuellen Pegels die Funktion ChannelGetLevel an. Sie liefert als Ergebnis einen DWORD-Wert (32 bit) zurück.

var level : DWord;...
level:=BASS_ChannelGetLevel(channel);

In dem Wert sind der linke (erste 16 Bit) und rechte (nächste 16 Bit) Audio-Kanal kodiert. Mit den Pascal-Funtionen LoWord() und HiWord() lassen sich die Kanäle wieder trennen.

links:=LoWord(level); //Linker Kanal
rechts:=HiWord(level); //Rechter Kanal

Die beiden Pegel-Werte liegen damit im Bereich von 0 bis 32768. Für die Umrechnung auf unsere 8×8 Matrix kümmert sich deswegen am besten eine eigene Funktion CalcLevel.

function CalcLevel(level : DWord) : integer;
begin
result:=Round(level/32768*9);
if result>8 then result:=8;
end;

Sie sorgt dafür, dass alle 9 Möglichkeiten (LED 1 bis 8 und alle LED aus) gleichmäßig verteilt sind.

Mehr zur Funktionen finden Sie in der Onlinedokumentation von Un4seen.com.

Die Beispiel App Miniplayer

Jetzt wird es aber Zeit, die Funktionen in einer Beispiel App zusammenzuführen und auszutesten.Wie man die 8×8 Matrix am Raspberry Pi anschließt und mit der TMS-Komponente programmiert, habe ich im Artikel „Artikel 8×8 LED-Matrix mit Raspberry und Lazarus“ beschrieben. Bitte schauen Sie sich diesen vorher an, wenn Sie noch keine Erfahrungen mit Lazarus haben.Wir nutzen in diesem Beispiel auch den Vorteil von Lazarus und erstellen von der 8×8 Matrix eine visuelle Kopie für den Bildschirm.

Beispiel App

Beispiel App im Lazarus Desktop und in der Laufzeitversion[/caption]

 In der App kümmern sich zwei Timer um die Aktualisierung der Anzeigen. Timer2 sorgt dafür, dass der Fortschritt des MP3 Songs entsprechend der Abspieldauer dargestellt wird. Das passiert jede Sekunde.In der Ereignisroutine von Timer1 wird die LED-Matrix mit dem Audio-Pegel akualisiert. Das muss etwas schneller erfolgen. Das Intervall liegt hier bei 200 ms.

Aktualisierung
Aktualisierung mit 2 Timern

 Die Visualisierung zu realisieren ist mit den obigen Befehlen einfach möglich. Das verleitet zum Experimentieren. Schauen wir uns nun an, was bei meinen Experimenten für Ergebnisse herauskamen. 

Klassische Balkenanzeige

tms2_typ_balken
Beispiel für die Balken-Darstellung

Die klassische Balkenanzeige für den linken und rechten Stereo-Kanal darf nicht fehlen. Deshalb geht es gleich mir ihr los.Die notwendigen Funktionen sind oben bereits anhand dieses Beispiels beschrieben.Was noch fehlt, ist die Ausgabe auf der 8×8 LED Matrix.Über das Array Peak (0 .. 7) werden die Balkenhöhen pro Matrix Spalte hinterlegt.  Der linke Balken ist Peak[1] und Peak[2], der rechte Peak[6] und Peak[7].Über eine Schleife werden die LED’s der Balkenhöhe entsprechend eingeschaltet.

level:=BASS_ChannelGetLevel(channel);

Peak[1]:=CalcLevel(LoWord(level)); //Linker Kanal
Peak[2]:=Peak[1];

Peak[5]:=CalcLevel(HiWord(level)); //Rechter Kanal
Peak[6]:=Peak[1];

//Matrix zeichnen - LEDs ein-/ausschalten
for x:=0 to 7 do
for y:=0 to 7 do
if y+1<=Peak[x] then SetPixel(x,7-y,true)
else SetPixel(x,7-y,false);
end;

KITT – Knight-Rider

Beispiel für die Knight-Rider-Variante
Beispiel für die Knight-Rider-Variante

Nachdem die KITT-Variante schon im Titelbild auftaucht, müssen Sie schon erfahren, wie sie umgesetzt werden kann.Ich habe einfach einen Kanal nach oben gezeichnet und einen nach unten.Das ganze wird auf 3 Spalten aufgeteilt, wobei die äußeren identisch sind und nur von der Höhe eine LED-Reihe kleiner gezeichnet oder beleuchtet werden. 

level:=BASS_ChannelGetLevel(channel);

links:=CalcLevel(LoWord(level)) div 2; //Linker Kanal
rechts:=CalcLevel(HiWord(level)) div 2; //Rechter Kanal

Peak[3]:=links;

//Matrix zeichnen - LEDs ein-/ausschalten
//paint matrix - switch LEDs on or off
for y:=1 to 4 do
begin
//Linker Kanal nach oben
if y<=links then onoff:=true
else onoff:=false;
SetPixel(3,3+y,onoff);
SetPixel(4,3+y,onoff);

//Rechter Kanal nach unten
if y<=rechts then onoff:=true
else onoff:=false;
SetPixel(3,5-y,onoff);
SetPixel(4,5-y,onoff);

if y<=links-1 then onoff:=true
else onoff:=false;
SetPixel(0,3+y,onoff);
SetPixel(1,3+y,onoff);
SetPixel(6,3+y,onoff);
SetPixel(7,3+y,onoff);

if y<=rechts-1 then onoff:=true
else onoff:=false;
SetPixel(0,5-y,onoff);
SetPixel(1,5-y,onoff);
SetPixel(6,5-y,onoff);
SetPixel(7,5-y,onoff);
end;

 Verlauf

tms2_typ_verlauf
Beispiel für einen Pegel-Verlauf

Verschiebt man die Werte der Matrix nach links und blendet nur ganz rechts jeweils den Pegel ein, kann man einen schönen Laufeffekt erreichen.Dafür wird wieder das Array Peak verwendet. Die rechte Spalte Peak[7] ist dabei immer der aktuelle Pegel.Matrix-Bild nach links verschieben:

for x:=0 to 6 do
Peak[x]:=Peak[x+1];

Peak[7]:=0;

 Dann rechts den aktuellen Pegel einzeichnen:

level:=BASS_ChannelGetLevel(channel);

Peak[7]:=(CalcLevel(LoWord(level))+CalcLevel(HiWord(level))) div 2;
//Zeichnen
for y:=0 to 7 do
if y+1<=Peak[7] then SetPixel(7,7-y,true)
else SetPixel(7,7-y,false);

Kreis

tms2_typ_kreis
Beispiel für ein Matrix-Bild

Interessant kann es auch sein, nur verschiedene, selbst designte LED-Bilder, anzuzeigen. Dafür eignet sich ebenfalls die Beispiel App meines vorherigen Artikels zur 8×8 Matrix.In diesem Beispiel ist das ein Kreis, der je nach Pegel immer größer wird.Die Animation habe ich dem dem Tool im Artikel erstellt und gespeichert. Die erstellte Textdatei (animation.txt) wird in diesem Beispiel als Konstante definiert. 

const

CircleAnimation : array[0..7] of string = (
'0000000000000000000000000000000000000000000000000000000000000000',
'0000000000000000000000000001100000011000000000000000000000000000',
'0000000000000000000110000011110000111100000110000000000000000000',
'0000000000011000001111000111111001111110001111000001100000000000',
'0011110001111110111111111111111111111111111111110111111000111100',
'1111111111111111111111111110011111100111111111111111111111111111',
'1111111111111111111001111100001111000011111001111111111111111111',
'0111111010111101110000111100001111000011110000111011110101111110');

Im Timer-Ereignis wird das entsprechende Bild je nach Höhe des Pegels ausgewählt und mit der ebenfalls geklauten Funktion StrToMatrix8x8 aus dem Artikel angezeigt.

level:=BASS_ChannelGetLevel(channel);

//Linker Kanal + rechter Kanal
level:=(CalcLevel(LoWord(level))+CalcLevel(HiWord(level))) div 2;
level:=Round(level/9*High(CircleAnimation));
if level>High(CircleAnimation) then level:=High(CircleAnimation);

StrToMatrix8x8(CircleAnimation[level]);

Mit dieser Variante lassen sich auch leicht sehr individuelle Pegel-Anzeigen basteln.  Sie sehen, es ist ziemlich einfach mit BASS und der TMS-Komponente, eine Pegel-Anzeige in den unterschiedlichsten Varianten zu realisieren.

Jetzt sind Sie dran:

  1. Beispiel App herunterladen und
  2. selber experimentieren.

 Los geht’s.  

Download

symbol_download

Quellcode der Beispiel-App Pegel-Anzeige
(mit direkt lauffähiger Anwendung miniplayer)
Entpacken mit tar xfvz DE.tar.gz

Links

BASS-Bibliothek für Lazarus: http://www.un4seen.com/

Webseite von TMS-Software: https://www.tmssoftware.com/site/tmslclhwpack.asp

TMS Package auf github: https://github.com/tmssoftware/TMS-LCL-HW-Pack-for-Raspberry-Pi

Meine Blog-Artikel zu TACTbox+, TACTbox – Mein Mediaplayer und 8×8 LED-Matrix mit Raspberry und Lazarus


Read this article in English

Dieser Beitrag hat 2 Kommentare

Kommentar verfassen