Skip to content

Commit 68a30f8

Browse files
committed
Add example rec_unlimited.py
1 parent c8b72b9 commit 68a30f8

File tree

2 files changed

+82
-0
lines changed

2 files changed

+82
-0
lines changed

doc/examples.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,10 @@ Real-Time Text-Mode Spectrogram
3030
:download:`spectrogram.py <../examples/spectrogram.py>`
3131

3232
.. literalinclude:: ../examples/spectrogram.py
33+
34+
Recording with Arbitrary Duration
35+
---------------------------------
36+
37+
:download:`rec_unlimited.py <../examples/rec_unlimited.py>`
38+
39+
.. literalinclude:: ../examples/rec_unlimited.py

examples/rec_unlimited.py

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
#!/usr/bin/env python3
2+
"""Create a recording with arbitrary duration.
3+
4+
PySoundFile (https://github.com/bastibe/PySoundFile/) has to be installed!
5+
6+
WARNING: This works only in Python 3.x!
7+
8+
"""
9+
import argparse
10+
import tempfile
11+
from queue import Queue
12+
13+
14+
def int_or_str(text):
15+
"""Helper function for argument parsing."""
16+
try:
17+
return int(text)
18+
except ValueError:
19+
return text
20+
21+
parser = argparse.ArgumentParser(description=__doc__)
22+
parser.add_argument(
23+
'-l', '--list-devices', action='store_true',
24+
help='show list of audio devices and exit')
25+
parser.add_argument(
26+
'-d', '--device', type=int_or_str,
27+
help='input device (numeric ID or substring)')
28+
parser.add_argument(
29+
'-r', '--samplerate', type=int, help='sampling rate')
30+
parser.add_argument(
31+
'-c', '--channels', type=int, default=1, help='number of input channels')
32+
parser.add_argument(
33+
'filename', nargs='?', metavar='FILENAME',
34+
help='audio file to store recording to')
35+
parser.add_argument(
36+
'-t', '--subtype', type=str, help='sound file subtype (e.g. "PCM_24")')
37+
args = parser.parse_args()
38+
39+
try:
40+
import sounddevice as sd
41+
import soundfile as sf
42+
43+
if args.list_devices:
44+
print(sd.query_devices())
45+
parser.exit()
46+
if args.samplerate is None:
47+
device_info = sd.query_devices(args.device, 'input')
48+
# soundfile expects an int, sounddevice provides a float:
49+
args.samplerate = int(device_info['default_samplerate'])
50+
if args.filename is None:
51+
args.filename = tempfile.mktemp(prefix='rec_unlimited_',
52+
suffix='.wav', dir='')
53+
queue = Queue()
54+
55+
def callback(indata, frames, time, status):
56+
"""This is called (from a separate thread) for each audio block."""
57+
if status:
58+
print(status, flush=True)
59+
queue.put(indata.copy())
60+
61+
# Make sure the file is opened before recording anything:
62+
with sf.SoundFile(args.filename, mode='x', samplerate=args.samplerate,
63+
channels=args.channels, subtype=args.subtype) as file:
64+
with sd.InputStream(samplerate=args.samplerate, device=args.device,
65+
channels=args.channels, callback=callback):
66+
print("#" * 80)
67+
print("press Ctrl+C to stop the recording")
68+
print("#" * 80)
69+
while True:
70+
file.write(queue.get())
71+
72+
except KeyboardInterrupt:
73+
parser.exit('\nRecording finished: ' + repr(args.filename))
74+
except Exception as e:
75+
parser.exit(type(e).__name__ + ': ' + str(e))

0 commit comments

Comments
 (0)