|  |  |  | @ -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()  # 调用蜂鸣函数 |