Skip to content

Commit c151bc1

Browse files
committed
Created hostapis
1 parent 8ccbe85 commit c151bc1

File tree

1 file changed

+79
-2
lines changed

1 file changed

+79
-2
lines changed

sounddevice.py

Lines changed: 79 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -818,7 +818,7 @@ def query_hostapis(index=None):
818818
The dictionaries have the following keys:
819819
820820
``'name'``
821-
The name of the host API.
821+
The name of the host API and suitable for user display.
822822
``'devices'``
823823
A list of device IDs belonging to the host API.
824824
Use `query_devices()` to get information about a device.
@@ -831,6 +831,16 @@ def query_hostapis(index=None):
831831
overwritten by assigning to `default.device` -- take(s)
832832
precedence over `default.hostapi` and the information in
833833
the abovementioned dictionaries.
834+
``'apiname'``
835+
*apiname* is a string that is suitable for use as a Python
836+
identifier. Unlike *name*, *apiname* does not contain
837+
spaces or any other characters that are not suitable as a
838+
Python identifier. These strings are derived from
839+
PortAudio's PaHostApiTypeId enumeration and thus correspond
840+
on a one-to-one basis with that enumeration. These strings
841+
shall never be eubject to locale settings such as LANG,
842+
LC_ALL, or LC_MESSAGES. In short, these strings are safer
843+
than *name* for hard coding into an application.
834844
``'api'``
835845
A namedtuple containing the platform-specific API from
836846
PortAudio. If a platform-specific API is unavailable, this
@@ -858,6 +868,7 @@ def query_hostapis(index=None):
858868
for i in range(info.deviceCount)],
859869
'default_input_device': info.defaultInputDevice,
860870
'default_output_device': info.defaultOutputDevice,
871+
'apiname': _get_host_apiname(info.type),
861872
'api': api,
862873
}
863874

@@ -2353,6 +2364,48 @@ class CallbackAbort(Exception):
23532364
# Host-API:
23542365

23552366

2367+
# Is there a way to query the names used in "enum PaHostApiTypeId"?
2368+
# If so, then I wouldn't bother with this dict, _typeid_to_apiname.
2369+
#
2370+
# Q: Should _typeid_to_apiname be brought out to global scope, with
2371+
# the leading underscore? I'm averse to relying upon converting the
2372+
# current .name field as an api identifier, since those strings are
2373+
# intended for user consumption. (For example, will Port Audio one
2374+
# day provide language translations of those names? If they did,
2375+
# these names could differ for each user purely based on locale
2376+
# settings, such as LANG, LC_ALL, or LC_MESSAGES!)
2377+
_typeid_to_apiname = {
2378+
_lib.paInDevelopment : str.lower('InDevelopment'),
2379+
_lib.paDirectSound : str.lower('DirectSound'),
2380+
_lib.paMME : str.lower('MME'),
2381+
_lib.paASIO : str.lower('ASIO'),
2382+
_lib.paSoundManager : str.lower('SoundManager'),
2383+
_lib.paCoreAudio : str.lower('CoreAudio'),
2384+
_lib.paOSS : str.lower('OSS'),
2385+
_lib.paALSA : str.lower('ALSA'),
2386+
_lib.paAL : str.lower('AL'),
2387+
_lib.paBeOS : str.lower('BeOS'),
2388+
_lib.paWDMKS : str.lower('WDMKS'),
2389+
_lib.paJACK : str.lower('JACK'),
2390+
_lib.paWASAPI : str.lower('WASAPI'),
2391+
_lib.paAudioScienceHPI : str.lower('AudioScienceHPI'),
2392+
}
2393+
def _get_host_apiname(hostapi_typeid):
2394+
# Assume int for building '_hostapi###' strings.
2395+
assert isinstance(hostapi_typeid, int)
2396+
try:
2397+
# Pre-assigned name:
2398+
return _typeid_to_apiname[hostapi_typeid]
2399+
except KeyError:
2400+
# Make up new names on the fly:
2401+
if hostapi_typeid>=0:
2402+
# 42 -> '_hostapi42'
2403+
return '_hostapi'+str(hostapi_typeid)
2404+
else:
2405+
# -37 -> '_hostapi_37'
2406+
return '_hostapi_'+str(-hostapi_typeid)
2407+
2408+
23562409
_api_dicts = {}
23572410
def _get_host_api(hostapi_typeid):
23582411
"""Lookup hostapi_typeid and return the results as a namedtuple.
@@ -2376,11 +2429,34 @@ def _get_host_api(hostapi_typeid):
23762429
23772430
"""
23782431
api_dict = _api_dicts[hostapi_typeid]
2379-
API = _namedtuple('_API_'+str(hostapi_typeid), api_dict.keys())
2432+
# Using .upper() to distinguish that we're using _get_host_apiname
2433+
# to name a type:
2434+
API = _namedtuple(_get_host_apiname(hostapi_typeid).upper(),
2435+
api_dict.keys())
23802436
api = API(**api_dict)
23812437
return api
23822438

23832439

2440+
# hostapis is an alternative interface to query_hostapis and populated during _initialize().
2441+
hostapis = None
2442+
2443+
2444+
def _populate_hostapis():
2445+
global hostapis
2446+
# For now, just invoke query_hostapis() to get the list. Later if
2447+
# we deprecate query_hostapis, we can move its guts into here.
2448+
hostapi_list = query_hostapis()
2449+
# There is one _HostAPI for each field in _HostAPIs:
2450+
_HostAPI = _namedtuple('_HostAPI', ('name', 'devices',
2451+
'default_input_device',
2452+
'default_output_device',
2453+
'apiname', 'api'))
2454+
class HostAPIs(_namedtuple('HostAPIs', (h['apiname'] for h in hostapi_list))):
2455+
"""Access to PortAudio Host API's"""
2456+
__slots__ = ()
2457+
hostapis = HostAPIs(*(_HostAPI(**h) for h in hostapi_list))
2458+
2459+
23842460
# Host-API: ASIO
23852461

23862462

@@ -3176,6 +3252,7 @@ def _initialize():
31763252
global _initialized
31773253
_check(_lib.Pa_Initialize(), 'Error initializing PortAudio')
31783254
_initialized += 1
3255+
_populate_hostapis()
31793256

31803257

31813258
def _terminate():

0 commit comments

Comments
 (0)