From 85b213e194fd404987ec2519cdef8cab55893b15 Mon Sep 17 00:00:00 2001 From: jake Date: Sun, 16 Nov 2025 15:56:29 +0000 Subject: [PATCH] no more pulseaudio, stream straight to /tmp/esp32_audio --- new_latency_test.py | 16 ++++++++++ service/esp32-audio.service | 34 ++++++--------------- wavemonitor | Bin 0 -> 70888 bytes wavemonitor.c | 57 ++++++++++++++++++++++++++++++++++++ 4 files changed, 82 insertions(+), 25 deletions(-) create mode 100644 new_latency_test.py create mode 100755 wavemonitor create mode 100644 wavemonitor.c diff --git a/new_latency_test.py b/new_latency_test.py new file mode 100644 index 0000000..d23edef --- /dev/null +++ b/new_latency_test.py @@ -0,0 +1,16 @@ +import numpy as np +import time + +SAMPLERATE = 16000 +CHANNELS = 1 +BLOCKSIZE = 256 + +with open("/tmp/esp32_audio", "rb") as f: + while True: + data = f.read(BLOCKSIZE * CHANNELS * 2) # 2 bytes per sample + if not data: + continue + audio = np.frombuffer(data, dtype=np.int16) + peak = np.max(np.abs(audio)) + bar = "#" * int(peak * 50 / 32767) + print(f"[{bar:<50}] {peak}", flush=True) diff --git a/service/esp32-audio.service b/service/esp32-audio.service index dbcccf6..3c15968 100644 --- a/service/esp32-audio.service +++ b/service/esp32-audio.service @@ -1,34 +1,18 @@ [Unit] -Description=ESP32 Serial Audio to PulseAudio -After=pulseaudio.service -Requires=pulseaudio.service -# Tie service start to the device node so it won’t launch until udev creates it -Requires=dev-ttyESP32_A.device -After=dev-ttyESP32_A.device +Description=ESP32 Audio Stream to FIFO +After=default.target +Requires=dev-ttyESP32.device +BindsTo=dev-ttyESP32.device [Service] -# Ensure PulseAudio runtime dir is set so pactl can connect -Environment=XDG_RUNTIME_DIR=/run/user/%U -Environment=LANG=en_US.UTF-8 -WorkingDirectory=/home/littlesophia/serial_audio_catcher +ExecStartPre=/bin/mkdir -p /tmp +ExecStartPre=/bin/rm -f /tmp/esp32_audio +ExecStartPre=/usr/bin/mkfifo /tmp/esp32_audio -# Wait until PulseAudio socket and serial device exist -ExecStartPre=/bin/sh -c 'until [ -S "$XDG_RUNTIME_DIR/pulse/native" ] && [ -e /dev/ttyESP32_A ]; do sleep 2; done' +ExecStart=/bin/sh -c '/home/radxa/serial_audio_catcher/serial_to_stdout /dev/ttyESP32 > /tmp/esp32_audio' -# Create the null sink before starting the pipeline -ExecStartPre=/usr/bin/pactl unload-module module-null-sink -ExecStartPre=/usr/bin/pactl load-module module-null-sink sink_name=esp32 rate=16000 channels=2 format=s16le - -# Run the pipeline through a shell so the pipe is interpreted correctly -ExecStart=/bin/sh -c '/home/littlesophia/serial_audio_catcher/serial_to_stdout /dev/ttyESP32_A | /usr/bin/pacat --raw --rate=16000 --channels=2 --format=s16le --device=esp32' - -# Restart automatically if it crashes or exits Restart=always -RestartSec=5 - -# Log output to journal -StandardOutput=journal -StandardError=journal +RestartSec=2 [Install] WantedBy=default.target diff --git a/wavemonitor b/wavemonitor new file mode 100755 index 0000000000000000000000000000000000000000..d8a29d53ac060377e2453c8743545b6d4e75831a GIT binary patch literal 70888 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 LKhg*!1YGzpf +#include +#include +#include +#include +#include + +#define BLOCK_FRAMES 256 // ~16 ms at 16 kHz +#define CHANNELS 1 // adjust to 2 if stereo +#define SAMPLE_RATE 16000 +#define BYTES_PER_SAMPLE 2 // s16le + +int main(void) { + int fd = open("/tmp/esp32_audio", O_RDONLY); + if (fd < 0) { + perror("open"); + return 1; + } + + size_t block_bytes = BLOCK_FRAMES * CHANNELS * BYTES_PER_SAMPLE; + int16_t *buffer = malloc(block_bytes); + + if (!buffer) { + perror("malloc"); + close(fd); + return 1; + } + + while (1) { + ssize_t n = read(fd, buffer, block_bytes); + if (n <= 0) { + usleep(1000); + continue; + } + + // compute peak + int16_t peak = 0; + for (ssize_t i = 0; i < n / 2; i++) { + int16_t sample = buffer[i]; + if (sample < 0) sample = -sample; + if (sample > peak) peak = sample; + } + + // scale to 50 chars + int bar_len = (peak * 50) / 32767; + char bar[51]; + memset(bar, '#', bar_len); + bar[bar_len] = '\0'; + + printf("[%s]\r", bar); + fflush(stdout); + } + + free(buffer); + close(fd); + return 0; +}