446 lines
17 KiB
C
446 lines
17 KiB
C
#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: 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";
|
||
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)
|
||
{
|
||
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 = AUDIO_FMT_S16;
|
||
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] | 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;
|
||
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);
|
||
} 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;
|
||
}
|