10

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)
Brian Leach
  • 3,974
  • 8
  • 36
  • 75
  • Does your computer have a microphone you could attempt to slave? – wnnmaw Jan 17 '14 at 17:37
  • Sounds like a very cool geeky thing we'd all love to dream a solution up for, but there has to be a better way of doing it. No exit code, or process you can monitor? Even screengrabbing would probably be easier. – NibblyPig Jan 17 '14 at 17:38
  • @wnnmaw I do not think I understand what you mean by slaving a mic. I could find a mic if needed. – Brian Leach Jan 17 '14 at 17:39
  • What architecture and operating system are you on? – Sven Marnach Jan 17 '14 at 17:40
  • @SLC the process does not exit, it waits for an operator to click something. There are some areas of the screen that change, but we run multiple instances of the executable and they lay on top of each other. Upon test completion, the instance in question does not come to the front – Brian Leach Jan 17 '14 at 17:41
  • @BrianLeach, If you have a mic you can get at the underlying C code in windows and literally listen for a 1Hz tone – wnnmaw Jan 17 '14 at 17:42
  • @SvenMarnach Win XP, likely x86 (question edited) – Brian Leach Jan 17 '14 at 17:42
  • And the program is an old DOS program? – Sven Marnach Jan 17 '14 at 18:01
  • @SvenMarnach, the program is a Windows executable (with GUI) written in C++ – Brian Leach Jan 20 '14 at 16:12
  • @BrianLeach is this still an issue? – wnnmaw Jan 27 '14 at 17:41
  • 1
    @wnnmaw As much as I would have liked to implement and test your proposed method, I couldn't justify the learning curve to solve this particular problem. I appreciate the reply, someone will find it useful I am sure. I ended up training some lab techs what to do when they heard the beep :) – Brian Leach Jan 27 '14 at 21:31
  • @BrianLeach, no problem, just checking in since you haven't accepted any answer, glad you found a workaround though! – wnnmaw Jan 27 '14 at 21:37

3 Answers3

3

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:

  • Recording sound: "Record" example from PyAudio page
  • FFT and finding frequency: This post

Good luck

Community
  • 1
  • 1
wnnmaw
  • 5,444
  • 3
  • 38
  • 63
2

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.

AShelly
  • 34,686
  • 15
  • 91
  • 152
0

I'm not familiar with programming on Windows, so lots of guesses in this answer.

Presumably the program calls some Windows API function to beep the speaker, which is probably provided by some DLL. On a POSIX platform, I would use LD_PRELOAD to load a shared library that overwrites that API function by my own version, which would then be able to notify me. There's probably a similar technique on Windows; maybe DLL injection helps.

Sven Marnach
  • 574,206
  • 118
  • 941
  • 841