def open_stream(self, device): self.log_supported_input_formats(device) self.logger.info("Opening the stream for device '%s'", device['name']) # by default we open the device stream with all the channels # (interleaved in the data buffer) stream = rtmixer.Recorder( device=device['index'], channels=device['max_input_channels'], blocksize=FRAMES_PER_BUFFER, # latency=latency, samplerate=SAMPLING_RATE) sampleSize = 4 # the sample size in bytes (float32) nchannels_max = device['max_input_channels'] # the number of channels that we record elementSize = nchannels_max * sampleSize # arbitrary size to avoid overflows without using too much memory ringbufferSeconds = 3. # The number of elements in the buffer (must be a power of 2) ringbufferSize = 2**int(math.log2(ringbufferSeconds * SAMPLING_RATE)) ringBuffer = rtmixer.RingBuffer(elementSize, ringbufferSize) # action can be used to read the count of input overflows action = stream.record_ringbuffer(ringBuffer) lat_ms = 1000 * stream.latency self.logger.info("Device claims %d ms latency", lat_ms) return (stream, ringBuffer, action, nchannels_max)
stream = rtmixer.MixerAndRecorder(device=device, channels=channels, blocksize=0, latency=latency, samplerate=samplerate) print(' input latency:', stream.latency[0]) print(' output latency:', stream.latency[1]) print(' sum:', sum(stream.latency)) print(' DSP size:', dsp_size) samplesize = 4 assert stream.dtype == ('float32', 'float32') assert stream.samplesize == (samplesize, samplesize) q_in = rtmixer.RingBuffer(samplesize * channels, buffersize_in) q_out = rtmixer.RingBuffer(samplesize * channels, buffersize_out) # Pre-fill output queue: q_out.write(np.zeros((pre_filling, channels), dtype='float32')) try: with stream: assert dsp_buffer.dtype == 'float32' assert dsp_buffer.flags.c_contiguous record_action = stream.record_ringbuffer(q_in) play_action = stream.play_ringbuffer(q_out) print('=== Start Processing') while True: while (q_in.read_available < dsp_size and record_action in stream.actions):
def __init__(self, elementsize, size): # Python 2.x doesn't have math.log2(), and it needs int(): size = 2**int(math.ceil(math.log(size, 2))) self.ringbuffer = rtmixer.RingBuffer(elementsize, size) self.buffer = bytearray() self.action = None
fig, ax = plt.subplots() lines = ax.plot(plotdata) if channels > 1: ax.legend(['channel {}'.format(c + 1) for c in range(channels)], loc='lower left', ncol=channels) ax.axis((0, len(plotdata), -1, 1)) ax.set_yticks([0]) ax.yaxis.grid(True) ax.tick_params(bottom='off', top='off', labelbottom='off', right='off', left='off', labelleft='off') fig.tight_layout(pad=0) stream = rtmixer.Recorder(device=device, channels=channels, blocksize=blocksize, latency=latency, samplerate=samplerate) ani = FuncAnimation(fig, update_plot, interval=interval, blit=True) with stream: elementsize = channels * stream.samplesize q = rtmixer.RingBuffer(elementsize, stepsize * qsize) action = stream.record_ringbuffer(q) plt.show() # TODO: check for ringbuffer errors? print('Input overflows:', action.stats.input_overflows)
samplerate = 48000 safety = 0.001 # Increase if Python interpreter is slow rb_size = 2**math.ceil(math.log2(delay * samplerate)) stream = rtmixer.MixerAndRecorder( channels=channels, blocksize=blocksize, samplerate=samplerate, latency=latency) with stream: samplesize = 4 assert {samplesize} == set(stream.samplesize) print(' input latency:', stream.latency[0]) print(' output latency:', stream.latency[1]) print(' sum:', sum(stream.latency)) print('requested delay:', delay) rb = rtmixer.RingBuffer(samplesize * channels, rb_size) start = stream.time + safety record_action = stream.record_ringbuffer(rb, start=start, allow_belated=False) play_action = stream.play_ringbuffer(rb, start=start + delay, allow_belated=False) # Dummy recording to wait until "start" has passed: stream.wait(stream.record_buffer(b'', channels=1, start=start)) if record_action not in stream.actions: if record_action.actual_time == 0: raise RuntimeError('Increase "safety"') else: # TODO: could there be another error? raise RuntimeError('Ring buffer overflow (increase "delay"?)') # Dummy playback to wait until "start + delay" has passed: stream.wait(stream.play_buffer(b'', channels=1, start=start + delay))
def print_action(action): print(' type:', next( k for k, v in vars(rtmixer).items() if v == action.type)) print(' requested time:', action.requested_time) print(' actual time:', action.actual_time) print(' total frames:', action.total_frames) print(' done frames:', action.done_frames) stream = rtmixer.Recorder( device=device, channels=channels, blocksize=blocksize, latency=latency, samplerate=samplerate) buffer = bytearray(10 * int(stream.samplerate) * stream.samplesize) ringbuffer = rtmixer.RingBuffer(stream.samplesize * channels, 128) print('checking stats before opening stream:') print_stats(stream) assert stream.stats.blocks == 0 assert stream.stats.input_overflows == 0 with stream: print('waiting a few seconds ...') sd.sleep(3 * 1000) print('checking stats:') action = stream.fetch_and_reset_stats() stream.wait(action) print_stats(action) print('starting recording to buffer ...') action = stream.record_buffer(buffer, channels=channels)
import sounddevice as sd import soundfile as sf filename = sys.argv[1] playback_blocksize = None latency = None reading_blocksize = 1024 # (reading_blocksize * rb_size) has to be power of 2 rb_size = 16 # Number of blocks with sf.SoundFile(filename) as f: with rtmixer.Mixer(channels=f.channels, blocksize=playback_blocksize, samplerate=f.samplerate, latency=latency) as m: elementsize = f.channels * m.samplesize rb = rtmixer.RingBuffer(elementsize, reading_blocksize * rb_size) # Pre-fill ringbuffer: _, buf, _ = rb.get_write_buffers(reading_blocksize * rb_size) written = f.buffer_read_into(buf, dtype='float32') rb.advance_write_index(written) action = m.play_ringbuffer(rb) while True: while rb.write_available < reading_blocksize: if action not in m.actions: break sd.sleep(int(1000 * reading_blocksize / f.samplerate)) if action not in m.actions: break size, buf1, buf2 = rb.get_write_buffers(reading_blocksize) assert not buf2 written = f.buffer_read_into(buf1, dtype='float32')