Doberenz_gewinnus_ Arrays mit LINQ initialisieren5In dieser Blogserie möchten wir Ihnen eine kleine Auswahl aus den „Rezepten“ unserer neuen Titel Visual Basic 2015 und Visual C# 2015 präsentieren, und Ihnen als künftigen oder fortgeschrittenen VB- oder C#-Programmierer, einen Vorgeschmack darauf geben, wie Sie einzelne Komponenten für das Microsoft .NET Framework programmieren können. Heute geht es darum, Pegeldiagramm aufzeichnen. Lesen Sie auch die Rezepte  Arrays mit LINQ initialisieren, Echte ZIP-Dateien erstellen, Eine Aktionsabfrage aufrufen und Rechner für komplexe Zahlen. Heute geht es darum, ein Pegeldiagramm aufzuzeichnen.

Los gehts mit dem Visual Basic-Rezept, Pegeldiagramm aufzeichnen:

So richtig professionell wirkt ein Sound Recorder erst dann, wenn man den Pegelverlauf in Echtzeit auf einem Diagramm verfolgen kann. Diese, u.a. auch bei medizinischen Überwachungsgeräten übliche Darstellung wollen wir anhand einer Programmerweiterung des Vorgängerbeispiels

  • Fehler: Referenz nicht gefunden Fehler: Referenz nicht gefunden

demonstrieren.

Oberfläche

Ergänzen Sie das vorhandene Formular durch eine PaintBox und ein repräsentatives Label. Gestalten Sie die Oberfläche entsprechend der Laufzeitabbildung am Schluss dieses Praxisbeispiels.

Quellcode-Ergänzungen

Um wegen der zahlreichen Ergänzungen die Übersicht nicht zu verlieren, empfiehlt es sich insbesondere bei diesem Beispiel, den kompletten Code von den Buchdaten in die Entwicklungsumgebung zu laden und in seiner Gesamtheit zu analysieren.

...
using System.Drawing;                     // für die Grafikoperationen      

Zunächst sind einige globale Variablen hinzuzufügen:

private Graphics g = null;               // Graphics-Objekt für PaintBox
private byte[] BA = null;                 // Puffer-Array für die Pegelwerte
private float xmax,                       // Breite des Diagramms (Pixel)
                y0,                       // y-Koordinate der Diagramm-Mittellinie (Pixel)
                 v;                       // Streckungsfaktor der Amplitude
private int i = 0;                       // Zähler für Timer-Ticks ab Start
private DateTime dStart;                 // Startzeit der Aufnahme
private int msec = 0;                     // seit Start abgelaufene Zeit in Millisekunden
Die Pegelabfrage soll alle 10 ms stattfinden, die Länge des aufzuzeichnenden
Diagramms be­trägt 1 Minute:
private const int iv = 10;               // Timer-Intervall (ms)
private const int tmax = 60000;           // Länge der Zeitachse (ms)
Initialisieren der globalen Variablen:
private void Form1_Load(object sender, EventArgs e)
{
     ...
     g = pictureBox1.();
        iv = timer1.Interval;               // Voreinstellung 10ms
Die erforderliche Größe des Puffer-Arrays ergibt sich aus obigen Konstanten:
     BA = new Byte[tmax / iv];         // 6000 gespeicherte Werte (ca. 60sek)
Abmessungen des Diagramms:
     xmax = pictureBox1.Width;
     y0 = pictureBox1.Height / 2;
Der vom MCI gelieferte Maximalpegel beträgt 127. Da bei Vollaussteuerung der 
obere bzw. untere Rand der PaintBox nicht ganz erreicht werden soll, bauen 
wir in den Streckungsfaktor eine kleine Reserve ein:
     v = y0 / 140;
     ...
}
Eine Ergänzung im Click-Eventhandler des Buttons:
private void button1_Click(object sender, EventArgs e)
{
     if (button1.Text == "START")
     {
Nach dem Klick auf START beginnt eine neue Aufnahme, d.h., die Startzeit wird 
zugewiesen, der Zähler zurückgesetzt und das alte Diagramm gelöscht:
               dStart = DateTime.Now;
               i = 0;
               pictureBox1.Refresh();
     }
     ...
}

 

Bei einem Timerintervall von 10ms und 6000 Durchläufen ist die Endzeit in der Regel deutlich
länger als die erwarteten 60 Sekunden[1]. Wollen Sie eine exakte Anzeige der seit Start abgelaufenen Sekunden hinzufügen, so kommen Sie um ein TimeSpan-Objekt nicht herum.

Der Aufbau der Grafik erfolgt synchron mit der Pegelabfrage. Der vorhandene Tick-Eventhandler des Timers ist deshalb wie folgt zu ergänzen:

private void timer1_Tick(object sender, EventArgs e)
{
   ...
Die folgende Zeile ist kein Fehler, denn während die Soundaufnahme läuft, 
hat der Button die Beschriftung STOPP:
     if (button1.Text == "STOPP")
     {
         TimeSpan ts = new TimeSpan(DateTime.Now.Ticks - dStart.Ticks);
         int sec = Convert.ToInt32(ts.TotalSeconds);
Anzeige der abgelaufenen Zeit (Sekunden):
               label1.Text = sec.ToString();
Abbruchkriterium bei 60sek:
               if (sec == tmax / 1000)
               {
Speichern am Schluss:
                   saveFile();
                   button1.Text = "START";
                   return;
                }
Um die x-Koordinate exakt berechnen zu können, muss die Auflösung zumindest 
im Millisekundenbereich liegen:
               msec = Convert.ToInt32(ts.TotalMilliseconds);
               float x = msec * xmax / tmax;
Pegel in Diagramm eintragen:
               g.DrawLine(new Pen(Color.Red, 0.1F), x, y0 - level * v, x, y0 + level * v);
Abspeichern des Pegels im Puffer-Array:
               BA[i] = level;
               i++;
     }
}

Damit nach einem Verdecken des Formulars das Diagramm nicht auf Nimmerwiedersehen verschwin­det, muss es im Paint-Eventhandler der PaintBox komplett neu gezeichnet werden:

private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
     Graphics g = e.Graphics;
Abstand zwischen zwei gespeicherten Werten auf Position i:
     float dx = xmax / tmax * msec / i;
Der Pufferspeicher wird bis zur Position i ausgelesen und angezeigt:
     for (int j = 0; j < i; j++)
         g.DrawLine(new Pen(Color.Red, 0.1F),j*dx, y0 - BA[j] * v, j*dx, y0 + BA[j] * v);
}

Test

Jetzt können Sie unmittelbar während der Sound-Aufnahme den kontinuierlichen Pegelverlauf „live“ beobachten.

Abb. Einheitskreis aus dem Fachbuch Visual Basic 2015 von Walter Doberenz und Thomas Gewinnus, Carl Hanser Verlag, München 2015.

Abb. Einheitskreis aus dem Fachbuch Visual Basic 2015 von Walter Doberenz und Thomas Gewinnus, Carl Hanser Verlag, München 2015.

Bei STOPP, bzw. nach Ablauf von 60 Sekunden, wird die Datei Test.wav automatisch im Projektverzeichnis abgelegt.

[1]     Windows ist kein Echtzeit-Betriebssystem und Prozesse mit höherer Priorität können solche mit niedrigerer Priorität ausbremsen.