一、source device和sink device的概念

source device:source可以理解为源泉,表示声音的源,即声音产生的地方

sink device:sink可以理解为水槽,表示声音的接受一方

那么就可以理解为声音从source device流出,流到sink device里面

二、获取sink device的音量和是不是静音

#include <iostream>
#include <memory>
#include <vector>
#include <string>

#include <pulse/error.h>
#include <pulse/pulseaudio.h>
#include <pulse/simple.h>

enum class PulseAudioContextState {
    PULSE_CONTEXT_INITIALIZING,
    PULSE_CONTEXT_READY,
    PULSE_CONTEXT_FINISHED
};

struct SinkInfo {
    pa_cvolume volume;  //音量
    int mute;           //是不是静音,1表示静音,0表示非静音
};

void DisconnectPulseAudioContext(pa_mainloop** pa_ml, pa_context** pa_ctx) {
    assert(pa_ml);
    assert(pa_ctx);

    if (*pa_ctx) {
        pa_context_set_state_callback(*pa_ctx, NULL, NULL);
        pa_context_disconnect(*pa_ctx);
        pa_context_unref(*pa_ctx);
    }

    if (*pa_ml) pa_mainloop_free(*pa_ml);
    *pa_ml = NULL;
    *pa_ctx = NULL;
}

void PaContextStateCallback(pa_context* pa_ctx, void* userdata) {
    PulseAudioContextState* context_state = (PulseAudioContextState*)userdata;
    switch (pa_context_get_state(pa_ctx)) {
        case PA_CONTEXT_FAILED:
        case PA_CONTEXT_TERMINATED:
            *context_state = PulseAudioContextState::PULSE_CONTEXT_FINISHED;
            break;
        case PA_CONTEXT_READY:
            *context_state = PulseAudioContextState::PULSE_CONTEXT_READY;
            break;
        default:
            break;
    }
}

int ConnectPulseAudioContext(pa_mainloop** pa_ml, pa_context** pa_ctx,
                             const char* server, const char* description) {
    int ret;
    *pa_ml = NULL;
    *pa_ml = pa_mainloop_new();
    if (!(*pa_ml)) { return -1; }

    pa_mainloop_api* pa_mlapi = NULL;
    pa_mlapi = pa_mainloop_get_api(*pa_ml);
    if (!pa_mlapi) { return -1; }

    *pa_ctx = NULL;
    *pa_ctx = pa_context_new(pa_mlapi, description);
    if (!(*pa_ctx)) { return -1; }

    PulseAudioContextState context_state =
            PulseAudioContextState::PULSE_CONTEXT_INITIALIZING;
    pa_context_set_state_callback(*pa_ctx, PaContextStateCallback,
                                  &context_state);
    if (pa_context_connect(*pa_ctx, server, PA_CONTEXT_NOFLAGS, NULL) < 0) {
        return -1;
    }

    while (context_state == PulseAudioContextState::PULSE_CONTEXT_INITIALIZING)
        pa_mainloop_iterate(*pa_ml, 1, NULL);
    if (context_state == PulseAudioContextState::PULSE_CONTEXT_FINISHED) {
        return -1;
    }
    return 0;
}

void PulseAudioSinkDeviceInfoCallback(pa_context* c, const pa_sink_info* info,
                                      int eol, void* userdata) {
    SinkInfo* sink_info = (SinkInfo*)userdata;
    if (info != nullptr) {
        sink_info->volume = info->volume;
        sink_info->mute = info->mute;
        for (int i = 0; i < info->volume.channels; ++i) {
            // 打印各个声道的音量
            std::cout << (info->volume.values[i]*1.0 / info->base_volume)*100 << std::endl;
        }
        std::cout << "mute:" << info->mute << std::endl;
    }
}

SinkInfo GetPulseAudioSinkDeviceInfo(const std::string& sink_device) {
    SinkInfo sink_info;
    pa_mainloop* pa_ml = nullptr;
    pa_operation* pa_op = nullptr;
    pa_context* pa_ctx = nullptr;
    ConnectPulseAudioContext(&pa_ml, &pa_ctx, nullptr, "audio recorder");
    std::shared_ptr<void> raii_connect(nullptr, [&](void*) {
        DisconnectPulseAudioContext(&pa_ml, &pa_ctx);
    });
    if (pa_ctx == nullptr) { return sink_info; }
    pa_op = pa_context_get_sink_info_by_name(pa_ctx, sink_device.c_str(),
                                             PulseAudioSinkDeviceInfoCallback,
                                             &sink_info);
    while (pa_operation_get_state(pa_op) == PA_OPERATION_RUNNING) {
        pa_mainloop_iterate(pa_ml, 1, nullptr);
    }
    pa_operation_unref(pa_op);
    return sink_info;
}

void PulseAudioSinkDeviceCallback(pa_context* c, const pa_sink_info* dev,
                                  int eol, void* userdata) {
    std::vector<std::string>* devices = (std::vector<std::string>*)userdata;
    if (dev != nullptr) { devices->push_back(dev->name); }
}

std::vector<std::string> GetPulseAudioSinkDevice() {
    std::vector<std::string> sink_devices_vec;
    pa_mainloop* pa_ml = nullptr;
    pa_operation* pa_op = nullptr;
    pa_context* pa_ctx = nullptr;
    ConnectPulseAudioContext(&pa_ml, &pa_ctx, nullptr, "audio recorder");
    std::shared_ptr<void> raii_connect(nullptr, [&](void*) {
        DisconnectPulseAudioContext(&pa_ml, &pa_ctx);
    });
    if (pa_ctx == nullptr) { return sink_devices_vec; }
    pa_op = pa_context_get_sink_info_list(pa_ctx, PulseAudioSinkDeviceCallback,
                                          &sink_devices_vec);
    while (pa_operation_get_state(pa_op) == PA_OPERATION_RUNNING) {
        pa_mainloop_iterate(pa_ml, 1, nullptr);
    }
    pa_operation_unref(pa_op);
    return sink_devices_vec;
}

int main(void) {
    std::vector<std::string> sink_devices_vec = GetPulseAudioSinkDevice();
    SinkInfo sink_info = GetPulseAudioSinkDeviceInfo(sink_devices_vec[0]);
    return 0;
}

编译:g++ main.cpp -lpulse -lpulse-simple -o audio_test

运行:./audio_test

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐