A1: meow_audiod 独占音频服务(agent-244 骨架 + agent-246 真机收尾)
- meow_audiod:启动门控(/tmp/meow_boot_mode 非 meow exit 0)、IO/播放/路由三线程、
ring ~200ms、ALSA UAPI 独占直开、切率 mute-重配-恢复、抢占(后开赢+PREEMPTED)、
socket EOF 回收、xrun PREPARE 恢复;meow_route 轮询逻辑并入路由线程
- meow_audio CLI:status/tone/route/vol;services.d/audiod.conf(trigger=manual,on-fail)
- SDK meow_ui_audio.c 编进 meow_ui.a:懒连接/读线程分发 PREEMPTED 回调/EPIPE 标死/
无 audiod 返回 MEOW_ERR_NOENT 不隐式直开(MEOW_AUDIO_DIRECT=1 仅调试)
- launcher:确认 MeowOS 模式后 meow_svcd start audiod(double-fork best-effort 不阻塞)
- meow_route 退役:S89 预置换 audiod.conf + 清理旧 meow_route.conf(防拉起已删二进制
重启循环);fw_content.conf 删 meow_route、加 meow_audiod+meow_audio(md5 打包时填)
- 真机定位并修复 ALSA UAPI 两大坑(即 ncm 播放无声根因):
① hw_params 须全放行初始化(mask 全 1/interval [0,UINT_MAX]),memset 0 interval
等价只能取 0 → refine 必空 → HW_PARAMS 恒 EINVAL
② access/format mask 先清零再单点(pmask 只 OR),全 1 被 choose 成 MMAP_INTERLEAVED
→ write() 恒 EINVAL
- 附带修复:dev_write fd<0 防日志风暴丢 PCM 限频日志;SDK state 解析 app_id 截空白;
meow_ui.h 文档负错误码表述修正
- 验证:宿主 FILE sink 全 IPC 链+抢占+tone WAV 20/20,SDK 客户端 13/13;
qemu-mipsel 同过;真机 tone 44.1k/96k 切率 written=played、underrun=0、日志完整
- meow_ui.a 53a3edd2 → ncm 重链 d2031829(已热更到卡);launcher a2f932a1
This commit is contained in:
1 parent
94874107b6
commit
ade0a57353
24 files changed
+2795
-11
No files matched your search
Executable
+40
@@ -0,0 +1,40 @@
|
||||
#!/bin/bash
|
||||
# build_audiod.sh — meow_audiod 交叉编译(mipsel 静态)+ 宿主多目标可选。
|
||||
#
|
||||
# 目标:R3 Pro II / MeowOS(mipsel-linux-muslsf,静态)。audiod 复用 meow_ipc.h(共享协议头,
|
||||
# -I../meow_launcherd)。audiod 为 svcd 托管的独立守护;meow_audio 为用户侧 CLI。
|
||||
#
|
||||
# 用法:
|
||||
# ./build_audiod.sh # 交叉编译 → meow_audiod(mipsel 静态)
|
||||
# ./build_audiod.sh cli # 交叉编译 meow_audio CLI → meow_audio(mipsel 静态)
|
||||
# ./build_audiod.sh host # 宿主编译 meow_audiod_host + meow_audio_host(集成测试)
|
||||
set -e
|
||||
cd "$(dirname "$0")"
|
||||
ROOT="$HOME/Project/R3ProII"
|
||||
CC="$ROOT/.tools/zig-mipsel-cc"
|
||||
INC="-I$ROOT/patch_v2/meow_launcherd"
|
||||
|
||||
if [ "$1" = "host" ]; then
|
||||
HOST_CC="${CC_HOST:-cc}"
|
||||
"$HOST_CC" $INC -Os -Wall -Wextra meow_audiod.c -o meow_audiod_host -lpthread
|
||||
"$HOST_CC" $INC -Os -Wall -Wextra meow_audio.c -o meow_audio_host -lm
|
||||
echo "== host builds =="
|
||||
ls -la meow_audiod_host meow_audio_host
|
||||
file meow_audiod_host meow_audio_host
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "$1" = "cli" ]; then
|
||||
"$CC" $INC -Os -Wall -Wextra -static meow_audio.c -o meow_audio -lm
|
||||
echo "== meow_audio (mipsel static CLI) =="
|
||||
ls -la meow_audio
|
||||
md5sum meow_audio
|
||||
file meow_audio
|
||||
exit 0
|
||||
fi
|
||||
|
||||
"$CC" $INC -Os -Wall -Wextra -static meow_audiod.c -o meow_audiod -lpthread
|
||||
echo "== meow_audiod (mipsel static) =="
|
||||
ls -la meow_audiod
|
||||
md5sum meow_audiod
|
||||
file meow_audiod
|
||||
Executable
BIN
Binary file not shown.
@@ -0,0 +1,309 @@
|
||||
#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)
|
||||
*
|
||||
* 传输: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 <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;
|
||||
}
|
||||
|
||||
static int connect_sock(void)
|
||||
{
|
||||
int fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (fd < 0) return -1;
|
||||
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);
|
||||
if (connect(fd, (struct sockaddr *)&a, sizeof a) < 0) { close(fd); return -1; }
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/* 正弦生成:填 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]\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, "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);
|
||||
}
|
||||
usage(argv[0]);
|
||||
return 2;
|
||||
}
|
||||
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
File diff suppressed because it is too large.
Load diff
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
@@ -0,0 +1,75 @@
|
||||
#define _GNU_SOURCE
|
||||
/* probe_alsa.c — 探测 R3 Pro II CS43198 PCM 设备的 hw_params 约束(找可用的 access/format/rate)。
|
||||
* mipsel 交叉编译后推到设备运行,输出 kernel 支持哪些组合,诊断「hw_params ioctl 失败」。 */
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sound/asound.h>
|
||||
|
||||
static void pmask(struct snd_mask *m, unsigned bit) { m->bits[bit / 32] |= 1u << (bit % 32); }
|
||||
static void pivset(struct snd_interval *iv, unsigned val)
|
||||
{
|
||||
iv->min = iv->max = val; iv->integer = 1; iv->openmin = iv->openmax = 0; iv->empty = 0;
|
||||
}
|
||||
static const char *fmt_name(unsigned f)
|
||||
{
|
||||
#define N(x) case x: return #x
|
||||
switch (f) {
|
||||
N(SNDRV_PCM_FORMAT_S8); N(SNDRV_PCM_FORMAT_U8); N(SNDRV_PCM_FORMAT_S16_LE);
|
||||
N(SNDRV_PCM_FORMAT_S16_BE); N(SNDRV_PCM_FORMAT_U16_LE); N(SNDRV_PCM_FORMAT_U16_BE);
|
||||
N(SNDRV_PCM_FORMAT_S24_LE); N(SNDRV_PCM_FORMAT_S24_BE); N(SNDRV_PCM_FORMAT_S32_LE);
|
||||
N(SNDRV_PCM_FORMAT_S32_BE); N(SNDRV_PCM_FORMAT_FLOAT_LE); N(SNDRV_PCM_FORMAT_FLOAT_BE);
|
||||
N(SNDRV_PCM_FORMAT_S24_3LE); N(SNDRV_PCM_FORMAT_S24_3BE); N(SNDRV_PCM_FORMAT_U24_LE);
|
||||
N(SNDRV_PCM_FORMAT_S20_3LE); N(SNDRV_PCM_FORMAT_S20_3BE);
|
||||
default: return "?";
|
||||
}
|
||||
#undef N
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
setvbuf(stdout, NULL, _IONBF, 0); /* 每行立即刷出,防 kill 丢输出 */
|
||||
const char *dev = "/dev/snd/pcmC0D0p";
|
||||
unsigned access[] = { SNDRV_PCM_ACCESS_MMAP_INTERLEAVED, SNDRV_PCM_ACCESS_RW_INTERLEAVED,
|
||||
SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED, SNDRV_PCM_ACCESS_RW_NONINTERLEAVED, ~0u };
|
||||
unsigned fmts[] = { SNDRV_PCM_FORMAT_S16_LE, SNDRV_PCM_FORMAT_S24_LE, SNDRV_PCM_FORMAT_S32_LE,
|
||||
SNDRV_PCM_FORMAT_S24_3LE, ~0u };
|
||||
unsigned rates[] = { 44100, 48000, 96000, 88200, ~0u };
|
||||
for (unsigned ai = 0; access[ai] != ~0u; ai++) {
|
||||
for (unsigned fi = 0; fmts[fi] != ~0u; fi++) {
|
||||
printf("== access=%u fmt=%s ==\n", access[ai], fmt_name(fmts[fi]));
|
||||
struct snd_pcm_hw_params p;
|
||||
for (unsigned ri = 0; rates[ri] != ~0u; ri++) {
|
||||
int fd = open(dev, O_RDWR | O_NONBLOCK);
|
||||
if (fd < 0) { printf(" open: %s\n", strerror(errno)); break; }
|
||||
/* 「全放行」初始化:memset 0 的 interval =「只能取 0」→ refine 必空 EINVAL
|
||||
*(2026-08-31 真机全灭的根因;libasound snd_pcm_hw_params_any 语义) */
|
||||
memset(&p, 0, sizeof p);
|
||||
unsigned nm = SNDRV_PCM_HW_PARAM_LAST_MASK - SNDRV_PCM_HW_PARAM_FIRST_MASK + 1;
|
||||
for (unsigned i = 0; i < nm; i++)
|
||||
for (unsigned b = 0; b < sizeof p.masks[i].bits / sizeof p.masks[i].bits[0]; b++)
|
||||
p.masks[i].bits[b] = ~0u;
|
||||
unsigned ni = SNDRV_PCM_HW_PARAM_LAST_INTERVAL - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL + 1;
|
||||
for (unsigned i = 0; i < ni; i++) { p.intervals[i].min = 0; p.intervals[i].max = ~0u; }
|
||||
/* mask 先清零再单点:全 1 会被 choose 取最低 bit(access→MMAP_INTERLEAVED) */
|
||||
struct snd_mask *am = &p.masks[SNDRV_PCM_HW_PARAM_ACCESS - SNDRV_PCM_HW_PARAM_FIRST_MASK];
|
||||
memset(am, 0, sizeof *am); pmask(am, access[ai]);
|
||||
struct snd_mask *fm = &p.masks[SNDRV_PCM_HW_PARAM_FORMAT - SNDRV_PCM_HW_PARAM_FIRST_MASK];
|
||||
memset(fm, 0, sizeof *fm); pmask(fm, fmts[fi]);
|
||||
struct snd_mask *sm = &p.masks[SNDRV_PCM_HW_PARAM_SUBFORMAT - SNDRV_PCM_HW_PARAM_FIRST_MASK];
|
||||
memset(sm, 0, sizeof *sm); pmask(sm, SNDRV_PCM_SUBFORMAT_STD);
|
||||
pivset(&p.intervals[SNDRV_PCM_HW_PARAM_CHANNELS - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL], 2);
|
||||
pivset(&p.intervals[SNDRV_PCM_HW_PARAM_RATE - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL], rates[ri]);
|
||||
p.rmask = ~0u; p.info = 0; p.msbits = 0; p.rate_num = 0; p.rate_den = 0; p.flags = 0;
|
||||
int rc = ioctl(fd, SNDRV_PCM_IOCTL_HW_PARAMS, &p);
|
||||
printf(" @%u: %s\n", rates[ri], rc ? strerror(errno) : "OK");
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
# meow_audiod 板载服务配置(svcd 托管;由 S89meow_svcd 首启预置到 /usr/data/meow/services.d/)
|
||||
#
|
||||
# trigger=manual:audiod 不在开机自动拉起 —— 由 launcher 在确认 MeowOS 模式后 `meow_svcd start
|
||||
# audiod`(幂等)。audiod 自身还有启动门控(/tmp/meow_boot_mode != meow → exit(0)),双重保证
|
||||
# HibyOS 模式下它物理不存在、绝不碰声卡。
|
||||
# restart=on-fail:运行中崩溃 → svcd 重拉(关流状态由客户端 socket EOF 回收兜底),不级联。
|
||||
exec=/usr/bin/meow_audiod
|
||||
args=
|
||||
enabled=1
|
||||
restart=on-fail
|
||||
backoff_ms=2000
|
||||
max_restart=5
|
||||
trigger=manual
|
||||
cap=
|
||||
Executable
BIN
Binary file not shown.
@@ -0,0 +1,310 @@
|
||||
#define _GNU_SOURCE
|
||||
|
||||
/* test_audiod_host.c — meow_audiod 宿主集成测试(FILE sink,无 /dev/snd 环境)。
|
||||
*
|
||||
* 验证 AUDIOD_DESIGN.md 里程碑 A1:
|
||||
* - 全 IPC 链路:OPEN→WRITE→QUERY→PAUSE/RESUME→STOP→EOF 回收
|
||||
* - 两个客户端抢占:旧流收 MSG_AUDIO_PREEMPTED(PCM 丢弃)
|
||||
* - tone 生成 WAV 可核验正弦(440Hz,帧数/过零率粗检)
|
||||
*
|
||||
* 启动被测试 audiod(宿主二进制,env MEOW_AUDIO_SINK=file / MEOW_AUDIO_OUT=<wav> /
|
||||
* MEOW_AUDIO_SOCK=<sock> / MEOW_AUDIO_FORCE=1 跳过门控)。
|
||||
*
|
||||
* 编译(宿主):cc -I../meow_launcherd -Os -Wall -Wextra test_audiod_host.c -o test_audiod_host
|
||||
*/
|
||||
#include "meow_ipc.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <math.h>
|
||||
#include <poll.h>
|
||||
#include <signal.h>
|
||||
#include <time.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#define TSOCK "/tmp/meow_audio_test.sock"
|
||||
#define TWAV "/tmp/meow_audio_test.wav"
|
||||
|
||||
static int g_audiod_pid = -1;
|
||||
static int g_pass = 0, g_fail = 0;
|
||||
|
||||
static void check(int cond, const char *name)
|
||||
{
|
||||
if (cond) { printf(" PASS %s\n", name); g_pass++; }
|
||||
else { printf(" FAIL %s\n", name); g_fail++; }
|
||||
}
|
||||
|
||||
/* ---- 原始 IPC 客户端 ---- */
|
||||
struct cli { int fd; };
|
||||
static int cli_connect(struct cli *c)
|
||||
{
|
||||
c->fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (c->fd < 0) return -1;
|
||||
struct sockaddr_un a;
|
||||
memset(&a, 0, sizeof a);
|
||||
a.sun_family = AF_UNIX;
|
||||
snprintf(a.sun_path, sizeof a.sun_path, "%s", TSOCK);
|
||||
if (connect(c->fd, (struct sockaddr *)&a, sizeof a) < 0) { close(c->fd); return -1; }
|
||||
return 0;
|
||||
}
|
||||
static int w_all(int fd, const void *p, size_t n)
|
||||
{
|
||||
const uint8_t *b = p; size_t o = 0;
|
||||
while (o < n) { ssize_t r = write(fd, b + o, n - o); if (r < 0) { if (errno == EINTR) continue; return -1; } o += (size_t)r; }
|
||||
return 0;
|
||||
}
|
||||
static int r_all(int fd, void *p, size_t n)
|
||||
{
|
||||
uint8_t *b = p; size_t o = 0;
|
||||
while (o < n) { ssize_t r = read(fd, b + o, n - o); if (r <= 0) return -1; o += (size_t)r; }
|
||||
return 0;
|
||||
}
|
||||
static int sf(int fd, uint16_t type, uint32_t req, const void *pl, uint32_t len)
|
||||
{
|
||||
uint8_t hdr[MEOW_IPC_HDR_LEN];
|
||||
uint32_t magic = MEOW_IPC_MAGIC; uint16_t ver = MEOW_IPC_VERSION;
|
||||
memcpy(hdr, &magic, 4); memcpy(hdr + 4, &ver, 2); memcpy(hdr + 6, &type, 2); memcpy(hdr + 8, &req, 4); memcpy(hdr + 12, &len, 4);
|
||||
if (w_all(fd, hdr, sizeof hdr)) return -1;
|
||||
if (len && pl && w_all(fd, pl, len)) return -1;
|
||||
return 0;
|
||||
}
|
||||
static int rf(int fd, uint16_t *type, uint8_t *rbuf, uint32_t rcap)
|
||||
{
|
||||
uint8_t hdr[MEOW_IPC_HDR_LEN];
|
||||
if (r_all(fd, hdr, sizeof hdr)) 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 && r_all(fd, rbuf, len)) return -1;
|
||||
return (int)len;
|
||||
}
|
||||
/* 带超时读一帧(poll) */
|
||||
static int rf_tmo(int fd, int tmo_ms, uint16_t *type, uint8_t *rbuf, uint32_t rcap)
|
||||
{
|
||||
struct pollfd p; p.fd = fd; p.events = POLLIN; p.revents = 0;
|
||||
if (poll(&p, 1, tmo_ms) <= 0) return -2;
|
||||
return rf(fd, type, rbuf, rcap);
|
||||
}
|
||||
|
||||
static void stop_audiod(void)
|
||||
{
|
||||
if (g_audiod_pid > 0) { kill(g_audiod_pid, SIGTERM); waitpid(g_audiod_pid, NULL, 0); g_audiod_pid = -1; }
|
||||
}
|
||||
|
||||
static int start_audiod(void)
|
||||
{
|
||||
pid_t pid = fork();
|
||||
if (pid == 0) {
|
||||
setenv("MEOW_AUDIO_SINK", "file", 1);
|
||||
setenv("MEOW_AUDIO_OUT", TWAV, 1);
|
||||
setenv("MEOW_AUDIO_SOCK", TSOCK, 1);
|
||||
setenv("MEOW_AUDIO_FORCE", "1", 1);
|
||||
execl("./meow_audiod_host", "meow_audiod_host", (char *)0);
|
||||
_exit(127);
|
||||
}
|
||||
return pid;
|
||||
}
|
||||
|
||||
/* 直接客户端 open */
|
||||
static int do_open(struct cli *c, int rate, int ch, int fmt, m_audio_open_ack_t *ack)
|
||||
{
|
||||
m_audio_open_t o; memset(&o, 0, sizeof o);
|
||||
snprintf(o.app_id, sizeof o.app_id, "test");
|
||||
o.rate = (uint32_t)rate; o.ch = (uint16_t)ch; o.fmt = (uint16_t)fmt; o.flags = 0;
|
||||
if (sf(c->fd, MSG_AUDIO_OPEN, 1, &o, sizeof o)) return -1;
|
||||
uint8_t rbuf[256]; uint16_t rt;
|
||||
if (rf(c->fd, &rt, rbuf, sizeof rbuf) < 0) return -1;
|
||||
if (rt != MSG_AUDIO_OPEN_ACK) return -2;
|
||||
memcpy(ack, rbuf, sizeof *ack);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_write(struct cli *c, const void *pcm, uint32_t len)
|
||||
{
|
||||
return sf(c->fd, MSG_AUDIO_WRITE, 0, pcm, len);
|
||||
}
|
||||
static int do_ctrl(struct cli *c, uint16_t type, uint32_t sid)
|
||||
{
|
||||
m_audio_stream_t s; s.stream_id = sid;
|
||||
if (sf(c->fd, type, 2, &s, sizeof s)) return -1;
|
||||
uint8_t rbuf[64]; uint16_t rt;
|
||||
if (rf(c->fd, &rt, rbuf, sizeof rbuf) < 0) return -1;
|
||||
return (rt == MSG_ACK) ? 0 : -2;
|
||||
}
|
||||
static int do_query(struct cli *c, char *out, size_t n)
|
||||
{
|
||||
if (sf(c->fd, MSG_AUDIO_QUERY, 3, NULL, 0)) return -1;
|
||||
uint8_t rbuf[MEOW_IPC_MAX]; uint16_t rt;
|
||||
int r = rf(c->fd, &rt, rbuf, sizeof rbuf);
|
||||
if (r < 0 || rt != MSG_AUDIO_STATE) return -1;
|
||||
size_t m = ((size_t)r < n - 1) ? (size_t)r : (n - 1);
|
||||
memcpy(out, rbuf, m);
|
||||
out[m] = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 生成正弦 PCM(S16 or S32,stereo 交织),返回字节数 */
|
||||
static size_t gen_sine(uint8_t *buf, size_t cap, int fmt, int frames, double freq, int rate)
|
||||
{
|
||||
int bps = (fmt == AUDIO_FMT_S16) ? 2 : 4;
|
||||
size_t fs = 2; /* 双声道 */
|
||||
size_t n = (frames > (int)(cap / (fs * bps))) ? (cap / (fs * bps)) : (size_t)frames;
|
||||
size_t o = 0;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
double ph = 2.0 * M_PI * freq * (double)i / (double)rate;
|
||||
int16_t s = (int16_t)(sin(ph) * 0.3 * 32767);
|
||||
for (int c = 0; c < 2; c++) {
|
||||
int32_t v = (fmt == AUDIO_FMT_S16) ? (int32_t)s : ((int32_t)s << 16);
|
||||
if (fmt == AUDIO_FMT_S16) { memcpy(buf + o, &s, 2); o += 2; }
|
||||
else { memcpy(buf + o, &v, 4); o += 4; }
|
||||
}
|
||||
}
|
||||
return o;
|
||||
}
|
||||
|
||||
/* ---- WAV 校验:合法 + 近似正弦(逐帧过零/幅度粗检) ---- */
|
||||
static int verify_wav(const char *path, int rate_expect, int ch_expect, int bits_expect)
|
||||
{
|
||||
FILE *f = fopen(path, "rb");
|
||||
if (!f) return -1;
|
||||
unsigned char h[44];
|
||||
if (fread(h, 1, 44, f) != 44) { fclose(f); return -1; }
|
||||
if (memcmp(h, "RIFF", 4) || memcmp(h + 8, "WAVE", 4) || memcmp(h + 12, "fmt ", 4)) { fclose(f); return -1; }
|
||||
uint16_t ch, bits; uint32_t rate, byterate;
|
||||
memcpy(&ch, h + 22, 2); memcpy(&rate, h + 24, 4); memcpy(&byterate, h + 28, 4); memcpy(&bits, h + 34, 2);
|
||||
if (ch != ch_expect || rate != (uint32_t)rate_expect || bits != bits_expect) { fclose(f); return -1; }
|
||||
if (byterate != rate * ch * bits / 8) { fclose(f); return -1; }
|
||||
uint32_t data_len; memcpy(&data_len, h + 40, 4);
|
||||
if (data_len < 44) { fclose(f); return -1; }
|
||||
/* 读「左声道」样本(跳右声道)统计过零比例:正弦 freq@rate 单声道期望过零比例 ≈ 2*freq/rate。
|
||||
* 仅对「近似正弦」做粗检(40% 容差),确认不是静音/噪声/满幅方波。 */
|
||||
int bytes_samp = bits / 8;
|
||||
int frame_bytes = ch * bytes_samp;
|
||||
uint8_t framebuf[8];
|
||||
long zero_cross = 0, total = 0, prev_neg = 0;
|
||||
for (long i = 0; i < (long)(data_len / frame_bytes); i++) {
|
||||
if (fread(framebuf, 1, (size_t)frame_bytes, f) != (size_t)frame_bytes) break;
|
||||
int32_t val = 0;
|
||||
if (bytes_samp == 2) { int16_t v; memcpy(&v, framebuf, 2); val = v; }
|
||||
else { int32_t v; memcpy(&v, framebuf, 4); val = v >> 16; }
|
||||
if (val != 0) {
|
||||
int neg = val < 0;
|
||||
if (total > 0 && neg != prev_neg) zero_cross++;
|
||||
prev_neg = neg;
|
||||
}
|
||||
total++;
|
||||
}
|
||||
fclose(f);
|
||||
if (total < 100) return -3;
|
||||
double obs = (double)zero_cross / (double)total;
|
||||
double exp = 2.0 * 440.0 / (double)rate_expect;
|
||||
if (obs < exp * 0.6 || obs > exp * 1.4) return -2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
unlink(TSOCK); unlink(TWAV);
|
||||
g_audiod_pid = start_audiod();
|
||||
if (g_audiod_pid < 0) { printf("cannot fork audiod\n"); return 1; }
|
||||
usleep(300000);
|
||||
printf("=== meow_audiod host test (FILE sink) ===\n");
|
||||
|
||||
/* ---- T1:全链路 OPEN→WRITE→QUERY→PAUSE/RESUME→STOP ---- */
|
||||
printf("[T1 full chain]\n");
|
||||
struct cli a;
|
||||
check(cli_connect(&a) == 0, "connect A");
|
||||
m_audio_open_ack_t ack;
|
||||
check(do_open(&a, 44100, 2, AUDIO_FMT_S16, &ack) == 0 && ack.err == MEOW_OK && ack.stream_id > 0, "open A");
|
||||
printf(" grant=%u/%u/%s sid=%u\n", (unsigned)ack.grant_rate, (unsigned)ack.grant_ch, (ack.grant_fmt == AUDIO_FMT_S16 ? "S16" : "S32"), (unsigned)ack.stream_id);
|
||||
uint8_t pcm[4096];
|
||||
size_t wb = gen_sine(pcm, sizeof pcm, (int)(ack.grant_fmt ? ack.grant_fmt : AUDIO_FMT_S16), 100, 440, 44100);
|
||||
check(wb > 0 && do_write(&a, pcm, (uint32_t)wb) == 0, "write A");
|
||||
usleep(150000);
|
||||
char q[1024];
|
||||
check(do_query(&a, q, sizeof q) == 0 && strstr(q, "active=1"), "query active");
|
||||
printf(" query: %s\n", q);
|
||||
check(do_ctrl(&a, MSG_AUDIO_PAUSE, ack.stream_id) == 0, "pause A");
|
||||
usleep(50000);
|
||||
do_query(&a, q, sizeof q);
|
||||
check(strstr(q, "paused=1") != NULL, "query paused");
|
||||
check(do_ctrl(&a, MSG_AUDIO_RESUME, ack.stream_id) == 0, "resume A");
|
||||
usleep(50000);
|
||||
check(do_ctrl(&a, MSG_AUDIO_STOP, ack.stream_id) == 0, "stop A");
|
||||
usleep(100000);
|
||||
do_query(&a, q, sizeof q);
|
||||
check(strstr(q, "active=0") != NULL, "stop -> inactive");
|
||||
close(a.fd);
|
||||
|
||||
/* ---- T2:两个客户端抢占(旧收 PREEMPTED,PCM 丢弃) ---- */
|
||||
printf("[T2 preemption]\n");
|
||||
struct cli a2, b2;
|
||||
check(cli_connect(&a2) == 0, "connect A2");
|
||||
m_audio_open_ack_t ackA2;
|
||||
check(do_open(&a2, 48000, 2, AUDIO_FMT_S16, &ackA2) == 0 && ackA2.err == MEOW_OK, "open A2");
|
||||
size_t wb2 = gen_sine(pcm, sizeof pcm, (int)(ackA2.grant_fmt ? ackA2.grant_fmt : AUDIO_FMT_S16), 100, 440, 48000);
|
||||
do_write(&a2, pcm, (uint32_t)wb2);
|
||||
check(cli_connect(&b2) == 0, "connect B2");
|
||||
m_audio_open_ack_t ackB2;
|
||||
check(do_open(&b2, 96000, 2, AUDIO_FMT_S16, &ackB2) == 0 && ackB2.err == MEOW_OK, "open B2 (preempt)");
|
||||
/* A2 应收到 PREEMPTED */
|
||||
uint8_t rbuf[128]; uint16_t rt;
|
||||
int npr = rf_tmo(a2.fd, 1000, &rt, rbuf, sizeof rbuf);
|
||||
check(npr >= (int)sizeof(m_audio_preempted_t) && rt == MSG_AUDIO_PREEMPTED, "A2 got PREEMPTED");
|
||||
if (npr >= (int)sizeof(m_audio_preempted_t)) {
|
||||
m_audio_preempted_t pm; memcpy(&pm, rbuf, sizeof pm);
|
||||
printf(" preempted by %s (stream %u)\n", pm.by_app, (unsigned)pm.stream_id);
|
||||
}
|
||||
/* A2 写被丢弃;B2 写生效 */
|
||||
do_write(&a2, pcm, (uint32_t)wb2); /* 应丢弃(A2 已 draining) */
|
||||
usleep(100000);
|
||||
do_query(&a2, q, sizeof q);
|
||||
printf(" after preempt, A2 query: %s\n", q);
|
||||
do_write(&b2, pcm, (uint32_t)wb2);
|
||||
usleep(100000);
|
||||
do_query(&b2, q, sizeof q);
|
||||
check(strstr(q, "active=1") && strstr(q, "rate=96000"), "B2 active after preempt");
|
||||
do_ctrl(&b2, MSG_AUDIO_STOP, ackB2.stream_id);
|
||||
do_ctrl(&a2, MSG_AUDIO_STOP, ackA2.stream_id);
|
||||
close(a2.fd); close(b2.fd);
|
||||
|
||||
/* ---- T3:EOF 回收(客户端直接断链) ---- */
|
||||
printf("[T3 EOF recycle]\n");
|
||||
struct cli c3;
|
||||
check(cli_connect(&c3) == 0, "connect C3");
|
||||
m_audio_open_ack_t ackC3;
|
||||
check(do_open(&c3, 44100, 2, AUDIO_FMT_S16, &ackC3) == 0, "open C3");
|
||||
/* 不 STOP,直接断链(close fd)→ audiod poll 读到 EOF → 回收 */
|
||||
do_write(&c3, pcm, (uint32_t)gen_sine(pcm, sizeof pcm, AUDIO_FMT_S16, 100, 440, 44100));
|
||||
close(c3.fd);
|
||||
usleep(200000);
|
||||
struct cli c3q; check(cli_connect(&c3q) == 0, "connect C3q");
|
||||
do_query(&c3q, q, sizeof q);
|
||||
printf(" after EOF: %s\n", q);
|
||||
check(strstr(q, "active=0") != NULL, "EOF recycle -> inactive");
|
||||
close(c3q.fd);
|
||||
|
||||
/* ---- T4:tone 写 WAV(由 CLI 走完整 IPC 链路;mipsel/host CLI 共用) ---- */
|
||||
printf("[T4 tone -> WAV]\n");
|
||||
unlink(TWAV);
|
||||
pid_t tone_pid = fork();
|
||||
if (tone_pid == 0) {
|
||||
setenv("MEOW_AUDIO_SOCK", TSOCK, 1);
|
||||
execl("./meow_audio_host", "meow_audio_host", "tone", "440", "500", "44100", (char *)0);
|
||||
_exit(127);
|
||||
}
|
||||
waitpid(tone_pid, NULL, 0);
|
||||
usleep(300000);
|
||||
check(verify_wav(TWAV, 44100, 2, 16) == 0, "WAV valid sine");
|
||||
printf(" wav size: ");
|
||||
{ FILE *f = fopen(TWAV, "rb"); if (f) { fseek(f, 0, SEEK_END); printf("%ld bytes\n", ftell(f)); fclose(f); } }
|
||||
|
||||
stop_audiod();
|
||||
printf("=== SUMMARY: pass=%d fail=%d ===\n", g_pass, g_fail);
|
||||
return g_fail ? 1 : 0;
|
||||
}
|
||||
Reference in new issue
Block a user