Appearance
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
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
?
- Implementieren Sie den Konstruktor von Model gemäss Beschreibung. Vergessen Sie nicht den Thread am Ende zu starten!
- Implementieren Sie auch die restlichen Methoden.
Aufgabe 3
Implementieren Sie die View gemäss Kommentaren im Source.