def test_ringbuffer_overflow(): buffer = RingBuffer(format='B', capacity=7) remaining = buffer.push(b'spam') assert remaining is None remaining = buffer.push(b'eggs') assert bytes(remaining) == b's' assert bytes(buffer.pop(7)) == b'spamegg'
def __init__( self, playback_stopped_callback = lambda: print('playback stopped'), frames_played_callback = lambda: print(".", end="", flush=True) ): self.playback_stopped_callback = playback_stopped_callback self.frames_played_callback = frames_played_callback # the audio stops and should be started # from scratch as soon as this lock is released self.running = threading.RLock() self.running.acquire() self.playing = threading.Event() self.pause() self.stream = RingBuffer(format='B', capacity=BUFFER_SIZE) self.frames_callback_executor = ThreadPoolExecutor(max_workers=1) self.thread = threading.Thread( target=self._start_device, name='audio device' ) self.thread.daemon = True self.thread.start()
def test_wrapping_overwriting_push_pop(self): R = RingBuffer(3) R.push("1") R.push("2") R.push("3") R.push("a") self.assertEqual(R.pop(), "a")
def test_ringbuffer_underflow(): buffer = RingBuffer(format='b', capacity=1) assert buffer.pop(1) is None
def test_ringbuffer_push_pop(expected): capacity = expected.size buffer = RingBuffer(format=expected.dtype.char, capacity=capacity) assert buffer.capacity == capacity assert buffer.is_lock_free assert buffer.read_available == 0 assert buffer.write_available == capacity # test full write remaining = buffer.push(expected) assert remaining is None assert buffer.read_available == capacity assert buffer.write_available == 0 # test full read assert np.array_equal(buffer.pop(capacity), expected) assert buffer.read_available == 0 assert buffer.write_available == capacity # test writing chunks prev_read_available = 0 for chunk in np.array_split(expected, 6): remaining = buffer.push(chunk) assert remaining is None assert buffer.read_available == prev_read_available + len(chunk) prev_read_available += len(chunk) assert buffer.read_available == capacity assert buffer.write_available == 0 # test reading chunks prev_write_available = 0 for chunk in np.array_split(expected, 6): assert np.array_equal(buffer.pop(len(chunk)), chunk) assert buffer.write_available == prev_write_available + len(chunk) prev_write_available += len(chunk) # test reading more than available buffer.push(expected[:10]) assert np.array_equal(buffer.pop(capacity), expected[:10]) # teest reading more than capacity buffer.push(expected) assert np.array_equal(buffer.pop(capacity + 123), expected)
def test_ringbuffer_invalid(): with pytest.raises(ValueError): RingBuffer(format='B', capacity=0) buffer = RingBuffer(format='B', capacity=10) # Empty data is okay buffer.push(b'') buffer.push(b'hello') # Can't pop negative or zero for i in (-1, 0): with pytest.raises(ValueError): buffer.pop(i) # Types must match with pytest.raises(TypeError): buffer.push(np.array('f', [1.0]))
def test_ringbuffer_reset(): buffer = RingBuffer(format='B', capacity=5) buffer.push(b'hello') buffer.reset() assert buffer.pop(5) is None
from ringbuf import RingBuffer if __name__ == "__main__": buff = RingBuffer(50) temp = str("123456789") # print("temp size : " + str(len(temp))) # print(temp[:]) for i in range(10, 40): buff.push(chr(i)) for i in range(10, 40): test = buff.pop() print("Pop : " + test + "is Oct" + " is a type, " + str(type(test)))
class MiniaudioSink(object): """ Audio device interface. Internally uses a stream from which frames are read into an output sound device. The stream is backed by a buffer. This buffer is appended to with track PCM data. This object doesn't care about the overall state of the application -- it just feeds frames into sound card and expects an outside actor to tell it what other file to load. Since the buffer maintains a healthy headroom gapless playback is achieved with no special effort. `ffmpeg` is invoked to perform conversion to PCM. Tracks are loaded at once into memory. """ def __init__( self, playback_stopped_callback = lambda: print('playback stopped'), frames_played_callback = lambda: print(".", end="", flush=True) ): self.playback_stopped_callback = playback_stopped_callback self.frames_played_callback = frames_played_callback # the audio stops and should be started # from scratch as soon as this lock is released self.running = threading.RLock() self.running.acquire() self.playing = threading.Event() self.pause() self.stream = RingBuffer(format='B', capacity=BUFFER_SIZE) self.frames_callback_executor = ThreadPoolExecutor(max_workers=1) self.thread = threading.Thread( target=self._start_device, name='audio device' ) self.thread.daemon = True self.thread.start() def _start_device(self): with miniaudio.PlaybackDevice( output_format=miniaudio.SampleFormat.SIGNED16, backends=[miniaudio.Backend.PULSEAUDIO], nchannels=CHANNELS, sample_rate=SAMPLE_RATE) as device: generator = self._read_frames() next(generator) device.start(generator) self.running.acquire() # keep the thread running or else audio stops def _read_frames(self): required_frames = yield b'' while True: self.playing.wait() required_bytes = required_frames * CHANNELS * SAMPLE_WIDTH sample_data = self.stream.pop(required_bytes) if not sample_data: self.playback_stopped_callback() break self._on_frames_played(required_frames) required_frames = yield sample_data def _on_frames_played(self, frames): self.frames_callback_executor.submit(self.frames_played_callback, frames) def buffer_track(self, track_file_name): logger.debug('Loading track %s into buffer', track_file_name) pcm_data = subprocess.run( [ "ffmpeg", "-v", "fatal", "-hide_banner", "-nostdin", "-i", track_file_name, "-f", "s16le", "-acodec", "pcm_s16le", "-ac", str(CHANNELS), "-ar", str(SAMPLE_RATE), "-" ], capture_output=True ).stdout self.stream.push(pcm_data) return self.get_frame_count(pcm_data) def get_frame_count(self, pcm_data): return len(pcm_data) // (CHANNELS * SAMPLE_WIDTH) def pause(self): self.playing.clear() def resume(self): self.playing.set() def release(self): """ Once this has been called a new object should be created and the existing one cannot be used anymore. """ self.running.release()
def test_push_push_pop_pop(self): R = RingBuffer(5) R.push("1") R.push("2") self.assertEqual(R.pop()+R.pop(), "12")
def test_write_read_read(self): R = RingBuffer(5) with self.assertRaises(ValueError): R.write("abcdef")
def test_write_read_read(self): R = RingBuffer(5) R.write("abcd") self.assertEqual(R.read(4), "abcd") self.assertEqual(R.read(2), "")
def test_wrapping_write_wrapping_read(self): R = RingBuffer(5) R.write("abcd") self.assertEqual(R.read(1), "a") R.write("e1") self.assertEqual(R.read(5), "bcde1")
def test_wrapping_push_pop(self): R = RingBuffer(3) R.push("1") R.push("2") R.push("3") self.assertEqual(R.pop(), "1") R.push("a") self.assertEqual(R.read(2), "23") self.assertEqual(R.pop(), "a")