#discord #discord-bot #pipe #unix #style #input

app stdinman

Pipe audio to discord, unix style

1 unstable release

0.1.0 Sep 4, 2023

#87 in #discord-bot

WTFPL license

44KB
152 lines

stdinman

Tool to pipe raw audio to Discord via a bot. Input data must be PCM 32-bit floating-point little-endian (aka f32le).

If your audio is in any other format, it is possible to use tools like ffmpeg to convert it to the required format.

ffmpeg -re -i sample.mp3 -map 0:a:0 -c:a:0 pcm_f32le -ar 48000 -ac 2 -f f32le - | stdinman

Motivation

There are several interesting discord bots and such floating around, such as for playing Youtube links, soundcloud etc. However, as an Arch Linux user, I wanted something super composable - the ability to play any raw audio into Discord, via a bot.

That's exactly what this program does. It is entirely up to the user how they wish to prepare the audio source - for instance their microphone, their speaker output (i.e. alsa monitor), an internet audio stream (or anything that can be passed into ffmpeg), or spinning vinyl*. Check out the recipes section for more.

Building

Ensure you have the rust toolchain installed, for e.g. via rustup, then run:

cargo build --release

The binary would be in ./target/release/stdinman . Copy it somewhere in your PATH or run it from here directly.

Usage

stdinman works via a Discord Bot, connected to a Voice Channel. You must ensure this bot is:

  • added to the server where you want to stream audio
  • has the "Connect" & "Speak" voice permissions

You'll need to provide the bot's token, and the ID of the voice channel you want it to connect to. These can be passed via CLI args:

stdinman --bot-token [BOT_TOKEN] --voice-channel-id [VOICE_CHANNEL_ID]

Alternatively, just run stdinman once, and it will generate a config file in $XDG_CONFIG_HOME/stdinman/stdinman.toml , where you can store the bot token & voice channel id.

Now just pipe audio to stdinman! Check out the recipes section for some examples.

Logging can be configured via an environment variable: export RUST_LOG=stdinman=DEBUG

Recipes

The recipes involving pactl are intended for use on linux with PulseAudio (or Pipewire with the Pulseaudio shim). Examples with ffmpeg could be used on Mac as well.

Stream ffmpeg output to Discord

ffmpeg is an amazing A/V utility, that can handle an incredible amount of input formats. If you can pass audio from a source into ffmpeg, or even a video (assuming you only want the audio), you can configure it to output the audio as 32-bit floating point PCM, which can then be piped to stdinman and streamed to Discord.

You should use the -re flag on the input, to ensure ffmpeg consumes it in real time.

ffmpeg -re -i sample.mp3 -map 0:a:0 -c:a:0 pcm_f32le -ar 48000 -ac 2 -f f32le - | stdinman

Stream Spotify directly to Discord (via librespot, ffmpeg)

Using the incredible work by the people over at librespot , it is possible to create a "Spotify Connect" device your account can play audio to, who's output can be piped to another program.

Using ffmpeg in the middle to ensure the format matches, we can then pipe it over to stdinman and stream it straight to Discord!

librespot -n stdinman_connect --backend pipe -b 320 | ffmpeg -f s16le -ac 2 -ar 44100 -re -i pipe:0 -map 0:a:0 -c:a:0 pcm_f32le -ar 48000 -ac 2 -f f32le - | stdinman

For more information on how to use librespot check out their repo.

Note: Using librespot is probably forbidden by Spotify.

(Linux) Play your computer's speakers' output via Discord

Note: If you're in the VC on the same computer, you would hear a kind of "echo" on the audio - first your headphones / speakers, and then the audio from discord with some latency. In such situations, it is recommended to output the audio to a virtual sink, and then play that via the bot (see the next recipe). This has the additional advantage of sharing a specific application's audio instead of the whole system.

You can use pactl to view the monitor input corresponding to your speakers:

$ pactl list short sources
77	alsa_output.pci-0000_00_1f.3.3.analog-stereo.monitor	PipeWire	s32le 2ch 48000Hz	SUSPENDED
78	alsa_input.pci-0000_00_1f.3.3.analog-stereo	PipeWire	s32le 2ch 48000Hz	SUSPENDED

In this case, alsa_output.pci-0000_00_1f.3.3.analog-stereo.monitor is the speakers' monitor. To use it with stdinman:

parec -d alsa_output.pci-0000_00_1f.3.3.analog-stereo.monitor --format=float32le --rate=48000 | stdinman

(Linux) Play a specific app's audio ONLY via Discord

This would route all audio from the app to the virtual sink, who's monitor you can then pass to stdinman to stream to Discord.

To do this, we first need to create a virtual sink via pactl. You can replace stdinman-demo with whatever name you want.

pactl load-module module-null-sink media.class=Audio/Sink sink_name=stdinman-demo channel_map=left,right

Then, using some GUI like pavucontrol , set the output of the program to this new sink:

changing the output of a program to the new sink

(Note: you won't be able to hear this application on your normal speakers anymore)

Then, use this sink's monitor with parec and pass the output to stdinman!

parec -d stdinman-demo.monitor --format=float32le --rate=48000 | stdinman

Thanks

Many thanks to Enitoni for pulseshitter, which was my inspiration for this project.

Also thanks to the amazing developers of serenity & songbird , for making working with Discord bots and streaming audio in Rust so easy.

Dependencies

~14–22MB
~309K SLoC