2

I'm using these precompiled binaries of pyaudio with WASAPI support. I want to play a wav file via WASAPI. I found index of default output device for this api:

import pyaudio

p = pyaudio.PyAudio()

print p.get_host_api_info_by_index(3)
>>{'index': 3, 'name': u'Windows WASAPI', 'defaultOutputDevice': 11L, 'type': 13L, 'deviceCount': 3L, 'defaultInputDevice': 12L, 'structVersion': 1L}

Then I play a wav file via this device:

import pyaudio
import wave

CHUNK = 1024

wf = wave.open('test.wav', 'rb')

# instantiate PyAudio (1)
p = pyaudio.PyAudio()

# open stream (2)
stream = p.open(format=p.get_format_from_width(wf.getsampwidth()),
                channels=wf.getnchannels(),
                rate=wf.getframerate(),
                output_device_index=11,
                output=True)

# read data
data = wf.readframes(CHUNK)

# play stream (3)
while data != '':
    stream.write(data)
    data = wf.readframes(CHUNK)

# stop stream (4)
stream.stop_stream()
stream.close()

# close PyAudio (5)
p.terminate()

When file is playing I'm still able to hear another sounds in the system, but in exclusive WASAPI mode all other sounds must be blocked. So how to enable WASAPI exclusive mode in pyaudio?

2 Answers 2

3

There is need to change sources of pyaudio. We need to modify _portaudiomodule.c.

Include pa_win_wasapi.h:

#include pa_win_wasapi.h

Change this line:

outputParameters->hostApiSpecificStreamInfo = NULL;

On this:

struct PaWasapiStreamInfo wasapiInfo;
wasapiInfo.size = sizeof(PaWasapiStreamInfo);
wasapiInfo.hostApiType = paWASAPI;
wasapiInfo.version = 1;
wasapiInfo.flags = (paWinWasapiExclusive|paWinWasapiThreadPriority);
wasapiInfo.threadPriority = eThreadPriorityProAudio;

outputParameters->hostApiSpecificStreamInfo = (&wasapiInfo);

Now we need to compile pyaudio.

  1. Place portaudio dir in pyaudio with name portaudio-v19, name is important
  2. Install MinGW/MSYS: gcc, make and MSYS console we need
  3. In MSYS console cd to portaudio-v19
  4. ./configure --with-winapi=wasapi --enable-shared=no
  5. make
  6. cd ..
  7. change these lines:

    external_libraries += ['winmm']

    extra_link_args += ['-lwinmm']

    in setup.py on these:

    external_libraries += ["winmm","ole32","uuid"]

    extra_link_args += ["-lwinmm","-lole32","-luuid"]

  8. python setup.py build --static-link -cmingw32
  9. python setup.py install --skip-build

That's all. Now pyadio is able to play sound in WASAPI exclusive mode.

Sign up to request clarification or add additional context in comments.

Comments

0

Easier solution nowadays is to use sounddevice instead of PyAudio.

Check the index:

import sounddevice as sd

print(sd.query_devices())

Your example would translate to:

import sounddevice as sd
import soundfile as sf


CHUNK = 1024

wf = sf.SoundFile('test.wav', 'rb')


# open stream
stream = sd.RawOutputStream(
        samplerate=wf.samplerate,
        device=11,
        channels=wf.channels,
        dtype='int16',
        extra_settings=sd.WasapiSettings(exclusive=True))
stream.start()

# read data
data = wf.read(CHUNK, dtype='int16')

# play stream
while len(data):
    stream.write(wf.read(CHUNK, dtype='int16'))
    data = wf.read(CHUNK, dtype='int16')

# stop stream
stream.stop()
stream.close()

Extra example with callback

For me, this example glitches. As stream.write is blocking, it actually might be better to use a callback (which runs in another thread):

import sounddevice as sd
import soundfile as sf


CHUNK = 1024

wf = sf.SoundFile('test.wav', 'rb')


# callback to read data
def callback(outdata, frames, time, status):
    data = wf.read(frames, dtype='int16', always_2d=True)
    if len(data) < len(outdata):
        outdata[:len(data)] = data
        outdata[len(data):] = 0
        raise sd.CallbackStop
    else:
        outdata[:] = data


# open stream
stream = sd.OutputStream(
        samplerate=wf.samplerate,
        device=11,
        blocksize=CHUNK,
        channels=wf.channels,
        dtype='int16',
        extra_settings=sd.WasapiSettings(exclusive=True),
        callback=callback)
stream.start()
sd.sleep(int(len(wf) / wf.samplerate * 1000))  # let it play until finished

# stop and close stream
stream.stop()
stream.close()

References

Documentation can be found here: https://python-sounddevice.readthedocs.io/en/0.5.1/api/platform-specific-settings.html#sounddevice.WasapiSettings

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.