diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index cef8f3d..397d997 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,6 +2,13 @@ + + + + + + + { + private AudioRecord recorder; + private final int sampleRateInHz = 44100; + private final int channelConfig = AudioFormat.CHANNEL_IN_MONO; + private final int audioFormat = AudioFormat.ENCODING_PCM_16BIT; + private int minPufferGroesseInBytes; + private int pufferGroesseInBytes; + private RingPuffer ringPuffer = new RingPuffer(10); + + AufnahmeTask() { + minPufferGroesseInBytes = AudioRecord.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat); + pufferGroesseInBytes = minPufferGroesseInBytes * 2; + recorder = new AudioRecord(MediaRecorder.AudioSource.MIC, sampleRateInHz, channelConfig, audioFormat, pufferGroesseInBytes); + +// textViewMinPufferGroesseInBytes.setText("" + minPufferGroesseInBytes); +// textViewPufferGroesseInBytes.setText("" + pufferGroesseInBytes); +// textViewAbtastrate.setText("" + recorder.getSampleRate()); +// textViewAnzahlKanaele.setText("" + recorder.getChannelCount()); + + logger.log("Puffergroeße: "+ minPufferGroesseInBytes + " " + pufferGroesseInBytes); + logger.log("Recorder (SR, CH): "+ recorder.getSampleRate() + " " + recorder.getChannelCount()); + + int anzahlBytesProAbtastwert; + String s; + switch (recorder.getAudioFormat()) { + case AudioFormat.ENCODING_PCM_8BIT: + s = "8 Bit PCM "; + anzahlBytesProAbtastwert = 1; + break; + case AudioFormat.ENCODING_PCM_16BIT: + s = "16 Bit PCM"; + anzahlBytesProAbtastwert = 2; + break; + case AudioFormat.ENCODING_PCM_FLOAT: + s = "Float PCM"; + anzahlBytesProAbtastwert = 4; + break; + default: + throw new IllegalArgumentException(); + } +// textViewAudioFormat.setText(s); + + switch (recorder.getChannelConfiguration()) { + case AudioFormat.CHANNEL_IN_MONO: + s = "Mono"; + break; + case AudioFormat.CHANNEL_IN_STEREO: + s = "Stereo"; + anzahlBytesProAbtastwert *= 2; + break; + case AudioFormat.CHANNEL_INVALID: + s = "ungültig"; + break; + default: + throw new IllegalArgumentException(); + } +// textViewKanalKonfiguration.setText(s); + logger.log("Konfiguration: "+ s); + + int pufferGroesseInAnzahlAbtastwerten = pufferGroesseInBytes / anzahlBytesProAbtastwert; + int pufferGroesseInMillisekunden = 1000 * pufferGroesseInAnzahlAbtastwerten / recorder.getSampleRate(); + +// textViewPufferGroesseInAnzahlAbtastwerte.setText("" + pufferGroesseInAnzahlAbtastwerten); +// textViewPufferGroesseInMillisekunden.setText("" + pufferGroesseInMillisekunden); + } + + @Override + protected Void doInBackground(Long... params) { + recorder.startRecording(); + short[] puffer = new short[pufferGroesseInBytes / 2]; + long lastTime = System.currentTimeMillis(); + float verarbeitungsrate = 0; + final int maxZaehlerZeitMessung = 10; + int zaehlerZeitMessung = 0; + int anzahlVerarbeitet = 0; + GleitenderMittelwert gleitenderMittelwert = new GleitenderMittelwert(0.3f); + + for (; ; ) { + if (aufnahmeTask.isCancelled()) { + break; + } else { + int n = recorder.read(puffer, 0, puffer.length); + Verarbeitungsergebnis ergebnis = verarbeiten(puffer, n); + anzahlVerarbeitet += n; + + zaehlerZeitMessung++; + if (zaehlerZeitMessung == maxZaehlerZeitMessung) { + long time = System.currentTimeMillis(); + long deltaTime = time - lastTime; + verarbeitungsrate = 1000.0f * anzahlVerarbeitet / deltaTime; + verarbeitungsrate = gleitenderMittelwert.mittel(verarbeitungsrate); + zaehlerZeitMessung = 0; + anzahlVerarbeitet = 0; + lastTime = time; + } + + float noiseLevel = gleitenderMittelwert.MittelwertPuffer(puffer); + + // logger.log("Noise Level:" + noiseLevel); + ergebnis.noiseLevel = noiseLevel; + ergebnis.verarbeitungsrate = (int) verarbeitungsrate; + publishProgress(ergebnis); + + try { + Thread.sleep(100); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + recorder.release(); + return null; + } + + private Verarbeitungsergebnis verarbeiten(short[] daten, int n) { + String status; + short maxAmp = -1; + if (n == AudioRecord.ERROR_INVALID_OPERATION) { + status = "ERROR_INVALID_OPERATION"; + } else if (n == AudioRecord.ERROR_BAD_VALUE) { + status = "ERROR_BAD_VALUE"; + } else { + status = "OK"; + short max = 0; + for (int i = 0; i < n; i++) { + if (daten[i] > max) { + max = daten[i]; + } + } + + ringPuffer.hinzufuegen(max); + maxAmp = ringPuffer.maximum(); + } + + return new Verarbeitungsergebnis(status, maxAmp, 0, 0); + } + + @Override + protected void onProgressUpdate(Verarbeitungsergebnis... progress) { + super.onProgressUpdate(progress); +// textViewMaxAmp.setText("" + progress[0].maxAmp); +// textViewVerarbeitungsrate.setText("" + progress[0].verarbeitungsrate); + logger.overwriteLastlog("VR, Max, NL:" + progress[0].verarbeitungsrate + ", " + progress[0].maxAmp + + ", " + progress[0].noiseLevel); + + if (progress[0].maxAmp >= Schwellwert_Alarm) { + Alarm = true; + } + } + } + + class Verarbeitungsergebnis { + String status; + short maxAmp; + int verarbeitungsrate; + float noiseLevel; + + Verarbeitungsergebnis(String status, short maxAmp, int verarbeitungsrate, float noiseLevel) { + this.status = status; + this.maxAmp = maxAmp; + this.verarbeitungsrate = verarbeitungsrate; + this.noiseLevel = noiseLevel; + } + } + + class RingPuffer { + private short[] puffer; + private final int laenge; + private int anzahlEnthaltenerDaten; + private int position; + + public RingPuffer(int n) { + laenge = n; + anzahlEnthaltenerDaten = 0; + position = 0; + puffer = new short[laenge]; + } + + public void hinzufuegen(short wert) { + puffer[position] = wert; + position++; + if (position >= laenge) { + position = 0; + } + if (anzahlEnthaltenerDaten < laenge) { + anzahlEnthaltenerDaten++; + } + } + + public void hinzufuegen(short[] daten) { + for (short d : daten) { + puffer[position] = d; + position++; + if (position >= laenge) { + position = 0; + } + } + if (anzahlEnthaltenerDaten < laenge) { + anzahlEnthaltenerDaten += daten.length; + if (anzahlEnthaltenerDaten >= laenge) { + anzahlEnthaltenerDaten = laenge; + } + } + } + + public short maximum() { + short max = 0; + for (int i = 0; i < anzahlEnthaltenerDaten; i++) { + if (puffer[i] > max) { + max = puffer[i]; + } + } + return max; + } + + public float mittelwert() { + float summe = 0; + for (int i = 0; i < anzahlEnthaltenerDaten; i++) { + summe += puffer[i]; + } + return summe / anzahlEnthaltenerDaten; + } + } + + class GleitenderMittelwert { + private final float wichtungNeuerWert; + private final float wichtungAlterWert; + private float mittelwert = 0; + private boolean istMittelwertGesetzt = false; + + GleitenderMittelwert(float wichtungNeuerWert) { + this.wichtungNeuerWert = wichtungNeuerWert; + this.wichtungAlterWert = 1 - this.wichtungNeuerWert; + } + + float MittelwertPuffer(short[] puffer) { + + for (int i = 0; i < puffer.length; i++) { + mittelwert = Math.abs(puffer[i]); + } + mittelwert = mittelwert/puffer.length; + + return mittelwert; + } + + float mittel(float wert) { + if (istMittelwertGesetzt) { + mittelwert = wert * wichtungNeuerWert + mittelwert * wichtungAlterWert; + } else { + mittelwert = wert; + istMittelwertGesetzt = true; + } + return mittelwert; + } + } +} diff --git a/app/src/main/java/com/example/ueberwachungssystem/Mikrofon.java b/app/src/main/java/com/example/ueberwachungssystem/Mikrofon.java index 510d104..2d3c815 100644 --- a/app/src/main/java/com/example/ueberwachungssystem/Mikrofon.java +++ b/app/src/main/java/com/example/ueberwachungssystem/Mikrofon.java @@ -1,4 +1,329 @@ package com.example.ueberwachungssystem; +import android.Manifest; +import android.app.Activity; +import android.content.Context; +import android.content.pm.PackageManager; +import android.media.AudioFormat; +import android.media.AudioRecord; +import android.media.MediaRecorder; +import android.os.AsyncTask; +import android.os.Bundle; +import android.os.Environment; +import android.widget.TextView; + +import androidx.core.app.ActivityCompat; +import androidx.core.content.ContextCompat; + +import com.example.ueberwachungssystem.logger.Logger; + +import java.io.File; + public class Mikrofon { + + Logger logger; + private static final int RECHTEANFORDERUNG_MIKROFON = 1; + + private Activity MainActivityForClass; + private AufnahmeTask aufnahmeTask; + public boolean Alarm = false; + public int Schwellwert_Alarm = 500; + + public void onCreate(Activity MainActivity, Logger MainLogger) { + MainActivityForClass = MainActivity; + logger = MainLogger; //Class uses the same logger as the MainActivity + logger.log(this.getClass().getSimpleName() + ".onCreate"); + + if (!istZugriffAufMikrofonErlaubt()) { + zugriffAufMikrofonAnfordern(); + } + } + + public void onResume() { + logger.log(this.getClass().getSimpleName() + ".onResume"); + if (!istZugriffAufMikrofonErlaubt()) { + zugriffAufMikrofonAnfordern(); + } + if (istZugriffAufMikrofonErlaubt()) { + aufnahmeTask = new AufnahmeTask(); + aufnahmeTask.execute(); + } + } + + public void onPause() { + logger.log(this.getClass().getSimpleName() + ".onPause"); + if(aufnahmeTask!=null) { + aufnahmeTask.cancel(true); + // aufnahmeTask = null; // if aufnahmeTask = null, break in for loop would not work (Nullpointer Exception) + } + } + + private boolean istZugriffAufMikrofonErlaubt() { + if (ContextCompat.checkSelfPermission(MainActivityForClass, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) { + logger.log("Zugriff auf Mikrofon ist verboten."); + return false; + } else { + logger.log("Zugriff auf Mikrofon ist erlaubt."); + return true; + } + } + private void zugriffAufMikrofonAnfordern() { + ActivityCompat.requestPermissions(MainActivityForClass, new String[]{Manifest.permission.RECORD_AUDIO}, RECHTEANFORDERUNG_MIKROFON); + } + + class AufnahmeTask extends AsyncTask { + private AudioRecord recorder; + private final int sampleRateInHz = 44100; + private final int channelConfig = AudioFormat.CHANNEL_IN_MONO; + private final int audioFormat = AudioFormat.ENCODING_PCM_16BIT; + private int minPufferGroesseInBytes; + private int pufferGroesseInBytes; + private RingPuffer ringPuffer = new RingPuffer(10); + + AufnahmeTask() { + minPufferGroesseInBytes = AudioRecord.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat); + pufferGroesseInBytes = minPufferGroesseInBytes * 2; + recorder = new AudioRecord(MediaRecorder.AudioSource.MIC, sampleRateInHz, channelConfig, audioFormat, pufferGroesseInBytes); + +// textViewMinPufferGroesseInBytes.setText("" + minPufferGroesseInBytes); +// textViewPufferGroesseInBytes.setText("" + pufferGroesseInBytes); +// textViewAbtastrate.setText("" + recorder.getSampleRate()); +// textViewAnzahlKanaele.setText("" + recorder.getChannelCount()); + + logger.log("Puffergroeße: "+ minPufferGroesseInBytes + " " + pufferGroesseInBytes); + logger.log("Recorder (SR, CH): "+ recorder.getSampleRate() + " " + recorder.getChannelCount()); + + int anzahlBytesProAbtastwert; + String s; + switch (recorder.getAudioFormat()) { + case AudioFormat.ENCODING_PCM_8BIT: + s = "8 Bit PCM "; + anzahlBytesProAbtastwert = 1; + break; + case AudioFormat.ENCODING_PCM_16BIT: + s = "16 Bit PCM"; + anzahlBytesProAbtastwert = 2; + break; + case AudioFormat.ENCODING_PCM_FLOAT: + s = "Float PCM"; + anzahlBytesProAbtastwert = 4; + break; + default: + throw new IllegalArgumentException(); + } +// textViewAudioFormat.setText(s); + + switch (recorder.getChannelConfiguration()) { + case AudioFormat.CHANNEL_IN_MONO: + s = "Mono"; + break; + case AudioFormat.CHANNEL_IN_STEREO: + s = "Stereo"; + anzahlBytesProAbtastwert *= 2; + break; + case AudioFormat.CHANNEL_INVALID: + s = "ungültig"; + break; + default: + throw new IllegalArgumentException(); + } +// textViewKanalKonfiguration.setText(s); + logger.log("Konfiguration: "+ s); + + int pufferGroesseInAnzahlAbtastwerten = pufferGroesseInBytes / anzahlBytesProAbtastwert; + int pufferGroesseInMillisekunden = 1000 * pufferGroesseInAnzahlAbtastwerten / recorder.getSampleRate(); + +// textViewPufferGroesseInAnzahlAbtastwerte.setText("" + pufferGroesseInAnzahlAbtastwerten); +// textViewPufferGroesseInMillisekunden.setText("" + pufferGroesseInMillisekunden); + } + + @Override + protected Void doInBackground(Long... params) { + recorder.startRecording(); + short[] puffer = new short[pufferGroesseInBytes / 2]; + long lastTime = System.currentTimeMillis(); + float verarbeitungsrate = 0; + final int maxZaehlerZeitMessung = 10; + int zaehlerZeitMessung = 0; + int anzahlVerarbeitet = 0; + GleitenderMittelwert gleitenderMittelwert = new GleitenderMittelwert(0.3f); + + for (; ; ) { + if (aufnahmeTask.isCancelled()) { + break; + } else { + int n = recorder.read(puffer, 0, puffer.length); + Verarbeitungsergebnis ergebnis = verarbeiten(puffer, n); + anzahlVerarbeitet += n; + + zaehlerZeitMessung++; + if (zaehlerZeitMessung == maxZaehlerZeitMessung) { + long time = System.currentTimeMillis(); + long deltaTime = time - lastTime; + verarbeitungsrate = 1000.0f * anzahlVerarbeitet / deltaTime; + verarbeitungsrate = gleitenderMittelwert.mittel(verarbeitungsrate); + zaehlerZeitMessung = 0; + anzahlVerarbeitet = 0; + lastTime = time; + } + + float noiseLevel = gleitenderMittelwert.MittelwertPuffer(puffer); + + // logger.log("Noise Level:" + noiseLevel); + ergebnis.noiseLevel = noiseLevel; + ergebnis.verarbeitungsrate = (int) verarbeitungsrate; + publishProgress(ergebnis); + + try { + Thread.sleep(100); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + recorder.release(); + return null; + } + + private Verarbeitungsergebnis verarbeiten(short[] daten, int n) { + String status; + short maxAmp = -1; + if (n == AudioRecord.ERROR_INVALID_OPERATION) { + status = "ERROR_INVALID_OPERATION"; + } else if (n == AudioRecord.ERROR_BAD_VALUE) { + status = "ERROR_BAD_VALUE"; + } else { + status = "OK"; + short max = 0; + for (int i = 0; i < n; i++) { + if (daten[i] > max) { + max = daten[i]; + } + } + + ringPuffer.hinzufuegen(max); + maxAmp = ringPuffer.maximum(); + } + + return new Verarbeitungsergebnis(status, maxAmp, 0, 0); + } + + @Override + protected void onProgressUpdate(Verarbeitungsergebnis... progress) { + super.onProgressUpdate(progress); +// textViewMaxAmp.setText("" + progress[0].maxAmp); +// textViewVerarbeitungsrate.setText("" + progress[0].verarbeitungsrate); + logger.overwriteLastlog("VR, Max, NL:" + progress[0].verarbeitungsrate + ", " + progress[0].maxAmp + + ", " + progress[0].noiseLevel); + + if (progress[0].maxAmp >= Schwellwert_Alarm) { + Alarm = true; + } + } + } + + class Verarbeitungsergebnis { + String status; + short maxAmp; + int verarbeitungsrate; + float noiseLevel; + + Verarbeitungsergebnis(String status, short maxAmp, int verarbeitungsrate, float noiseLevel) { + this.status = status; + this.maxAmp = maxAmp; + this.verarbeitungsrate = verarbeitungsrate; + this.noiseLevel = noiseLevel; + } + } + + class RingPuffer { + private short[] puffer; + private final int laenge; + private int anzahlEnthaltenerDaten; + private int position; + + public RingPuffer(int n) { + laenge = n; + anzahlEnthaltenerDaten = 0; + position = 0; + puffer = new short[laenge]; + } + + public void hinzufuegen(short wert) { + puffer[position] = wert; + position++; + if (position >= laenge) { + position = 0; + } + if (anzahlEnthaltenerDaten < laenge) { + anzahlEnthaltenerDaten++; + } + } + + public void hinzufuegen(short[] daten) { + for (short d : daten) { + puffer[position] = d; + position++; + if (position >= laenge) { + position = 0; + } + } + if (anzahlEnthaltenerDaten < laenge) { + anzahlEnthaltenerDaten += daten.length; + if (anzahlEnthaltenerDaten >= laenge) { + anzahlEnthaltenerDaten = laenge; + } + } + } + + public short maximum() { + short max = 0; + for (int i = 0; i < anzahlEnthaltenerDaten; i++) { + if (puffer[i] > max) { + max = puffer[i]; + } + } + return max; + } + + public float mittelwert() { + float summe = 0; + for (int i = 0; i < anzahlEnthaltenerDaten; i++) { + summe += puffer[i]; + } + return summe / anzahlEnthaltenerDaten; + } + } + + class GleitenderMittelwert { + private final float wichtungNeuerWert; + private final float wichtungAlterWert; + private float mittelwert = 0; + private boolean istMittelwertGesetzt = false; + + GleitenderMittelwert(float wichtungNeuerWert) { + this.wichtungNeuerWert = wichtungNeuerWert; + this.wichtungAlterWert = 1 - this.wichtungNeuerWert; + } + + float MittelwertPuffer(short[] puffer) { + + for (int i = 0; i < puffer.length; i++) { + mittelwert = Math.abs(puffer[i]); + } + mittelwert = mittelwert/puffer.length; + + return mittelwert; + } + + float mittel(float wert) { + if (istMittelwertGesetzt) { + mittelwert = wert * wichtungNeuerWert + mittelwert * wichtungAlterWert; + } else { + mittelwert = wert; + istMittelwertGesetzt = true; + } + return mittelwert; + } + } } diff --git a/app/src/main/java/com/example/ueberwachungssystem/logger/Logger.java b/app/src/main/java/com/example/ueberwachungssystem/logger/Logger.java new file mode 100644 index 0000000..07af51a --- /dev/null +++ b/app/src/main/java/com/example/ueberwachungssystem/logger/Logger.java @@ -0,0 +1,61 @@ +package com.example.ueberwachungssystem.logger; + +import android.util.Log; +import android.widget.TextView; + +import java.io.PrintWriter; +import java.io.StringWriter; + +public class Logger { + private TextView textView; + private StringBuffer sb = new StringBuffer(); + private String tag; + private int lengthOfLastLog = 0; + private boolean overwrite = false; + + public Logger(String tag, TextView textView, String logInitText) { + this.tag = tag; + this.textView = textView; + sb.append(logInitText); + } + + public void log(String s) { + overwrite = false; + Log.d(tag, s); + sb.append(s).append("\n"); + if (textView != null) { + textView.setText(sb.toString()); + } + } + + public void overwriteLastlog(String s) { + Log.d(tag, s); + lengthOfLastLog = s.length(); + if (overwrite) + { + sb.setLength(sb.length() - (lengthOfLastLog + 1)); + } + sb.append(s).append("\n"); + overwrite = true; + if (textView != null) { + textView.setText(sb.toString()); + } + } + + public void log(Exception e) { + StringWriter sw = new StringWriter(); + e.printStackTrace(new PrintWriter(sw)); + log(sw.toString()); + } + + public void clearLog() { + sb.setLength(0); + if (textView != null) { + textView.setText(""); + } + } + + public String getLoggedText() { + return sb.toString(); + } +} diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 17eab17..bea580a 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -7,12 +7,22 @@ tools:context=".MainActivity"> + + \ No newline at end of file