I am trying to write a python script which displays a macOS alert and starts an alarm at the same time.
The alarm sound should be stopped after the alert is closed, however it isn't.
def show_alert(message="Flashlight alarm"):
"""Display a macOS dialog."""
message = json.dumps(str(message))
exit_status = os.system("osascript dialog.scpt {0}".format(message))
return exist_status
def play_alarm(file_name = "beep.wav", repeat=3):
"""Repeat the sound specified to mimic an alarm."""
process = subprocess.Popen(['sh', '-c', 'while :; do afplay "$1"; done', '_', file_name], shell=False)
return process
def alert_after_timeout(timeout, message, sound = True):
"""After timeout seconds, show an alert and play the alarm sound."""
time.sleep(timeout)
process = None
if sound:
process = play_alarm()
exit_status = show_alert(message)
if process is not None:
os.killpg(os.getpgid(process.pid), signal.SIGINT)
process.kill()
# also, this below line doesn't seem to open an alert.
show_alert(exit_status)
alert_after_timeout(1, "1s alarm")
The above code should display a macOS alert after starting to loop an alarm sound (in the file beep.wav). When the alert is closed, the alarm sound should instantly stop.
The AppleScript file dialog.scpt triggers the alert, it is only a few lines long:
on run argv
tell application "System Events" to display dialog (item 1 of argv) with icon file (path of container of (path to me) & "Icon.png")
end run
I admit I don't know why you cannot kill your process running in a shell, using subprocess to mimic running as background..., and the fact that no other command runs after that means that there's probably a deadlock somewhere. So let's drop that solution.
Let me propose a more pythonic solution. The audio play part was adapted from how to play wav file in python? but now plays in a loop and works with python 3 as well.
The idea is to start a thread that plays a sound in a loop using only python modules. The thread is aware of a global variable. If the stop_audio variable is set, then the thread knows it has to quit the infinite loop and stop playing.
you control the flag from the other procedure. Once the message has been clicked on, set the flag, audio stops playing immediately.
import pyaudio
import wave
import threading
# global variable used to gently tell the thread to stop playing
stop_audio = False
def show_alert(message="Flashlight alarm"):
"""Display a macOS dialog."""
message = json.dumps(str(message))
exit_status = os.system("osascript dialog.scpt {0}".format(message))
return exit_status
# initialize audio
def play_alarm(file_name = "beep.wav"):
#define stream chunk
chunk = 1024
#open a wav format music
f = wave.open(file_name,"rb")
#instantiate PyAudio
p = pyaudio.PyAudio()
#open stream
stream = p.open(format = p.get_format_from_width(f.getsampwidth()),
channels = f.getnchannels(),
rate = f.getframerate(),
output = True)
while not stop_audio:
f.rewind()
#read data
data = f.readframes(chunk)
#play stream
while data and not stop_audio:
stream.write(data)
data = f.readframes(chunk)
#stop stream
stream.stop_stream()
stream.close()
#close PyAudio
p.terminate()
def alert_after_timeout(timeout, message, sound = True):
"""After timeout seconds, show an alert and play the alarm sound."""
time.sleep(timeout)
process = None
if sound:
t = threading.Thread(target=play_alarm,args=("beep.wav",))
t.start()
exit_status = show_alert(message)
global stop_sound
if sound:
stop_sound = True # tell the thread to exit
t.join()
show_alert(exit_status)
alert_after_timeout(1, "1s alarm")
Note that I've dropped the repeat=3 parameter as it wasn't used and I made no sense of it.
An alternative without using pyaudio would be to call the external player in a loop, replace play_alarm above by this:
def play_alarm(file_name = "beep.wav"):
global stop_sound
while not stop_sound:
subprocess.call(["afplay",file_name])
when stop_sound is True, the sound keeps on playing till the end, but doesn't resume. So the effect is not instantaneous, but it's simple.
And another alternative to cut the sound in a more reactive way:
def play_alarm(file_name = "beep.wav"):
global stop_sound
while not stop_sound:
process = subprocess.Popen(["afplay",file_name])
while not stop_sound:
if process.poll() is not None:
break # process has ended
time.sleep(0.1) # wait 0.1s before testing process & stop_sound flag
if stop_sound:
process.kill() # kill if exit by stop_sound
Related
I have a script in Python where I record the stream from four cameras to mp4 through gstreamer. I define a signal so that the capture terminates if Ctrl-C is pressed, and it works fine. In the gstreamer pipeline itself, I have a property added at the source numbuffers = 600 because I want the capture to stop after 600 frames anyway if I don't press Ctrl-C before then.
My problem is this, if I interrupt through the keyboard all four mp4 videos are saved correctly, but if I let it finish by itself after the 600 frames the second to fourth videos are fine while the first video will have "no playable stream", even if having the same size as the other videos.
I don't understand why only the first video is not saved or closed correctly, any hints?
This is my code:
import gi
import signal
import threading
import logging
from time import time, sleep
gi.require_version('Gst', '1.0')
from gi.repository import Gst, GObject
logging.basicConfig(level=logging.INFO)
def on_message(bus: Gst.Bus, message: Gst.Message, loop: GObject.MainLoop):
mtype = message.type
"""
Gstreamer Message Types and how to parse
"""
if mtype == Gst.MessageType.EOS:
logging.info("End-of-stream\n")
loop.quit()
elif mtype == Gst.MessageType.ERROR:
err, debug = message.parse_error()
logging.info("Warning: %s: %s\n" % (err, debug))
loop.quit()
elif mtype == Gst.MessageType.WARNING:
err, debug = message.parse_warning()
logging.info("Error: %s: %s\n" % (err, debug))
return True
def signal_handler(signal, frame):
for i in range(0,n_cams):
pipelines[i].send_event(Gst.Event.new_eos())
signal.signal(signal.SIGINT, signal_handler)
# Initialize
GObject.threads_init()
Gst.init(None)
n_cams = 4
buses = []
pipelines = []
for i in range(0,n_cams):
logging.info("Starting camera " + str(i))
command = ("nvarguscamerasrc sensor-id={} num-buffers=600 ! "
"video/x-raw(memory:NVMM), width=(int)1920, height=(int)1080, format=(string)NV12, framerate=(fraction)30/1 ! "
"queue max-size-buffers=4 leaky=2 ! "
"nvv4l2h265enc control-rate=1 bitrate=8000000 ! video/x-h265, stream-format=(string)byte-stream ! "
"h265parse ! qtmux ! filesink location=test_{}.mp4").format(i)
logging.info("Parse launch " + command)
pipelines.append(Gst.parse_launch(command))
loop = GObject.MainLoop()
for i in range(0,n_cams):
buses.append(pipelines[i].get_bus())
buses[i].add_signal_watch()
buses[i].connect("message", on_message, loop)
logging.info("Starting pipelines")
for i in range(0,n_cams):
pipelines[i].set_state(Gst.State.PLAYING)
loop.run()
# stop
for i in range(0,n_cams):
pipelines[i].send_event(Gst.Event.new_eos())
pipelines[i].set_state(Gst.State.NULL)
I think you are getting lucky in the ctrl-c case in the first place. In the ctrl-case when you send an EOS, you should wait for the EOS to reach the bus before setting the pipeline sate to NULL.
Similar problem with your actual described problem. You have 4 pipelines but only one main loop. You quit the loop after the first pipeline reaches EOS. Instead you should wait until all pipelines have reached EOS.
P.s. I think the send_event after # stop does not actually do anything.
I'm creating an application that will play audio on a different thread. The thread runs fine until it reaches the statement where I create an AudioSegement from the pydub library. I want it to continue executing after this statement so that the audio will play in the background.
def add_stream_queue(self):
search_result = self.search()
stream = Stream(search_result.get_title(), search_result.get_link())
self.stream_queue.append_queue(stream)
if self.first_stream:
self.stream_thread = threading.Thread(target=self.stream_queue.play_stream_queue, args=[self.play_audio])
self.stream_thread.start()
Here is where I create the thread and it calls the function play_stream_queue() which is in the class StreamQueue.
def play_stream_queue(self, play_audio):
while self.current_stream < self.get_size():
self.first_stream = False
self.manage_stream.download_stream(self.get_stream(self.current_stream).get_link())
play_audio.start(self.get_stream(self.current_stream).get_title())
self.current_stream += 1
print("reached")
self.first_stream = True
In this function I start to play the audio in the line play_audio.start(...). The thread executed perfectly at this point.
def start(self, filename):
self.filename = self.STREAM_FOlDER + filename + ".m4a"
self.stream_file = AudioSegment.from_file(self.filename, format="m4a")
print("TEST")
# Create an interface to PortAudio
self.p = pyaudio.PyAudio()
.
.
.
In the PlayAudio class is where the thread is giving me issues. The thread will run fine until it reaches the statement,
self.stream_file = AudioSegment.from_file(self.filename, format="m4a")
The thread will not continue after this statement.
The odd part is that it used to work perfectly fine and the thread would continue executing after this statement but then it randomly stopped working. I also notice if the program has an error and crashes the stream_thread will start playing.
I am trying to record screen events and execute them after for replay.
I wrote a small python script which listens for events,converts them from hexadecimal to decimal,waits for 5 seconds and executes recorded events with adb sendevent.
But for some reason sendevent never executes correctly, sometimes it touches wrong coordinates, sometimes touches for too long also there are problems with delays between touches.
I couldnt understand why this is happening ? What i expect is it should just replay since getevent captured all necessary data needed(?)
import subprocess
import threading
import os
from time import sleep
eventsToSend = []
def eventSender():
while(True):
if(len(eventsToSend) > 200):
print("starting to execute in 5 seconds...")
sleep(5)
for command in eventsToSend:
#with open('output.txt', 'a') as f1:
#f1.write(command+os.linesep)
subprocess.Popen(command.split(), stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
print("done")
break
else:
None
eventSenderStarter = threading.Thread(target = eventSender)
eventSenderStarter.start()
def runProcess(exe):
p = subprocess.Popen(exe, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
while(True):
# returns None while subprocess is running
retcode = p.poll()
line = p.stdout.readline()
yield line
if retcode is not None or len(eventsToSend)>200:
print("Executing events...")
break
print("Listening for events...")
for line in runProcess('adb shell -- getevent /dev/input/event1'.split()):
myLine = line.decode().strip()
splittedLine = myLine.split(" ")
decimalString = ""
for index,hexadecimal in enumerate(splittedLine):
decimal = int(hexadecimal, 16)
if(index==0):
decimalString = decimalString+str(decimal)
if(index>0):
decimalString = decimalString+" "+str(decimal)
eventsToSend.append("adb shell sendevent /dev/input/event1 "+decimalString)
Just connect your phone to pc then run this script play with your screen after 200 events it will start replay(be careful because it might press wrong coordinates :P ) .In my case it was
/dev/input/event1
so you might need to edit event1 for testing.
Consider adding some small delay between the events you send - time.sleep(0.5). You may have to change the value of 0.5 - try some values until it will work.
A friend of mine asked me to wrote a simple code to play a video each time a motion is detected. The hardwares used are a Raspberry Pi B+ and a Logitech C170 webcam.
This is full code: http://pastebin.com/Z6nS9MXf. Overall, it works almost as expected.
So the issue is the timeout_command part:
def timeout_command(command, timeout):
cmd = command.split(" ")
start = datetime.datetime.now()
process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
while process.poll() is None:
time.sleep(0.1)
now = datetime.datetime.now()
if (now - start).seconds > timeout:
os.kill(process.pid, signal.SIGKILL)
os.waitpid(-1, os.WNOHANG)
return None
return process.stdout.read()
It's supossed to do something within a timeout period, and after the period is reached, then the process is stopped.
I wrote this in the code:
timeout_command("omxplayer /home/pi/video/trololo.mp4", 10)
Which is supposed to make omxplayer play the video for only 10 seconds, then close after that. Yet omxplayer plays the video till the end.
How to fix this?
I build a keypad activated sensor with a Raspberry Pi and Python. Everything seems to go well but after a few minutes and several keypad entries, the Pi has a total crash and switches off immediately - no error messages pop up.
The script will continously wait for a keypad entry and if the code is correct, switch on a sensor, if it's not you have to try again. If the sensor is activated you have to put in the correct numbers to avoid an alarm being triggered after 30s)
Could anyone point me in the direction of what might be the problem? Here are the things I've tried so far without success
1) Exchange Pi to new Pi 2
2) Different OS, both NOOBS and Raspbian Wheezy
3) Different sensor (accelerometer vs IR sensor)
4) Disconnect monitor, keyboard and use SSH connection via Cygwin
5) Get log file until crash - log file was empty after reboot
python bad_script &> full_log.txt
6) Different log file command: causes instant crash and is also empty after reboot:
python script.py >> /logdir/script.py.log 2>&1
The question is: how am I able to crash Linux? If it's a memory problem, isn't there a prevention in linux to stop processed before they appear?
Here is the full script I am running:
import sys
from time import sleep
import threading
import signal
from matrix_keypad import RPi_GPIO1
import RPi.GPIO as GPIO
import smbus
import time
passcode = [1,2,3,4] # this is the correct code you have to enter
kp = RPi_GPIO1.keypad(columnCount = 3)
alarm_active = threading.Event() # shared variable used to control the sensor monitoring thread
alarm_active.clear() # Clear the alarm initially
monitor_thread = None # Global variable used to store the monitoring thread
#Set up all the pins correctio
GPIO.setmode(GPIO.BCM)
PINIR=7
GPIO.setup(7, GPIO.IN) # infrad-sensor
#Now activate the kaypad and listen for keypad inputs
def digit():
r = None
while r == None:
r = kp.getKey()
return r
def get_keycode():
# Function to loop around reading 4 keypresses in a row
# Compare against chosen code
# If match, switch the alarm state
entered = []
while len(entered) < 4:
key = digit()
sleep(0.5)
print key
entered.append( key )
if entered == passcode:
entered = []
print "Code correct"
switch_alarm_state()
else:
# Just clear the keypad buffer if the wrong code went in
# Could say "BAD CODE!" here, or even force immediate alarm perhaps
print "Wrong Code - Try again"
GPIO.output(27, True) # let red LED blink as indicator that code was wrong
time.sleep(1)
GPIO.output(27, False)
entered = []
def switch_alarm_state():
# Function to control the state of the alarm
# If the alarm should be on, run a thread monitoring the sensor
# If the alarm should be off, make sure the thread is stopped
global monitor_thread
if alarm_active.is_set():
# If the alarm is currently set, stop it
print "Alarm was abolished"
GPIO.output(17, False) #switch green LED off
alarm_active.clear() # Signals to the monitor thread that we wish to shut it down
monitor_thread.join() # Wait until the monitor thread has stopped
else:
# Alarm is not currently set, so start the sensor monitor thread going
print "Alarm was activated"
GPIO.output(17, True)
monitor_thread = threading.Thread( target=sensor_monitor )
alarm_active.set()
monitor_thread.start()
def sensor_monitor():
# Function to be run in a separate thread, monitoring the sensor
alarm_timer = None # Variable to hold a timer object that will fire the alarm
while alarm_active.is_set():
#print xrota
if GPIO.input(PINIR):
print "Alarm has been triggered"
if alarm_timer is None:
alarm_timer = threading.Timer( 30.0, fire_alarm )
alarm_timer.start()
sleep(0.5)
# The alarm must have been deactivated to get here
# Stop any outstanding alarms and shutdown
if alarm_timer is not None:
alarm_timer.cancel()
return
def fire_alarm():
# Here you implement the actions to be carried out by an alarm
print "Alarm is send to server"
msg = "Alarm"
publish.single("alarm/demo",msg, hostname="52.17.194.125")
def shutdown_handler( signum, frame ):
# Shut down the child thread nicely
alarm_active.clear()
monitor_thread.join()
if __name__ == '__main__': # This is the Python way to check you are being directly run, and not just imported by another script
signal.signal( signal.SIGINT, shutdown_handler ) # If you stop the script with ctrl+C, make sure it shuts down properly
signal.signal( signal.SIGTERM, shutdown_handler )
try:
while True:
get_keycode()
except KeyboardInterrupt:
GPIO.cleanup()
print "Quit program"