4. 音频测试源播放到声卡

任务目标

构建一条最小GStreamer管道,使用audiotestsrc生成测试音频信号,经autoaudiosink输出到系统默认声卡,并通过volume元素实时调节音量。最终通过命令行参数控制音量和波形类型,验证声卡出声且音量可调。

环境依赖

  • GStreamer核心库及基础插件:gstreamer-1.0、gst-plugins-base、gst-plugins-good(audiotestsrc位于good插件包)
  • ALSA或PulseAudio系统声卡驱动(autoaudiosink自动探测)
  • 安装命令(Debian/Ubuntu):sudo apt install gstreamer1.0-tools gstreamer1.0-plugins-base gstreamer1.0-plugins-good gstreamer1.0-alsa

完整实现

以下C程序构建管道:audiotestsrc → volume → autoaudiosink。通过命令行参数指定波形类型(sine/square/saw)和音量(0.0~1.0),并支持键盘交互实时调节音量。

// play_test_tone.c
#include <gst/gst.h>
#include <stdio.h>
#include <string.h>

static GstElement *volume = NULL;

static gboolean bus_call(GstBus *bus, GstMessage *msg, gpointer data) {
    switch (GST_MESSAGE_TYPE(msg)) {
        case GST_MESSAGE_ERROR: {
            GError *err = NULL;
            gchar *debug = NULL;
            gst_message_parse_error(msg, &err, &debug);
            g_printerr("Error: %s\n", err->message);
            g_error_free(err);
            g_free(debug);
            g_main_loop_quit((GMainLoop *)data);
            break;
        }
        case GST_MESSAGE_EOS:
            g_main_loop_quit((GMainLoop *)data);
            break;
        default:
            break;
    }
    return TRUE;
}

static gboolean keyboard_cb(GIOChannel *src, GIOCondition cond, gpointer data) {
    gchar buf[16];
    gsize len;
    g_io_channel_read_line(src, &buf, &len, NULL, NULL);
    gdouble vol = 0.0;
    if (sscanf(buf, "%lf", &vol) == 1) {
        vol = CLAMP(vol, 0.0, 1.0);
        g_object_set(volume, "volume", vol, NULL);
        g_print("音量已设为: %.2f\n", vol);
    } else {
        g_print("输入无效,请输入0.0~1.0之间的数值\n");
    }
    return TRUE;
}

int main(int argc, char *argv[]) {
    gst_init(&argc, &argv);

    // 解析参数: 波形类型(默认sine) 音量(默认0.5)
    const gchar *wave = "sine";
    gdouble vol = 0.5;
    if (argc > 1) wave = argv[1];
    if (argc > 2) vol = atof(argv[2]);
    vol = CLAMP(vol, 0.0, 1.0);

    GMainLoop *loop = g_main_loop_new(NULL, FALSE);

    // 创建元素
    GstElement *pipeline = gst_pipeline_new("test-pipeline");
    GstElement *src = gst_element_factory_make("audiotestsrc", "src");
    volume = gst_element_factory_make("volume", "volume");
    GstElement *sink = gst_element_factory_make("autoaudiosink", "sink");

    if (!pipeline || !src || !volume || !sink) {
        g_printerr("元素创建失败,请检查插件是否安装\n");
        return -1;
    }

    // 设置属性
    g_object_set(src, "wave", wave, "freq", 440, NULL);
    g_object_set(volume, "volume", vol, NULL);

    // 组装管道
    gst_bin_add_many(GST_BIN(pipeline), src, volume, sink, NULL);
    if (!gst_element_link_many(src, volume, sink, NULL)) {
        g_printerr("管道连接失败\n");
        gst_object_unref(pipeline);
        return -1;
    }

    // 总线监听
    GstBus *bus = gst_element_get_bus(pipeline);
    gst_bus_add_watch(bus, bus_call, loop);
    gst_object_unref(bus);

    // 键盘监听(stdin)
    GIOChannel *io = g_io_channel_unix_new(fileno(stdin));
    g_io_add_watch(io, G_IO_IN, keyboard_cb, NULL);

    g_print("波形: %s, 初始音量: %.2f\n", wave, vol);
    g_print("输入0.0~1.0数值实时调节音量,Ctrl+C退出\n");

    gst_element_set_state(pipeline, GST_STATE_PLAYING);
    g_main_loop_run(loop);

    // 清理
    g_io_channel_unref(io);
    gst_element_set_state(pipeline, GST_STATE_NULL);
    gst_object_unref(pipeline);
    g_main_loop_unref(loop);
    return 0;
}

编译与运行

# 编译
gcc play_test_tone.c -o play_test_tone $(pkg-config --cflags --libs gstreamer-1.0)

# 运行(默认正弦波440Hz,音量0.5)
./play_test_tone

# 指定方波,音量0.8
./play_test_tone square 0.8

# 指定锯齿波,音量0.2
./play_test_tone saw 0.2

验证方法

  1. 出声验证:运行程序后应听到对应波形的声音(正弦波为单音,方波为更尖锐的同频音,锯齿波为明亮音色)。若无声,检查系统音量及声卡设备:aplay -l确认声卡存在,pactl list sinks确认默认输出。
  2. 音量调节验证:程序运行后,在终端输入0.0应静音,输入1.0为最大音量,输入0.3等中间值应听到音量明显变化。输入非法字符(如abc)应提示无效。
  3. 波形切换验证:分别以sine、square、saw参数运行,应听到音色差异。若波形参数拼写错误,程序会报错退出(GStreamer属性设置失败)。

常见问题

  • 无声输出:先执行gst-launch-1.0 audiotestsrc num-buffers=100 ! autoaudiosink测试基础管道。若仍无声,检查系统音频服务(PulseAudio/Alsa)是否正常运行。
  • 音量调节无效果:确认输入的是0~1之间的浮点数(如0.5),整数1会被解析为1.0。若输入2会被自动钳位到1.0。
  • 编译错误:确认已安装libgstreamer1.0-dev开发包,且pkg-config能正确返回GStreamer编译参数。