Files
MeowOS-For-R3P2/patch_v2/meow_audiod/meow_audio.c
T

455 lines
18 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#define _GNU_SOURCE
/* meow_audio.c — meow_audiod 用户侧 CLI(root 免鉴权,ssh 直呼)。
*
* 命令:
* meow_audio status 当前流/参数/帧数/underrun/路由/音量
* meow_audio tone [freq] [ms] [rate] 正弦测试音(默认 440Hz 500ms 44100;排查无声不须开 App)
* meow_audio route <0|2|3> 手动切路由(0=auto 2=3.5 3=4.4;调试/面板)
* meow_audio vol [0-100] 设置音量(无参=查询;v1 透传写 DAC numid=1/2 + settings.conf)
* meow_audio output <dac|bt> 输出目标(P2 蓝牙:bt=A2DP source,音量走 AVRCP 不下发 DAC 反转)
* meow_audio monitor [秒] 终端实时电平表(rms/peak dBFS + 文本电平条,200ms 刷新)
*
* 传输:AF_UNIX SOCK_STREAM 到 audiod socket(env MEOW_AUDIO_SOCK / --sock 覆盖,宿主/qemu)。
* 帧格式复用 meow_ipc.h 16B 头 + m_audio_* 负载。无 audiod → 连接失败/超时 → 打印友好错误并退出。
*
* 编译(mipsel 静态):build_audiod.sh cli。宿主编译:build_audiod.sh host。
*/
#include "meow_ipc.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <poll.h>
#include <math.h>
#include <time.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <sys/un.h>
#define AUD_SOCK_DEFAULT "/usr/data/meow_audio.sock"
static char g_sock[108] = AUD_SOCK_DEFAULT;
static int g_fd = -1;
static int write_all(int fd, const uint8_t *p, size_t n)
{
size_t off = 0;
while (off < n) {
ssize_t w = write(fd, p + off, n - off);
if (w < 0) { if (errno == EINTR) continue; return -1; }
if (w == 0) return -1;
off += (size_t)w;
}
return 0;
}
static int read_all(int fd, uint8_t *p, size_t n)
{
size_t off = 0;
while (off < n) {
ssize_t r = read(fd, p + off, n - off);
if (r < 0) { if (errno == EINTR) continue; return -1; }
if (r == 0) return -1;
off += (size_t)r;
}
return 0;
}
/* 连接 audiod:connect 非阻塞 + poll 2s 有界(对端 backlog 满/wedge 时绝不永久阻塞);
* 建连后 SO_SNDTIMEO 2s(tone 长写在 audiod 停摆时报错退出而非挂死)。 */
static int connect_sock(void)
{
int fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (fd < 0) return -1;
int fl = fcntl(fd, F_GETFL, 0);
if (fl >= 0) fcntl(fd, F_SETFL, fl | O_NONBLOCK);
struct sockaddr_un a;
memset(&a, 0, sizeof a);
a.sun_family = AF_UNIX;
snprintf(a.sun_path, sizeof a.sun_path, "%s", g_sock);
int r = connect(fd, (struct sockaddr *)&a, sizeof a);
if (r < 0 && errno == EINPROGRESS) {
struct pollfd p;
p.fd = fd; p.events = POLLOUT; p.revents = 0;
if (poll(&p, 1, 2000) > 0) {
int err = 0;
socklen_t el = sizeof err;
if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &el) == 0 && err != 0) { errno = err; r = -1; }
} else {
r = -1;
}
}
if (r < 0) { close(fd); return -1; }
if (fl >= 0) fcntl(fd, F_SETFL, fl);
struct timeval tv;
tv.tv_sec = 2; tv.tv_usec = 0;
setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof tv);
return fd;
}
static int send_frame(int fd, uint16_t type, uint32_t req_id, const void *payload, uint32_t len)
{
uint8_t hdr[MEOW_IPC_HDR_LEN];
uint32_t magic = MEOW_IPC_MAGIC;
uint16_t ver = MEOW_IPC_VERSION;
memcpy(hdr + 0, &magic, 4);
memcpy(hdr + 4, &ver, 2);
memcpy(hdr + 6, &type, 2);
memcpy(hdr + 8, &req_id, 4);
memcpy(hdr + 12, &len, 4);
if (write_all(fd, hdr, sizeof hdr) != 0) return -1;
if (len && payload && write_all(fd, payload, len) != 0) return -1;
return 0;
}
/* 读一帧头部+负载;返回负载长(≥0),-1=断/坏帧/超时。 */
static int read_frame(int fd, uint16_t *type, uint8_t *rbuf, uint32_t rcap)
{
uint8_t hdr[MEOW_IPC_HDR_LEN];
if (read_all(fd, hdr, sizeof hdr) != 0) return -1;
uint32_t magic, len; uint16_t ver;
memcpy(&magic, hdr, 4); memcpy(&ver, hdr + 4, 2); memcpy(type, hdr + 6, 2);
memcpy(&len, hdr + 12, 4);
if (magic != MEOW_IPC_MAGIC || ver != MEOW_IPC_VERSION) return -1;
if (len > rcap) len = rcap;
if (len > 0 && read_all(fd, rbuf, len) != 0) return -1;
return (int)len;
}
/* 请求-响应:发一帧、读响应(带 3s 超时防挂)。返回响应负载长;-1=失败。 */
static int req_resp(uint16_t type, const void *pl, uint32_t len,
uint16_t *otype, uint8_t *obuf, uint32_t ocap)
{
if (send_frame(g_fd, type, 1, pl, len) != 0) return -1;
struct pollfd p;
p.fd = g_fd; p.events = POLLIN; p.revents = 0;
int r = poll(&p, 1, 3000);
if (r <= 0 || !(p.revents & POLLIN)) return -1;
return read_frame(g_fd, otype, obuf, ocap);
}
static int fmt_bps(int fmt)
{
switch (fmt) {
case AUDIO_FMT_S16: return 2;
case AUDIO_FMT_S24:
case AUDIO_FMT_S32:
case AUDIO_FMT_FLOAT:
case AUDIO_FMT_DOP: return 4;
default: return 2;
}
}
static const char *fmt_name(int fmt)
{
switch (fmt) {
case AUDIO_FMT_S16: return "S16";
case AUDIO_FMT_S24: return "S24";
case AUDIO_FMT_S32: return "S32";
case AUDIO_FMT_FLOAT: return "FLOAT";
case AUDIO_FMT_DOP: return "DoP";
default: return "?";
}
}
static int cmd_status(void)
{
uint8_t rbuf[MEOW_IPC_MAX];
uint16_t rt;
int n = req_resp(MSG_AUDIO_QUERY, NULL, 0, &rt, rbuf, sizeof rbuf);
if (n < 0) { fprintf(stderr, "meow_audio: no audiod (%s), status unavailable\n", g_sock); return 1; }
if (rt != MSG_AUDIO_STATE) { fprintf(stderr, "meow_audio: unexpected reply type 0x%04x\n", rt); return 1; }
rbuf[n < (int)sizeof rbuf - 1 ? n : (int)sizeof rbuf - 1] = 0;
fputs((const char *)rbuf, stdout);
return 0;
}
/* ---- monitor:终端实时电平表(200ms 刷新,文本电平条;数据源=MSG_AUDIO_STATE 的 rms/peak 键) ---- */
static volatile sig_atomic_t g_mon_stop = 0;
static void on_mon_sig(int sig) { (void)sig; g_mon_stop = 1; }
/* 从 STATE 文本取整型字段(key 含 '=',如 "rms=";缺省 def) */
static int txt_get_int(const char *t, const char *key, int def)
{
const char *p = strstr(t, key);
return p ? atoi(p + strlen(key)) : def;
}
static void txt_get_str(const char *t, const char *key, char *out, size_t n)
{
const char *p = strstr(t, key);
if (!p) { snprintf(out, n, "-"); return; }
p += strlen(key);
size_t k = 0;
while (p[k] && p[k] != ' ' && p[k] != '\n' && p[k] != '\t' && k < n - 1) { out[k] = p[k]; k++; }
out[k] = 0;
}
/* deci-dB → "-13.1"(地板显 "-inf") */
static void fmt_db10(char *out, size_t n, int v)
{
if (v <= -990) { snprintf(out, n, " -inf"); return; }
int neg = v < 0;
int a = neg ? -v : v;
snprintf(out, n, "%s%3d.%d", neg ? "-" : " ", a / 10, a % 10);
}
/* 文本电平条:-60..0 dBFS → 0..30 格 */
static void print_bar(int db10)
{
int cells = 0;
if (db10 > -600) cells = (db10 + 600) * 30 / 600;
if (cells > 30) cells = 30;
putchar('[');
for (int i = 0; i < 30; i++) putchar(i < cells ? '#' : '-');
putchar(']');
}
static int cmd_monitor(int secs)
{
if (g_fd < 0) { fprintf(stderr, "meow_audio: no audiod (%s) — 音频服务不可用\n", g_sock); return 1; }
signal(SIGINT, on_mon_sig);
signal(SIGTERM, on_mon_sig);
printf("monitor: 5Hz 刷新,%s(Ctrl-C 退出)\n", secs > 0 ? "定时自动停" : "持续监视");
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
uint64_t t0 = (uint64_t)ts.tv_sec * 1000 + (uint64_t)ts.tv_nsec / 1000000;
uint8_t rbuf[MEOW_IPC_MAX];
uint16_t rt;
while (!g_mon_stop) {
int n = req_resp(MSG_AUDIO_QUERY, NULL, 0, &rt, rbuf, sizeof rbuf);
if (n < 0 || rt != MSG_AUDIO_STATE) {
fprintf(stderr, "\nmeow_audio: query failed (audiod died?)\n");
return 1;
}
rbuf[n < (int)sizeof rbuf - 1 ? n : (int)sizeof rbuf - 1] = 0;
const char *txt = (const char *)rbuf;
int active = txt_get_int(txt, "active=", 0);
int rms = txt_get_int(txt, "rms=", -990);
int pk = txt_get_int(txt, "peak=", -990);
int vol = txt_get_int(txt, "vol=", -1);
int port = txt_get_int(txt, "port=", -1);
int rate = txt_get_int(txt, "rate=", 0);
char app[40];
txt_get_str(txt, "app=", app, sizeof app);
char rmsb[16], pkb[16];
fmt_db10(rmsb, sizeof rmsb, rms);
fmt_db10(pkb, sizeof pkb, pk);
if (active) {
printf("\rLIVE rms=%sdBFS peak=%sdBFS ", rmsb, pkb);
print_bar(pk);
printf(" %s %dHz port=%d vol=%d%% ", app, rate, port, vol);
} else {
printf("\rIDLE 无活跃流(等待播放…) ");
}
fflush(stdout);
if (secs > 0) {
clock_gettime(CLOCK_MONOTONIC, &ts);
uint64_t now = (uint64_t)ts.tv_sec * 1000 + (uint64_t)ts.tv_nsec / 1000000;
if (now - t0 >= (uint64_t)secs * 1000) break;
}
usleep(200000); /* 200ms 刷新 */
}
putchar('\n');
return 0;
}
static int cmd_route(int port)
{
m_audio_route_t r; r.port = (uint32_t)port;
uint8_t rbuf[256]; uint16_t rt;
int n = req_resp(MSG_AUDIO_ROUTE, &r, sizeof r, &rt, rbuf, sizeof rbuf);
if (n < 0) { fprintf(stderr, "meow_audio: no audiod (%s)\n", g_sock); return 1; }
printf("route %d -> %s\n", port, (port == 0) ? "auto" : (port == 3 ? "4.4" : "3.5"));
return 0;
}
static int cmd_vol(int pct)
{
m_audio_volume_t v; v.pct = (int32_t)pct;
uint8_t rbuf[256]; uint16_t rt;
int n = req_resp(MSG_AUDIO_VOLUME, &v, sizeof v, &rt, rbuf, sizeof rbuf);
if (n < 0 || rt != MSG_ACK) { fprintf(stderr, "meow_audio: no audiod (%s)\n", g_sock); return 1; }
m_ack_t a; memcpy(&a, rbuf, sizeof a);
printf("vol %u%%%s\n", (unsigned)a.status, (pct >= 0 ? " (set)" : " (query)"));
return 0;
}
/* P2 蓝牙:切换输出目标(0=DAC 1=BT)。audiod 侧重配 PCM;BT 时音量走 AVRCP,不下发 DAC 反转。 */
static int cmd_output(int target)
{
m_audio_output_t o; o.target = (uint32_t)target;
uint8_t rbuf[256]; uint16_t rt;
int n = req_resp(MSG_AUDIO_OUTPUT, &o, sizeof o, &rt, rbuf, sizeof rbuf);
if (n < 0 || rt != MSG_ACK) { fprintf(stderr, "meow_audio: no audiod (%s)\n", g_sock); return 1; }
m_ack_t a; memcpy(&a, rbuf, sizeof a);
printf("output %s\n", a.status == AUDIO_OUT_BT ? "bt" : "dac");
return 0;
}
/* 正弦生成:填 nframes 帧(grant 交织格式)到 buf(上限 bufcap,帧对齐)。
* start 为起始帧号;返回实际写入字节数。 */
static size_t gen_sine(uint8_t *buf, size_t bufcap, int grant_fmt, int grant_ch,
double freq, int rate, size_t start, size_t nframes, double amp)
{
int bps = fmt_bps(grant_fmt);
size_t fs = (size_t)grant_ch * (size_t)bps;
if (fs == 0) fs = 1;
size_t n = (bufcap / fs) * fs;
if (n / fs > nframes) n = nframes * fs;
size_t done = 0;
for (size_t i = 0; i < n / fs; i++) {
double ph = 2.0 * M_PI * freq * (double)(start + i) / (double)rate;
double s = sin(ph) * amp;
long v = (long)(s * 32767.0);
if (v > 32767) v = 32767;
if (v < -32768) v = -32768;
long vs = v; /* S16 值 */
if (grant_fmt == AUDIO_FMT_S32 || grant_fmt == AUDIO_FMT_S24) vs = v << 16;
for (int c = 0; c < grant_ch; c++) {
if (grant_fmt == AUDIO_FMT_S16) {
int16_t xx = (int16_t)v;
memcpy(buf + done, &xx, 2); done += 2;
} else {
int32_t xx = (int32_t)(vs);
memcpy(buf + done, &xx, 4); done += 4;
}
}
}
return done;
}
static int cmd_tone(int freq, int ms, int rate, int fmt)
{
m_audio_open_t o;
memset(&o, 0, sizeof o);
snprintf(o.app_id, sizeof o.app_id, "audio-cli");
o.rate = (uint32_t)rate;
o.ch = 2;
o.fmt = (uint16_t)fmt;
uint8_t rbuf[512]; uint16_t rt;
int n = req_resp(MSG_AUDIO_OPEN, &o, sizeof o, &rt, rbuf, sizeof rbuf);
if (n < 0) { fprintf(stderr, "meow_audio: no audiod (%s) — 音频服务不可用\n", g_sock); return 1; }
if (rt != MSG_AUDIO_OPEN_ACK) { fprintf(stderr, "meow_audio: unexpected reply 0x%04x\n", rt); return 1; }
m_audio_open_ack_t a; memcpy(&a, rbuf, sizeof a);
if (a.err != MEOW_OK) {
fprintf(stderr, "meow_audio: open failed (err=%d): %s\n", a.err,
a.err == MEOW_ERR_NODEV ? "no usable PCM device" :
a.err == MEOW_ERR_INVAL ? "invalid params" : "error");
return 1;
}
printf("tone: sid=%u grant=%uHz/%uch/%s\n", (unsigned)a.stream_id,
(unsigned)a.grant_rate, (unsigned)a.grant_ch, fmt_name((int)a.grant_fmt));
double amp = 0.3;
size_t total_frames = (size_t)((uint64_t)ms * (uint64_t)rate / 1000);
uint8_t pcm[MEOW_IPC_MAX];
size_t fs = (size_t)a.grant_ch * (size_t)fmt_bps((int)a.grant_fmt);
if (fs == 0) fs = 1;
size_t sent_frames = 0;
while (sent_frames < total_frames) {
size_t want = total_frames - sent_frames;
if (want > (size_t)(sizeof pcm / fs)) want = sizeof pcm / fs;
size_t by = gen_sine(pcm, sizeof pcm, (int)a.grant_fmt, (int)a.grant_ch,
(double)freq, rate, sent_frames, want, amp);
if (by == 0) break;
if (send_frame(g_fd, MSG_AUDIO_WRITE, 0, pcm, (uint32_t)by) != 0) {
fprintf(stderr, "meow_audio: write failed (audiod died?)\n");
return 1;
}
sent_frames += by / fs;
}
/* 停止 + 等待排空(WAV 完整落盘 / 真实 DAC 播放完) */
m_audio_stream_t s; s.stream_id = a.stream_id;
req_resp(MSG_AUDIO_STOP, &s, sizeof s, &rt, rbuf, sizeof rbuf);
uint64_t deadline_ms = (uint64_t)ms + 3000;
uint64_t t0 = 0;
{
struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts);
t0 = (uint64_t)ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
}
for (;;) {
struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts);
uint64_t now = (uint64_t)ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
if (now - t0 > deadline_ms) break;
req_resp(MSG_AUDIO_QUERY, NULL, 0, &rt, rbuf, sizeof rbuf);
if (rt == MSG_AUDIO_STATE) {
if (strstr((const char *)rbuf, "active=0")) break;
}
usleep(100000); /* 100ms 轮询 */
}
printf("tone done: sent %zu frames\n", sent_frames);
close(g_fd);
g_fd = -1;
return 0;
}
static void usage(const char *argv0)
{
fprintf(stderr, "usage: %s status | tone [freq] [ms] [rate] [S16|S24|S32] | route <0|2|3> | vol [0-100] | output <dac|bt> | monitor [秒]\n"
" output: 切换输出目标(dac=本地 hw:0,0 / bt=蓝牙 A2DP source;bt 时音量=AVRCP 侧控)\n"
" monitor: 终端实时电平表(200ms 刷新+文本电平条;缺省 10s,0=持续到 Ctrl-C)\n"
" env: MEOW_AUDIO_SOCK (socket 缺省 %s)\n", argv0, AUD_SOCK_DEFAULT);
}
int main(int argc, char **argv)
{
const char *e = getenv("MEOW_AUDIO_SOCK");
if (e && e[0]) snprintf(g_sock, sizeof g_sock, "%s", e);
for (int i = 1; i < argc; i++) {
if (!strcmp(argv[i], "--sock") && i + 1 < argc) { snprintf(g_sock, sizeof g_sock, "%s", argv[++i]); }
else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) { usage(argv[0]); return 0; }
}
if (argc < 2) { usage(argv[0]); return 2; }
g_fd = connect_sock();
if (g_fd < 0 && strcmp(argv[1], "status") != 0 && strcmp(argv[1], "vol") != 0
&& strcmp(argv[1], "route") != 0 && strcmp(argv[1], "tone") != 0) {
/* 未知命令用 status 兜底说明 */
}
/* 连接失败:status/vol/route/tone 各自报错;未知命令直接 usage */
const char *cmd = argv[1];
if (!strcmp(cmd, "status")) {
return cmd_status();
} else if (!strcmp(cmd, "route")) {
if (argc < 3) { usage(argv[0]); return 2; }
int port = atoi(argv[2]);
if (port != 0 && port != 2 && port != 3) { fprintf(stderr, "route: port 0/2/3\n"); return 2; }
if (g_fd < 0) { fprintf(stderr, "meow_audio: no audiod (%s)\n", g_sock); return 1; }
int rc = cmd_route(port); close(g_fd); return rc;
} else if (!strcmp(cmd, "vol")) {
if (g_fd < 0) { fprintf(stderr, "meow_audio: no audiod (%s)\n", g_sock); return 1; }
int pct = (argc >= 3) ? atoi(argv[2]) : -1;
if (pct > 100) pct = 100;
int rc = cmd_vol(pct); close(g_fd); return rc;
} else if (!strcmp(cmd, "output")) {
if (argc < 3) { fprintf(stderr, "output: <dac|bt>\n"); return 2; }
int t = (strcmp(argv[2], "bt") == 0) ? AUDIO_OUT_BT : (strcmp(argv[2], "dac") == 0) ? AUDIO_OUT_DAC : -1;
if (t < 0) { fprintf(stderr, "output: dac|bt\n"); return 2; }
if (g_fd < 0) { fprintf(stderr, "meow_audio: no audiod (%s)\n", g_sock); return 1; }
int rc = cmd_output(t); close(g_fd); return rc;
} else if (!strcmp(cmd, "tone")) {
int freq = (argc >= 3) ? atoi(argv[2]) : 440;
int ms = (argc >= 4) ? atoi(argv[3]) : 500;
int rate = (argc >= 5) ? atoi(argv[4]) : 44100;
int fmt = AUDIO_FMT_S16;
if (argc >= 6) {
if (!strcmp(argv[5], "S16")) fmt = AUDIO_FMT_S16;
else if (!strcmp(argv[5], "S24")) fmt = AUDIO_FMT_S24;
else if (!strcmp(argv[5], "S32")) fmt = AUDIO_FMT_S32;
else { fprintf(stderr, "tone: fmt S16|S24|S32(DoP 由 App 侧生成,CLI 不产 DoP)\n"); return 2; }
}
if (freq <= 0 || ms <= 0 || rate <= 0) { fprintf(stderr, "tone: bad params\n"); return 2; }
if (g_fd < 0) { fprintf(stderr, "meow_audio: no audiod (%s) — 音频服务不可用\n", g_sock); return 1; }
return cmd_tone(freq, ms, rate, fmt);
} else if (!strcmp(cmd, "monitor")) {
int secs = (argc >= 3) ? atoi(argv[2]) : 10;
if (secs < 0) secs = 10;
return cmd_monitor(secs);
}
usage(argv[0]);
return 2;
}