110 lines
2.9 KiB
C
110 lines
2.9 KiB
C
// serial_to_wav.c
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <termios.h>
|
|
#include <signal.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
|
|
#define SERIAL_PORT "/dev/ttyESP32_A"
|
|
#define BAUD_RATE B1500000
|
|
#define RATE 16000
|
|
#define CHANNELS 2
|
|
#define BPS 16
|
|
|
|
static volatile sig_atomic_t stop_flag = 0;
|
|
void on_sigint(int sig) { (void)sig; stop_flag = 1; }
|
|
|
|
// Write RIFF/WAVE header placeholders; sizes will be patched on exit
|
|
void write_wav_header(FILE *f, uint32_t data_bytes) {
|
|
uint32_t byte_rate = RATE * CHANNELS * (BPS/8);
|
|
uint16_t block_align = CHANNELS * (BPS/8);
|
|
uint32_t riff_size = 36 + data_bytes;
|
|
|
|
fseek(f, 0, SEEK_SET);
|
|
fwrite("RIFF", 1, 4, f);
|
|
fwrite(&riff_size, 4, 1, f);
|
|
fwrite("WAVE", 1, 4, f);
|
|
|
|
fwrite("fmt ", 1, 4, f);
|
|
uint32_t fmt_size = 16;
|
|
fwrite(&fmt_size, 4, 1, f);
|
|
uint16_t audio_fmt = 1;
|
|
fwrite(&audio_fmt, 2, 1, f);
|
|
uint16_t ch = CHANNELS;
|
|
fwrite(&ch, 2, 1, f);
|
|
uint32_t sr = RATE;
|
|
fwrite(&sr, 4, 1, f);
|
|
fwrite(&byte_rate, 4, 1, f);
|
|
fwrite(&block_align, 2, 1, f);
|
|
uint16_t bits = BPS;
|
|
fwrite(&bits, 2, 1, f);
|
|
|
|
fwrite("data", 1, 4, f);
|
|
fwrite(&data_bytes, 4, 1, f);
|
|
}
|
|
|
|
// Configure serial port for raw 8N1, no flow control, blocking reads
|
|
int open_serial(const char *path) {
|
|
int fd = open(path, O_RDONLY | O_NOCTTY);
|
|
if (fd < 0) { perror("open"); return -1; }
|
|
|
|
struct termios tio;
|
|
if (tcgetattr(fd, &tio) != 0) { perror("tcgetattr"); close(fd); return -1; }
|
|
|
|
cfsetispeed(&tio, BAUD_RATE);
|
|
cfsetospeed(&tio, BAUD_RATE);
|
|
|
|
// Raw mode: disable all translations
|
|
tio.c_iflag &= ~(IGNBRK | BRKINT | ICRNL | INLCR | IXON | IXOFF | IXANY | PARMRK | INPCK | ISTRIP);
|
|
tio.c_oflag &= ~(OPOST);
|
|
tio.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN | ISIG);
|
|
tio.c_cflag &= ~(CSIZE | PARENB | CSTOPB | CRTSCTS);
|
|
tio.c_cflag |= (CS8 | CLOCAL | CREAD);
|
|
|
|
// Blocking read: return when at least N bytes available
|
|
tio.c_cc[VMIN] = 1; // at least 1 byte
|
|
tio.c_cc[VTIME] = 0; // no timeout
|
|
|
|
if (tcsetattr(fd, TCSANOW, &tio) != 0) { perror("tcsetattr"); close(fd); return -1; }
|
|
|
|
return fd;
|
|
}
|
|
|
|
int main() {
|
|
signal(SIGINT, on_sigint);
|
|
|
|
int fd = open_serial(SERIAL_PORT);
|
|
if (fd < 0) return 1;
|
|
|
|
FILE *out = fopen("capture.wav", "wb+");
|
|
if (!out) { perror("fopen"); close(fd); return 1; }
|
|
|
|
// Reserve header space
|
|
uint32_t data_bytes = 0;
|
|
fseek(out, 44, SEEK_SET);
|
|
|
|
// Capture loop
|
|
uint8_t buf[8192];
|
|
while (!stop_flag) {
|
|
ssize_t n = read(fd, buf, sizeof(buf));
|
|
if (n > 0) {
|
|
fwrite(buf, 1, (size_t)n, out);
|
|
data_bytes += (uint32_t)n;
|
|
}
|
|
}
|
|
|
|
// Finalize header
|
|
write_wav_header(out, data_bytes);
|
|
fflush(out);
|
|
fclose(out);
|
|
close(fd);
|
|
|
|
fprintf(stderr, "Saved capture.wav with %u bytes of audio\n", data_bytes);
|
|
return 0;
|
|
}
|