Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

get_microphone(), get_speaker(): Improved comparison between the call parameter "id" and existing device names #147

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 4 additions & 26 deletions soundcard/coreaudio.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@
import numpy
import collections
import time
import re
import math
import threading
import warnings

from soundcard.utils import match_device

_ffi = cffi.FFI()
_package_dir, _ = os.path.split(__file__)
with open(os.path.join(_package_dir, 'coreaudio.py.h'), 'rt') as f:
Expand Down Expand Up @@ -60,7 +61,7 @@ def get_speaker(id):
fuzzy-matched pattern for the speaker name.

"""
return _match_device(id, all_speakers())
return match_device(id, all_speakers())


def default_microphone():
Expand All @@ -79,30 +80,7 @@ def get_microphone(id, include_loopback=False):
fuzzy-matched pattern for the microphone name.

"""
return _match_device(id, all_microphones(include_loopback))


def _match_device(id, devices):
"""Find id in a list of devices.

id can be a CoreAudio id, a substring of the device name, or a
fuzzy-matched pattern for the microphone name.

"""
devices_by_id = {device.id: device for device in devices}
devices_by_name = {device.name: device for device in devices}
if id in devices_by_id:
return devices_by_id[id]
# try substring match:
for name, device in devices_by_name.items():
if id in name:
return device
# try fuzzy match:
pattern = '.*'.join(id)
for name, device in devices_by_name.items():
if re.match(pattern, name):
return device
raise IndexError('no device with id {}'.format(id))
return match_device(id, all_microphones(include_loopback))


def get_name():
Expand Down
29 changes: 4 additions & 25 deletions soundcard/mediafoundation.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@

import os
import cffi
import re
import time
import struct
import collections
import platform

import numpy

from soundcard.utils import match_device

_ffi = cffi.FFI()
_package_dir, _ = os.path.split(__file__)
with open(os.path.join(_package_dir, 'mediafoundation.py.h'), 'rt') as f:
Expand Down Expand Up @@ -123,7 +124,7 @@ def get_speaker(id):
fuzzy-matched pattern for the speaker name.

"""
return _match_device(id, all_speakers())
return match_device(id, all_speakers())

def all_microphones(include_loopback=False):
"""A list of all connected microphones.
Expand Down Expand Up @@ -151,29 +152,7 @@ def get_microphone(id, include_loopback=False):
fuzzy-matched pattern for the microphone name.

"""
return _match_device(id, all_microphones(include_loopback))

def _match_device(id, devices):
"""Find id in a list of devices.

id can be a WASAPI id, a substring of the device name, or a
fuzzy-matched pattern for the microphone name.

"""
devices_by_id = {device.id: device for device in devices}
devices_by_name = {device.name: device for device in devices}
if id in devices_by_id:
return devices_by_id[id]
# try substring match:
for name, device in devices_by_name.items():
if id in name:
return device
# try fuzzy match:
pattern = '.*'.join(id)
for name, device in devices_by_name.items():
if re.match(pattern, name):
return device
raise IndexError('no device with id {}'.format(id))
return match_device(id, all_microphones(include_loopback))

def _str2wstr(string):
"""Converts a Python str to a Windows WSTR_T."""
Expand Down
42 changes: 4 additions & 38 deletions soundcard/pulseaudio.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@
import atexit
import collections
import time
import re
import threading
import warnings
import numpy
import cffi

from soundcard.utils import match_device

_ffi = cffi.FFI()
_package_dir, _ = os.path.split(__file__)
with open(os.path.join(_package_dir, 'pulseaudio.py.h'), 'rt') as f:
Expand Down Expand Up @@ -302,8 +303,7 @@ def get_speaker(id):
speaker : _Speaker

"""
speakers = _pulse.sink_list
return _Speaker(id=_match_soundcard(id, speakers)['id'])
return match_device(id, all_speakers())


def all_microphones(include_loopback=False, exclude_monitors=True):
Expand Down Expand Up @@ -367,41 +367,7 @@ def get_microphone(id, include_loopback=False, exclude_monitors=True):
-------
microphone : _Microphone
"""

if not exclude_monitors:
warnings.warn("The exclude_monitors flag is being replaced by the include_loopback flag", DeprecationWarning)
include_loopback = not exclude_monitors

microphones = _pulse.source_list
return _Microphone(id=_match_soundcard(id, microphones, include_loopback)['id'])


def _match_soundcard(id, soundcards, include_loopback=False):
"""Find id in a list of soundcards.

id can be a pulseaudio id, a substring of the microphone name, or
a fuzzy-matched pattern for the microphone name.
"""
if not include_loopback:
soundcards_by_id = {soundcard['id']: soundcard for soundcard in soundcards
if not 'monitor' in soundcard['id']}
soundcards_by_name = {soundcard['name']: soundcard for soundcard in soundcards
if not 'monitor' in soundcard['id']}
else:
soundcards_by_id = {soundcard['id']: soundcard for soundcard in soundcards}
soundcards_by_name = {soundcard['name']: soundcard for soundcard in soundcards}
if id in soundcards_by_id:
return soundcards_by_id[id]
# try substring match:
for name, soundcard in soundcards_by_name.items():
if id in name:
return soundcard
# try fuzzy match:
pattern = '.*'.join(id)
for name, soundcard in soundcards_by_name.items():
if re.match(pattern, name):
return soundcard
raise IndexError('no soundcard with id {}'.format(id))
return match_device(id, all_microphones(include_loopback))


def get_name():
Expand Down
37 changes: 37 additions & 0 deletions soundcard/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import re

def match_device(id, devices):
"""Find id in a list of devices.

id can be a platfom specific id, a substring of the device name, or a
fuzzy-matched pattern for the microphone name.
"""
devices_by_id = {device.id: device for device in devices}
real_devices_by_name = {
device.name: device for device in devices
if not getattr(device, 'isloopback', True)}
bastibe marked this conversation as resolved.
Show resolved Hide resolved
loopback_devices_by_name = {
device.name: device for device in devices
if getattr(device, 'isloopback', True)}
if id in devices_by_id:
return devices_by_id[id]
for device_map in real_devices_by_name, loopback_devices_by_name:
if id in device_map:
return device_map[id]
# MacOS/coreaudio uses integer IDs where string operations of course
# make no sense.
if isinstance(id, int):
raise IndexError('no device with id {}'.format(id))
# try substring match:
for device_map in real_devices_by_name, loopback_devices_by_name:
for name, device in device_map.items():
if id in name:
return device
# try fuzzy match:
id_parts = [re.escape(c) for c in id]
pattern = '.*'.join(id_parts)
for device_map in real_devices_by_name, loopback_devices_by_name:
for name, device in device_map.items():
if re.search(pattern, name):
return device
raise IndexError('no device with id {}'.format(id))
Loading