bishoph / sopare

Real time sound pattern recognition in Python for Raspberry/Banana Pi.
Other
317 stars 86 forks source link

Pausing SOPARE #74

Open HerrAugust opened 5 years ago

HerrAugust commented 5 years ago

In my application I need to stop SOPARE while another thread is executing.

Instead of calling every time

subprocess.Popen(['./sopare.py', '--loop'], stdout=subprocess.PIPE, ...)

I decided to send a SIGSTOP and then SIGCONT to SOPARE to make it stop and start over, avoiding to load PyAudio, which is quite heavy in terms of time on a Raspberry 3. My question is: is it the right way to freeze SOPARE? Or it must be done in a polite way, maybe via SOPARE plugins? It seems to me that, if I send SIGSTOP, then I speak for some seconds and then I send a SIGCONT to SOPARE, it also recognizes the words I said during the pause, which is not an expected behaviour

bishoph commented 5 years ago

Never tried to pause SOPARE and there is no build in feature to do so. As pyAudio continues in your environment (as far as I understand) there is no reader attached and the input stream piles up. When SOPARE comes back it starts reading the piled up stream and you get the past input. Not sure how big the buffer in pyAudio and sub systems are so your findings may vary.

If you just don't want to execute the predictions you could think about a flag in your custom plugin which can be set from an external instance/script/program...

classicryder commented 5 years ago

Will kill -CONT do the trick? Haven't tried it yet but might be interesting to test (im just too lazy to try it out 😀)

HerrAugust commented 5 years ago

@classicryder As pointed out in the question, SIGSTOP + SIGCONT is not an option because PyAudio seems to pass data anyways. And that's weird.

I am proceeding as suggested by @bishoph . I am a newbie in Python 2.7. I have created a new plugin and, in init.py, I put this content:

// file suspend_continue/__init__.py

from sopare import analyze, recorder

def suspend():
    print("suspend_cont. Suspending SOPARE")
    #analyze.analyze.suspended = True

   print('suspend. before it was recorder.suspended =' + str(recorder.recorder.suspended)) # corrent..
    recorder.recorder.suspended = True
    print('suspend. recorder.suspended =' + str(recorder.recorder.suspended)) # correct answer

def run(readable_results, data, rawbuf):
    suspend()
    pass

and then, I have modified the recorder class this way:

// file recorder.py

class recorder:
    suspended = False  # added by me

The problem arises when I try to read recorder.suspended in recorder.recording(self):

// class/file recorder.py

def recording(self):
        print ('recorder.recording()')
        self.stream = self.audio_factory.open(self.cfg.getintoption('stream', 'SAMPLE_RATE')) # pyaudio stream
        print ('recorder: audio factory open')
        self.debug_info()
        self.logger.info("start endless recording")
        while self.running:
            try:
                print("recording(). recorder.suspended: " + str(recorder.suspended))  # **my problem!**
                if recorder.suspended:
                    print('recorder suspended')  # it **never** gets here!
                    time.sleep(1)
                    continue
                if (self.buffering.is_alive()):
                    buf = self.stream.read(self.cfg.getintoption('stream', 'CHUNK'))
                    self.queue.put(buf)
                else:
                    self.logger.info("Buffering not alive, stop recording")
                    self.queue.close()
                    break

Now, I have spent so many hours trying to read the static variable recorder.suspended in recorder.recording() and I don't get it right. I think it might be because plugins and recorder live in 2 different threads. But in [1] it is suggested that it should work. Can you give me any clue?

Refs: [1] https://stackoverflow.com/questions/1072821/is-modifying-a-class-variable-in-python-threadsafe