Skip to content

Commit 36763c4

Browse files
committed
Work-around to avoid channel duplication if mapping=[1]
Fixes #18.
1 parent cc654c9 commit 36763c4

File tree

1 file changed

+12
-4
lines changed

1 file changed

+12
-4
lines changed

sounddevice.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,7 @@ def play(data, samplerate=None, mapping=None, blocking=False, **kwargs):
289289
290290
"""
291291
ctx = _CallbackContext()
292-
ctx.frames = ctx.check_data(data, mapping)
292+
ctx.frames = ctx.check_data(data, mapping, kwargs.get('device'))
293293

294294
def callback(outdata, frames, time, status):
295295
assert len(outdata) == frames
@@ -420,7 +420,7 @@ def playrec(data, samplerate=None, channels=None, dtype=None,
420420
421421
"""
422422
ctx = _CallbackContext()
423-
output_frames = ctx.check_data(data, output_mapping)
423+
output_frames = ctx.check_data(data, output_mapping, kwargs.get('device'))
424424
if dtype is None:
425425
dtype = ctx.data.dtype # ignore module defaults
426426
input_frames = ctx.check_out(out, output_frames, channels, dtype,
@@ -2175,20 +2175,28 @@ def __init__(self):
21752175
self.event = threading.Event()
21762176
self.status = CallbackFlags()
21772177

2178-
def check_data(self, data, mapping):
2178+
def check_data(self, data, mapping, device):
21792179
"""Check data and output mapping."""
21802180
import numpy as np
21812181
data = np.asarray(data)
21822182
if data.ndim < 2:
21832183
data = data.reshape(-1, 1)
21842184
frames, channels = data.shape
21852185
dtype = _check_dtype(data.dtype)
2186+
mapping_is_explicit = mapping is not None
21862187
mapping, channels = _check_mapping(mapping, channels)
21872188
if data.shape[1] == 1:
21882189
pass # No problem, mono data is duplicated into arbitrary channels
21892190
elif data.shape[1] != len(mapping):
21902191
raise ValueError(
21912192
"number of output channels != size of output mapping")
2193+
# Apparently, some PortAudio host APIs duplicate mono streams to the
2194+
# first two channels, which is unexpected when specifying mapping=[1].
2195+
# In this case, we play silence on the second channel, but only if the
2196+
# device actually supports a second channel:
2197+
if (mapping_is_explicit and np.array_equal(mapping, [0]) and
2198+
query_devices(device, 'output')['max_output_channels'] >= 2):
2199+
channels = 2
21922200
silent_channels = np.setdiff1d(np.arange(channels), mapping)
21932201
if len(mapping) + len(silent_channels) != channels:
21942202
raise ValueError("each channel may only appear once in mapping")
@@ -2201,7 +2209,7 @@ def check_data(self, data, mapping):
22012209
return frames
22022210

22032211
def check_out(self, out, frames, channels, dtype, mapping):
2204-
"""Check out, frames, channels, dtype and mapping."""
2212+
"""Check out, frames, channels, dtype and input mapping."""
22052213
import numpy as np
22062214
if out is None:
22072215
if frames is None:

0 commit comments

Comments
 (0)