|
|
|
@ -9,96 +9,114 @@ See the file 'LICENSE' for copying permission
|
|
|
|
|
|
|
|
|
|
import os
|
|
|
|
|
import sys
|
|
|
|
|
import wave
|
|
|
|
|
import wave # 用于处理 WAV 文件格式
|
|
|
|
|
|
|
|
|
|
# 指定蜂鸣音频文件的路径
|
|
|
|
|
BEEP_WAV_FILENAME = os.path.join(os.path.dirname(__file__), "beep.wav")
|
|
|
|
|
|
|
|
|
|
def beep():
|
|
|
|
|
"""根据操作系统播放蜂鸣声。"""
|
|
|
|
|
try:
|
|
|
|
|
# 检测操作系统并调用相应的播放方法
|
|
|
|
|
if sys.platform.startswith("win"):
|
|
|
|
|
_win_wav_play(BEEP_WAV_FILENAME)
|
|
|
|
|
_win_wav_play(BEEP_WAV_FILENAME) # Windows 系统使用 WAV 播放
|
|
|
|
|
elif sys.platform.startswith("darwin"):
|
|
|
|
|
_mac_beep()
|
|
|
|
|
_mac_beep() # macOS 系统使用系统蜂鸣
|
|
|
|
|
elif sys.platform.startswith("cygwin"):
|
|
|
|
|
_cygwin_beep(BEEP_WAV_FILENAME)
|
|
|
|
|
_cygwin_beep(BEEP_WAV_FILENAME) # Cygwin 使用音频文件播放
|
|
|
|
|
elif any(sys.platform.startswith(_) for _ in ("linux", "freebsd")):
|
|
|
|
|
_linux_wav_play(BEEP_WAV_FILENAME)
|
|
|
|
|
_linux_wav_play(BEEP_WAV_FILENAME) # Linux 和 FreeBSD 系统使用 WAV 播放
|
|
|
|
|
else:
|
|
|
|
|
_speaker_beep()
|
|
|
|
|
_speaker_beep() # 其他系统使用控制台蜂鸣
|
|
|
|
|
except:
|
|
|
|
|
# 捕获异常并使用控制台蜂鸣
|
|
|
|
|
_speaker_beep()
|
|
|
|
|
|
|
|
|
|
def _speaker_beep():
|
|
|
|
|
sys.stdout.write('\a') # doesn't work on modern Linux systems
|
|
|
|
|
"""在控制台播放蜂鸣声(警报声)。"""
|
|
|
|
|
sys.stdout.write('\a') # 在现代 Linux 系统上可能无效
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
sys.stdout.flush()
|
|
|
|
|
sys.stdout.flush() # 尝试刷新标准输出
|
|
|
|
|
except IOError:
|
|
|
|
|
pass
|
|
|
|
|
pass # 忽略任何 I/O 错误
|
|
|
|
|
|
|
|
|
|
# Reference: https://lists.gnu.org/archive/html/emacs-devel/2014-09/msg00815.html
|
|
|
|
|
# Cygwin 使用系统命令播放音频文件
|
|
|
|
|
def _cygwin_beep(filename):
|
|
|
|
|
os.system("play-sound-file '%s' 2>/dev/null" % filename)
|
|
|
|
|
|
|
|
|
|
# macOS 系统使用 Carbon 库的 SysBeep 函数
|
|
|
|
|
def _mac_beep():
|
|
|
|
|
import Carbon.Snd
|
|
|
|
|
Carbon.Snd.SysBeep(1)
|
|
|
|
|
import Carbon.Snd # 导入 Carbon 库
|
|
|
|
|
Carbon.Snd.SysBeep(1) # 播放系统蜂鸣声
|
|
|
|
|
|
|
|
|
|
# Windows 系统播放 WAV 文件
|
|
|
|
|
def _win_wav_play(filename):
|
|
|
|
|
import winsound
|
|
|
|
|
import winsound # 导入 winsound 库
|
|
|
|
|
|
|
|
|
|
winsound.PlaySound(filename, winsound.SND_FILENAME)
|
|
|
|
|
winsound.PlaySound(filename, winsound.SND_FILENAME) # 播放指定的 WAV 文件
|
|
|
|
|
|
|
|
|
|
# Linux 系统播放 WAV 文件的实现
|
|
|
|
|
def _linux_wav_play(filename):
|
|
|
|
|
# 尝试使用不同的命令播放音频文件
|
|
|
|
|
for _ in ("aplay", "paplay", "play"):
|
|
|
|
|
if not os.system("%s '%s' 2>/dev/null" % (_, filename)):
|
|
|
|
|
return
|
|
|
|
|
return # 成功播放音乐后返回
|
|
|
|
|
|
|
|
|
|
import ctypes
|
|
|
|
|
import ctypes # 导入 ctypes 库以调用 C 函数
|
|
|
|
|
|
|
|
|
|
# PulseAudio 的相关常量定义
|
|
|
|
|
PA_STREAM_PLAYBACK = 1
|
|
|
|
|
PA_SAMPLE_S16LE = 3
|
|
|
|
|
BUFFSIZE = 1024
|
|
|
|
|
BUFFSIZE = 1024 # 缓冲区大小
|
|
|
|
|
|
|
|
|
|
# 定义 PulseAudio 样本规格的结构
|
|
|
|
|
class struct_pa_sample_spec(ctypes.Structure):
|
|
|
|
|
_fields_ = [("format", ctypes.c_int), ("rate", ctypes.c_uint32), ("channels", ctypes.c_uint8)]
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
pa = ctypes.cdll.LoadLibrary("libpulse-simple.so.0")
|
|
|
|
|
pa = ctypes.cdll.LoadLibrary("libpulse-simple.so.0") # 加载 PulseAudio 库
|
|
|
|
|
except OSError:
|
|
|
|
|
return
|
|
|
|
|
return # 如果加载失败,则返回
|
|
|
|
|
|
|
|
|
|
# 打开 WAV 文件
|
|
|
|
|
wave_file = wave.open(filename, "rb")
|
|
|
|
|
|
|
|
|
|
# 设置 PulseAudio 样本规格
|
|
|
|
|
pa_sample_spec = struct_pa_sample_spec()
|
|
|
|
|
pa_sample_spec.rate = wave_file.getframerate()
|
|
|
|
|
pa_sample_spec.channels = wave_file.getnchannels()
|
|
|
|
|
pa_sample_spec.format = PA_SAMPLE_S16LE
|
|
|
|
|
pa_sample_spec.rate = wave_file.getframerate() # 获取采样频率
|
|
|
|
|
pa_sample_spec.channels = wave_file.getnchannels() # 获取声道数
|
|
|
|
|
pa_sample_spec.format = PA_SAMPLE_S16LE # 设置样本格式
|
|
|
|
|
|
|
|
|
|
error = ctypes.c_int(0)
|
|
|
|
|
|
|
|
|
|
pa_stream = pa.pa_simple_new(None, filename, PA_STREAM_PLAYBACK, None, "playback", ctypes.byref(pa_sample_spec), None, None, ctypes.byref(error))
|
|
|
|
|
# 创建 PulseAudio 流
|
|
|
|
|
pa_stream = pa.pa_simple_new(None, filename, PA_STREAM_PLAYBACK, None,
|
|
|
|
|
"playback", ctypes.byref(pa_sample_spec), None, None, ctypes.byref(error))
|
|
|
|
|
if not pa_stream:
|
|
|
|
|
raise Exception("Could not create pulse audio stream: %s" % pa.strerror(ctypes.byref(error)))
|
|
|
|
|
|
|
|
|
|
while True:
|
|
|
|
|
# 获取延迟
|
|
|
|
|
latency = pa.pa_simple_get_latency(pa_stream, ctypes.byref(error))
|
|
|
|
|
if latency == -1:
|
|
|
|
|
raise Exception("Getting latency failed")
|
|
|
|
|
|
|
|
|
|
buf = wave_file.readframes(BUFFSIZE)
|
|
|
|
|
buf = wave_file.readframes(BUFFSIZE) # 从 WAV 文件读取帧
|
|
|
|
|
if not buf:
|
|
|
|
|
break
|
|
|
|
|
break # 如果没有更多帧可读,退出循环
|
|
|
|
|
|
|
|
|
|
# 播放读取的帧
|
|
|
|
|
if pa.pa_simple_write(pa_stream, buf, len(buf), ctypes.byref(error)):
|
|
|
|
|
raise Exception("Could not play file")
|
|
|
|
|
|
|
|
|
|
wave_file.close()
|
|
|
|
|
wave_file.close() # 关闭 WAV 文件
|
|
|
|
|
|
|
|
|
|
# 确保所有数据都已播放完成
|
|
|
|
|
if pa.pa_simple_drain(pa_stream, ctypes.byref(error)):
|
|
|
|
|
raise Exception("Could not simple drain")
|
|
|
|
|
|
|
|
|
|
pa.pa_simple_free(pa_stream)
|
|
|
|
|
pa.pa_simple_free(pa_stream) # 释放 PulseAudio 流资源
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
beep()
|
|
|
|
|
beep() # 调用蜂鸣函数
|