You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

ALSA sequencer队列运行异常:snd_seq_queue_status_get_tick_time()始终返回0

解决ALSA队列snd_seq_queue_status_get_tick_time()始终返回0的问题

我来帮你搞定这个问题——你的代码里创建并启动了队列,但tick_time一直返回0的核心原因是这个队列没有绑定有效的时钟源,也没有配置节拍器来驱动tick计数。ALSA的序列队列不会自动运行tick,必须手动配置时钟和节拍参数才行。

问题根源

ALSA的seq队列默认是“静止”的:

  • 没有指定时钟源,队列不知道用什么来计时
  • 没有设置节拍(tempo)和tick分辨率,队列没有递增tick的依据
  • 单纯调用snd_seq_start_queue只会让队列进入“就绪”状态,但没有实际的驱动源来推进tick

修复步骤

我们需要给队列添加三个关键配置:

  1. 绑定系统时钟作为队列的计时源
  2. 设置节拍(比如每分钟120拍)和每拍对应的tick数
  3. 优化代码,避免重复分配释放queue_status结构(可选,但更高效)

修改后的完整代码

#include <stdio.h>
#include <alsa/asoundlib.h>
#include <unistd.h> // 确保sleep函数可用

int queue;
snd_seq_t *seq_handle;
snd_seq_tick_time_t current_tick;
snd_seq_queue_status_t *status;
snd_seq_queue_tempo_t *tempo;

int main (int argc, char **argv) {
    int err;

    // 打开序列设备
    if ((err = snd_seq_open(&seq_handle, "default", SND_SEQ_OPEN_INPUT | SND_SEQ_OPEN_OUTPUT, 0)) < 0) {
        fprintf(stderr, "无法打开序列设备: %s\n", snd_strerror(err));
        return 1;
    }

    // 分配队列
    queue = snd_seq_alloc_queue(seq_handle);
    if (queue < 0) {
        fprintf(stderr, "分配队列失败: %s\n", snd_strerror(queue));
        snd_seq_close(seq_handle);
        return 1;
    }
    fprintf(stderr,"queue id=%d\n", queue);

    // 1. 设置队列时钟源为系统时钟
    if ((err = snd_seq_set_queue_clock(seq_handle, queue, SND_SEQ_CLOCK_SYSTEM)) < 0) {
        fprintf(stderr, "设置队列时钟失败: %s\n", snd_strerror(err));
        goto cleanup;
    }

    // 2. 分配并设置节拍参数:120BPM,每拍48个tick
    snd_seq_queue_tempo_malloc(&tempo);
    snd_seq_queue_tempo_set_tempo(tempo, 120, 48); // 参数:tempo结构体,BPM,每拍tick数
    if ((err = snd_seq_set_queue_tempo(seq_handle, queue, tempo)) < 0) {
        fprintf(stderr, "设置队列节拍失败: %s\n", snd_strerror(err));
        snd_seq_queue_tempo_free(tempo);
        goto cleanup;
    }
    snd_seq_queue_tempo_free(tempo);

    // 启动队列(此时队列会开始基于时钟和节拍递增tick)
    if ((err = snd_seq_start_queue(seq_handle, queue, NULL)) < 0) {
        fprintf(stderr, "启动队列失败: %s\n", snd_strerror(err));
        goto cleanup;
    }

    // 预分配队列状态结构体,避免循环内重复分配释放
    snd_seq_queue_status_malloc(&status);

    for(int i=0;i<4;i++) {
        // 获取队列状态
        if ((err = snd_seq_get_queue_status(seq_handle, queue, status)) < 0) {
            fprintf(stderr, "获取队列状态失败: %s\n", snd_strerror(err));
            break;
        }
        current_tick = snd_seq_queue_status_get_tick_time(status);
        fprintf(stderr,"tick=%lu\n", current_tick ); // 注意:tick_time是unsigned long类型,用%lu打印
        sleep(1);
    }

    snd_seq_queue_status_free(status);

cleanup:
    snd_seq_stop_queue(seq_handle, queue, NULL);
    snd_seq_free_queue(seq_handle, queue);
    snd_seq_close(seq_handle);
    return 0;
}

关键修改点说明

  • 绑定时钟源snd_seq_set_queue_clock指定用系统时钟驱动队列,确保队列有计时基准
  • 设置节拍参数snd_seq_queue_tempo_set_tempo定义了每分钟节拍数(BPM)和每拍对应的tick数,这样队列就能根据时间计算出tick的递增速度
  • 类型匹配snd_seq_tick_time_tunsigned long类型,打印时要用%lu而不是%d,避免类型转换错误
  • 资源优化:把queue_status的分配移到循环外,减少内存分配开销
  • 错误处理:添加了完整的错误检查,方便排查问题

现在运行修改后的代码,你应该能看到tick值随着时间递增了。

内容的提问来源于stack exchange,提问作者johnnyO

火山引擎 最新活动