68

I would like to record an output of a program with PulseAudio using command line/bash script. It is important not to record all output, but only the output from one specific program.

I thought I would have to create a new null-sink and than move the program's output to this new sink. Than tell parec to use this specific monitor to record.

The first step would be something like this:

pactl load-module module-null-sink sink_name=steam

But how to move the program's output now to this sink?
And how to record the specific sink with a bash script?

8 Answers 8

73

Try something like this:

In a terminal enter

pacmd

(this is the CLI of the PulseAudio-Server) then use

list-sink-inputs

(where you get the indices of the running inputs) Now find the index of your input. Now referred to as $INDEX

the scriptable part is:

pactl load-module module-null-sink sink_name=steam
pactl move-sink-input $INDEX steam
parec -d steam.monitor | oggenc -b 192 -o steam.ogg --raw -

Explanations:

  • The first command will add a null-sink as you already knew.
  • The second command moves the sink-input from your standard-audio-sink to steam
  • The third command records the monitor of the device steam (-d) and puts the output (raw-wave-stream) into oggenc, which encodes this wave-stream to an oga-file. (for mp3 use lame)
5
  • Brilliant, thank you so much for this great answer! :)
    – Force
    Nov 24, 2011 at 11:32
  • 2
    can you please explain how to undo this operation? my null sinks are still mapped to various applications, and now i have no sound!
    – ixtmixilix
    Aug 20, 2012 at 12:44
  • 1
    @ixtmixilix: the easiest way to restore all settings to default is to just kill and restart the pulseaudio server. If set to autospawn (this is the default) you simply issue pulseaudio -k in a terminal.
    – Takkat
    Mar 2, 2013 at 19:48
  • 2
    @ixtmixilix When you run list-sink-inputs take note of the sink value. To swap back just run pactl move-sink-input $INDEX $SINK. Something like pactl move-sink-input 4719 1
    – Ciaran
    Feb 21, 2014 at 10:28
  • Im trying to redirect stream to UDP endpoint, no success so far, Any tips? thanks
    – NotABot
    Mar 9, 2021 at 15:31
46

Improving Waschtl answer of this thread so you can BOTH LISTEN AND RECORD the app sound:

First, we look for our default output and put its sink name in $DEFAULT_OUTPUT:

$ pacmd list-sinks | grep -A1 "* index"
  * index: 1
    name: <alsa_output.pci-0000_00_1b.0.analog-stereo>
$ DEFAULT_OUTPUT=$(pacmd list-sinks | grep -A1 "* index" | grep -oP "<\K[^ >]+")
$ echo $DEFAULT_OUTPUT
alsa_output.pci-0000_00_1b.0.analog-stereo

Then, we create a combined sink that has only one slave: $DEFAULT_OUTPUT. The sound generated by the app (source) will be forwarded to the slave sink (ie. real output), and we'll also record it. It is different than a null sink where the source sound is not forwarded.

$ pactl load-module module-combine-sink \
  sink_name=record-n-play slaves=$DEFAULT_OUTPUT \
  sink_properties=device.description="Record-and-Play"

sink_properties is optional and may bug if you use spaces in the description name.

Then, we could use pactl move-sink-input ... command of Waschtl answer (with record-n-play instead of steam) but GUI pavucontrol is more simple (and great for checking/troubleshooting):

$ sudo apt-get install pavucontrol
$ pavucontrol &

Then, we play some sound in the app we want to record. In pavucontrol Playback tab, we select in the app dropdown list: "Record-and-Play".

Finally, we're good to record and listen at the same time! (lame mp3 example, run in foreground)

$ parec --format=s16le -d record-n-play.monitor | \
  lame -r --quiet -q 3 --lowpass 17 --abr 192 - "temp.mp3"

Or we can record in background and stop at any time:

$ parec --format=s16le -d record-n-play.monitor | \
  lame -r --quiet -q 3 --lowpass 17 --abr 192 - "temp.mp3" \
   > /dev/null &1>/dev/null
$ killall -q parec lame

NB:

  • To unmess everything or retry the procedure: Delete or reset the sinks by using this answer. pulseaudio -k works great to reset everything to session's defaults.
  • If we change the default output in the System Sound Settings, the custom app Playback setting will be overwritten and we will have to go back in pavucontrol to set it back to the combined interface.
  • To be able to listen to the sound from several "real" interfaces (eg headphones, HDMI output, etc...), we should include all "real" outputs that we may use to listen, as record-n-play slink slaves, like: pactl load-module module-combine-sink sink_name=record-n-play slaves=real-output-1,real-output-2.

EDIT: Beware, since Ubuntu 18 (maybe 17 too), the combined sink tends to become the default system output device, instead of the real output device. So when you change the volume using the sound icon in the system tray it impacts your record sound. Workaround: After creating the combined sink, open pavucontrol in Output tab. Select "View: Virtual Output Devices" and reset the sound volume of the combined sink to 100%. Then select "View: Hardware Output Devices" and press the green icon "Define as alternative" on the real output device.

2
  • 1
    This is amazingly useful, especially combined with the extra information in the answer by Waschtl Nov 24, 2019 at 21:35
  • Im trying to redirect stream to UDP endpoint, no success so far, Any tips? thanks
    – NotABot
    Mar 9, 2021 at 15:30
10

@Waschtl's answer is fantastic. @ixtmixilix asked about restoring regular audio after the recording is finished. Here's the easiest way I know of:

Install and run the pavucontrol GUI. You should see your audio-outputting application and it's volume meter under the Playback tab in there. There will be a button next to it showing that it's playing on Null Output. Click on that and change it to your ordinary audio output, Built-in Audio Analog Stereo in my case.

Here's a screenshot of what you're looking for:

screenshot

You can also use this approach to set up your recording in the future, after you've run the load-module command in @Waschtl's answer to create the sink.

2
  • If you can, upload a screen-shot in Imgur and link it into your answer.
    – Lucio
    Mar 2, 2013 at 19:11
  • I don't know if it's feasible, but I think the best way is to unload the null output module, so that new playbacks don't pick up it by default
    – Jack
    May 8, 2019 at 8:25
8

Improving KrisWebDev's answer further, if you want record-n-play to always be available, first find out the default output:

pacmd list-sinks | grep -A1 "* index" | grep -oP "<\K[^ >]+"

which will output something like:

alsa_output.pci-0000_00_1b.0.analog-stereo

Next, create a file ~/.config/pulse/default.pa:

.include /etc/pulse/default.pa

load-module module-combine-sink sink_name=record-n-play slaves=alsa_output.pci-0000_00_1b.0.analog-stereo sink_properties=device.description="Record-and-Play"

Please replace alsa_output.pci-0000_00_1b.0.analog-stereo with whatever output you got from the pacmd command. Pulse does not read the default config file when a custom config file exists - that's why the first line above includes the default config file. That way pulseaudio still loads the default config first.

Run pulseaudio -k to kill the current pulseaudio instance so a new one is started with the new configuration.

If at any time you want to undo the changes here, just remove the ~/.config/pulse/default.pa file and run pulseaudio -k.

If you want the record-n-play to be the default sink for all outputs, you can do that by adding yet another line to the end of ~/.config/pulse/default.pa:

set-default-sink record-n-play

For programs that pulseaudio already has stored information on, it remembers whatever output device they used last, so you'll have to manually reconfigure those using one of the methods described in KrisWebDev's answer.

7

I've created this bash script based on the answers provided by @Waschtl and @KrisWebDev answers.

Available here: https://gist.github.com/ramast/4be3314bc73f28f55e3604497188b007

How to use?

$ ./pulse-recorder.bash 
    index: 225
                application.name = "ALSA plug-in [mplayer]"
                module-stream-restore.id = "sink-input-by-application-name:ALSA plug-in [mplayer]"
Choose recording index: 225
temp.mp3 file already exist, replace (y/n)? y

New version

I've created a python script that offer some improvements over the old one. https://gist.github.com/ramast/c47bd5e57586e9c2deb74975e27089f0

How to use?

$ ./pulse-recorder.py 

1 - ALSA plug-in [mplayer]
2 - AudioIPC Server

Please enter a number: 1

Your selection was: ALSA plug-in [mplayer]
Please press enter when you are ready to start

When you press enter the recording will start immediately. if by the time you hit enter the application was already stopped (i.e you closed mplayer for example) the script will wait until the app appear again and start recording.

The only draw back to that script (vs original one) is that if the list contain two entries with same name, the script won't behave correctly. For example

$ ./pulse-recorder.py                                                                                                      

1 - ALSA plug-in [mplayer]
2 - ALSA plug-in [mplayer]

Edit: This is an improved version of the script by @anarcat

Usage:

To produce ogg recording

./pulse-recorder.py -i -o 'test.ogg'

To produce mp3 recording

./pulse-recorder.py -i --encoder "lame -r -q 3 --lowpass 17 --abr 192 - '%s'" -o 'test.mp3'

6
  • 1
    In the case that you have multiple outputs (e.g. speakers, several headsets, monitor audio), you can replace on line 21 with e.g.: default_output=alsa_output.pci-0000_00_1b.0.analog-stereo where this is from the output of pacmd list-sinks
    – smaudet
    Mar 10, 2018 at 22:08
  • 1
    i made a variation of this in gitlab.com/anarcat/scripts/-/blob/master/pulse-recorder.py
    – anarcat
    May 27, 2020 at 17:38
  • 1
    update: the script fixes the issue identified by @smaudet where it was having trouble with multiple outputs. it also supports running automatically without prompting the user.
    – anarcat
    May 27, 2020 at 20:19
  • 2
    @anarcat the script is nice, but there is no need to pipe to oggenc, lame or another encoder. With ffmpeg you can directly capture a Pulse Audio device: trac.ffmpeg.org/wiki/Capture/PulseAudio. This also gives you a lot of other options like segmenting or setting the recording duration.
    – rene
    Feb 1, 2021 at 21:12
  • 2
    @anarcat: you've got a merge request. please be gentle! :)
    – rene
    Feb 3, 2021 at 21:16
6

If a single program is outputting sound you can use this bash one-liner:

parec --monitor-stream  $(pacmd list-sink-inputs|tac|perl -E'undef$/;$_=<>;/RUNNING.*?index: (\d+)\n/s;say $1') --format=s16le --channels=2 --file-format=aiff newrecording.aiff

Output seems to be about 10MB per minute, and don't mute the program!

I believe this to be a good solution as I don't download a new script, and I don't have to create a new null sink in pulseaudio. It should give odd results if the program has no audio-output when the one-liner starts, or if multiple programs are outputting audio when the command starts.

parec should stop recording when you are done with the application that's playing audio. Some editing applications might have trouble with a aiff file that's 600 minutes long though, if they try and load it into RAM twice.

This is also a poor solution if the application stops and starts outputting audio.

1
  • 1
    Nice one-liner. I combined it with the timeout command to make sure I didn't record any longer than needed, but if the stream stops playing parec also stops on its own.
    – remcycles
    Jul 16, 2021 at 23:57
3

All these answers are amazing and demonstrate the power of PulseAudio ! Digging-in to try to understand them I found a simpler solution (maybe it's a new feature) : we can directly tap into the default output's monitor.

DEFAULT_OUTPUT=$(pacmd list-sinks | grep -A1 "* index" | grep -oP "<\K[^ >]+")  # From previous answer
ffmpeg -f pulse -ac 2 -i $DEFAULT_OUTPUT.monitor output.mp3

Works great if you want to capture video as well.

1

One small improvement to solutions here. First, the problem: you may need to adjust the volume of the playback (monitor) of the application while listening to other audio, but not the recording volume. The module-null-sink works if no output is desired, but what if some is needed or this needs to be controlled on-the-fly?

The problem is that PulseAudio does not allow separate adjustment of a single "slave" or output separately from the sink volume. In other words, you could adjust the application (which will affect everything below in the chain, so that's not good) or the sink the application has been connected to, but the end result in both cases is that playback and recording volume will both be affected - regardless if the recording software connects to the application or the sink.

One solution is: we need to add at least one more sink to the chain.

A solution:

SLAVE_FOR_PLAYBACK=alsa_output.pci-0000_2f_00.4.iec958-stereo
VOLCTRL_SINK_NAME=rec-mon-vol
RECMON_SINK_NAME=rec-n-play

pacmd load-module module-combine-sink sink_name=$VOLCTRL_SINK_NAME slaves=$SLAVE_FOR_PLAYBACK sink_properties=device.description="Recording-Monitor-Volume" 
pacmd load-module module-combine-sink sink_name=$RECMON_SINK_NAME  slaves=$VOLCTRL_SINK_NAME sink_properties=device.description="Record-and-Monitor"

Solution here is obvious: by adding one more sink into chain to allow decreasing volume after the recording step without affecting recording. I.e. a simple diagram for the above setup:

APP -> REC -> MON -> MASTER_SINK (speakers)
         (\--->recording software)

This way we can adjust playback volume at MON separately from volume incoming to REC and other applications (music, voice chat etc.) connected to MASTER_SINK (=speakers).

Alternative solution:

But in case REC volume has been decreased and one wants to increase it at MON, some bitdepth will be lost (if I guess correctly how PulseAudio works). Another (perhaps more elegant solution?) would hence be:

APP -> REC_AND_MONITOR ---> REC (---> recording software)
                       \--> MONITOR -> MASTER_SINK (speakers)

But this requires a third sink. But now both recording and playback volume can be genuinely adjusted separately without affecting each other (the first solution works nicely only if the volume needs to be decreased only). Like so:

pactl load-module module-null-sink    sink_name=rec_monitor   sink_properties=device.description="Recording_monitor_volume"
pactl load-module module-null-sink    sink_name=rec           sink_properties=device.description="Record_with_no_monitoring"
pactl load-module module-combine-sink sink_name=rec-n-monitor sink_properties=device.description="Record_with_monitoring" slaves=rec,rec_monitor
pactl load-module module-loopback source=rec_monitor.monitor sink=${SLAVE_FOR_PLAYBACK} latency_msec=${DESIRED_LATENCY}

Now connect your applications output to rec-n-monitor, recording software with rec and can adjust volume of monitor by adjusting rec_monitor and recording volume by adjusting rec.

For monitor latency, set DESIRED_LATENCY for whatever you want. I use 20 (unit is ms). Of course, it depends on your system what the actual latency will be; also see pactl man page. There are other general tricks for lowering Pulseaudio latency, but that is out of scope for this question...

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.