@@ -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