17

Summary of what I am trying to achieve:

I'm currently doing some work on a Discord bot. I'm trying to join a voice channel, which is the easy part, and then use the combined audio of the speakers in that voice channel as input for a webpage in a web browser. It doesn't really matter which browser it is as long as it can be controlled with Selenium.


What I've tried/looked into so far

My bot so far is written up in Python using the discord.py API wrapper. Unfortunately listening to, as opposed to putting in, audio hasn't been exactly implemented great − let alone documented − with discord.py. This made me decide to switch to node.js (i.e. discord.js) for the voice channel stuff of my bot.

After switching to discord.js it was pretty easy to determine who's talking and create an audio stream (PCM stream) for that user. For the next part I though I'd just pipe the audio stream to a virtual microphone and select that as the audio input on the browser. You can even use FFMPEG from within node.js 1, to get something that looks like this:

const Discord = require("discord.js");
const client = new Discord.Client();

client.on('ready', () => {
  voiceChannel = client.channels.get('SOME_CHANNEL_ID');
  voiceChannel.join()
    .then(conn => {
      console.log('Connected')

      const receiver = conn.createReceiver();

      conn.on('speaking', (user, speaking) => {
        if (speaking) {
          const audioStream = receiver.createPCMStream(user);

          ffmpeg(stream)
              .inputFormat('s32le')
              .audioFrequency(16000)
              .audioChannels(1)
              .audioCodec('pcm_s16le')
              .format('s16le')
              .pipe(someVirtualMic);          
        }
      });
    })
    .catch(console.log);
  }); 

client.login('SOME_TOKEN');

This last part, creating and streaming to a virtual microphone, has proven to be rather complicated. I've read a ton of SO posts and documentation on both The Advanced Linux Sound Architecture (ALSA) and the JACK Audio Connection Kit, but I simply can't figure out how to setup a virtual microphone that will show up as a mic in my browser, or how to pipe audio to it.

Any help or pointers to a solution would be greatly appreciated!


Addendum

For the past couple of days I've kept on looking into to this issue. I've now learned about ALSA loopback devices and feel that the solution must be there.

I've pretty much followed a post that talks about loopback devices and aims to achieve the following:

Simply imagine that you have a physical link between one OUT and one IN of the same device.

I've set up the devices as described in the post and now two new audio devices show up when selecting a microphone in Firefox. I'd expect one, but I that may be because I don't entirely understand the loopback devices (yet).

The loop back devices are created and I think that they're linked (if I understood the aforementioned article correctly). Assuming that's the case the only problem I have to tackle is streaming the audio via FFMPEG from within node.js.

Audio devices

Community
  • 1
  • 1
Niellles
  • 868
  • 10
  • 27
  • 2
    What you are doing is definitely cool, please update if you got the solution. – YLS Sep 11 '18 at 11:52
  • Did you find solution? – Lazar Nikolic Sep 21 '18 at 07:16
  • @LazarNikolic No, not yet. Although I'm thinking this might not be the best SE exchange community to ask this question. It's more ALSA related than Node.js, probably. – Niellles Oct 03 '18 at 08:02
  • You've probably figured this out already, but I'd recommend running `ffmpeg` directly from command-line until you figure out the microphone part. Easier to debug :/ – sebpiq Oct 13 '18 at 09:58
  • @sebpiq I've figured it out from the command line, but still have a hard time getting it to work from within node. – Niellles Oct 17 '18 at 21:01
  • DId you ever figure this out? Trying to do the same – Conner Sep 05 '20 at 16:37

1 Answers1

8

This is answered there : Linux pipe audio file to microphone input

Create a virtual microphone :

pactl load-module module-pipe-source source_name=virtmic file=/tmp/virtmic format=s16le rate=16000 channels=1

Pipe ffmpeg output to the virtmic file and it should work :

ffmpeg -re \
    -i input.mp3 \
    -f s16le -ar 16000 -ac 1 - > /tmp/virtmic

Note : I have noticed that if there are no readers to the pipe, ffmpeg just hangs. This can be fixed by simply opening Pulse Audio VU meter, command pavucontrol

sebpiq
  • 7,540
  • 9
  • 52
  • 69
  • I was gonna use a sink, but unfortunately ffmpeg won't actually output anything to the virtual device. – Niellles Oct 17 '18 at 20:59
  • 1
    Many thanks, this solved an issue where I wanted to loop a playing of an mp3 file over a "fake" microphone input (for a videoconference setup). – orangenarwhals Jun 01 '20 at 18:45
  • How did you pick rate=16000? ffprobe says my mp3 is 44100 Hz. But I tried rate=44100 and nothing worked. – ahoffer Aug 12 '20 at 05:07
  • 1
    @ahoffer `-f s16le -ar 16000` is resampling the input mp3 to match the sample rate declared when creating the virtual mic. The original sample rate of the mp3 is therefore not relevant : you should use the exact command from the answer. – sebpiq Aug 12 '20 at 11:50
  • @sebpiq I use RPI as Headset for my phone and I tried your steps to create virtual microphone but It doesn't pipe audio to my phone. What's wrong with that? – Mamdouh Saeed Mar 21 '21 at 16:46