I'm looking for a way to feed audio data from a file into the microphone so when 3rd party applications (such as arecord or Chromium's "search by voice" feature) use the microphone for audio input, they receive the audio data from the file instead.
Here's my scenario: An application I wrote records audio data from the microphone (using ALSA) and saves it to a file (audioFile0.raw). At some unknown point in time in the future, some unknown 3rd party application (as in, something I did not develop so I have no development control over, such as the Chromium web browser's "search by voice" feature) will use the microphone to obtain audio data. I would like the audio data that the 3rd party application is gathering to come from audioFile.raw rather than the actual microphone itself.
I was thinking if it were possible to change the default audio input device to an audio file, or maybe a named pipe and do something like cat audioFile0.raw > mypipe
(since I don't know when another application will try to read from the microphone). Perhaps there is a more simple way of doing this?
I hope I provided enough detail and clarity. Please let me know if something is unclear.
EDIT: So I figured out how to make a virtual microphone by creating the following .asoundrc file in my home directory:
pcm.!virtmic {
type file
slave.pcm "hw:0,0"
file /dev/null
infile "/home/charles/audioFiles/audioFile0.raw"
}
pcm.!default {
type hw
card 0
}
ctl.!default {
type hw
card 0
}
I then call arecord test.raw -c 1 -f S16_LE -r 16000 -t raw -D virtmic
from the command line and I'm able to record the audio data that's in audioFile0.raw
to test.raw
.
My goal now is to replace the default device with my virtual microphone so any application accessing the microphone will read the audio data in audioFile0.raw
instead of the actual microphone itself. So I edited my .asoundrc file to appear as follows:
pcm.!virtmic {
type file
slave.pcm "hw:0,0"
file /dev/null
infile "/home/charles/audioFiles/audioFile0.raw"
}
pcm.!default {
type asym
playback.pcm {
type hw
card 0
}
capture.pcm {
type plug
slave.pcm "virtmic"
}
}
ctl.!default {
type hw
card 0
}
Then I called arecord test.raw -c 1 -f S16_LE -r 16000 -t raw
from the command line. I then played back test.raw
but it seemed to be recording from the microphone itself and not audioFile0.raw
.
What am I doing wrong? How exactly do I change the default capture device so it will read the data from audioFile0.raw
rather than the input from the microphone?
EDIT 2: Okay, so I was on the right track. I'm using the same .asoundrc file in my home directory that I showed earlier where I changed the default device to be the virtmic. I needed to change the file /usr/share/alsa/alsa.conf.d/pulse.conf
so it looks like this:
# PulseAudio alsa plugin configuration file to set the pulseaudio plugin as
# default output for applications using alsa when pulseaudio is running.
hook_func.pulse_load_if_running {
lib "libasound_module_conf_pulse.so"
func "conf_pulse_hook_load_if_running"
}
@hooks [
{
func pulse_load_if_running
files [
# "/usr/share/alsa/pulse-alsa.conf"
"/home/charles/.asoundrc"
]
errors false
}
]
The only thing I did was comment out the line "/usr/share/alsa/pulse-alsa.conf"
and replaced it with "/home/charles/.asoundrc"
so the pulseaudio plugin isn't the default for applications using ALSA, but rather use my virtual mic as the default. This may not be the best solution, but it works.
This worked when I did arecord test.raw -t raw -c 1 -f S16_LE -r 16000
. It got the data from audiofile0.raw
instead of the microphone! I used the command lsof /dev/snd/*
to see what exactly was accessing the audio device while the arecord
command was running. The output was as follows:
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
pulseaudi 2044 charles 22u CHR 116,6 0t0 8977 /dev/snd/controlC0
pulseaudi 2044 charles 29u CHR 116,6 0t0 8977 /dev/snd/controlC0
arecord 4051 charles mem CHR 116,5 8976 /dev/snd/pcmC0D0c
arecord 4051 charles 4u CHR 116,5 0t0 8976 /dev/snd/pcmC0D0c
I then tried using Chromium browser's "search by voice" feature and saw that I couldn't get it to record from audioFile0.raw
. I then used lsof /dev/snd/*
to see what exactly was accessing the audio device.
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
pulseaudi 2044 charles mem CHR 116,5 8976 /dev/snd/pcmC0D0c
pulseaudi 2044 charles 22u CHR 116,6 0t0 8977 /dev/snd/controlC0
pulseaudi 2044 charles 29u CHR 116,6 0t0 8977 /dev/snd/controlC0
pulseaudi 2044 charles 30r CHR 116,33 0t0 7992 /dev/snd/timer
pulseaudi 2044 charles 31u CHR 116,5 0t0 8976 /dev/snd/pcmC0D0c
I see that they all have the same PID, 2044. They are all using the pulseaudio daemon, not going through ALSA.
My Question: How do I get pulseaudio to use my virtual microphone by default so all applications that go through pulseaudio for audio input will instead get the audio data from my file rather than the microphone?
Line-level signals are one volt, which may sound small but it's still a big leap from mic level signals. That is why the line level input and mic input cannot be used interchangeably. A line-in signal carries the signal from the pre-amp to the speaker amp.
In pavucontrol, in the Recording tab, set the "Loopback" device's from input device to the device which receives the line in signal. In the Playback tab, set the "Loopback" device's on output device to the device through which you want to hear the line in signal.
After many painstaking hours, I finally got something acceptable working. I undid everything I did with ALSA (since the default is for ALSA to use PulseAudio instead, which I initially overrode). I created a simple bash script install_virtmic.sh
to create a "virtual microphone" for PulseAudio to use as well as PulseAudio clients:
#!/bin/bash
# This script will create a virtual microphone for PulseAudio to use and set it as the default device.
# Load the "module-pipe-source" module to read audio data from a FIFO special file.
echo "Creating virtual microphone."
pactl load-module module-pipe-source source_name=virtmic file=/home/charles/audioFiles/virtmic format=s16le rate=16000 channels=1
# Set the virtmic as the default source device.
echo "Set the virtual microphone as the default device."
pactl set-default-source virtmic
# Create a file that will set the default source device to virtmic for all
PulseAudio client applications.
echo "default-source = virtmic" > /home/charles/.config/pulse/client.conf
# Write the audio file to the named pipe virtmic. This will block until the named pipe is read.
echo "Writing audio file to virtual microphone."
while true; do
cat audioFile0.raw > /home/charles/audioFiles/virtmic
done
A quick script uninstall_virtmic.sh
for undoing everything done by the install script:
#!/bin/bash
# Uninstall the virtual microphone.
pactl unload-module module-pipe-source
rm /home/charles/.config/pulse/client.conf
I then fired up Chromium and clicked the microphone to use its voice search feature and it worked! I also tried with arecord test.raw -t raw -f S16_LE -c 1 -r 16000
and it worked too! It isn't perfect, because I keep writing to the named pipe virtmic
in an infinite loop in the script (which quickly made my test.raw
file insanely large), but it will do for now.
Please feel free to let me know if anyone out there finds a better solution!
sudo apt install pavucontrol
.Selecting "Built-in Audio Analog Stereo" is the default behavior (redirect your voice to microphone output). You should also see "Monitor of Built-in Audio Analog Stereo". when you select this option, the output any application that plays audio (e.g VLC) should be redirected to the microphone instead of your voice.
Make sure to change it back to "Built-in Audio Analog Stereo" after the audio is finished playing for everything to go back to normal.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With