scottlawsonbc / audio-reactive-led-strip

:musical_note: :rainbow: Real-time LED strip music visualization using Python and the ESP8266 or Raspberry Pi
MIT License
2.68k stars 644 forks source link

How to use internal audio on rPi instead of microphone? #233

Open NickSutton opened 5 years ago

NickSutton commented 5 years ago

PyAudio uses your default audio input device which is a microphone for most cases. You can also create a virtual audio input device using software such as Voicemeeter on Windows, Loopback on OSX, or Jack on Linux.

This allows you to play music on a computer while also forwarding the audio data to a virtual audio input. PyAudio uses the virtual audio input device as if it were a real microphone.

Would any of these options resolve your issue?

Originally posted by @scottlawsonbc in https://github.com/scottlawsonbc/audio-reactive-led-strip/issues/10#issuecomment-271173120

Did anyone make any progress with this? It would be super helpful if the Pi could send audio virtually to the input of this software!

joeybab3 commented 5 years ago

I don't have a raspberry pi but if anyone has done this successfully and is willing to document the steps here so that I can add them to the readme that would be much appreciated.

NickSutton commented 5 years ago

Thanks.

Just to elaborate a little. The standard audio on the pi is pretty pants so most users who are putting together an audio project will invest in a HAT or add on that provides better audio out.

The issue when it comes to getting this project running is the requirement for USB audio. Specifically there doesn’t seem to be any way to use an external (or other than USB) way to play audio via a HAT or add on and then (virtually or physically) wire an audio stream to the mic input on a USB sound card.

For Pi users the simplest way is probably just to invest in a USB mic...

Currently, i have audio out via my IQaudIODAC board, but when I run visualization.py the output stops and the system becomes unresponsive requiring a reboot.

If I send audio to the USB sound card then visualization.py runs well, so I know that it does work. Just not the way I’d like it too...

lpearl commented 5 years ago

I was able to get it working a while ago using pulse audio and jack audio. With that said using the raspberry pi was not powerful enough for the number of LEDs I needed to work.

Codename-11 commented 4 years ago

@lpearl I've been struggling with finding a solution for this. I was able to use pulseaudio and pavucontrol along with python-dbus I believe to get it working on my Arch desktop however I've been unable to reproduce this on the pi. Any advice on a direction?

lpearl commented 4 years ago

@Tim-Dixon I really can't suggest using it (the pi just does not have enough power) but with that said you would need pulseaudio and qjackctl also take a look at this to see how to get them working together. Let me know if you run into any troubles.

NickSutton commented 4 years ago

Would this help? I’ve only looked briefly but to be able to loop back the audio might be the answer? https://github.com/intxcc/pyaudio_portaudio

NickSutton commented 4 years ago

As I can’t get shairport-sync (AirPlay music receiver) and the audio reactive LEDS to both work on the same Pi I’m actually using two.

Whilst this is excessive, it get’s the job done. Also, by way on an unintended bonus, as the audio-in for the LEDs is plugged directly in to my audio out (split to the speakers) the LEDs don’t kick off when I walk in to the room or when the TV is on, which is nice.

Also put together an iOS Shortcuts file to change the vis mode and turn the LEDs off from my phone. Happy to share if there’s any demand.

TekinaTawar commented 4 years ago

@NickSutton @joeybab3 Here Is What I am doing right now. I have bought a 50INR or 0.66$ (in India) sound card with audio jack input and USB output. Now I use following different configurations based on the need to play audio.

footy42 commented 4 years ago

@Tim-Dixon I really can't suggest using it (the pi just does not have enough power) but with that said you would need pulseaudio and qjackctl also take a look at this to see how to get them working together. Let me know if you run into any troubles.

@lpearl Can you elaborate a little on your issues with running it on a pi?

Which pi were you using? The pi4 seems leaps and bounds ahead of the previous models. Were you running everything on the pi or just the signal processing? Did you have the visualizer running?

I ask because I am working working on using a pi to capture an audio stream. It seems easy enough to create a loopback with pulse. I'd appreciate the insight.

lpearl commented 4 years ago

@footy42 Sure whatever helps, I was using the pi 3 and everything was running on it. The main problem with my setup was the number of LEDs I was trying to control (1200 pixels). The Pi was also running shairport sync (airplay sever) on top of the visualizer, which probably didn't help. I'm sure with fewer pixels, a different audio solution, and running a Pi 4 instead of the 3 should all help with the lag I was experiencing. How are you planning on running audio into the pi? My wireless solution worked (minus the pi 3 lag) but it was a huge pain to setup.

aca626 commented 4 years ago

After some time of researching I finally managed to get it work. I'm running shairport-sync and the visualizer (besides a homebridge server) on my raspberry pi 2 model b feeding the audio from shairport in the visualizer and also my speaker using alsa. The visualization runs at 25 fps smoothly (oddly tho it stutters at lower or higher framerates). I have to say, that I don't power the LEDs by the raspberry pi itself, but let it do the computing and then sent the commands to an ESP-01 to light up the LEDs. So the visualizer is running in the ESP8266 configuration.

Here is what I did to use the audio output as an input (from a fresh raspbian buster install):

First set up your usb-audio card as the default device in Alsa as described in the readme. Check whether you can play sound from it.

Set up the alsa loopback device: sudo modprobe snd-aloop

it will show up in aplay -l as "Loopback"

Then edit your /etc/asound.conf (and maybe .asoundrc although I think it gets overridden by the global asound.conf) and add:

#this will set your default output to a virtual virtual multi-channel device named CardAndLoop, which outputs to your USB-Card as well as the Loopback device
#Furthermore it sets the second Loopback device as your standard input
pcm.!default {
  type asym
  playback.pcm "CardAndLoop"
  capture.pcm "hw:Loopback,1"
}

# This is the interface you use for sound output
# It will send the output to the soundcard and loopback device
pcm.CardAndLoop {
  type plug
  slave.pcm MultiCh
  route_policy "duplicate"
}

ctl.!default {
        type hw           
        card 0 #set it to your Audio-Card
}

# Virtual multichannel device with four channels
# two the for the soundcard, two for the loopback
pcm.MultiCh {
  type multi
  slaves.a.pcm pcm.MixCard
  slaves.a.channels 2
  slaves.b.pcm pcm.MixLoopback
  slaves.b.channels 2
  bindings.0.slave a
  bindings.0.channel 0
  bindings.1.slave a
  bindings.1.channel 1
  bindings.2.slave b
  bindings.2.channel 0
  bindings.3.slave b
  bindings.3.channel 1
}

# Mixer for the soundcard
pcm.MixCard {
  type dmix
  ipc_key 1024
  slave {
    pcm "hw:PCH,0" #edit to set it to your Audio Card
#    rate 48000
    rate 44100
    periods 128
    period_time 0
    period_size 1024 # must be power of 2
    buffer_size 8192
  }
}

# Mixer for the loopback
pcm.MixLoopback {
  type dmix
  ipc_key 1025
  slave {
    pcm "hw:Loopback,0"
#    rate 48000
    rate 44100
    periods 128
    period_time 0
    period_size 1024 # must be power of 2
    buffer_size 8192
  }
}

Remember to set your USB-Audio-Card as the pcm device in the Mixer for the soundcard. Read the first article below to learn how to address audio devices in Alsa.

As everything sent to hw:Loopback,0 is also played on hw:Loopback,1, you will now have your audio output also available as an input (and on the speaker).

You can check if everything is working by opening a second terminal window and playing sound to the default output while recording audio from the default input in the other window.

speaker-test -c2 in window 1 arecord -f S16_LE -c 2 -r 44000 audio.wav in window 2

If everything works, make the Loopback device persistent: sudo echo 'snd-aloop' >> /etc/modules

Now you can install shairport-sync (or use your desired audio input). Don't hesitate if pyaudio throws some errors. Most of them are only informative and everything should work nevertheless.

These articles helped a lot: https://sysplay.in/blog/linux/2019/06/playing-with-alsa-loopback-devices/ https://unix.stackexchange.com/questions/175649/send-sound-output-to-application-and-speaker https://www.alsa-project.org/wiki/Asoundrc

Good luck!

krjan02 commented 3 years ago

@aca626 Thanks for this great writeup!

You just saved my new years eve party (with only family of course (covid))

happy holidays to all of you and a happy new year

krjan02 commented 3 years ago

Okay, it somehow does not work for me, i configured this in /usr/share/alsa/alsa.conf

There is nothing playing at the loopback, when using arecord -f S16_LE -c 2 -r 44000 audio.wav, it wont catch any sound nor is any sound played on my "normal" speakers

This is the config im using

# This is the interface you use for sound output
# It will send the output to the soundcard and loopback device
pcm.CardAndLoop {
  type plug
  slave.pcm MultiCh
  route_policy "duplicate"
}

ctl.!default {
        type hw
        card 1 #set it to your Audio-Card
}

# Virtual multichannel device with four channels
# two the for the soundcard, two for the loopback
pcm.MultiCh {
  type multi
  slaves.a.pcm pcm.MixCard
  slaves.a.channels 2
  slaves.b.pcm pcm.MixLoopback
  slaves.b.channels 2
  bindings.0.slave a
  bindings.0.channel 0
  bindings.1.slave a
  bindings.1.channel 1
  bindings.2.slave b
  bindings.2.channel 0
  bindings.3.slave b
  bindings.3.channel 1
}

# Mixer for the soundcard
pcm.MixCard {
  type dmix
  ipc_key 1024
  slave {
    pcm "hw:Headset,0" #edit to set it to your Audio Card
    #rate 48000
    rate 44100
    periods 128
    period_time 0
    period_size 1024 # must be power of 2
    buffer_size 8192
  }
}

# Mixer for the loopback
pcm.MixLoopback {
  type dmix
  ipc_key 1025
  slave {
    pcm "hw:Loopback,0"
    #rate 48000
    rate 44100
    periods 128
    period_time 0
    period_size 1024 # must be power of 2
    buffer_size 8192
  }
}
pi@rpi:~ $ cat /proc/asound/cards
 0 [Loopback       ]: Loopback - Loopback
                      Loopback 1
 1 [Headset        ]: USB-Audio - G432 Gaming Headset
                      Logitech G432 Gaming Headset at usb-0000:01:00.0-1.3, full speed
pi@rpi:~ $ 

Well, this is the "ghetto method" i guess:

germanshitevensmaller

aca626 commented 3 years ago

I'm sorry to hear that. If you want at some time to find the error, you could try this:

You could first test, whether your Loopback device is working as expected by playing speaker-test -c2 -D hw:0,0 in one terminal window and then recording arecord -D hw:0,1 -f S16_LE -c 2 -r 48000 audio.wav. If arecord catches audio the Loopback Device is working.

Then try playing audio directly to the new created "CardAndLoop" with speaker-test -c2 -D CardAndLoop. Arecord should be able to record the sound with arecord -D hw:0,1 -f S16_LE -c 2 -r 44000 audio.wav. If this is the case, the CardAndLoop Device is set correctly and just not the default device.

Additionally you should maybe consider to try the settings globally in /etc/asound.conf, as for me changes in /usr/share/alsa/alsa.conf were disregarded.

Nevertheless nice to see, that you found a 'ghetto style' workaround and your party is safe.

Happy new year and happy holidays!

lpearl commented 3 years ago

@krjan02 Yep that’s definitely the getto method you can try jack if this doesn’t work, but I wish you luck

aftrmth728 commented 3 years ago

After some time of researching I finally managed to get it work. I'm running shairport-sync and the visualizer (besides a homebridge server) on my raspberry pi 2 model b feeding the audio from shairport in the visualizer and also my speaker using alsa. The visualization runs at 25 fps smoothly (oddly tho it stutters at lower or higher framerates). I have to say, that I don't power the LEDs by the raspberry pi itself, but let it do the computing and then sent the commands to an ESP-01 to light up the LEDs. So the visualizer is running in the ESP8266 configuration.

Here is what I did to use the audio output as an input (from a fresh raspbian buster install):

First set up your usb-audio card as the default device in Alsa as described in the readme. Check whether you can play sound from it.

Set up the alsa loopback device: sudo modprobe snd-aloop

it will show up in aplay -l as "Loopback"

Then edit your /etc/asound.conf (and maybe .asoundrc although I think it gets overridden by the global asound.conf) and add:

#this will set your default output to a virtual virtual multi-channel device named CardAndLoop, which outputs to your USB-Card as well as the Loopback device
#Furthermore it sets the second Loopback device as your standard input
pcm.!default {
  type asym
  playback.pcm "CardAndLoop"
  capture.pcm "hw:Loopback,1"
}

# This is the interface you use for sound output
# It will send the output to the soundcard and loopback device
pcm.CardAndLoop {
  type plug
  slave.pcm MultiCh
  route_policy "duplicate"
}

ctl.!default {
        type hw           
        card 0 #set it to your Audio-Card
}

# Virtual multichannel device with four channels
# two the for the soundcard, two for the loopback
pcm.MultiCh {
  type multi
  slaves.a.pcm pcm.MixCard
  slaves.a.channels 2
  slaves.b.pcm pcm.MixLoopback
  slaves.b.channels 2
  bindings.0.slave a
  bindings.0.channel 0
  bindings.1.slave a
  bindings.1.channel 1
  bindings.2.slave b
  bindings.2.channel 0
  bindings.3.slave b
  bindings.3.channel 1
}

# Mixer for the soundcard
pcm.MixCard {
  type dmix
  ipc_key 1024
  slave {
    pcm "hw:PCH,0" #edit to set it to your Audio Card
#    rate 48000
    rate 44100
    periods 128
    period_time 0
    period_size 1024 # must be power of 2
    buffer_size 8192
  }
}

# Mixer for the loopback
pcm.MixLoopback {
  type dmix
  ipc_key 1025
  slave {
    pcm "hw:Loopback,0"
#    rate 48000
    rate 44100
    periods 128
    period_time 0
    period_size 1024 # must be power of 2
    buffer_size 8192
  }
}

Remember to set your USB-Audio-Card as the pcm device in the Mixer for the soundcard. Read the first article below to learn how to address audio devices in Alsa.

As everything sent to hw:Loopback,0 is also played on hw:Loopback,1, you will now have your audio output also available as an input (and on the speaker).

You can check if everything is working by opening a second terminal window and playing sound to the default output while recording audio from the default input in the other window.

speaker-test -c2 in window 1 arecord -f S16_LE -c 2 -r 44000 audio.wav in window 2

If everything works, make the Loopback device persistent: sudo echo 'snd-aloop' >> /etc/modules

Now you can install shairport-sync (or use your desired audio input). Don't hesitate if pyaudio throws some errors. Most of them are only informative and everything should work nevertheless.

These articles helped a lot: https://sysplay.in/blog/linux/2019/06/playing-with-alsa-loopback-devices/ https://unix.stackexchange.com/questions/175649/send-sound-output-to-application-and-speaker https://www.alsa-project.org/wiki/Asoundrc

Good luck!

Hi there,

Do you still use this? I've spent countless hours trying to get this to work alongside Shairport-sync but I have nothing to show for the 20+ hours unfortunately. If you still remember how you did this, would you be able to give me some guidance?

Thanks

aca626 commented 2 years ago

Hi @aftrmth728,

sorry for my late response. Did you finally manage to get it running?

I did use this for some years now without any problems. As I am now trying to change to ledfx in conjunction with wled on esp-01, I started back from scratch with a fresh installation of Raspberry Pi OS Bullseye on a Raspberry Pi 4. Trying to configuring the audio I ran into some problems, too.

Finally I got it back working.

First I made my USB-Soundcard the default audio device by disabling the onboard audio of the pi: Open /etc/modprobe.d/raspi-blacklist.conf and add blacklist snd_bcm2835. Open /lib/modprobe.d/aliases.conf and comment out the line options snd-usb-audio index=-2. (solution found on https://superuser.com/questions/989385/how-to-make-raspberry-pi-use-an-external-usb-sound-card-as-a-default)

Now, your USB-Soundcard should be card 0 in aplay -l.

Now set up the alsa loopback device as described above.

Doing aplay-l it should now be card 1 or 2.

If you now save the asound.conf and test it as described above, everything should work.

When you now add snd-aloop to /etc/modules and reboot the pi, you run into the problem, that the Loopback device is now card 0 instead of your usb-soundcard, as it is loaded before the driver for the latter.

As a result you have to edit the asound.conf again, to match your card (probably now card 1). This is mine:

#this will set your default output to a virtual virtual multi-channel device named CardAndLoop, which outputs to your USB-Card>
#Furthermore it sets the second Loopback device as your standard input
pcm.!default {
  type asym
  playback.pcm "CardAndLoop"
  capture.pcm "hw:Loopback,1"
}

ctl.!default {
        type hw
        card 1 #set it to your Audio-Card
}

# This is the interface you use for sound output
# It will send the output to the soundcard and loopback device
pcm.CardAndLoop {
  type plug
  slave.pcm MultiCh
  route_policy "duplicate"
}

# Virtual multichannel device with four channels
# two the for the soundcard, two for the loopback
pcm.MultiCh {
  type multi
  slaves.a.pcm pcm.MixCard
  slaves.a.channels 2
  slaves.b.pcm pcm.MixLoopback
  slaves.b.channels 2
  bindings.0.slave a
  bindings.0.channel 0
  bindings.1.slave a
  bindings.1.channel 1
  bindings.2.slave b
  bindings.2.channel 0
  bindings.3.slave b
  bindings.3.channel 1
}

# Mixer for the soundcard
pcm.MixCard {
  type dmix
  ipc_key 1024
  slave {
    pcm "hw:1,0" #edit to set it to your Audio Card
#    rate 48000
    rate 44100
    periods 128
    period_time 0
    period_size 1024 # must be power of 2
    buffer_size 8192
  }
}

# Mixer for the loopback
pcm.MixLoopback {
  type dmix
  ipc_key 1025
  slave {
    pcm "hw:Loopback,0"
#    rate 48000
    rate 44100
    periods 128
    period_time 0
    period_size 1024 # must be power of 2
    buffer_size 8192
  }
}

Hopefully everything works now!

Further notes: In the Shairport-Sync settings, you can uncomment output_device = "default"; in the alsa section of /etc/shairport-sync.conf.

In /usr/share/alsa/alsa.conf you can change defaults.ctl.card 1 and defaults.pcm.card 1 to match your card and set it as default (nonetheless the asound.conf should already do the job).

Similar approaches: https://noisybox.net/blog/2016/01/alsa_recording_of_device_output https://bbs.archlinux.org/viewtopic.php?id=147852

Good Luck!

Sonlis commented 2 years ago

I tried the solution mentionned by @aca626, but it won't work for me on raspberry pi 4 for some reasons (used to work on my pi 3). I had to add to python/microphone.py this line:

stream = p.open(format=pyaudio.paInt16,
                    channels=1,
                    rate=config.MIC_RATE,
                    input_device_index=1,   #Hard code the input, which is the loopback card.
                    input=True,
                    frames_per_buffer=frames_per_buffer) 

Hard coding the input worked for me.

aca626 commented 1 year ago

All right, tried it again on a raspberry pi 4 on raspbian 11 (bullseye) and got it working using this slightly better solution:

Instead of disabling the onboard audio, I just reordered the soundcards, so that the usb-soundcard is number 0.

This can be done by creating/modifying etc/modprobe.d/alsa-base.conf so that it includes these lines:

# This sets the index value of the cards but doesn't reorder.
options snd_usb_audio index=0
options snd_bcm2835 index=1

# Does the reordering.
options snd slots=snd_usb_audio,snd_bcm2835

Now your usb-soundcard should be card 0.

Next you simply put

#this will set your default output to a virtual virtual multi-channel device named CardAndLoop, which outputs to your USB-Card as well as the Loopback device
#Furthermore it sets the second Loopback device as your standard input
pcm.!default {
  type asym
  playback.pcm "CardAndLoop"
  capture.pcm "hw:Loopback,1"
}

# This is the interface you use for sound output
# It will send the output to the soundcard and loopback device
pcm.CardAndLoop {
  type plug
  slave.pcm MultiCh
  route_policy "duplicate"
}

ctl.!default {
        type hw           
        card 0 #set it to your Audio-Card
}

# Virtual multichannel device with four channels
# two the for the soundcard, two for the loopback
pcm.MultiCh {
  type multi
  slaves.a.pcm pcm.MixCard
  slaves.a.channels 2
  slaves.b.pcm pcm.MixLoopback
  slaves.b.channels 2
  bindings.0.slave a
  bindings.0.channel 0
  bindings.1.slave a
  bindings.1.channel 1
  bindings.2.slave b
  bindings.2.channel 0
  bindings.3.slave b
  bindings.3.channel 1
}

# Mixer for the soundcard
pcm.MixCard {
  type dmix
  ipc_key 1024
  slave {
    pcm "hw:0,0" #edit to set it to your Audio Card
#    rate 48000
    rate 44100
    periods 128
    period_time 0
    period_size 1024 # must be power of 2
    buffer_size 8192
  }
}

# Mixer for the loopback
pcm.MixLoopback {
  type dmix
  ipc_key 1025
  slave {
    pcm "hw:Loopback,0"
#    rate 48000
    rate 44100
    periods 128
    period_time 0
    period_size 1024 # must be power of 2
    buffer_size 8192
  }
}

in your /etc/asound.conffile.

Thanks to @rnagarajanmca for his guide: https://gist.github.com/rnagarajanmca/63badce0fe0e2ad126041c7c139970ea

Cheers!

DerDorfbewohner commented 1 year ago

Has anyone got this to work with the audio jack on the Raspberry PI 4? I tried it with a USB Soundcard and it worked. I tried setting the Headphone jack as default (card 0). The loopback works and the Leds light up but there is no audio comming out from the Headphone jack. I used the same /etc/asound.conf as above.