compare-aliyun-tts.sh 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172
  1. #!/bin/bash
  2. set -e
  3. B_URL='http://dashscope-a717.oss-cn-beijing.aliyuncs.com/1d/c2/20260712/9f107364/2b785586-afa2-445d-b4b3-5bb3a9939781.wav?Expires=1783879166&OSSAccessKeyId=LTAI5tPxpiCM2hjmWrFXrym1&Signature=cw%2BT4is4ceG0uxc7k7IWE8O9e0c%3D'
  4. curl -sS -m 30 -o /tmp/aliyun-B-slow.wav "$B_URL"
  5. ls -la /tmp/aliyun-B-slow.wav
  6. ffmpeg -i /tmp/aliyun-B-slow.wav -f null - 2>&1 | grep -E 'Duration|Stream' | head -2
  7. echo
  8. echo '=== C: A + ffmpeg atempo=0.78 后处理(模拟播放器减速) ==='
  9. ffmpeg -i /tmp/aliyun-A.mp3 -filter:a 'atempo=0.78' -y /tmp/aliyun-C-atempo.mp3 2>&1 | tail -1
  10. ffmpeg -i /tmp/aliyun-C-atempo.mp3 -f null - 2>&1 | grep Duration | head -1
  11. echo
  12. echo '=== 对比 ==='
  13. A_SEC=$(ffmpeg -i /tmp/aliyun-A.mp3 -f null - 2>&1 | grep Duration | head -1 | sed 's/Duration: //; s/,.*//' | awk -F: '{print ($1*3600+$2*60+$3)}')
  14. B_SEC=$(ffmpeg -i /tmp/aliyun-B-slow.wav -f null - 2>&1 | grep Duration | head -1 | sed 's/Duration: //; s/,.*//' | awk -F: '{print ($1*3600+$2*60+$3)}')
  15. C_SEC=$(ffmpeg -i /tmp/aliyun-C-atempo.mp3 -f null - 2>&1 | grep Duration | head -1 | sed 's/Duration: //; s/,.*//' | awk -F: '{print ($1*3600+$2*60+$3)}')
  16. python3 - <<EOF
  17. A=$A_SEC; B=$B_SEC; C=$C_SEC
  18. print(f"A 阿里云 1.0x 默认: {A:.2f}s")
  19. print(f"B 阿里云 Instruct 语速较慢: {B:.2f}s ({B/A:.3f}x)")
  20. print(f"C 阿里云 1.0x + atempo=0.78:{C:.2f}s ({C/A:.3f}x)")
  21. print()
  22. print(f"B vs C 差异: {abs(B-C)/A*100:.2f}%")
  23. if abs(B-C)/A < 0.05:
  24. print("=> 结论: B ≈ C, 阿里云 Instruct 语速控制也是后处理变速(类似 Edge TTS)")
  25. else:
  26. print("=> 结论: B 与 C 显著不同, 阿里云 Instruct 是真正的模型级慢速生成")
  27. EOF
  28. echo
  29. echo '=== 顺便看看波形是不是真不同(语谱图) ==='
  30. ffmpeg -i /tmp/aliyun-A.mp3 -lavfi showspectrumpic=s=1024x512 /tmp/aliyun-A-spec.png 2>&1 | tail -1
  31. ffmpeg -i /tmp/aliyun-B-slow.wav -lavfi showspectrumpic=s=1024x512 /tmp/aliyun-B-spec.png 2>&1 | tail -1
  32. ffmpeg -i /tmp/aliyun-C-atempo.mp3 -lavfi showspectrumpic=s=1024x512 /tmp/aliyun-C-spec.png 2>&1 | tail -1
  33. ls -la /tmp/aliyun-*-spec.png
  34. echo
  35. echo '=== 把三个 mp3 上传到 OSS 让你试听 ==='
  36. cp /data/ai/audio/server/.env /tmp/dashscope.env 2>/dev/null
  37. cd /data/ai/audio/server && node -e '
  38. const OSS = require("ali-oss").default || require("ali-oss");
  39. require("dotenv").config({ path: "/data/ai/audio/server/.env" });
  40. const c = {
  41. region: "oss-cn-shanghai",
  42. accessKeyId: process.env.OSS_ACCESS_KEY_ID,
  43. accessKeySecret: process.env.OSS_ACCESS_KEY_SECRET,
  44. bucket: process.env.OSS_BUCKET_NAME,
  45. internal: true,
  46. endpoint: process.env.OSS_INTERNAL_ENDPOINT,
  47. };
  48. const client = new OSS(c);
  49. const fs = require("fs");
  50. const files = {
  51. "aliyun-A-default": "/tmp/aliyun-A.mp3",
  52. "aliyun-B-instruct-slow": "/tmp/aliyun-B-slow.wav",
  53. "aliyun-C-atempo-postprocess": "/tmp/aliyun-C-atempo.mp3",
  54. };
  55. (async () => {
  56. for (const [name, path] of Object.entries(files)) {
  57. const key = `audio/voice-samples/aliyun-${name}.mp3`;
  58. await client.put(key, fs.readFileSync(path));
  59. console.log("uploaded:", key);
  60. }
  61. const PUB = process.env.OSS_ENDPOINT;
  62. for (const name of Object.keys(files)) {
  63. console.log(`https://${process.env.OSS_BUCKET_NAME}.${PUB}/audio/voice-samples/aliyun-${name}.mp3`);
  64. }
  65. })().catch(e => { console.error(e); process.exit(1); });
  66. '