/* * meow_ui_audio.h — SDK 独占音频客户端(§11,devdocs/plan/AUDIOD_DESIGN.md §7;meow_ui_audio.c)。 * * 原 meow_ui.h 拆分的一部分(audio 模块)。App 只经本 API 喂 PCM 给 meow_audiod * (独占 hw:0,0 守护,svcd 托管)。audiod 不在一律失败(open 返回 MEOW_ERR_NOENT,负值), * 不做隐式直开回退;调试可用 env MEOW_AUDIO_DIRECT=1 走自带直接 ALSA 直开路径(非正式能力)。 */ #ifndef MEOW_UI_AUDIO_H #define MEOW_UI_AUDIO_H #include "meow_ui_types.h" #ifdef __cplusplus extern "C" { #endif /* ============================================================ */ /* §11 独占音频客户端(devdocs/plan/AUDIOD_DESIGN.md §7;meow_ui_audio.c) */ /* ============================================================ */ /* 说明:App 只经本 API 喂 PCM 给 meow_audiod(独占 hw:0,0 守护,svcd 托管)。audiod 不在一律 * 失败(open 返回 MEOW_ERR_NOENT,负值),不做隐式直开回退;调试可用 env MEOW_AUDIO_DIRECT=1 走 * 自带直接 ALSA 直开路径(非正式能力,勿依赖)。 * 生命周期:open 取 stream_id>0 → write 喂 PCM(阻塞,socket 背压)→ pause/resume → close 释放。 * 被抢占(后开流赢):SDK 内部读线程收 MSG_AUDIO_PREEMPTED → 调 on_preempt 回调 + 标写死 * (后续 write 返回负值,App 应重新 open)。 */ /* 当前流快照(meow_audio_query 填充)。 */ typedef struct meow_audio_state { int active; /* 1=audiod 有活跃流(不一定是本 App 的) */ char app_id[32]; /* 活跃流 app_id(无流为 "-") */ uint32_t stream_id; /* 活跃流 id */ uint32_t rate; /* 活跃流采样率 */ uint16_t ch; /* 活跃流声道数 */ uint16_t fmt; /* 活跃流格式(AUDIO_FMT_*) */ uint64_t written_frames; /* 客户端写入 ring 的帧 */ uint64_t played_frames; /* 播放线程写到设备的帧 */ uint32_t underrun; /* xrun 计数 */ int paused; /* 当前流是否暂停 */ int route_port; /* 当前路由口(-1 未知) */ int vol; /* 当前音量 0-100 */ int dsd; /* 当前流是否为 DoP DSD(fmt==AUDIO_FMT_DOP 且 audiod 已授权) */ } meow_audio_state; /* 被抢占回调:by_app=抢占者 app_id(非空)。ctx 由 on_preempt 注册时传入。 */ typedef void (*meow_audio_preempt_fn)(const char *by_app, void *ctx); /* 开流。rate/ch/fmt=App 声源原生参数(fmt 值见 meow_ipc.h AUDIO_FMT_*)。 * 返回 stream_id(>0);负错误码(MEOW_ERR_NOENT=audiod 不在;MEOW_ERR_INVAL=参数非法; * MEOW_ERR_NODEV=无可用 PCM 设备;MEOW_ERR_* 常量本身即负值)。open 后客户端须按内部记录的 * grant 格式产出 PCM。 */ int meow_audio_open(int rate, int ch, int fmt); /* 开流(带 flags)。flags=AUDIO_FLAG_DSD 且 fmt=AUDIO_FMT_DOP 时为 DoP DSD 流(A3+DSD 定案): * App 侧须把 DSD 解码封装为 DoP(每样本 32-bit 容器:16bit DSD 数据 + 标记 + 0),audiod 只透传 * (不引 dsd2pcm、不做 DOP_EN 之外的任何处理)。DSD 打不开/不支持 → open 返回负错误码(报错关流)。 */ int meow_audio_open_ex(int rate, int ch, int fmt, uint32_t flags); /* 写 PCM。pcm=native fmt/ch 样本(open 传的参数),frames=帧数;内部归一成 grant 并分批发送。 * 返回写入帧数;<0=流已死(被抢占/audiod 断开),App 应重新 open。阻塞(socket 背压)。 */ int meow_audio_write(const void *pcm, int frames); /* 暂停(1)/恢复(0)当前流。返回 0 成功;<0 失败。 */ int meow_audio_pause(int sid, int paused); /* 关流(STOP + 释放)。返回 0;连接保留供下次 open。 */ int meow_audio_close(int sid); /* 查询 audiod 快照(填充 out)。返回 0 成功;<0 失败。 */ int meow_audio_query(meow_audio_state *out); /* 设置音量 0-100(audiod 唯一权威:写 DAC numid=1/2 + DSD_VOLUME 双组保切换连续 + 持久化 * settings.conf audio.volume,与 launcher/watchdog 同源同键)。BT 输出目标时音量由 AVRCP 侧控, * audiod 不下发 DAC 反转。返回 0 成功;<0 失败(audiod 不在)。 */ int meow_audio_set_volume(int pct); /* 注册被抢占回调(可选;内部读线程分发)。重复调用幂等。 */ void meow_audio_on_preempt(meow_audio_preempt_fn cb, void *ctx); #ifdef __cplusplus } #endif #endif /* MEOW_UI_AUDIO_H */