Open tvvladimir2 opened 3 years ago
It could be reasonable to add an example code for that. Although, it's configuration would be always very system dependent.
With "different devices" you mean different audio interfaces simultaneously? If so, we proposed a solution for something similar in https://github.com/spatialaudio/python-sounddevice/issues/338 (it is simultaneous recording, but shows different proposals to implement the required multiprocessing/threading).
For the playback you probably want to adapt the solution in play_long_file.py (although, you probably looked at that already?).
If you are a facing a specific challenge for you problem, it would be more clear if you post a short code example is executable and shows where you are stuck.
Hi HaHeho,
Here's the example code I have so far: Description: I have x2 GPIO buttons, x2 USB audio devices. Start stream with two channels for USB device1 and USB device2. If I press button1: if the music playing pause device1, channel1, if the music is not playing, start on device1, channel1. Same for the second button, but for device2, channel2.
import sounddevice as sd
import soundfile as sf
from time import sleep
import RPi.GPIO as GPIO
import threading
import argparse
import pathlib
import os
import numpy as np # do i use numpy or raw data?
GPIO.setmode(GPIO.BOARD)
button1=16
button2=12
s = sd.query_devices()
audio0 = f"""{s[0]["name"]})"""
audio1 = f"""{s[1]["name"]})"""
audio2 = f"""{s[2]["name"]})"""
audio3 = f"""{s[3]["name"]})"""
print(audio0) #bcm2835 HDMI 1: - (hw:0,0))
print(audio1) #bcm2835 Headphones: - (hw:1,0))
print(audio2) #USB Audio Device: - (hw:2,0))
print(audio3) #USB Audio Device: - (hw:3,0))
GPIO.setup(button1,GPIO.IN,pull_up_down=GPIO.PUD_UP)
GPIO.setup(button2,GPIO.IN,pull_up_down=GPIO.PUD_UP)
filename1 = '1.wav'
filename2 = '2.wav'
data1, fs1 = sf.read(filename1, dtype='float32')
data2, fs2 = sf.read(filename2, dtype='float32')
BS1=False # Set condition variables, not very operable. This is not good. Better check if audio is playing or not.
BS2=False
stream1 = sd.RawOutputStream(device=None, channels=2, dtype='float32')
stream1.start()
while(1):
if GPIO.input(button1)==0:
print ("Button 1 was pressed")
if BS1==False:
stream1.play(data1, channel=1, device=0) # Not working # How do I start stream on device 0, channel 1, data1 ?
# I don't understand how to pass data1 to stream1 to different devices simultaniously
print("Device=0 playback started")
BS1=True
sleep(.5)
else:
stream1.stop(data1, channel=1, device=0) # Not working # How do I stop stream on device 0, channel 1, data1 ?
print("Device 0 playback stopped")
BS1=False
sleep(.5)
if GPIO.input(button2)==0:
print ("Button 2 was pressed")
if BS2==False:
stream1.play(data2, channel=2, device=1) # Not working # How do I start stream on device 1, channel 2, data2 ?
print("Device 1 playback started")
BS2=True
sleep(.5)
else:
stream1.play(data2, channel=2, device=1) # Not working # How do I stop stream on device 1, channel 2, data2 ?
print("Device 1 playback stopped")
BS2=False
sleep(.5)
Thanks for fixing the code formatting. :) You can have ```python
so we also get the appropriate syntax highlighting.
The example you've provide is quite messy of course. And it's impossible to test on a different machine, since we don't have the hardware nor the audio files available. But here a first try to get started (all untested). See how or if that works, and we can get more fancy from there.
import RPi.GPIO as GPIO
import sounddevice as sd
import soundfile as sf
GPIO.setmode(GPIO.BOARD)
button1 = 16
button2 = 12
GPIO.setup(button1, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(button2, GPIO.IN, pull_up_down=GPIO.PUD_UP)
s = sd.query_devices()
audio0 = f'{s[0]["name"]})'
audio1 = f'{s[1]["name"]})'
audio2 = f'{s[2]["name"]})'
audio3 = f'{s[3]["name"]})'
print(audio0) # bcm2835 HDMI 1: - (hw:0,0))
print(audio1) # bcm2835 Headphones: - (hw:1,0))
print(audio2) # USB Audio Device: - (hw:2,0))
print(audio3) # USB Audio Device: - (hw:3,0))
device1 = 0
device2 = 1
filename1 = "1.wav"
filename2 = "2.wav"
data1, fs1 = sf.read(filename1, dtype="float32")
data2, fs2 = sf.read(filename2, dtype="float32")
# Set condition variables, not very operable. This is not good. Better check if audio is playing or not.
BS1 = False
BS2 = False
try:
while True:
if GPIO.input(button1) == 0:
print("Button 1 was pressed")
if not BS1:
print(f"Starting playback device={device1} ...")
sd.play(data=data1, samplerate=fs1, device=device1, blocking=False)
BS1 = True
sd.sleep(0.5)
else:
print(f"Stopping playback device={device1} ...")
sd.stop()
BS1 = False
sd.sleep(0.5)
if GPIO.input(button2) == 0:
print("Button 2 was pressed")
if not BS2:
print(f"Starting playback device={device2} ...")
sd.play(data=data2, samplerate=fs2, device=device2, blocking=False)
BS2 = True
sd.sleep(0.5)
else:
print(f"Stopping playback device={device2} ...")
sd.stop()
BS2 = False
sd.sleep(0.5)
except KeyboardInterrupt:
print("\nInterrupted by user.")
There is still plenty of quirks in there, why this is for sure not a good implementation, e.g.
sd.get_status()
. But since I haven't used that function, and I'm not able to test the code, you can see and play around with it.sleep(0,5)
is only there to prevent the loop from firing multiple times while the button is pressed? That's is of course very ugly. Is there no better way to handle the GPIO button presses in something like events?sd.play()
internally calls sd.stop()
, so no simultaneous playback of the two different files will be possible. But is that even required? If so, we will have to get more fancy (but getting this example to work is still good for general testing).Hi HaHeho,
You are correct sd.play()
and sd.stop()
is not an option. I need to make simultaneous playback possible. So Stream()
needs to be used.
I introduce a stream with two channels, 1 channel for device and 1 channel for device 1:
stream1 = sd.RawOutputStream(device=None, channels=2, dtype='float32')
Then I, start the stream
stream1.start()
Now I have to to pass an audio file, start audio playback on stream for different devices and be able to pause it. I don't understand how to do it. Perhaps use stream.write()
? Tried it, didn't work for me.
You are correct
sd.play()
andsd.stop()
is not an option.
Yes, but do they work (in a sense of that playback to the different interfaces works as expected)?
I need to make simultaneous playback possible. So
Stream()
needs to be used. I introduce a stream with two channels, 1 channel for device and 1 channel for device 1:stream1 = sd.RawOutputStream(device=None, channels=2, dtype='float32')
Then I, start the streamstream1.start()
Now I have to to pass an audio file, start audio playback on stream for different devices and be able to pause it. I don't understand how to do it. Perhaps usestream.write()
? Tried it, didn't work for me.
Starting the stream as correct, although you'll probably need two independent streams for the playback to either device. Then you are missing the part to actually fill the data1
and data2
(in chunks) into the stream. You can see in play_file.py or play_long_file.py how this can be done (in the callback).
Please add a new example to the "Example programs"
Example name: Open and run stream with two channels on different devices
Description: Open a stream with two channels. Play each channel on different devices. Play audiofile1 on channel1 device 1 and play audiofile2 on channel2 on the device 2. Use callback function to start, stop and pause each channel separately.
Problem: I know how to open a stream, but I don't understand how to assign a NumPy array or single audio file to the stream, and especially to a specific channel of the stream on a particular device.