You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

GStreamer audiofirfilter使用求助:管道故障及无FFT示例需求

解决GStreamer audiofirfilter单位脉冲响应管道无声音问题

我完全理解你在嵌入式系统上折腾audiofirfilter的烦恼——找不到可用示例,自己写的管道又没声音。先帮你排查代码里的几个关键问题,再给一个不需要FFT的极简可运行示例。

你的代码里的核心问题

  1. Kernel数组的GValueArray初始化错误
    你创建了一个长度为1的GValueArray,然后循环追加16个值,这会导致数组长度异常,audiofirfilter无法正确读取内核数据。应该直接初始化对应长度的数组,或者用更便捷的GStreamer辅助函数。

  2. Latency计算错误
    FIR滤波器的延迟是(内核长度 - 1)/2,你的内核长度是16,所以正确的latency应该是(16-1)/2 = 7,而不是16/2=8,错误的延迟设置可能导致音频被丢弃或不同步。

  3. FakeSrc的Handoff回调未生成正确的单位脉冲数据
    如果你的SourceHandoffCallback没有填充单位脉冲的音频样本(第一个样本为最大值,其余为0),管道自然输出静音。

修正后的无FFT示例代码

下面是一个简化且可运行的版本,专注于单位脉冲响应的实现:

#include <gst/gst.h>
#include <glib.h>

typedef struct _CustomData {
  GstElement *pipeline;
  GstElement *fakesrc;
  GstElement *capsfilter;
  GstElement *audioconvert;
  GstElement *audiofirfilter;
  GstElement *alsasink;
  gboolean pulse_generated; // 标记是否已经发送了单位脉冲
} CustomData;

// Handoff回调:生成单位脉冲数据
static void SourceHandoffCallback(GstElement *src, GstBuffer *buffer, GstPad *pad, CustomData *data) {
  if (data->pulse_generated) {
    // 已经发送过脉冲,后续填充静音
    GstMapInfo map;
    if (gst_buffer_map(buffer, &map, GST_MAP_WRITE)) {
      memset(map.data, 0, map.size);
      gst_buffer_unmap(buffer, &map);
    }
    return;
  }

  // 生成单位脉冲:第一个样本设为S16LE的最大值,其余为0
  GstMapInfo map;
  if (gst_buffer_map(buffer, &map, GST_MAP_WRITE)) {
    gint16 *samples = (gint16 *)map.data;
    // 立体声,所以第一个左声道样本设为32767,右声道也设为32767(或者按需调整)
    samples[0] = G_MAXINT16;
    samples[1] = G_MAXINT16;
    // 其余样本填充0
    memset(samples + 2, 0, map.size - sizeof(gint16)*2);
    gst_buffer_unmap(buffer, &map);
    data->pulse_generated = TRUE;
  }
}

int main(int argc, char *argv[]) {
  CustomData data = {0};
  GstCaps *caps;
  gdouble filter_kernel[16] = {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};

  gst_init(&argc, &argv);

  // 创建元素
  data.pipeline = gst_pipeline_new("pipeline");
  data.fakesrc = gst_element_factory_make("fakesrc", NULL);
  data.capsfilter = gst_element_factory_make("capsfilter", NULL);
  data.audioconvert = gst_element_factory_make("audioconvert", NULL);
  data.audiofirfilter = gst_element_factory_make("audiofirfilter", NULL);
  data.alsasink = gst_element_factory_make("alsasink", NULL);

  if (!data.pipeline || !data.fakesrc || !data.capsfilter || !data.audioconvert || !data.audiofirfilter || !data.alsasink) {
    g_printerr("Failed to create elements!\n");
    return -1;
  }

  // 添加元素到管道并链接
  gst_bin_add_many(GST_BIN(data.pipeline), data.fakesrc, data.capsfilter, data.audioconvert, data.audiofirfilter, data.alsasink, NULL);
  if (!gst_element_link_many(data.fakesrc, data.capsfilter, data.audioconvert, data.audiofirfilter, data.alsasink, NULL)) {
    g_printerr("Failed to link elements!\n");
    return -1;
  }

  // 设置Caps:立体声S16LE,44.1kHz(可根据你的SAMPLING_FREQUENCY调整)
  caps = gst_caps_new_simple("audio/x-raw",
    "format", G_TYPE_STRING, "S16LE",
    "rate", G_TYPE_INT, 44100,
    "channels", G_TYPE_INT, 2,
    "layout", G_TYPE_STRING, "interleaved",
    NULL);
  g_object_set(G_OBJECT(data.capsfilter), "caps", caps, NULL);
  gst_caps_unref(caps);

  // 设置FakeSrc:开启handoff回调,同步,设置合适的缓冲区大小
  g_object_set(G_OBJECT(data.fakesrc),
    "sync", TRUE,
    "signal-handoffs", TRUE,
    "sizemax", 4096,
    "sizetype", 2,
    NULL);
  g_signal_connect(data.fakesrc, "handoff", G_CALLBACK(SourceHandoffCallback), &data);

  // 设置audiofirfilter的Kernel:用更便捷的辅助函数
  GValueArray *kernel_array = gst_value_array_from_double_array(filter_kernel, 16);
  g_object_set(G_OBJECT(data.audiofirfilter), "kernel", kernel_array, NULL);
  g_value_array_free(kernel_array);

  // 设置正确的Latency
  gint kernel_length = 16;
  g_object_set(G_OBJECT(data.audiofirfilter), "latency", (gint64)((kernel_length - 1)/2), NULL);

  // 启动管道
  gst_element_set_state(data.pipeline, GST_STATE_PLAYING);

  // 等待用户输入退出
  g_print("Press enter to stop...\n");
  gchar *line = g_get_line(stdin);
  g_free(line);

  // 清理资源
  gst_element_set_state(data.pipeline, GST_STATE_NULL);
  gst_object_unref(data.pipeline);

  return 0;
}

关键说明

  • Kernel设置:用gst_value_array_from_double_array直接从double数组生成GValueArray,避免手动初始化的错误。
  • Latency修正:按照(内核长度-1)/2计算,确保音频同步。
  • 单位脉冲生成:在handoff回调中,只在第一次缓冲区填充脉冲信号,后续填充静音,这样你能清晰听到脉冲的输出。
  • 嵌入式适配:这个示例没有依赖FFT相关库,完全基于GStreamer基础组件,适合嵌入式系统编译运行。

编译时记得链接GStreamer库,比如:

gcc your_code.c -o audiofir_test `pkg-config --cflags --libs gstreamer-1.0 gstreamer-audio-1.0`

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:59:18