1

I am newbie in Threading and I am writing a simple program which can do two tasks simultaneously. One function will record audio for arbitrary duration and the other function will just print some messages. In the record function, the Ctrl+C keyboard interrupt is used to stop the recording. But, if I put this function into a python thread, the Ctrl+C is not working and I am not able to stop the recording. The code is as follows. Any help is kindly appreciated.

import argparse
import tempfile
import queue
import sys
import threading
import soundfile as sf
import sounddevice as sd

def callback(indata, frames, time, status):
    """
    This function is called for each audio block from the record function.
    """
    if status:
        print(status, file=sys.stderr)
    q.put(indata.copy())

def record(filename='sample_audio.wav'):
    """
    Audio recording.
    Args:
        filename (str): Name of the audio file
    Returns:
        Saves the recorded audio as a wav file
    """
    q = queue.Queue()
    try:
        # Make sure the file is open before recording begins
        with sf.SoundFile(filename, mode='x', samplerate=48000, channels=2, subtype="PCM_16") as file:
            with sd.InputStream(samplerate=48000, channels=2, callback=callback):
                print('START')
                print('#' * 80)
                print('press Ctrl+C to stop the recording')
                print('#' * 80)
                while True:
                    file.write(q.get())
    except OSError:
        print('The file to be recorded already exists.')
        sys.exit(1)
    except KeyboardInterrupt:
        print('The utterance is recorded.')

def sample():
    for i in range(10):
        print('---------------------------------------------------')

def main():
    # Threading
    thread_1 = threading.Thread(target=record, daemon=True)
    thread_2 = threading.Thread(target=sample, daemon=True)
    thread_1.start()
    thread_2.start()
    thread_1.join()
    thread_2.join()

if __name__ == "__main__":
    main()
  • 1
    the problem is probably because `get` blocks at OS level and you can't interrupt all blocking calls. `sleep` is an exception because it's easier to cut it in small pieces. – Jean-François Fabre Sep 24 '18 at 08:20
  • 1
    Multithreading doesn't really exist in python, learn more about this by checking out the [GIL](https://wiki.python.org/moin/GlobalInterpreterLock). For your problem, you should check [multiprocessing](https://docs.python.org/3/library/multiprocessing.html) – Exho Sep 24 '18 at 08:21
  • 1
    There's also some mistakes in your code, you're calling `self.callback` and even inside `callback` you're calling `self.q.put`, but you haven't defined any `self`. Also what are `sf` and `sd`? – toti08 Sep 24 '18 at 08:22
  • Also I think your `callback` function should receive the `queue` from your `record` function. – toti08 Sep 24 '18 at 08:23
  • @toti08 I am sorry, actually the function is wrapped in a class with other definitions. For simplicity, I typed only the particular function and i missed to edit those changes. I changed it now. sf and sd are soundfile and sounddevice library. –  Sep 24 '18 at 08:27
  • @Exho Thanks for your answer. I will take a look at multiprocessing –  Sep 24 '18 at 08:28
  • Ok, I see. I think @JeanFrancoisFabre had a point there, the `q.get()` is a blocking one. Looking at the [docs](https://docs.python.org/3/library/queue.html#queue.Queue.get) you could try to set either the `block` parameter to `False` (and then catch the `Empty` exception) or set a positive timeout. – toti08 Sep 24 '18 at 08:38
  • 1
    @Exho - multithreading in Python (CPython at least) exists and works flawlessly. [`GIL`](https://wiki.python.org/moin/GlobalInterpreterLock) prevents executing two threads at the same time but Python threads are real, honest-to-your-favorite-deity user-space, system-managed threads ([`pthreads`](https://en.wikipedia.org/wiki/POSIX_Threads) or equivalent) and as such they not only run independently, but under certain conditions they might even run on different CPUs. In some instances (mostly I/O) these threads will actually execute in parallel despite the GIL as it doesn't manage system calls. – zwer Sep 24 '18 at 09:08
  • 1
    the problem is solved by using multiprocessing instead of threading. Thank you all guys. –  Sep 24 '18 at 09:23
  • @zwer "Multithreading is a type of execution model that allows multiple threads to exist within the context of a process such that they execute independently but share their process resources." So Python has its own thread system, I agree, but they are not independent so basically, you can't multithread (the way you can with other languages) in Python. – Exho Sep 24 '18 at 09:33
  • 2
    @Exho - I hate to be a stickler for semantics, but using threads in Python does create multiple threads which exist within the context of a process and run independently but share their process resources - they just cannot run in parallel for the most of the Python byte-code processing parts due to GIL. But [concurrency != parallelism](https://stackoverflow.com/questions/1050222/what-is-the-difference-between-concurrency-and-parallelism) - claiming otherwise is equivalent to saying that 'threading doesn't really exist in C' (or any language for that matter) when executing on a single-core CPU. – zwer Sep 24 '18 at 11:03

0 Answers0