We are using a very old program to drive some equipment tests. These tests can run for a couple of days and I would like to know when a test has completed. When a test completes, the executable continuously beeps the motherboard speaker at ~1 beep per second until operator intervention.
Is there a way that I could "listen" for this beep and send out a notification when the MB starts beeping? I am hoping there is a sys
or os
library that I might use to indicate this.
We are running on Windows XP x86. I have not yet installed Python on the machine.
Pseudocode:
already_beeping = True
while True:
speaker_beeping = check_speaker() # returns True or False
if speaker_beeping == True:
if already_beeping == False:
send_notification()
already_beeping = True
else:
pass
else:
already_beeping = False
time.sleep(10)
A lot of boards have mostly gotten rid of speakers in favour of small LED/LCD displays that show error codes as they're clearer and easier to understand than a series of beeps. That said, a lot still have a small speaker that will beep during boot loops and such.
Alternatively referred to as a PC speaker, onboard speaker, and system speaker, the internal speaker is a basic speaker on a motherboard that creates beeps, beeping noises, and mono tones. This speaker is very basic and is not a speaker for playing songs, music, or other complex sounds generated in a game.
Is the speaker connected to the motherboard with a 2 pin header?
If so, it should be trivial to intercept it and monitor the signal. Start with an oscilloscope to verify the signal, then hook up some sort of USB digital I/O monitor - you should be able to count pulses and determine frequency. (There are off-the shelf solutions, or a simple Arduino program would work).
Or, if you want to get into really low-level programming, look into querying the "Programmable Interval Timer" chip that drives the speakers. Look specifically at the "output pin state" in the Read Back Status Byte.
You'd probably have to write a C extension to python to access thes ports: See here for some example C code to access the chip.
Ok, here's my attempt at a solution using PyAudio, let me know what you think. Unfortunately, I currently have no means of testing.
This is adapted from the "Record" example on the PyAudio page.
import threading
import PyAudio
import wave
import struct
import numpy as np
import os
import datetime
CHUNK = 1024
FORMAT = pyaudio.paInt16
CHANNELS = 2
RATE = 44100
RECORD_SECONDS = 5
SEARCHTIME = 5
LOWERBOUND = 0.9
UPPERBOUND = 1.1
class RecorderThread(threading.Thread):
def __init__(self, name):
threading.Thread.__init__(self)
self.name = name
self.stream = p.open(format=FORMAT,
channels=CHANNELS,
rate=RATE,
input=True,
frames_per_buffer=CHUNK)
self.start()
def run(self):
p = pyaudio.PyAudio()
print("* recording")
frames = []
for i in range(0, int(RATE / CHUNK * RECORD_SECONDS)):
data = self.stream.read(CHUNK)
frames.append(data)
print("* done recording")
self.stream.stop_stream()
self.stream.close()
p.terminate()
wf = wave.open(self.name, 'wb')
wf.setnchannels(CHANNELS)
wf.setsampwidth(p.get_sample_size(FORMAT))
wf.setframerate(RATE)
wf.writeframes(b''.join(frames))
wf.close()
frate = RATE
wav_file = wave.open(self.name,'r')
data = wav_file.readframes(wav_file.getnframes())
wav_file.close()
os.remove(self.file)
data =s truct.unpack('{n}h'.format(n=data_size), data)
data = np.array(data)
w = np.fft.fft(data)
freqs = np.fft.fftfreq(len(w))
idx=np.argmax(np.abs(w)**2)
freq=freqs[idx]
freq_in_hertz=abs(freq*frate)
if freq_in_herts > LOWERBOUND and freq_in_herts < UPPERBOUND:
curName = "found0.txt"
while os.path.exists(curName):
num = int(curName.split('.')[0][6:])
curName = "found{}.txt".format(str(num+1))
f = open(curName, 'r')
f.write("Found it at {}".format(datetime.datetime.now()))
f.close()
def main():
recordingThreads = []
totalTime = 0
while totalTime < SEARCHTIME*(24*3600) and not os.path.exists("found.txt"):
start = datetime.datetime(year=2012, month=2, day=25, hour=9)
curName = "record0.wav"
while os.path.exists(curName):
num = int(curName.split('.')[0][6:])
curName = "record{}.wav".format(str(num+1))
recorder = RecorderThread(curName)
time.sleep(4.5)
end = datetime.datetime(year=2012, month=2, day=25, hour=18)
totalTime += end - start
if __name__ == "__main__": main()
Ok, so that turned out a bit bigger than I expected. This will run for the number of days specified by SEARCHTIME
. Every 4.5 seconds, it will record for 5 seconds (to make sure we dont miss anything) This recording will be saved with a dynamic name (to prevent overwriting). Then we perform FFT on that .wav
file and see if the frequency is between LOWERBOUND
and UPPERBOUND
. If the frequency is between these two bounds, a file is created that says when that happens. This code continues until it SEARCHTIME
is reached AND at least one beep has been found. Since there is a bit of overlap, all the processing is done in threads.
Note that this can produce false positives, which is why it doesn't terminate after the first finding. Addtionally, if it never finds something, it'll keep running. Forever.
One final note: As I said earlier, I haven't' been able to test it, so it likely won't run on your fist go. I apologize in advance, but at the very least, this should give you a pretty good head start. Please let me know what breaks so I can fix it here!
References:
Good luck
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