83 lines
3.5 KiB
C
83 lines
3.5 KiB
C
/* tonepcm.c — 直开 /dev/snd/pcmC0D0p 写 N 秒正弦并计时(不需要耳朵的 DAC 时钟验证):
|
|
* 若写 5s PCM 的墙钟 ≈5s(扣除缓冲),说明硬件时钟在走、DMA 在消费;
|
|
* 若瞬间返回,说明硬件没在走(静音根因在时钟/电源域,不在数据)。
|
|
* 用法: tonepcm [sec] [freq] 静态 mipsel。 */
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <time.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 long long now_ms(void)
|
|
{
|
|
struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts);
|
|
return (long long)ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
int sec = argc > 1 ? atoi(argv[1]) : 5;
|
|
int freq = argc > 2 ? atoi(argv[2]) : 440;
|
|
int rate = 44100, ch = 2;
|
|
|
|
int fd = open("/dev/snd/pcmC0D0p", O_RDWR);
|
|
if (fd < 0) { printf("open pcmC0D0p fail\n"); return 1; }
|
|
|
|
struct snd_pcm_hw_params p;
|
|
memset(&p, 0, sizeof p);
|
|
pmask(&p.masks[SNDRV_PCM_HW_PARAM_ACCESS - SNDRV_PCM_HW_PARAM_FIRST_MASK],
|
|
SNDRV_PCM_ACCESS_RW_INTERLEAVED);
|
|
pmask(&p.masks[SNDRV_PCM_HW_PARAM_FORMAT - SNDRV_PCM_HW_PARAM_FIRST_MASK],
|
|
SNDRV_PCM_FORMAT_S16_LE);
|
|
pmask(&p.masks[SNDRV_PCM_HW_PARAM_SUBFORMAT - SNDRV_PCM_HW_PARAM_FIRST_MASK],
|
|
SNDRV_PCM_SUBFORMAT_STD);
|
|
pivset(&p.intervals[SNDRV_PCM_HW_PARAM_CHANNELS - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL], ch);
|
|
pivset(&p.intervals[SNDRV_PCM_HW_PARAM_RATE - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL], rate);
|
|
p.rmask = ~0u;
|
|
if (ioctl(fd, SNDRV_PCM_IOCTL_HW_PARAMS, &p) != 0) { printf("HW_PARAMS fail\n"); close(fd); return 1; }
|
|
printf("HW_PARAMS ok (rate interval now [%u,%u])\n",
|
|
p.intervals[SNDRV_PCM_HW_PARAM_RATE - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL].min,
|
|
p.intervals[SNDRV_PCM_HW_PARAM_RATE - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL].max);
|
|
if (ioctl(fd, SNDRV_PCM_IOCTL_PREPARE, 0) != 0) { printf("PREPARE fail\n"); close(fd); return 1; }
|
|
|
|
/* 生成 1 秒正弦(整数查表近似:简单累加相位,定点幅度 0.3 满幅) */
|
|
int frames1s = rate;
|
|
short *buf = malloc((size_t)frames1s * ch * 2);
|
|
for (int i = 0; i < frames1s; i++) {
|
|
/* 粗略正弦:三角波即可(验证用) */
|
|
int period = rate / freq;
|
|
int ph = i % period;
|
|
int v = (ph < period / 2) ? (ph * 4 - period) : (3 * period - ph * 4);
|
|
short s = (short)(v * 9830 / period); /* ~0.3 * 32767 */
|
|
buf[i * 2] = s; buf[i * 2 + 1] = s;
|
|
}
|
|
|
|
long long t0 = now_ms();
|
|
long long written = 0;
|
|
for (int s2 = 0; s2 < sec; s2++) {
|
|
ssize_t w = write(fd, buf, (size_t)frames1s * ch * 2);
|
|
if (w <= 0) { printf("write fail at sec %d (ret=%zd)\n", s2, w); break; }
|
|
written += w;
|
|
}
|
|
long long el = now_ms() - t0;
|
|
long long bytes = (long long)sec * frames1s * ch * 2;
|
|
printf("wrote %lld/%lld bytes in %lld ms (expected ~%d ms if hw clocking; instant = clock dead)\n",
|
|
written, bytes, el, sec * 1000);
|
|
|
|
struct snd_pcm_status st;
|
|
memset(&st, 0, sizeof st);
|
|
if (ioctl(fd, SNDRV_PCM_IOCTL_STATUS, &st) == 0)
|
|
printf("STATUS: state=%d avail=%lu\n", st.state, (unsigned long)st.avail);
|
|
close(fd);
|
|
return 0;
|
|
}
|