spatialaudio / python-sounddevice

:sound: Play and Record Sound with Python :snake:
https://python-sounddevice.readthedocs.io/
MIT License
1.03k stars 150 forks source link

rec_unlimited.py : How to implement without KeyboardInterrupt #132

Closed lgertel closed 6 years ago

lgertel commented 6 years ago

I need to know how to wait for the thread to finish to finish my program. The way that i implemented closes before it finishes, then the audio file is cropped.

q = queue.Queue()

def callback(indata, frames, time, status):
    if status:
        print(status, file=sys.stderr)
    q.put(indata.copy())

# Make sure the file is opened before recording anything:
with sf.SoundFile('audio.flac', mode='w', samplerate=samplerate,
                  channels=channels, subtype=None) as file:
    with sd.InputStream(samplerate=samplerate, device=device,
                        channels=channels, callback=callback):
        while True:
            file.write(q.get())
            if not button.is_pressed:
                break;

Thanks!

lgertel commented 6 years ago

.stop() on it!

lgertel commented 6 years ago

Still crop my audio. Think that break stops before queue:

from gpiozero import Button
from gpiozero import LED

from signal import pause

import queue
import sys
import transcript

import sounddevice as sd
import soundfile as sf

import time

button = Button(25)
red_led = LED(18)
green_led = LED(23)
red_led.on()
green_led.on()

device = 0
channels = 1
device_info = sd.query_devices(device, 'input')
samplerate = int(device_info['default_samplerate'])

def start_recording():
    print("RECORDING")
    device = 0
    channels = 1
    device_info = sd.query_devices(device, 'input')
    samplerate = int(device_info['default_samplerate'])

    q = queue.Queue()

    def callback(indata, frames, time, status):
        if status:
            print(status, file=sys.stderr)
        q.put(indata.copy())

    def finished_callback():
        return True

    # Make sure the file is opened before recording anything:
    with sf.SoundFile('audio.flac', mode='w', samplerate=samplerate,
                      channels=channels, subtype=None) as file:
        with sd.InputStream(samplerate=samplerate, device=device,
                            channels=channels, callback=callback, finished_callback=finished_callback) as sdis:
            while True:
                file.write(q.get())
                if not button.is_pressed:
                    sdis.stop()
                    break

button.when_pressed = start_recording
pause()
mgeier commented 6 years ago

I still don't fully understand the problem.

Is there some data left in the queue at the end?

If that's the case you can simply read the rest after the stream is closed:

with sf.SoundFile(...) as file:
    with sd.InputStream(...):
        while True:
            file.write(q.get())
            if not button.is_pressed:
                break
    file.write(q.get())

Note, however, that this might be problematic if the queue happens to be already empty, because the call q.get() would wait forever ... You should probably use the timeout argument or the block argument, but I haven't really tried this.

There is no need to stop the stream from within the with block. It is automatically stopped when the with block is left.

lgertel commented 6 years ago

I think that the problem is related to the queue being empty. I'll try a block argument but the way that I have managed to solve was to add a None value to the queue and checked for this value to stop!

Thank you very much!