跳转到主要内容

Python高级接口和基于ctypes的PulseAudio(libpulse)绑定

项目描述

Python (3.x 和 2.x) 阻塞高级接口和基于ctypes的PulseAudio(libpulse)绑定,用于在简单同步代码中应用。

包装器主要用于类似混音器的控制和使用内省相关操作,而不是例如提交声音样本以播放和类似客户端。

对于要配合asyncio使用的异步版本,请参阅pulsectl-asyncio项目。

最初从pulsemixer项目分支,该项目包含此代码。


仓库URL

用法

简单示例

import pulsectl

with pulsectl.Pulse('volume-increaser') as pulse:
  for sink in pulse.sink_list():
    # Volume is usually in 0-1.0 range, with >1.0 being soft-boosted
    pulse.volume_change_all_chans(sink, 0.1)

监听服务器状态改变事件

import pulsectl

with pulsectl.Pulse('event-printer') as pulse:
  # print('Event types:', pulsectl.PulseEventTypeEnum)
  # print('Event facilities:', pulsectl.PulseEventFacilityEnum)
  # print('Event masks:', pulsectl.PulseEventMaskEnum)

  def print_events(ev):
    print('Pulse event:', ev)
    ### Raise PulseLoopStop for event_listen() to return before timeout (if any)
    # raise pulsectl.PulseLoopStop

  pulse.event_mask_set('all')
  pulse.event_callback_set(print_events)
  pulse.event_listen(timeout=10)

其他杂项修改

>>> import pulsectl
>>> pulse = pulsectl.Pulse('my-client-name')

>>> pulse.sink_list()
[<PulseSinkInfo at 7f85cfd053d0 - desc='Built-in Audio', index=0L, mute=0, name='alsa-speakers', channels=2, volumes='44.0%, 44.0%'>]

>>> pulse.sink_input_list()
[<PulseSinkInputInfo at 7fa06562d3d0 - index=181L, mute=0, name='mpv Media Player', channels=2, volumes='25.0%, 25.0%'>]

>>> pulse.sink_input_list()[0].proplist
{'application.icon_name': 'mpv',
 'application.language': 'C',
 'application.name': 'mpv Media Player',
 ...
 'native-protocol.version': '30',
 'window.x11.display': ':1.0'}

>>> pulse.source_list()
[<PulseSourceInfo at 7fcb0615d8d0 - desc='Monitor of Built-in Audio', index=0L, mute=0, name='alsa-speakers.monitor', channels=2, volumes='100.0%, 100.0%'>,
 <PulseSourceInfo at 7fcb0615da10 - desc='Built-in Audio', index=1L, mute=0, name='alsa-mic', channels=2, volumes='100.0%, 100.0%'>]

>>> sink = pulse.sink_list()[0]
>>> pulse.volume_change_all_chans(sink, -0.1)
>>> pulse.volume_set_all_chans(sink, 0.5)

>>> pulse.server_info().default_sink_name
'alsa_output.pci-0000_00_14.2.analog-stereo'
>>> pulse.default_set(sink)

>>> card = pulse.card_list()[0]
>>> card.profile_list
[<PulseCardProfileInfo at 7f02e7e88ac8 - description='Analog Stereo Input', n_sinks=0, n_sources=1, name='input:analog-stereo', priority=60>,
 <PulseCardProfileInfo at 7f02e7e88b70 - description='Analog Stereo Output', n_sinks=1, n_sources=0, name='output:analog-stereo', priority=6000>,
 ...
 <PulseCardProfileInfo at 7f02e7e9a4e0 - description='Off', n_sinks=0, n_sources=0, name='off', priority=0>]
>>> pulse.card_profile_set(card, 'output:hdmi-stereo')

>>> help(pulse)
...

>>> pulse.close()

当前代码逻辑是所有方法都通过Pulse实例调用,从这些方法返回的所有内容都是“Pulse-Something-Info”对象 - 这是描述该事物的一个C结构体的薄包装,没有任何附加的方法。

除了少数添加的便捷方法外,大多数方法应具有类似的签名,并执行与C libpulse API对应方法相同的功能,因此请参阅pulseaudio doxygen文档以获取更多关于它们的信息。

可以使用Pulse.set_poll_func()或使用单独线程中的Pulse.event_listen()将Pulse客户端集成到现有的事件循环中(例如asyncio、twisted等)。

有关示例,请参阅pulseaudio-mixer-cli项目代码以及此处测试。

注意

本节描述了一些不太明显的事情。

尚未在Python中包装/公开的事物

libpulse中有许多信息、方法和其他事物尚未被本模块包装/公开,因为它们(目前)不是作者/开发者用例所需要的。

可以通过在PulseSomethingInfo对象中将属性名称添加到“c_struct_fields”值中,使它们从Python代码中可访问。

有关如何查找/添加此类内容的更具体示例,请参阅github #3

对于通过libpulse内省API不可用的信息和命令,可以使用pulsectl.connect_to_cli()回退函数,该函数将打开到服务器的“module-cli”的Unix套接字(如果需要,则加载它),可以像“pacmd”工具(不要与使用本地协议而不是module-cli的“pactl”混淆)或pulseaudio启动文件(例如“default.pa”)一样使用。

不过,解析那里命令的字符串输出可能不是个好主意,因为这些不仅会发生变化,还可能根据系统区域设置而有所不同。

音量

在PulseAudio中,“音量”对于任何事物都不是一个简单的数字,而是一个数字列表,每个通道一个(例如“左”,“右”,“前”,“后”等),它应与相关/应用的对象的通道映射相对应。

在这个模块中,此类列表由PulseVolumeInfo对象表示。

sink.volume是一个PulseVolumeInfo实例,所有接受对象索引的薄/简单包装器都期望传递此类实例,例如pulse.sink_input_volume_set(sink.index, sink.volume)

存在便捷的volume_get_all_chansvolume_set_all_chansvolume_change_all_chans方法,可以按单个数值获取/设置/调整音量,这些值也作为value_flat属性在PulseVolumeInfo对象上可用。

PulseVolumeInfo可以从数值音量值加上通道数或按通道数的Python列表构造。

PulseVolumeInfo中的所有通道音量值(以及上述包装器函数中的平面值),都是0-65536范围内的浮点对象,具有以下含义

  • 0.0音量是“无声音”(对应于PA_VOLUME_MUTED)。

  • 1.0值是“当前输出音量级别”,100%或PA_VOLUME_NORM。

  • >1.0和65536.0(PA_VOLUME_MAX / PA_VOLUME_NORM) - 软件增强音量(更高的值将负影响音质)。

可能最好只在0-1.0范围内设置音量,并通过硬件提升音量而不损失质量来提升音量,例如,如果可用,则调整输出音量(这对应于ALSA/硬件音量)。

请注意,pulseaudio daemon.conf中的flat-volumes=yes选项(某些发行版中默认为“yes”,例如Arch Linux中为“no”)已经将设备音量与“最响亮”的应用程序的音量成比例缩放,因此已经完成了上述操作。

模块中使用的分数体积值被转换为(线性方式)到/从libpulse的pa_volume_t整数。有关详细信息,请参阅pulseaudio源中的src/pulse/volume.h(例如,它与dB中的音量级的关系)。

代码示例

from pulsectl import Pulse, PulseVolumeInfo

with Pulse('volume-example') as pulse:
  sink_input = pulse.sink_input_list()[0] # first random sink-input stream

  volume = sink_input.volume
  print(volume.values) # list of per-channel values (floats)
  print(volume.value_flat) # average level across channels (float)

  time.sleep(1)

  volume.value_flat = 0.3 # sets all volume.values to 0.3
  pulse.volume_set(sink_input, volume) # applies the change

  time.sleep(1)

  n_channels = len(volume.values)
  new_volume = PulseVolumeInfo(0.5, n_channels) # 0.5 across all n_channels
  # new_volume = PulseVolumeInfo([0.15, 0.25]) # from a list of channel levels (stereo)
  pulse.volume_set(sink_input, new_volume)
  # pulse.sink_input_volume_set(sink_input.index, new_volume) # same as above

在大多数常见情况下,执行类似pulse.volume_set_all_chans(sink_input, 0.2)的操作应该就可以了,不需要在PulseVolumeInfo中担心特定的通道。

字符串值

libpulse显式返回utf-8编码的字符串值,在python-2(称为“unicode”)和python-3(称为“str”)中,总是解码为“抽象字符串”类型,以保持一致性。

在代码中最好避免将这些与编码字符串(“bytes”)混合,特别是在python-2中,其中“bytes”通常用作默认字符串类型。

枚举/命名值(枚举)

模块代替某些枚举或常量对应的C整数(例如,-1对应于PA_SINK_INVALID_STATE),返回EnumValue对象,类似于字符串(py2/py3中的“str”类型)。

例如

>>> pulsectl.PulseEventTypeEnum.change == 'change'
True
>>> pulsectl.PulseEventTypeEnum.change
<EnumValue event-type=change>
>>> pulsectl.PulseEventTypeEnum
<Enum event-type [change new remove]>

在代码中使用枚举而不是字符串可能更合适,这样解释器就可以在指定任何拼写错误或未知值时发出错误,而不是总是对无效字符串的检查静默失败。

事件处理代码,线程

尽管这个模块有点隐藏,但libpulse客户端始终作为一个事件循环运行。

所以任何调用(例如pulse.mute(...))发生的情况是

  • 调用libpulse,指定当操作完成时的回调。

  • 运行libpulse事件循环,直到该回调被调用。

  • 如果有的话,返回传递给该回调调用的结果(对于各种“get”方法)。

event_callback_set()event_listen()调用基本上在这里执行原始的第一和第二步。

这意味着在event_listen()(或通过此模块的任何其他pulse调用)等待返回值并运行libpulse循环时,不能在回调函数中使用任何pulse调用。

可以通过抛出PulseLoopStop异常来使event_listen()返回,运行随后的任何pulse调用,然后重新启动event_listen()

这不会错过任何事件,因为所有阻塞调用都执行与event_listen()相同的事情(如上所述的第二步),并且可以导致传递给event_callback_set()的可调用对象被调用(当循环运行时)。

此外,不能从不同的线程运行相同的libpulse事件循环,因此如果使用线程,则可以使用threading_lock=True选项(也可以接受锁实例而不是True)初始化客户端,以在上述列表中的第二步(运行事件循环)周围创建互斥锁,这样多个线程就不会同时执行它。

为了正确集成Python事件循环(例如twisted或asyncio),请使用pulsectl-asyncio模块。

有关将此模块塞入异步应用程序的技巧,请参阅github #11,但即使在没有asyncio事件循环的非异步情况下,从pulsectl-asyncio开始可能更容易。

测试

测试代码与模块一起打包/安装,在更改模块代码或检查当前Python、模块和pulseaudio版本是否都能正常工作时非常有用。

从检出目录或安装的模块运行测试的命令

% python2 -m unittest discover
% python3 -m unittest discover

请注意,如果“pulsectl”模块在当前目录(例如检出目录)和用户/系统Python模块路径中都可用,则前者在上述命令中应始终优先。

为上述命令添加例如 -k test_stream_move,以匹配并运行特定测试,当隔离特定故障时,也可以使用带有PA_DEBUG=1环境变量的运行方式来获取完整的详细脉冲音频日志,例如

% PA_DEBUG=1 python -m unittest discover -k test_module_funcs

测试套件运行一个独立的脉冲音频实例,该实例使用null-sinks(不接触硬件),自定义(非默认)启动脚本和环境,并且只与该实例交互,之后终止它。但仍然使用系统/用户daemon.conf文件,因此这些可能会影响测试。

任何测试失败都可能表明不兼容性、模块代码中的错误、脉冲音频(或其daemon.conf)和底层依赖的问题。没有“预期”的测试用例失败。

目前(v19.9.6)所有测试都可以运行最长10秒,因为其中一些涉及到播放(使用来自/dev/urandom的paplay),这是时间敏感的。

变更日志和版本方案

本软件包使用每提交一次版本方案(由pre-commit钩子更新)和基本上每提交一次git提交进行发布,除非计划有更及时的后续提交,或者太懒惰而不想为一些微不足道的README拼写错误修正运行py setup.py sdist bdist_wheel upload

版本方案:{year}.{month}.{git-commit-count-this-month}
即“16.9.10”是“2016年9月的第11次提交”。

存在一个CHANGES.rst文件,其中列出了任何有意的破坏性变更(应非常罕见,如果有)和新/添加的非平凡功能。

尽管如此,它可能有些过时,因为必须记住手动更新它。
那里的“最后同步/更新:”行可能会给出关于过时程度的提示。

安装

这是一个常规的Python(3.x或2.x)软件包。

如果您的发行版中有可用的软件包,建议使用您的软件包管理器来安装它。

否则,使用pip是最好的方式

% pip install pulsectl

(添加–user选项以仅安装到当前用户的$HOME)

请确保根据您想要为哪个Python版本安装模块,使用python3/python2,pip3/pip2,easy_install-…命令。

如果您没有“pip”命令

% python -m ensurepip
% python -m pip install --upgrade pip
% python -m pip install pulsectl

(与上面关于“install –user”的建议相同)

在非常旧的系统上,以下命令中可能有其中一个有效

% curl https://bootstrap.pypa.io/get-pip.py | python
% pip install pulsectl

% easy_install pulsectl

% git clone --depth=1 https://github.com/mk-fg/python-pulse-control
% cd python-pulse-control
% python setup.py install

(这里的所有安装命令都有–user选项)

可以像这样安装当前git版本

% pip install 'git+https://github.com/mk-fg/python-pulse-control#egg=pulsectl'

请注意,要将内容安装到系统范围内的PATH和site-packages(不使用–user),通常需要提升权限(即root和su/sudo)。

使用“…install –user”,~/.pydistutils.cfgvirtualenv以在自定义路径中进行无权限安装。

有关Python打包的更多信息,请参阅packaging.python.org

项目详情


发布历史 发布通知 | RSS源

下载文件

下载适合您平台的应用程序文件。如果您不确定选择哪一个,请了解有关安装包的更多信息。

源分布

pulsectl-24.8.0.tar.gz (41.2 kB 查看散列值)

上传时间

构建分布

pulsectl-24.8.0-py2.py3-none-any.whl (35.1 kB 查看散列值)

上传时间 Python 2 Python 3

由以下机构支持

AWS AWS 云计算和安全赞助商 Datadog Datadog 监控 Fastly Fastly CDN Google Google 下载分析 Microsoft Microsoft PSF 赞助商 Pingdom Pingdom 监控 Sentry Sentry 错误记录 StatusPage StatusPage 状态页面