Skip to content

Commit

Permalink
Work-around to avoid channel duplication if mapping=[1]
Browse files Browse the repository at this point in the history
Fixes #18.
  • Loading branch information
mgeier committed Mar 13, 2016
1 parent cc654c9 commit 36763c4
Showing 1 changed file with 12 additions and 4 deletions.
16 changes: 12 additions & 4 deletions sounddevice.py
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ def play(data, samplerate=None, mapping=None, blocking=False, **kwargs):
"""
ctx = _CallbackContext()
ctx.frames = ctx.check_data(data, mapping)
ctx.frames = ctx.check_data(data, mapping, kwargs.get('device'))

def callback(outdata, frames, time, status):
assert len(outdata) == frames
Expand Down Expand Up @@ -420,7 +420,7 @@ def playrec(data, samplerate=None, channels=None, dtype=None,
"""
ctx = _CallbackContext()
output_frames = ctx.check_data(data, output_mapping)
output_frames = ctx.check_data(data, output_mapping, kwargs.get('device'))
if dtype is None:
dtype = ctx.data.dtype # ignore module defaults
input_frames = ctx.check_out(out, output_frames, channels, dtype,
Expand Down Expand Up @@ -2175,20 +2175,28 @@ def __init__(self):
self.event = threading.Event()
self.status = CallbackFlags()

def check_data(self, data, mapping):
def check_data(self, data, mapping, device):
"""Check data and output mapping."""
import numpy as np
data = np.asarray(data)
if data.ndim < 2:
data = data.reshape(-1, 1)
frames, channels = data.shape
dtype = _check_dtype(data.dtype)
mapping_is_explicit = mapping is not None
mapping, channels = _check_mapping(mapping, channels)
if data.shape[1] == 1:
pass # No problem, mono data is duplicated into arbitrary channels
elif data.shape[1] != len(mapping):
raise ValueError(
"number of output channels != size of output mapping")
# Apparently, some PortAudio host APIs duplicate mono streams to the
# first two channels, which is unexpected when specifying mapping=[1].
# In this case, we play silence on the second channel, but only if the
# device actually supports a second channel:
if (mapping_is_explicit and np.array_equal(mapping, [0]) and
query_devices(device, 'output')['max_output_channels'] >= 2):
channels = 2
silent_channels = np.setdiff1d(np.arange(channels), mapping)
if len(mapping) + len(silent_channels) != channels:
raise ValueError("each channel may only appear once in mapping")
Expand All @@ -2201,7 +2209,7 @@ def check_data(self, data, mapping):
return frames

def check_out(self, out, frames, channels, dtype, mapping):
"""Check out, frames, channels, dtype and mapping."""
"""Check out, frames, channels, dtype and input mapping."""
import numpy as np
if out is None:
if frames is None:
Expand Down

0 comments on commit 36763c4

Please sign in to comment.