pythonportaudiopyaudioasio

Multichannel PyAudio with ASIO Support


I'm attempting to interface to a PreSonus AudioBox 1818VSL with PyAudio on Win7, but am having some trouble recording more than 2 channels (stereo) at a time. The PreSonus driver creates many stereo input audio devices (ex. stereo channels 1&2, 3&4, etc.) and an 18 input channel ASIO device. I can record from any of the stereo devices without issue. To minimize latency and record from > 2 channels, I'm trying to use the ASIO device.

I've been using a build of PyAudio from http://www.lfd.uci.edu/~gohlke/pythonlibs/#pyaudio, which has compiled support for ASIO, DS, WMME, WASAPI, WDMKS.

Calls to pyaudio_handle.is_format_supported() show the ASIO device supports 8 to 32-bit data at 44.1, 48, and 96 kHz.

Below is the dictionary returned by pa.get_device_info_by_index(32)

{'defaultHighInputLatency': 0.046439909297052155,
'defaultHighOutputLatency': 0.046439909297052155,
'defaultLowInputLatency': 0.046439909297052155,
'defaultLowOutputLatency': 0.046439909297052155,
'defaultSampleRate': 44100.0,
'hostApi': 2L,
'index': 32,
'maxInputChannels': 18L,
'maxOutputChannels': 18L,
'name': u'AudioBox ASIO Driver',
'structVersion': 2L}

Below is the code that I have been using to create the PyAudio input stream. The callback function simply pushes the data into a list and returns pyaudio.paContinue until I get the amount of samples I want, then it returns pyaudio.paComplete.

pyaudio_handle = pyaudio.PyAudio()
stream = pyaudio_handle.open(
    format=pyaudio.get_format_from_width(2,unsigned=False),
    channels=4,
    rate=48000,
    input=True,
    frames_per_buffer=256,
    input_device_index=32,
    stream_callback=pyaudio_stream_callback,
)

Attempting to initialize the ASIO driver at rates faster than 44.1 kHz cause PyAudio to hang and not return. Initializing at 44.1 kHz produces the following error: IOError: [Errno Unanticipated host error] -9999.

Any help that you can provide resolving this error would be helpful. I would even settle for proof that ASIO works with > 2 channels in PyAudio when running on Win7. Thanks.


Solution

  • I was able to record 8 channel audio (M-audio M-Track Eight) using the ASIO driver at 96 kHZ.

    From

    p = pyaudio.PyAudio()
    p.get_device_info_by_index(4) 
    

    I found that 'index': 4 was the ASIO driver:

    {'defaultLowInputLatency': 0.005804988662131519, 
     'defaultHighOutputLatency': 0.09287981859410431, 
     'defaultLowOutputLatency': 0.005804988662131519,
     'defaultSampleRate': 44100.0, 
     'maxInputChannels': 8, 
     'maxOutputChannels': 8,
     'structVersion': 2, 
     'name': 'M-Audio M-Track Eight ASIO', 
     'index': 4, 
     'hostApi': 2,
     'defaultHighInputLatency': 0.09287981859410431}
    

    So I started with the sample code on PyAudio but switched from wave to scipy.io.wavfile to write the multichannel .wav file since wave only supports stereo.

    import pyaudio
    import wave
    import numpy as np
    from scipy.io import wavefile
    
    CHUNK = 1024
    FORMAT = pyaudio.paInt16
    CHANNELS = 8
    RATE = 96000
    RECORD_SECONDS = 10
    WAVE_OUTPUT_FILENAME = "output.wav"
    
    p = pyaudio.PyAudio()
    
    stream = p.open(format=FORMAT,
                    channels=CHANNELS,
                    rate=RATE,
                    input=True,
                    input_device_index=4,
                    frames_per_buffer=CHUNK
                    )
    
    print("* recording")
    
    frames = []
    
    for i in range(0, int(RATE / CHUNK * RECORD_SECONDS)):
        data = stream.read(CHUNK)
        frames.append(data)
    
    print("* done recording")
    
    stream.stop_stream()
    stream.close()
    p.terminate()
    
    
    #Not really sure what b'' means in BYTE STRING but numpy needs it 
    #just like wave did...
    framesAll = b''.join(frames)
    
    #Use numpy to format data and reshape.  
    #PyAudio output from stream.read() is interlaced.
    result = np.fromstring(framesAll, dtype=np.int16)
    chunk_length = len(result) / CHANNELS
    result = np.reshape(result, (chunk_length, CHANNELS))
    
    #Write multi-channel .wav file with SciPy
    wavfile.write(WAVE_OUTPUT_FILENAME,RATE,result)
    

    Viola! 96 kHz, 16-bit, 8 channel .wav file!

    Oh, details