Skip to content

Commit 0162644

Browse files
committed
DOC: New example: signal_processing.py
1 parent f663f2f commit 0162644

1 file changed

Lines changed: 95 additions & 0 deletions

File tree

examples/signal_processing.py

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
#!/usr/bin/env python3
2+
"""How to do "live" signal processing with ring buffers."""
3+
import math
4+
import time
5+
6+
import numpy as np
7+
import rtmixer
8+
9+
device = None
10+
latency = 'low'
11+
samplerate = 48000
12+
channels = 1
13+
sleeptime = 0.001 # To avoid busy-waiting
14+
dsp_size = 4096
15+
# Simulated processing time in seconds:
16+
dsp_duration = 0.8 * (dsp_size / samplerate)
17+
# Buffer for hypothetical fixed-blocksize DSP algorithm:
18+
dsp_buffer = np.zeros((dsp_size, channels), dtype='float32')
19+
dsp_calls = 0
20+
21+
# This value adds to the overall latency
22+
pre_filling = 2 * dsp_size
23+
24+
25+
def hypothetical_dsp_algorithm(buffer):
26+
assert len(buffer) == dsp_size
27+
time.sleep(dsp_duration)
28+
global dsp_calls
29+
dsp_calls += 1
30+
31+
32+
def nextpow2(x):
33+
return 2**math.ceil(math.log2(x))
34+
35+
36+
buffersize_in = nextpow2(0.5 * samplerate)
37+
buffersize_out = nextpow2(0.5 * samplerate)
38+
39+
stream = rtmixer.MixerAndRecorder(
40+
device=device, channels=channels, blocksize=0, latency=latency,
41+
samplerate=samplerate)
42+
43+
print(' input latency:', stream.latency[0])
44+
print(' output latency:', stream.latency[1])
45+
print(' sum:', sum(stream.latency))
46+
print(' DSP size:', dsp_size)
47+
48+
samplesize = 4
49+
assert stream.dtype == ('float32', 'float32')
50+
assert stream.samplesize == (samplesize, samplesize)
51+
52+
q_in = rtmixer.RingBuffer(samplesize * channels, buffersize_in)
53+
q_out = rtmixer.RingBuffer(samplesize * channels, buffersize_out)
54+
55+
# Pre-fill output queue:
56+
q_out.write(np.zeros((pre_filling, channels), dtype='float32'))
57+
58+
try:
59+
with stream:
60+
assert dsp_buffer.dtype == 'float32'
61+
assert dsp_buffer.flags.c_contiguous
62+
record_action = stream.record_ringbuffer(q_in)
63+
play_action = stream.play_ringbuffer(q_out)
64+
print('=== Start Processing')
65+
while True:
66+
while (q_in.read_available < dsp_size
67+
and record_action in stream.actions):
68+
time.sleep(sleeptime)
69+
if record_action not in stream.actions:
70+
raise RuntimeError('Input ringbuffer overflow')
71+
frames = q_in.readinto(dsp_buffer)
72+
assert frames == dsp_size
73+
74+
hypothetical_dsp_algorithm(dsp_buffer)
75+
76+
while (q_out.write_available < dsp_size
77+
and play_action in stream.actions):
78+
time.sleep(sleeptime)
79+
if play_action not in stream.actions:
80+
raise RuntimeError('Output ringbuffer underflow')
81+
frames = q_out.write(dsp_buffer)
82+
assert frames == dsp_size
83+
print('=== Stop Processing')
84+
except KeyboardInterrupt:
85+
print('\n=== Interrupted by User')
86+
except RuntimeError as e:
87+
print('=== Error:', e)
88+
89+
print(' recorded blocks:', record_action.stats.blocks)
90+
print(' played blocks:', play_action.stats.blocks)
91+
print(' DSP calls:', dsp_calls)
92+
print(' input underflows:', stream.stats.input_underflows)
93+
print(' input overflows:', stream.stats.input_overflows)
94+
print('output underflows:', stream.stats.output_underflows)
95+
print('output overflows:', stream.stats.output_overflows)

0 commit comments

Comments
 (0)