no more pulseaudio, stream straight to /tmp/esp32_audio

master
jake 2025-11-16 15:56:29 +00:00
parent 1545f60d78
commit 85b213e194
4 changed files with 82 additions and 25 deletions

16
new_latency_test.py Normal file
View File

@ -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)

View File

@ -1,34 +1,18 @@
[Unit] [Unit]
Description=ESP32 Serial Audio to PulseAudio Description=ESP32 Audio Stream to FIFO
After=pulseaudio.service After=default.target
Requires=pulseaudio.service Requires=dev-ttyESP32.device
# Tie service start to the device node so it wont launch until udev creates it BindsTo=dev-ttyESP32.device
Requires=dev-ttyESP32_A.device
After=dev-ttyESP32_A.device
[Service] [Service]
# Ensure PulseAudio runtime dir is set so pactl can connect ExecStartPre=/bin/mkdir -p /tmp
Environment=XDG_RUNTIME_DIR=/run/user/%U ExecStartPre=/bin/rm -f /tmp/esp32_audio
Environment=LANG=en_US.UTF-8 ExecStartPre=/usr/bin/mkfifo /tmp/esp32_audio
WorkingDirectory=/home/littlesophia/serial_audio_catcher
# Wait until PulseAudio socket and serial device exist ExecStart=/bin/sh -c '/home/radxa/serial_audio_catcher/serial_to_stdout /dev/ttyESP32 > /tmp/esp32_audio'
ExecStartPre=/bin/sh -c 'until [ -S "$XDG_RUNTIME_DIR/pulse/native" ] && [ -e /dev/ttyESP32_A ]; do sleep 2; done'
# 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 Restart=always
RestartSec=5 RestartSec=2
# Log output to journal
StandardOutput=journal
StandardError=journal
[Install] [Install]
WantedBy=default.target WantedBy=default.target

BIN
wavemonitor Executable file

Binary file not shown.

57
wavemonitor.c Normal file
View File

@ -0,0 +1,57 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#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;
}