diff --git a/android-src/KV4PHT/app/src/main/java/com/vagell/kv4pht/radio/RadioAudioService.java b/android-src/KV4PHT/app/src/main/java/com/vagell/kv4pht/radio/RadioAudioService.java index a3e20b3..5ad13ed 100644 --- a/android-src/KV4PHT/app/src/main/java/com/vagell/kv4pht/radio/RadioAudioService.java +++ b/android-src/KV4PHT/app/src/main/java/com/vagell/kv4pht/radio/RadioAudioService.java @@ -131,7 +131,7 @@ public class RadioAudioService extends Service { // For transmitting audio to ESP32 / radio public static final int AUDIO_SAMPLE_RATE = 22050; public static final int channelConfig = AudioFormat.CHANNEL_IN_MONO; - public static final int audioFormat = AudioFormat.ENCODING_PCM_8BIT; + public static final int audioFormat = AudioFormat.ENCODING_PCM_16BIT; public static final int minBufferSize = AudioRecord.getMinBufferSize(AUDIO_SAMPLE_RATE, channelConfig, audioFormat) * 4; private UsbManager usbManager; private UsbDevice esp32Device; @@ -1258,6 +1258,18 @@ public static UsbSerialPort getUsbSerialPort() { return serialPort; } + public static byte[] convert8BitTo16Bit(byte[] pcm8) { + byte[] pcm16 = new byte[pcm8.length * 2]; // 2 bytes per 16-bit sample + for (int i = 0; i < pcm8.length; i++) { + int unsignedSample = pcm8[i] & 0xFF; // Convert to unsigned + short sample16 = (short)((unsignedSample) << 8); // Scale and shift + // Store as little-endian (least significant byte first) + pcm16[i * 2] = (byte)(sample16 & 0xFF); // LSB + pcm16[i * 2 + 1] = (byte)((sample16 >> 8) & 0xFF); // MSB + } + return pcm16; + } + private void handleESP32Data(byte[] data) { // Log.d("DEBUG", "Got bytes from ESP32: " + Arrays.toString(data)); /* try { @@ -1327,10 +1339,11 @@ private void handleESP32Data(byte[] data) { synchronized (audioTrack) { if (afskDemodulator != null) { // Avoid race condition at app start. // Play the audio. - audioTrack.write(data, 0, data.length); + byte[] pcm16 = convert8BitTo16Bit(data); + audioTrack.write(pcm16, 0, pcm16.length); // Add the audio samples to the AFSK demodulator. - float[] audioAsFloats = convertPCM8ToFloatArray(data); + float[] audioAsFloats = convertPCM8SignedToFloatArray(data); afskDemodulator.addSamples(audioAsFloats, audioAsFloats.length); } @@ -1351,7 +1364,8 @@ private void handleESP32Data(byte[] data) { audioTrack.play(); } synchronized (audioTrack) { - audioTrack.write(rxBytesPrebuffer, 0, PRE_BUFFER_SIZE); + byte[] pcm16 = convert8BitTo16Bit(rxBytesPrebuffer); + audioTrack.write(pcm16, 0, pcm16.length); } } @@ -1572,17 +1586,14 @@ private void handleParsedCommand(byte cmd, byte[] param) { } } - private float[] convertPCM8ToFloatArray(byte[] pcm8Data) { + private float[] convertPCM8SignedToFloatArray(byte[] pcm8Data) { // Create a float array of the same length as the input byte array float[] floatData = new float[pcm8Data.length]; // Iterate through the byte array and convert each sample for (int i = 0; i < pcm8Data.length; i++) { - // Convert unsigned 8-bit PCM to signed 8-bit value - int signedValue = (pcm8Data[i] & 0xFF) - 128; - // Normalize the signed 8-bit value to the range [-1.0, 1.0] - floatData[i] = signedValue / 128.0f; + floatData[i] = pcm8Data[i] / 128.0f; } return floatData; diff --git a/microcontroller-src/kv4p_ht_esp32_wroom_32/kv4p_ht_esp32_wroom_32.ino b/microcontroller-src/kv4p_ht_esp32_wroom_32/kv4p_ht_esp32_wroom_32.ino index 9584a5c..1e2b827 100644 --- a/microcontroller-src/kv4p_ht_esp32_wroom_32/kv4p_ht_esp32_wroom_32.ino +++ b/microcontroller-src/kv4p_ht_esp32_wroom_32/kv4p_ht_esp32_wroom_32.ino @@ -555,7 +555,7 @@ void loop() { // Apply attenuation to the sample int16_t sample = (int32_t)remove_dc(((2048 - (buffer16[i] & 0xfff)) << 4)) * attenuation >> 8; - buffer8[i] = (sample >> 8) + 128; // Unsigned PCM8 + buffer8[i] = (sample >> 8); // Signed } Serial.write(buffer8, samplesRead);