Как я мог "слушать" звуки на внутреннем динамике материнской платы

Мы используем очень старую программу для тестирования некоторых тестов оборудования. Эти тесты могут выполняться в течение нескольких дней, и я хотел бы знать, когда тест завершится. Когда тест завершается, исполняемый файл непрерывно подает звуковой сигнал на динамик материнской платы примерно на 1 звуковой сигнал в секунду до вмешательства оператора.

Есть ли способ, которым я мог бы "прослушать" этот звуковой сигнал и отправить уведомление, когда MB начнет звучать? Я надеюсь, что есть библиотека sys или os, которую я могу использовать для указания этого.

Мы работаем на Windows XP x86. Я еще не установил Python на машине.

псевдокод:

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)

Ответ 1

Подключен ли динамик к материнской плате с 2-контактным разъемом?

Если это так, должно быть тривиально перехватить его и контролировать сигнал. Начните с осциллографа, чтобы проверить сигнал, затем подключите какой-то USB-цифровой монитор ввода-вывода - вы должны иметь возможность подсчитывать импульсы и определять частоту. (Есть готовые решения, или простая программа Arduino будет работать).

Или, если вы хотите получить действительно низкоуровневое программирование, загляните в запрос "Programmable Interval Timer" , который управляет динамиками. Посмотрите конкретно на "состояние выходного вывода" в "Байт состояния чтения" .

Вам, вероятно, придется написать расширение C на python для доступа к портам: см. здесь для примера C-код для доступа к чипу.

Ответ 2

Хорошо, здесь моя попытка решения с помощью PyAudio, сообщите мне, что вы думаете. К сожалению, у меня нет средств для тестирования.

Это адаптировано из примера "Запись" на странице PyAudio.

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()

Хорошо, так что оказалось немного больше, чем я ожидал. Это будет работать в течение нескольких дней, указанных SEARCHTIME. Каждые 4,5 секунды он будет записываться в течение 5 секунд (чтобы убедиться, что мы ничего не пропустили) Эта запись будет сохранена с динамическим именем (чтобы предотвратить перезапись). Затем мы выполняем FFT в этом .wav файле и видим, находится ли частота между LOWERBOUND и UPPERBOUND. Если частота находится между этими двумя границами, создается файл, который говорит, когда это произойдет. Этот код продолжается до достижения SEARCHTIME И, по крайней мере, один звуковой сигнал найден. Так как существует немного перекрытия, вся обработка выполняется в потоках.

Обратите внимание, что это может привести к ложным срабатываниям, поэтому он не заканчивается после первого нахождения. Кроме того, если он никогда не найдет что-то, он будет продолжать работать. Навсегда.

Последнее замечание: как я уже говорил ранее, я не смог проверить его, поэтому он, скорее всего, не будет работать на вашем кулаке. Я заранее извиняюсь, но, по крайней мере, это должно дать вам неплохой начальный старт. Пожалуйста, дайте мне знать, что ломается, поэтому я могу исправить это здесь!

Литература:

  • Звук записи: пример "Запись" на странице PyAudio
  • FFT и частота поиска: Этот пост

Удачи.

Ответ 3

Я не знаком с программированием в Windows, поэтому в этом ответе много догадок.

Предположительно, программа вызывает некоторую функцию Windows API для подачи звукового сигнала на динамик, который, вероятно, предоставляется некоторой DLL. На платформе POSIX я использовал бы LD_PRELOAD для загрузки разделяемой библиотеки, которая перезаписывает эту функцию API моей собственной версией, которая затем сможет уведомить меня. Вероятно, подобная техника работает в Windows; возможно DLL-инъекция помогает.