How to play two sounds at once using Python? - python

I'm looking to make a loop system (inspired by Marc Rebillet, check him out) and am struggling with getting 2 sounds to play over themselves.
I've started out using sounddevice, as it's the first audio module that I found, but I'm not sure if there's a different once that would work better.
import time
def record(duration): # records a few seconds of audio
print('recording in:')
print('3')
time.sleep(1)
print('2')
time.sleep(1)
print('1')
time.sleep(1)
print('recording')
record = sd.rec(int(duration * fs), samplerate=48000, channels=2)
sd.wait()
print('done\n\n')
return record
duration = 3.5 # seconds
recordOne = record(duration)
recordTwo = record(duration)
while True: # repeats both pieces of audio
sd.play(recordOne, fs)
sd.play(recordTwo, fs)
sd.wait()
This code ended up only playing the recordTwo, and doesn't layer them over eachother. Again, I'd like to be able to play multiple sounds at the same time. Thank you for any help!

Have you tried a multi-thread solution?
import time
import threading
def record(duration): # records a few seconds of audio
print('recording in:')
for t in range(3, 0, -1):
print(t)
time.sleep(1)
print('recording')
print("mock record process...")
start = time.time()
time.sleep(10)
print(f'done, {time.time() - start} seconds total.\n\n')
return record
duration = 3.5 # seconds
recordOne = record(duration)
recordTwo = record(duration)
def play(record_piece):
print("paly the sound")
time.sleep(duration)
while True: # repeats both pieces of audio
t1 = threading.Thread(target=play, args=(recordOne,))
t2 = threading.Thread(target=play, args=(recordTwo,))
t1.start()
t2.start()
t1.join()
t2.join()
or you can try to find some library that can combine the two audio tracks before you play them.
I read the doc of sounddevice, it provides a playback method, have you tried it?
playback

Related

How can I improve what I already have?

I have created a windows internet speed test, I'd like to improve it and make the code more presentable as well as better define my functions.
When the computer reaches initialise, due to the variable being in another function, it cannot call it. How can I rectify this as I have various variables being called in different functions.
Feel free to use this speedtester as well, I will be working on developing a useful phone app to run the code as well.
The code prints the current date and time, searches for the connected SSID, initialises the speedtest module, scans for servers, selects the best server, initiates ping test, then download speed test, then upload speed test, followed by printing the results on screen and writing it to a simple txt file for viewing later.
Each function shows its run time using the time module and lastly total execution time with date and time also.
It works perfectly without the functions, and on android without find_ssid(): but I keep running into the trouble of localised variables.
import speedtest
from datetime import datetime
import subprocess
import re
import time
def main():
def date():
dt_now = datetime.now()
dtn = dt_now.strftime("%a %d-%m-%Y, %H:%M:%S%p")
return dtn
print(date())
def find_ssid():
stt = time.time()
cdop = subprocess.run(["netsh", "WLAN", "show", "interfaces"], capture_output=True).stdout.decode()
ssid = (re.findall("SSID : (.*)\r", cdop))
for char in ssid:
ssid = f"Network Name: {char} \n"
sid = time.time() - stt
print(f'SSID found in: {sid:.2f}s')
print(ssid)
find_ssid()
def initialise():
print("Initialising network speed test... ")
st = speedtest.Speedtest()
print("Network speed test active.")
sta = time.time() - stt
print(f'Speed test activation time: {sta - sid:.2f}s')
def scan_servers():
print("Scanning for available servers...")
st.get_servers()
print("Found available servers.")
sft = time.time() - stt
print(f'Servers found in: {sft - sta:.2f}s')
def best_server():
print("Choosing best server...")
bserv = st.get_best_server()
print(f"Best server is: {bserv['sponsor']} - {bserv['host']} located in {bserv['name']}, {bserv['country']}")
bst = time.time() - stt
print(f'Best server found in: {bst - sft:.2f}s')
def ping_test():
print("Ping testing...")
p = st.results.ping
ph = f"Ping: {p:.2f}ms"
print("Ping test complete.")
ptt = time.time() - stt
print(f'Ping test completed in: {ptt - bst:.2f}s')
def download_speed_test():
print("Download speed testing...")
ds = st.download()
dsh = f"Download speed: {ds / 1024 / 1024:.2f}mb/s"
print("Download speed test complete.")
dst = time.time() - stt
print(f'Download speed test completed in: {dst - ptt:.2f}s')
def upload_speed_test():
print("Upload speed testing...")
us = st.upload()
ust = time.time() - stt
ush = f"Upload speed: {us / 1024 / 1024:.2f}mb/s \n"
print("Upload speed test complete. \n")
print(f'Upload speed test completed in: {ust - dst:.2f}s')
def result():
print("Speed test results are: \n")
print(ssid)
print(ph)
print(dsh)
print(ush)
ttn = datetime.now()
fdt = ttn.strftime("%a %d-%m-%Y, %H:%M:%S%p")
tt = time.time() - stt
print(f"Start Time: {dtn}")
print(f"Finish Time: {fdt}")
print(f'Total execution time: {tt:.2f}s')
results = [ssid, ph, dsh, ush, dtn]
txt = "Speedtest Results.txt"
with open(txt, 'a') as f:
f.write("\n")
f.write("\n".join(results))
f.write("\n")
f.close()
main()
You can run this on, one line i believe by
ssid = (re.findall("SSID : (.*)\r", cdop))
for char in ssid:
ssid = f"Network Name: {char} \n/
Which should make it quicker, have a look at list comprehension

Multithreading makes my code having some issues

I have tried to implement multithreading into a tool I've written, but I'm getting unexpected results.
Here is the working code:
#Import modules
import os
import time
import sys
#Params
print("___________Auto ShutApp___________\n")
print("Type the AppName then the time before app will shut")
apptokill = str(input("\nWhich App you'd like to Shut: "))
Time = int(input("\nHow long before App should stop(in minutes): "))
def timeS(arg): #arg = time in seconds
time.sleep(arg*60)
def killer(apptokill, Time):
timeS(Time)
os.system("pkill %s"%(apptokill))
killer(apptokill, Time)
This works fine, but I wanted to know when it would kill an app so I added a new function
def Timer():
time_start = time.time()
seconds = 0
minutes = 0
while True:
try:
sys.stdout.write("\rTimer have start since {minutes} Minutes {seconds} Seconds".format(minutes=minutes, seconds=seconds))
sys.stdout.flush()
time.sleep(1)
seconds = int(time.time() - time_start) - minutes * 60
if seconds >= 60:
minutes += 1
seconds = 0
except KeyboardInterrupt:
break
This one also works well, but if I place the function it will run either killer or Timer - not both.
I then tried some mutlithreading, adding the code below:
if __name__ == "__main__":
t1 = threading.Thread(target=killer, name='t1')
t2 = threading.Thread(target=Timer, name='t2')
t1.start()
t2.start()
t1.join()
t2.join()
but from this code I get this error:
Traceback (most recent call last):
File "/usr/lib/python3.7/threading.py", line 926, in _bootstrap_inner
self.run()
File "/usr/lib/python3.7/threading.py", line 870, in run
self._target(*self._args, **self._kwargs)
TypeError: killer() missing 2 required positional arguments: 'apptokill' and 'Time'
I assumed apptokill is saved in memory and so Time as well. I'm completely lost.
You have to provide arguments to killer
When calling by hand you are doing it like this
killer(apptokill, Time)
When starting killer in a thread it still needs to know what to use as arguments. The solution would look like this:
t1 = threading.Thread(target=killer, name='t1', args=(apptokill, Time))

Timeout when read data by radio (NRF24L01 PYTHON)

I'm working with NRF24L01 in python and I'm confused about reading data. I have simple function to read data for 20 seconds. There is no problem if I get some data but if there is no radio signals my program stuck on "radio.read(revc_buffer)" till it gets data. There is an option to fight this? set up some kind of timeout ?
def recv():
global dictt
radio.openWritingPipe(pipes[0])
radio.openReadingPipe(1, pipes[1])
radio.startListening()
radio.stopListening()
radio.printDetails()
radio.startListening()
t_end = time.time() + 20
while time.time() < t_end:
pipe = [0]
while not radio.available(pipe):
time.sleep(1000/1000000.0)
recv_buffer = []
radio.read(recv_buffer)
if recv_buffer[0] == 144:
list_temp.append(recv_buffer)
print(list_temp)
return

Raspberry pi button delay time to a relay

I am new to Raspberry pi and python and am having a bit of trouble with some code
I wish to push a button and have the gpio pin that corresponds to that button trigger a relay to turn on for a given amount of time then turn off. I have it working with the code below but by using the 'time.sleep(my-variable)' it holds up the raspberry pi for the duration of the time and i am unable to do anything else.
What i am after is the ability to push one button and get the relay to act for say 10 seconds and within those 10 seconds be able to press another button to fire another relay and do the same thing without tying up the pi
my code below first checks if input_state_LHS is equel to false, then clears the LCD display, writes text to the LCD on one line then on the next line it write the value of my variable(LHS_feedtime) then fires the relay with the time on the next line time.sleep, this is the bit i wish to be rid of but am unable to figure out the code to do it.
if input_state_LHS == False:
## calls the LCD_Clear function which has to be in the same folder as this file
mylcd.lcd_clear()
mylcd.lcd_display_string("LHS Feedtime",1,2)
mylcd.lcd_display_string(str(round(LHS_feedtime, 2)) + " sec" , 2,5)
GPIO.output(27, GPIO.input(12) )
time.sleep(LHS_feedtime)
mylcd.lcd_clear()
mylcd.lcd_display_string("Flatson Feeding", 1)
mylcd.lcd_display_string("Systems", 2,4)
GPIO.output(27, GPIO.input(12) )
menuitem = 0
thanks for the help
The functionality you need is in the Python standard library class threading.Timer. When you start a timer it launches another thread, which consists of a time delay followed by a call to a function that you specify. In contrast to time.sleep() which stops your main thread at that point, with a Timer your main thread will keep going.
Here is roughly what you want:
from threading import Timer
def turn_off_lcd():
mylcd.lcd_clear()
mylcd.lcd_display_string("Flatson Feeding", 1)
mylcd.lcd_display_string("Systems", 2,4)
GPIO.output(27, GPIO.input(12) )
if input_state_LHS == False:
## calls the LCD_Clear function which has to be in the same folder as this file
mylcd.lcd_clear()
mylcd.lcd_display_string("LHS Feedtime",1,2)
mylcd.lcd_display_string(str(round(LHS_feedtime, 2)) + " sec" , 2,5)
GPIO.output(27, GPIO.input(12) )
t = Timer(LHS_feedtime, turn_off_led)
t.start()
menuitem = 0
Here you go, this will constantly loop the code until the 10 seconds have passed. But it will constantly print "10 seconds havent passed" (you can remove this line.) As you will notice, this code does not use the time.sleep() and so will not hold the script up.
import time
#Get the initial system time
timebuttonpressed = time.strftime("%H%M%S")
elapsedtime = time.strftime("%H%M%S")
while True:
elapsedtime = time.strftime("%H%M%S")
if input_state_LHS == False:
#Get the new system time, but only set timesample2
timebuttonpressed = time.strftime("%H%M%S")
## calls the LCD_Clear function which has to be in the same folder as this file
mylcd.lcd_clear()
mylcd.lcd_display_string("LHS Feedtime",1,2)
mylcd.lcd_display_string(str(round(LHS_feedtime, 2)) + " sec" , 2,5)
GPIO.output(27, GPIO.input(12) )
#Check if 10 seconds have passed
if((int(elapsedtime) - int(timebuttonpressed)) == 10):
timebuttonpressed = time.strftime("%H%M%S")
mylcd.lcd_clear()
mylcd.lcd_display_string("Flatson Feeding", 1)
mylcd.lcd_display_string("Systems", 2,4)
GPIO.output(27, GPIO.input(12) )
menuitem = 0
print("10 seconds havent passed...")

Keep one method constantly running and another method executing every certain period

So currently I am these two method where one reads the RF Data from another device constantly and another method sends that data every so often.
How could I do this? I need the RF Data incoming to be constantly updated and received while the sendData() method just grabs the data from the global variable whenever it can.
Heres the code below so far but it's not working...
import httplib, urllib
import time, sys
import serial
from multiprocessing import Process
key = 'MY API KEY'
rfWaterLevelVal = 0
ser = serial.Serial('/dev/ttyUSB0',9600)
def rfWaterLevel():
global rfWaterLevelVal
rfDataArray = ser.readline().strip().split()
print 'incoming: %s' %rfDataArray
if len(rfDataArray) == 5:
rfWaterLevelVal = float(rfDataArray[4])
print 'RFWater Level1: %.3f cm' % (rfWaterLevelVal)
#rfWaterLevel = 0
def sendData():
global rfWaterLevelVal
params = urllib.urlencode({'field1':rfWaterLevelVal, 'key':key})
headers = {"Content-type" : "application/x-www-form-urlencoded","Accept": "text/plain"}
conn = httplib.HTTPConnection("api.thingspeak.com:80", timeout = 5)
conn.request("POST", "/update", params, headers)
#print 'RFWater Level2: %.3f cm' % (rfWaterLevelVal)
response = conn.getresponse()
print response.status, response.reason
data = response.read()
conn.close()
while True:
try:
rfWaterLevel()
p = Process(target=sendData(), args())
p.start()
p.join()
#Also tried threading...did not work..
#t1 = threading.Thread(target=rfWaterLevel())
#t2 = threading.Thread(target=sendData())
#t1.start()
#t1.join()
#t2.join()
except KeyboardInterrupt:
print "caught keyboard interrupt"
sys.exit()
Please help!
Just to clarify, I need rfWaterLevel() method to run constantly as the rf data is incoming constantly, and I need sendData() to just be called as soon as it's ready to send again (roughly every 5 seconds or so). But it seems as if, if there is any sort of delay to the incoming rf data then rf data stops updating itself (the received end) and thus the data being sent is not accurate to what is being sent from the rf transmitter.
Thanks in advance!
I can't give you a full solution but I can guide you into the right direction.
Your code has three problems.
Process starts (as the name suggests) a new process and not a new thread.
A new process cannot share data with the old process.
You should use mutlithreading instead.
Have a look at threading as explained here
You are calling rfWaterLevel() inside the main thread.
You need to start the second thread before entering the while Loop.
Your are creating the second thread again and again inside the while Loop.
Create it only once and put the while Loop inside the function
Your basic program structure should be like this:
import time
def thread_function_1():
while True:
rfWaterLevel()
def thread_function_2():
while True:
sendData()
time.sleep(5)
# start thread 1
thread1 = Thread(target = thread_function_1)
thread1.start()
# start thread 2
thread2 = Thread(target = thread_function_2)
thread2.start()
# wait for both threads to finish
thread1.join()
thread2.join()

Categories