PulseAudio:ソース情報

ソース情報
「シンク (sink)」は出力のことでしたが、「ソース (source)」は、サーバー側で認識される、オーディオ入力のことです。
シンクと同様に、ソースの情報を取得してみます。
ソース情報の取得
pa_operation *pa_context_get_source_info_by_index(pa_context *c, uint32_t idx,
    pa_source_info_cb_t cb, void *userdata);

pa_operation *pa_context_get_source_info_by_name(pa_context *c, const char *name,
    pa_source_info_cb_t cb, void *userdata);

pa_operation *pa_context_get_source_info_list(pa_context *c, pa_source_info_cb_t cb, void *userdata);

//コールバック関数
typedef void (*pa_source_info_cb_t)(pa_context *c, const pa_source_info *i, int eol, void *userdata);

関数名が異なるだけで、シンクの場合とほぼ同じです。
情報構造体
typedef struct pa_source_info {
    const char *name;
    uint32_t index;
    const char *description;
    pa_sample_spec sample_spec;
    pa_channel_map channel_map;
    uint32_t owner_module;
    pa_cvolume volume;
    int mute;
    uint32_t monitor_of_sink;
    const char *monitor_of_sink_name;
    pa_usec_t latency;
    const char *driver;
    pa_source_flags_t flags;
    pa_proplist *proplist;
    pa_usec_t configured_latency;
    pa_volume_t base_volume;
    pa_source_state_t state;
    uint32_t n_volume_steps;
    uint32_t card;
    uint32_t n_ports;
    pa_source_port_info **ports;
    pa_source_port_info *active_port;
    uint8_t n_formats;
    pa_format_info **formats;
} pa_source_info;

//ポート情報

typedef struct pa_source_port_info {
    const char *name;
    const char *description;
    uint32_t priority;
    int available;
    const char *availability_group;
    uint32_t type;
} pa_source_port_info;

//フラグ
typedef enum pa_source_flags {
    PA_SOURCE_NOFLAGS = 0x0000U,
    PA_SOURCE_HW_VOLUME_CTRL = 0x0001U,
    PA_SOURCE_LATENCY = 0x0002U,
    PA_SOURCE_HARDWARE = 0x0004U,
    PA_SOURCE_NETWORK = 0x0008U,
    PA_SOURCE_HW_MUTE_CTRL = 0x0010U,
    PA_SOURCE_DECIBEL_VOLUME = 0x0020U,
    PA_SOURCE_DYNAMIC_LATENCY = 0x0040U,
    PA_SOURCE_FLAT_VOLUME = 0x0080U
};

//状態
typedef enum pa_source_state {
    PA_SOURCE_INVALID_STATE = -1,
    PA_SOURCE_RUNNING = 0,
    PA_SOURCE_IDLE = 1,
    PA_SOURCE_SUSPENDED = 2
};

pa_source_info 構造体なども、基本的に内容はシンクと同じです。
monitor_of_sink と monitor_of_sink_name メンバが異なるくらいです。
プログラム
サーバーから、ソースの情報を取得して、出力するプログラムです。

$ cc -o 09-source 09-source.c util.c -lpulse

#include <stdio.h>
#include <pulse/pulseaudio.h>
#include "util.h"

PulseData *pulse;

static void _put_flags(uint32_t f)
{
    if(f & PA_SOURCE_HW_VOLUME_CTRL) printf("HW_VOLUME_CTRL:");
    if(f & PA_SOURCE_LATENCY) printf("LATENCY:");
    if(f & PA_SOURCE_HARDWARE) printf("HARDWARE:");
    if(f & PA_SOURCE_NETWORK) printf("NETWORK:");
    if(f & PA_SOURCE_HW_MUTE_CTRL) printf("HW_MUTE_CTRL:");
    if(f & PA_SOURCE_DECIBEL_VOLUME) printf("DECIBEL_VOLUME:");
    if(f & PA_SOURCE_DYNAMIC_LATENCY) printf("DYNAMIC_LATENCY:");
    if(f & PA_SOURCE_FLAT_VOLUME) printf("FLAT_VOLUME:");

    printf("\n");
}

static void _source_callback(pa_context *c,const pa_source_info *i,int eol,void *userdata)
{
    int j;
    char m[64];
    pa_source_port_info *sport;

    if(!i)
    {
        pa_threaded_mainloop_signal(pulse->mainloop, 0);
        return;
    }

    printf("=== [%d] ===\n", i->index);
    printf("name: %s\n", i->name);
    printf("description: '%s'\n", i->description);
    
    pa_sample_spec_snprint(m, 64, &i->sample_spec);
    printf("sample_spcec: %s\n", m);

    pa_channel_map_snprint(m, 64, &i->channel_map);
    printf("channel_map: '%s'\n", m);

    printf("owner_module: %u\n", i->owner_module);

    pa_cvolume_snprint(m, 64, &i->volume);
    printf("volume: '%s'\n", m);

    printf("mute: %d\n", i->mute);
    printf("monitor_of_sink: %u\n", i->monitor_of_sink);
    printf("monitor_of_sink_name: %s\n", i->monitor_of_sink_name);
    printf("latency(us): %lu\n", i->latency);
    printf("driver: %s\n", i->driver);
    printf("flags: 0x%X\n ", i->flags);

    _put_flags(i->flags);

    printf("configured_latency(us): %lu\n", i->configured_latency);
    
    pa_volume_snprint(m, 64, i->base_volume);
    printf("base_volume: %u '%s'\n", i->base_volume, m);

    printf("state: %d\n", i->state);
    printf("n_volume_steps: %u\n", i->n_volume_steps);
    printf("card: %u\n", i->card);
    printf("n_ports: %u\n", i->n_ports);
    printf("n_formats: %u\n", i->n_formats);

    pulse_put_proplist(i->proplist);

    //ports

    printf("\n-- ports --\n");

    for(j = 0; j < i->n_ports; j++)
    {
        sport = i->ports[j];
        
        printf("-[%d]-%s\n", j, (sport == i->active_port)? "(*)": "");

        printf("name: %s\n", sport->name);
        printf("description: %s\n", sport->description);
        printf("priority: %u\n", sport->priority);
        printf("available: %d\n", sport->available);
        printf("availability_group: %s\n", sport->availability_group);
        printf("type: %u\n", sport->type);
    }

    //formats

    printf("\n-- formats --\n");

    for(j = 0; j < i->n_formats; j++)
    {
        printf("[%d] encoding:%d\n", j, i->formats[j]->encoding);

        pulse_put_proplist(i->formats[j]->plist);
    }

    printf("\n");
}

static pa_operation *_get_info(PulseData *p,void *data)
{
    return pa_context_get_source_info_list(pulse->ctx, _source_callback, NULL);
}

int main(void)
{
    pulse = pulse_connect(0);
    if(!pulse) return 1;

    pulse_wait_operation(pulse, _get_info, NULL);

    pulse_free(pulse);

    return 0;
}
実行結果
pavucontrol などで、オーディオ入力が利用可能なプロファイルを選択しておいてください。

=== [0] ===
name: alsa_output.pci-0000_00_1f.3.analog-stereo.monitor
description: 'Monitor of 内部オーディオ アナログステレオ'
sample_spcec: s16le 2ch 44100Hz
channel_map: 'front-left,front-right'
owner_module: 6
volume: '0: 100% 1: 100%'
mute: 0
monitor_of_sink: 0
monitor_of_sink_name: alsa_output.pci-0000_00_1f.3.analog-stereo
latency(us): 0
driver: module-alsa-card.c
flags: 0x62
 LATENCY:DECIBEL_VOLUME:DYNAMIC_LATENCY:
configured_latency(us): 0
base_volume: 65536 '100%'
state: 2
n_volume_steps: 65537
card: 0
n_ports: 0
n_formats: 1
<proplist>
device.description = "Monitor of 内部オーディオ アナログステレオ"
device.class = "monitor"
alsa.card = "0"
alsa.card_name = "HDA Intel PCH"
alsa.long_card_name = "HDA Intel PCH at 0xdf140000 irq 130"
alsa.driver_name = "snd_hda_intel"
device.bus_path = "pci-0000:00:1f.3"
sysfs.path = "/devices/pci0000:00/0000:00:1f.3/sound/card0"
device.bus = "pci"
device.vendor.id = "8086"
device.vendor.name = "Intel Corporation"
device.product.id = "a170"
device.product.name = "100 Series/C230 Series Chipset Family HD Audio Controller"
device.form_factor = "internal"
device.string = "0"
module-udev-detect.discovered = "1"
device.icon_name = "audio-card-pci"

-- ports --

-- formats --
[0] encoding:1
<proplist>


=== [1] ===
name: alsa_input.pci-0000_00_1f.3.analog-stereo
description: '内部オーディオ アナログステレオ'
sample_spcec: s16le 2ch 44100Hz
channel_map: 'front-left,front-right'
owner_module: 6
volume: '0:  27% 1:  27%'
mute: 0
monitor_of_sink: 4294967295
monitor_of_sink_name: (null)
latency(us): 0
driver: module-alsa-card.c
flags: 0x77
 HW_VOLUME_CTRL:LATENCY:HARDWARE:HW_MUTE_CTRL:DECIBEL_VOLUME:DYNAMIC_LATENCY:
configured_latency(us): 0
base_volume: 6554 ' 10%'
state: 2
n_volume_steps: 65537
card: 0
n_ports: 3
n_formats: 1
<proplist>
alsa.resolution_bits = "16"
device.api = "alsa"
device.class = "sound"
alsa.class = "generic"
alsa.subclass = "generic-mix"
alsa.name = "ALC892 Analog"
alsa.id = "ALC892 Analog"
alsa.subdevice = "0"
alsa.subdevice_name = "subdevice #0"
alsa.device = "0"
alsa.card = "0"
alsa.card_name = "HDA Intel PCH"
alsa.long_card_name = "HDA Intel PCH at 0xdf140000 irq 130"
alsa.driver_name = "snd_hda_intel"
device.bus_path = "pci-0000:00:1f.3"
sysfs.path = "/devices/pci0000:00/0000:00:1f.3/sound/card0"
device.bus = "pci"
device.vendor.id = "8086"
device.vendor.name = "Intel Corporation"
device.product.id = "a170"
device.product.name = "100 Series/C230 Series Chipset Family HD Audio Controller"
device.form_factor = "internal"
device.string = "front:0"
device.buffering.buffer_size = "352800"
device.buffering.fragment_size = "176400"
device.access_mode = "mmap+timer"
device.profile.name = "analog-stereo"
device.profile.description = "アナログステレオ"
device.description = "内部オーディオ アナログステレオ"
module-udev-detect.discovered = "1"
device.icon_name = "audio-card-pci"

-- ports --
-[0]-(*)
name: analog-input-front-mic
description: フロントマイクロフォン
priority: 8500
available: 1
availability_group: (null)
type: 5
-[1]-
name: analog-input-rear-mic
description: リアマイクロフォン
priority: 8200
available: 1
availability_group: (null)
type: 5
-[2]-
name: analog-input-linein
description: ラインイン
priority: 8100
available: 1
availability_group: (null)
type: 4

-- formats --
[0] encoding:1
<proplist>

シンクの時とは異なり、2つのソースが作成されています。

「Monitor of 内部オーディオ アナログステレオ」は、flags に HARDWARE がないので、PulseAudio が仮想的に作成したソースです。
物理的なポートは利用できないので、ポート情報はありません。

monitor_of_sink, monitor_of_sink_name は、後で説明しますが、モニターソースが接続しているシンクの、インデックスと名前です。
録音時、このソースに接続すると、シンクでオーディオ出力されたものを、入力にすることができます。

もう一方の「内部オーディオ アナログステレオ」は、ハードウェア上のアナログ入力です。
前面マイク端子・背面マイク端子・ラインインの、3つのポートがあります。