Skip to content

Instantly share code, notes, and snippets.

@larsoner
Created March 15, 2019 13:41
Show Gist options
  • Save larsoner/fd9228f321d369c8a00c66a246fcc83f to your computer and use it in GitHub Desktop.
Save larsoner/fd9228f321d369c8a00c66a246fcc83f to your computer and use it in GitHub Desktop.
"""libsoundio interface for sound output."""
# Authors: Dan McCloy <[email protected]>
# Eric Larson <[email protected]>
#
# License: BSD (3-clause)
import ctypes
import numpy as np
# https://github.com/andrewrk/libsoundio
# Todo:
# - loop=True
# - backends
# - options file
# - fix threading weirdness
class SoundPlayer(object):
def __init__(self, data, fs, loop=False,
backend=None, block_size=4096):
self._data = np.clip(data.T, -1, 1).astype(np.float32)
# if backend is None:
# from pysoundio import SoundIoBackendAlsa
# backend = SoundIoBackendAlsa
self._backend = backend
self._fs = fs
self._block_size = block_size
self._n_samples, self._n_channels = self._data.shape
assert self._n_channels in (1, 2)
self._ec_duration = self._n_samples / float(fs)
# don't actually start it
self._open_stream()
self.playing = False
def _open_stream(self):
from pysoundio import (PySoundIo, SoundIoFormatFloat32LE,
SoundIoOutStream)
self._offset = 0
self._pysoundio = PySoundIo(backend=self._backend)
self._started = False
temp = self._pysoundio._start_output_stream
self._pysoundio._start_output_stream = lambda: None
self._pysoundio.start_output_stream(
device_id=None, channels=self._n_channels,
sample_rate=self._fs, block_size=self._block_size,
dtype=SoundIoFormatFloat32LE,
write_callback=self._callback)
self._pysoundio._start_output_stream = temp
stream = ctypes.cast(self._pysoundio.output['stream'],
ctypes.POINTER(SoundIoOutStream))
print('Opening stream with latency %0.1fms'
% (1000 * stream.contents.software_latency,))
def play(self):
if not self._started:
self._pysoundio._start_output_stream()
self._started = True
self.playing = True
else:
self._pysoundio.pause_output_stream(False)
def pause(self):
self._pysoundio.pause_output_stream(True)
self.playing = False
def stop(self):
self.pause()
self._close_stream()
self._open_stream()
def _close_stream(self):
if self._pysoundio is not None:
self._pysoundio.close()
self._pysoundio = None
self._offset = 0
def _callback(self, data, length):
dlen = min(length, self._n_samples - self._offset)
if dlen > 0:
data[:] = self._data[self._offset:self._offset + dlen].data
self._offset += dlen
def delete(self):
self.__del__()
def __del__(self):
self.stop()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment