pythonaudiopyaudiosoundcard

How to control a sound card programmatically?


I'm playing with pyaudio on a mac using a Saffire Pro 40 sound card. Currently I have two inputs plugged in and I'd like to control the levels of the second input channel programmatically. (This works fine using the sound card's mix control software).

I've been going through the pyaudio docs, but haven't found anything glaring on this issue so far. What's the simplest way to essentially do what the mix control software does (control volume per channel) programmatically? (A Python API would be nice, but not essential)

To simplify: it looks like it's possible to manually read the streams from the channels I want to control, scale them using numpy, them write them as output, but I'm hoping there is a method to simply send a normalized value per channel to control it.

So instead of something like this:

stream1 = pyaudioInstance.open( format             = FORMAT,
                                channels           = CHANNELS,
                                rate               = RATE,
                                input              = True,
                                output             = True,
                                input_device_index = 0,
                                frames_per_buffer  = CHUNK
                                )
stream2 = pyaudioInstance.open( format             = FORMAT,
                                channels           = CHANNELS,
                                rate               = RATE,
                                input              = True,
                                input_device_index = 1,
                                frames_per_buffer  = CHUNK
                                )

while processingAudio:
    # manually fetch each channel
    data1In = stream1.read(CHUNK)
    data2In = stream2.read(CHUNK)
    # convert to numpy to easy scale the arrays
    decodeddata1 = numpy.fromstring(data1In, numpy.int16)
    decodeddata2 = numpy.fromstring(data2In, numpy.int16)
    newdata = (decodeddata1 * 0.5 + decodeddata2* 0.1).astype(numpy.int16)
    # finally write the processed data
    stream1.write(result.tostring())

This is a bit misleading but I would need to mix separate channels from the same input device index. However what I'm hoping is something like:

someSoundCardAPI.channels[0].setVolume(0.2)

Having a look at the Channel Maps example feels closer to what I'm after. At the moment I find the host_api_specific part of API a bit confusing and I was hoping someone already has some experience successfully using this.

I am using OSX 10.10


Solution

  • I don't really have any experience with OSX, so I don't know, but normally you can remote-control everything with AppleScript. See, for example, this question. It doesn't say how to control the volume of a single channel separately, though.

    Probably you should ask there ...

    Regarding the inferior work-around, you can use python-sounddevice to create a little (untested) Python script:

    import sounddevice as sd
    
    def callback(indata, outdata, *stuff):
        outdata[:] = indata * [1, 0.5]
    
    with sd.Stream(channels=2, callback=callback):
        input()
    

    This script will run until you press <Return> and it will reduce the volume of the second channel.