pulseaudio-utils 的简单使用
2021年9月30日 · 1048 字 · 3 分钟
使用 Pulse Audio:
- 查看 sinks, sources
- 播放 wav 文件,
- 录制系统输出的声音
- 录制指定软件输出的声音
- 通过代码(c),录制和播放 pcm 数据
## 安装
sudo apt install pulseaudio-utils
命令行工具的使用
基本使用
pactl list [TAB 联想]
#------------------------------------------------------
cards -- list available cards
clients -- list connected clients
modules -- list loaded modules
samples -- list samples
sink-inputs -- list connected sink inputs
sinks -- list available sinks
source-outputs -- list connected source outputs
sources -- list available sources
#------------------------------------------------------
## sinks
pactl list short sinks
#------------------------------------------------------
0 alsa_output.pci-0000_00_1f.3.analog-stereo module-alsa-card.c s16le 2ch 44100Hz RUNNING
1 recording module-null-sink.c s16le 2ch 44100Hz SUSPENDED
#------------------------------------------------------
## sources
pactl list short sources
#------------------------------------------------------
0 alsa_input.usb-046d_HD_Pro_Webcam_C920_D4E907BF-02.analog-stereo module-alsa-card.c s16le 2ch 32000Hz SUSPENDED
1 alsa_output.pci-0000_00_1f.3.analog-stereo.monitor module-alsa-card.c s16le 2ch 44100Hz RUNNING
2 alsa_input.pci-0000_00_1f.3.analog-stereo module-alsa-card.c s16le 2ch 44100Hz SUSPENDED
3 recording.monitor module-null-sink.c s16le 2ch 44100Hz SUSPENDED
#------------------------------------------------------
## parec & pacat
## 录制
parec --channels=2 -d alsa_output.pci-0000_00_1f.3.analog-stereo.monitor out.wav
## 播放
pacat out.wav
## parecord & paplay
## 录制
parecord --channels=2 -d alsa_output.pci-0000_00_1f.3.analog-stereo.monitor out.wav
## 播放
paplay out.wav
????
> ll /usr/bin | grep "parec\|pacat\|paplay\|parecord"
#------------------------------------------------------
-rwxr-xr-x 1 root root 43K Sep 22 2020 pacat
lrwxrwxrwx 1 root root 5 Sep 22 2020 pamon -> pacat
lrwxrwxrwx 1 root root 5 Sep 22 2020 paplay -> pacat
lrwxrwxrwx 1 root root 5 Sep 22 2020 parec -> pacat
lrwxrwxrwx 1 root root 5 Sep 22 2020 parecord -> pacat
#------------------------------------------------------
原来 paplay
,parec
,parecord
都是 pacat
程序,但是,但是,但是,为啥我使用 parec
录制的 out.wav
文件,paplay
无法播放, pacat
可以播放;反之 parecord
录制的 out.wav
文件,paplay
和 pacat
都可以播放 ??????
录制指定软件输出的声音
## 增加一个 sink,用于录制制定的 application
> pacmd load-module module-null-sink sink_name=recording sink_properties=device.description=recording
## 【可选】 将前面创建的 sink,再输出到默认输出设备,用于用户听取
> pacmd load-module module-combine-sink sink_name=combined sink_properties=device.description=combined \
slaves=recording,alsa_output.pci-0000_00_1f.3.analog-stereo
假如我们这里要录制 Chrome
浏览器播放的声音
## 寻找软件对应的 sink-input
> pactl list sink-inputs
#-output-----------------------------------------------
Sink Input #2
Driver: protocol-native.c
Owner Module: 13
Client: 12
Sink: 0
Sample Specification: float32le 2ch 44100Hz
Channel Map: front-left,front-right
Format: pcm, format.sample_format = "\"float32le\"" format.rate = "44100" format.channels = "2" format.channel_map = "\"front-left,front-right\""
Corked: no
Mute: no
Volume: front-left: 65536 / 100% / 0.00 dB, front-right: 65536 / 100% / 0.00 dB
balance 0.00
Buffer Latency: 44625 usec
Sink Latency: 23599 usec
Resample method: copy
Properties:
application.icon_name = "google-chrome"
media.name = "Playback"
application.name = "Google Chrome"
native-protocol.peer = "UNIX socket client"
native-protocol.version = "32"
application.process.id = "5881"
application.process.user = "ban"
application.process.host = "ban-pc"
application.process.binary = "chrome"
application.language = "en_US.UTF-8"
window.x11.display = ":0"
application.process.machine_id = "7862687160be40c5925f67f9055ef4ea"
application.process.session_id = "2"
module-stream-restore.id = "sink-input-by-application-name:Google Chrome"
#------------------------------------------------------
## 寻找我们前面创建的 record sink
> pactl list short sinks
#-output-----------------------------------------------
0 alsa_output.pci-0000_00_1f.3.analog-stereo module-alsa-card.c s16le 2ch 44100Hz RUNNING
1 recording module-null-sink.c s16le 2ch 44100Hz SUSPENDED
#------------------------------------------------------
## 将 chrome 的 stream 桥接到 record sink 上
> pactl move-sink-input 2 1
## 其中 2 是 chrome stream 的 id
## 其中 1 是 record sind 的 id
## 录制
parec --channels=2 -d recording.monitor test.wav
## 播放
pacat test.wav
pulse-simple 依赖库的使用
main.c
#include <stdio.h>
#include <pulse/simple.h>
#include <pulse/error.h>
#include <string.h>
#include <errno.h>
static const int BUF_SIZE = 1024;
/* The Sample format to use */
static const pa_sample_spec ss = {
.format = PA_SAMPLE_S16LE,
.rate = 44100,
.channels = 2
};
int pa_play_pcm(char *path) {
pa_simple *s = NULL;
int ret = 1, error;
FILE *file = fopen(path, "rb");
if (file == NULL) goto finish;
/* Create a new playback stream */
if (!(s = pa_simple_new(NULL, "pa.play", PA_STREAM_PLAYBACK,
NULL, "playback", &ss, NULL, NULL, &error))) {
fprintf(stderr, __FILE__": pa_simple_new() failed: %s\n", pa_strerror(error));
goto finish;
}
for (int i = 0;; ++i) {
if (i % 50 == 0) fprintf(stderr, "read loop %d/2000\n", i);
uint8_t buf[BUF_SIZE];
size_t r;
if ((r = fread(buf, sizeof(uint8_t), sizeof(buf), file)) <= 0) {
if (r == 0) break;
fprintf(stderr, __FILE__": read() failed or over : %s\n", strerror(errno));
goto finish;
}
if (pa_simple_write(s, buf, (size_t) r, &error) < 0) {
fprintf(stderr, __FILE__": pa_simple_write() failed: %s\n", pa_strerror(error));
goto finish;
}
}
/* Make sure that every single sample was played */
if (pa_simple_drain(s, &error) < 0) {
fprintf(stderr, __FILE__": pa_simple_drain() failed: %s\n", pa_strerror(error));
goto finish;
}
ret = 0;
finish:
if (s) pa_simple_free(s);
if (file) fclose(file);
return ret;
}
int pa_record_pcm(char *path) {
pa_simple *s = NULL;
int ret = 1, error = 0;
FILE *file = fopen(path, "wb");
if (file == NULL) goto finish;
/* Create a new playback stream */
if (!(s = pa_simple_new(NULL, "pa.record", PA_STREAM_RECORD,
"alsa_output.pci-0000_00_1f.3.analog-stereo.monitor",
"playback", &ss, NULL, NULL, &error))) {
fprintf(stderr, __FILE__": pa_simple_new() failed: %s\n", pa_strerror(error));
goto finish;
}
for (int i = 0; i < 2000; ++i) {
if (i % 50 == 0) fprintf(stderr, "write loop %d/2000\n", i);
uint8_t buf[BUF_SIZE];
if (pa_simple_read(s, buf, sizeof(buf), &error) < 0) {
fprintf(stderr, __FILE__": pa_simple_read() failed: %s\n", pa_strerror(error));
goto finish;
}
/* And write it to STDOUT */
if (fwrite(buf, sizeof(uint8_t), sizeof(buf), file) != sizeof(buf)) {
fprintf(stderr, __FILE__": write() failed: %s\n", strerror(errno));
goto finish;
}
}
ret = 0;
finish:
if (s) pa_simple_free(s);
if (file) fclose(file);
return ret;
}
int main() {
char *file = (char *) "temp.pcm";
pa_record_pcm(file);
// start record
pa_play_pcm(file);
remove(file);
return 0;
}
## 编译
> gcc main.c -o main.app -lpulse -lpulse-simple
## 运行
> ./main.app
REF
1. Linux audio recording guide
2. Record a program’s output with PulseAudio
3. PulseAudio cannot change the sink for one specific program
4. pacat-simple.c
5. parec-simple.c