generate_voices.py 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778
  1. """使用本机 SAPI 语音合成生成 assets/voices/*.wav"""
  2. import os
  3. import sys
  4. OUTPUT_DIR = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "assets", "voices")
  5. VOICES = {
  6. "hooking.wav": "挂包中",
  7. "ready.wav": "挂包成功",
  8. "releasing.wav": "放包中",
  9. "arrive_1.wav": "到达1号工位",
  10. "arrive_2.wav": "到达2号工位",
  11. "arrive_3.wav": "到达3号工位",
  12. "arrive_4.wav": "到达4号工位",
  13. "arrive_5.wav": "到达5号工位",
  14. "offline_reader.wav": "读卡器离线",
  15. "offline_cam_left.wav": "左侧摄像头离线",
  16. "offline_cam_right.wav": "右侧摄像头离线",
  17. }
  18. def main():
  19. import struct
  20. os.makedirs(OUTPUT_DIR, exist_ok=True)
  21. import win32com.client
  22. speaker = win32com.client.Dispatch("SAPI.SpVoice")
  23. SSFMCreateForWrite = 3
  24. SAFT16kHz16BitMono = 4
  25. for filename, text in VOICES.items():
  26. path = os.path.join(OUTPUT_DIR, filename)
  27. stream = win32com.client.Dispatch("SAPI.SpFileStream")
  28. af = win32com.client.Dispatch("SAPI.SpAudioFormat")
  29. af.Type = SAFT16kHz16BitMono
  30. stream.Format = af
  31. stream.Open(path, SSFMCreateForWrite)
  32. speaker.AudioOutputStream = stream
  33. speaker.Speak(text)
  34. stream.Close()
  35. with open(path, 'rb+') as f:
  36. data = bytearray(f.read())
  37. fmt_chunk = b'fmt '
  38. fmt_pos = data.find(fmt_chunk)
  39. if fmt_pos == -1:
  40. print(f" 跳过音量放大(无 fmt chunk)")
  41. else:
  42. channels = struct.unpack_from('<H', data, fmt_pos + 10)[0]
  43. sample_rate = struct.unpack_from('<I', data, fmt_pos + 12)[0]
  44. bits_per_sample = struct.unpack_from('<H', data, fmt_pos + 22)[0]
  45. data_chunk = b'data'
  46. data_pos = data.find(data_chunk)
  47. if data_pos != -1:
  48. data_size = struct.unpack_from('<I', data, data_pos + 4)[0]
  49. sample_start = data_pos + 8
  50. sample_end = sample_start + data_size
  51. if bits_per_sample == 16:
  52. fmt = '<' + 'h' * (data_size // 2)
  53. samples = list(struct.unpack_from(fmt, bytes(data[sample_start:sample_end])))
  54. gain = 3.0
  55. samples = [max(-32768, min(32767, int(s * gain))) for s in samples]
  56. data[sample_start:sample_end] = struct.pack(fmt, *samples)
  57. elif bits_per_sample == 8:
  58. samples = list(data[sample_start:sample_end])
  59. gain = 3.0
  60. samples = [max(0, min(255, int((s - 128) * gain) + 128)) for s in samples]
  61. data[sample_start:sample_end] = bytes(samples)
  62. with open(path, 'wb') as f:
  63. f.write(data)
  64. print(f"生成: {path} (增益 {gain}x, {sample_rate}Hz, {bits_per_sample}bit, {channels}ch)")
  65. else:
  66. print(f"生成: {path} (无 data chunk)")
  67. if __name__ == "__main__":
  68. main()