From c329ce9279a26810a79aa9d10e17f1718062613c Mon Sep 17 00:00:00 2001 From: pi Date: Tue, 21 Oct 2025 12:29:15 +0200 Subject: [PATCH 1/2] first try --- .../com/example/babyphone/MainActivity.java | 161 +++++++++++------- 1 file changed, 102 insertions(+), 59 deletions(-) diff --git a/app/src/main/java/com/example/babyphone/MainActivity.java b/app/src/main/java/com/example/babyphone/MainActivity.java index 1a5fe78..c7fc441 100644 --- a/app/src/main/java/com/example/babyphone/MainActivity.java +++ b/app/src/main/java/com/example/babyphone/MainActivity.java @@ -12,17 +12,19 @@ import android.widget.Toast; import androidx.appcompat.app.AppCompatActivity; import androidx.core.app.ActivityCompat; +import androidx.core.content.ContextCompat; +import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; import java.util.ArrayList; import java.util.Iterator; import java.util.List; -import com.example.babyphone.R; - public class MainActivity extends AppCompatActivity { private static final int SAMPLE_RATE = 44100; @@ -42,23 +44,23 @@ public class MainActivity extends AppCompatActivity { Button startStreamButton = findViewById(R.id.startStreamButton); startStreamButton.setOnClickListener(v -> { - if (!isStreaming) { - if (checkPermissions()) { - startStreaming(); - startStreamButton.setText("Stop Streaming"); + if (!isStreaming) { + if (checkPermissions()) { + startStreaming(); + startStreamButton.setText("Stop Streaming"); + } + } else { + stopStreaming(); + startStreamButton.setText("Start Streaming"); } - } else { - stopStreaming(); - startStreamButton.setText("Start Streaming"); - } - }); + }); } private boolean checkPermissions() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (checkSelfPermission(Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, - new String[]{Manifest.permission.RECORD_AUDIO}, 1); + new String[]{Manifest.permission.RECORD_AUDIO}, 1); return false; } } @@ -78,65 +80,81 @@ public class MainActivity extends AppCompatActivity { // Thread: Neue Clients annehmen clientListenerThread = new Thread(() -> { - while (isStreaming) { - try { - Socket client = serverSocket.accept(); - synchronized (clients) { - clients.add(client); - } - showToast("Neuer Client: " + client.getInetAddress()); - } catch (IOException e) { - if (isStreaming) { - showToast("Fehler beim Client-Connect: " + e.getMessage()); + while (isStreaming) { + try { + Socket client = serverSocket.accept(); + synchronized (clients) { + clients.add(client); + } + showToast("Neuer Client: " + client.getInetAddress()); + } catch (IOException e) { + if (isStreaming) { + showToast("Fehler beim Client-Connect: " + e.getMessage()); + } } } - } - }); + }); clientListenerThread.start(); - // Thread: Audio aufnehmen und an alle Clients senden + // Thread: Audio aufnehmen und an alle Clients senden (mit µ-law) recordingThread = new Thread(() -> { - int minBufferSize = AudioRecord.getMinBufferSize(SAMPLE_RATE, - AudioFormat.CHANNEL_IN_MONO, - AudioFormat.ENCODING_PCM_16BIT); + int minBufferSize = AudioRecord.getMinBufferSize(SAMPLE_RATE, + AudioFormat.CHANNEL_IN_MONO, + AudioFormat.ENCODING_PCM_16BIT); - AudioRecord recorder = new AudioRecord(MediaRecorder.AudioSource.MIC, - SAMPLE_RATE, - AudioFormat.CHANNEL_IN_MONO, - AudioFormat.ENCODING_PCM_16BIT, - minBufferSize); + if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) + != PackageManager.PERMISSION_GRANTED) { + ActivityCompat.requestPermissions(this, + new String[]{Manifest.permission.RECORD_AUDIO}, + 1001); + return; + } - byte[] buffer = new byte[minBufferSize]; + AudioRecord recorder = null; + try { + recorder = new AudioRecord( + MediaRecorder.AudioSource.MIC, + SAMPLE_RATE, + AudioFormat.CHANNEL_IN_MONO, + AudioFormat.ENCODING_PCM_16BIT, + minBufferSize + ); + } catch (SecurityException e) { + e.printStackTrace(); + return; + } - recorder.startRecording(); + byte[] pcmBuffer = new byte[minBufferSize]; + recorder.startRecording(); - while (isStreaming) { - int read = recorder.read(buffer, 0, buffer.length); - if (read > 0) { - synchronized (clients) { - Iterator iterator = clients.iterator(); - while (iterator.hasNext()) { - Socket client = iterator.next(); - try { - OutputStream out = client.getOutputStream(); - out.write(buffer, 0, read); - } catch (IOException e) { - // Verbindung verloren – entfernen + while (isStreaming) { + int read = recorder.read(pcmBuffer, 0, pcmBuffer.length); + if (read > 0) { + // PCM → µ-Law konvertieren + byte[] muLawData = pcm16ToMuLaw(pcmBuffer, read); + + synchronized (clients) { + Iterator iterator = clients.iterator(); + while (iterator.hasNext()) { + Socket client = iterator.next(); try { - client.close(); - } catch (IOException ignored) { + OutputStream out = client.getOutputStream(); + out.write(muLawData, 0, muLawData.length); + } catch (IOException e) { + try { + client.close(); + } catch (IOException ignored) {} + iterator.remove(); + showToast("Client getrennt"); } - iterator.remove(); - showToast("Client getrennt"); } } } } - } - recorder.stop(); - recorder.release(); - }); + recorder.stop(); + recorder.release(); + }); recordingThread.start(); } @@ -147,11 +165,10 @@ public class MainActivity extends AppCompatActivity { // Schließe alle Clients synchronized (clients) { for (Socket client : clients) { - try { - client.close(); - } catch (IOException ignored) { + try { + client.close(); + } catch (IOException ignored) {} } - } clients.clear(); } @@ -168,4 +185,30 @@ public class MainActivity extends AppCompatActivity { private void showToast(String msg) { runOnUiThread(() -> Toast.makeText(MainActivity.this, msg, Toast.LENGTH_SHORT).show()); } + + // ======================== + // PCM16 → µ-Law Umwandlung + // ======================== + private static byte linearToMuLawSample(short sample) { + final int MU = 255; + int sign = (sample >> 8) & 0x80; + if (sign != 0) sample = (short) -sample; + if (sample > 32635) sample = 32635; + sample = (short) (sample + 132); + int exponent = 7; + for (int expMask = 0x4000; (sample & expMask) == 0 && exponent > 0; exponent--, expMask >>= 1) {} + int mantissa = (sample >> ((exponent == 0) ? 4 : (exponent + 3))) & 0x0F; + int muLawByte = ~(sign | (exponent << 4) | mantissa); + return (byte) muLawByte; + } + + private static byte[] pcm16ToMuLaw(byte[] pcmData, int length) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(length / 2); + ByteBuffer bb = ByteBuffer.wrap(pcmData, 0, length).order(ByteOrder.LITTLE_ENDIAN); + while (bb.remaining() > 1) { + short sample = bb.getShort(); + baos.write(linearToMuLawSample(sample)); + } + return baos.toByteArray(); + } } -- 2.49.1 From abced7d982d85ba200973c2e5188a14c059c2720 Mon Sep 17 00:00:00 2001 From: pi Date: Tue, 21 Oct 2025 16:33:24 +0200 Subject: [PATCH 2/2] first try and works --- .../com/example/babyphone/MainActivity.java | 34 ++++++++++++++----- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/com/example/babyphone/MainActivity.java b/app/src/main/java/com/example/babyphone/MainActivity.java index c7fc441..f02c1ed 100644 --- a/app/src/main/java/com/example/babyphone/MainActivity.java +++ b/app/src/main/java/com/example/babyphone/MainActivity.java @@ -27,7 +27,7 @@ import java.util.List; public class MainActivity extends AppCompatActivity { - private static final int SAMPLE_RATE = 44100; + private static final int SAMPLE_RATE = 8000; private static final int PORT = 50005; private boolean isStreaming = false; private Thread recordingThread; @@ -97,8 +97,9 @@ public class MainActivity extends AppCompatActivity { clientListenerThread.start(); // Thread: Audio aufnehmen und an alle Clients senden (mit µ-law) +// Thread: Audio aufnehmen und an alle Clients senden (µ-Law, 8kHz) recordingThread = new Thread(() -> { - int minBufferSize = AudioRecord.getMinBufferSize(SAMPLE_RATE, + int minBufferSize = AudioRecord.getMinBufferSize(8000, // neue Sample-Rate AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT); @@ -114,7 +115,7 @@ public class MainActivity extends AppCompatActivity { try { recorder = new AudioRecord( MediaRecorder.AudioSource.MIC, - SAMPLE_RATE, + 44100, // Aufnahme weiterhin mit nativer Rate AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, minBufferSize @@ -127,23 +128,37 @@ public class MainActivity extends AppCompatActivity { byte[] pcmBuffer = new byte[minBufferSize]; recorder.startRecording(); + // Buffer für Resampling + short[] pcmSamples = new short[minBufferSize / 2]; + while (isStreaming) { int read = recorder.read(pcmBuffer, 0, pcmBuffer.length); if (read > 0) { - // PCM → µ-Law konvertieren - byte[] muLawData = pcm16ToMuLaw(pcmBuffer, read); + // PCM Byte-Array → short[] + ByteBuffer.wrap(pcmBuffer, 0, read).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(pcmSamples, 0, read / 2); + // Resample 44.1kHz -> 8kHz (einfaches Downsampling, z.B. jede 5.5. Probe nehmen) + short[] downsampled = new short[(int) (pcmSamples.length * 8000f / 44100f)]; + for (int i = 0; i < downsampled.length; i++) { + downsampled[i] = pcmSamples[(int) (i * 44100f / 8000f)]; + } + + // PCM → µ-Law + byte[] muLawData = new byte[downsampled.length]; + for (int i = 0; i < downsampled.length; i++) { + muLawData[i] = linearToMuLawSample(downsampled[i]); + } + + // Senden an alle Clients synchronized (clients) { Iterator iterator = clients.iterator(); while (iterator.hasNext()) { Socket client = iterator.next(); try { OutputStream out = client.getOutputStream(); - out.write(muLawData, 0, muLawData.length); + out.write(muLawData); } catch (IOException e) { - try { - client.close(); - } catch (IOException ignored) {} + try { client.close(); } catch (IOException ignored) {} iterator.remove(); showToast("Client getrennt"); } @@ -156,6 +171,7 @@ public class MainActivity extends AppCompatActivity { recorder.release(); }); + recordingThread.start(); } -- 2.49.1