Skip to content
On this page

Versuch 8: Sound Level Meter

In dieser Übung wird ein Sound Level Meter erstellt. Exceptions und Threads sind neue Komponentent. Das Arbeiten mit Arrays (und Multibuffering) wird nochmals geübt.

Clonen Sie die Vorlage und erstellen Sie folgendes GUI:

bash
git@gitlab.fhnw.ch:oop/oop2-versuche/v07-sound-level-meter.git

Importieren Sie das Projekt in Eclipse.

Aufbau

Sound Level Meter

Screenshot

Auf der Linken Seite befindet sich ein PlotPanel, welches aktuelle Sound Sampels plottet. Die Klasse ist bereits implementiert und steht Ihnen für das GUI zur Verfügung.

Auf der rechten Seite befindet isch ein GaugePanel, welches den Schallpegel (RMS) in dB anzeigt (Hierbei entspricht das obere Ende 0 dB und das untere Ende 60 dB).

Audio Samples

Audio Samples werden vom System als byte [] produziert. In userem Fall besteht ein Sample aus einem short und ist damit 2 Byte lang. Je nach Architektur (Big Endian oder Little Endian) wird ein short aus zwei Byte wieder zusammengesetztt.

Hier das Code Snippet, welches aus dem Byte Array einen short und dannach einen double generiert.

java
for (int i = 0; i < bytesRead / 2; i++) {
        if (i >= len) {
            break;
        }

        byte b1 = byteBuffer[2 * i];
        byte b2 = byteBuffer[2 * i + 1];

        short s = (short) (((b1 & 0xFF) << 8) | (b2 & 0xFF));
        samples[i] = (double) s / (double) Short.MAX_VALUE;
}

Threads

Die Klasse AudioRecorder verfügt über eine Methode int read(double[] samples, int len).

Dieses Konzept ist sehr universell. Man übergibt der Methode ein Array, welches gefüllt wird. Der Parameter len gibt an, wieviele Samples gelesen werden sollen (maximal). Als return gibt die Methode die Anzahl samples zurück welche tatsächlich gelesen wurden. Es kann also sein, dass read weniger Samples lesen konnte als gewünscht.

Die Methode read ist blockierend. Das heisst, dass das Programm beim Aufruf so lange stehen bleibt, bis die Methode returned.

Blockierende Methoden blockieren die Ausführung von anderem Code. Unterdessen kann also nichts anderes im Programm getätigt werden.

Threads zu Hilfe

Threads sind Sturkturen, welche parallel Code ausführen können. Threads können mit start() gestartet werden und nehmen ein Runnable im Konstruktor entgegen.

Runnable ist ein Interface mit genau einer Methode: void run(). Diese Methode wird nach dem Starten des Threads ausgeführt.

Aufgabe 1: Blocking Call

Schauen Sie sich die Klasse AudioTest an und implementieren Sie die Methoden.

In der Main Methode soll im Loop ständig read aufgerufen werden. Warum ist das ein Problem, wenn wir mit GUI arbeiten?

Aufgabe 2: Model

Im Model finden Sie bereits ein Runnable, welches ständig Audio Samples liest.

java
private final Runnable audioRunnable = new Runnable() {

    @Override
    public void run() {
        for (;;) { // for ever (wie while(true))
            int len = recorder.readSamples(buffer, buffer.length);
            
            synchronized (lock) {
                currentLength = len;
                System.arraycopy(buffer, 0, samples, 0, len);
                rms = calculateRms(samples, len);
            }

            notifyObservers();
        }
    }
};

Für was könnte synchronized stehen? Wo finden Sie sonst noch synchronized?

  1. Implementieren Sie den Konstruktor von Model gemäss Beschreibung. Vergessen Sie nicht den Thread am Ende zu starten!
  2. Implementieren Sie auch die restlichen Methoden.

Aufgabe 3

Implementieren Sie die View gemäss Kommentaren im Source.