# sys.soundPool

游戏音效池：程序启动时载入音效，之后一句 `play(名字)` 播放，多个声音可以同时响。

- 基于系统自带的 XAudio2（Windows 8 以上），混音在系统的音频线程里完成。`play` 只是提交请求（约 0.03 毫秒，首次创建声道约 0.4 毫秒），声音延迟约 10 毫秒，在界面线程里直接调用即可，不需要创建线程。
- 创建音效池会打开音频设备（约 80 毫秒），程序启动时创建一个即可。
- 没有声卡或系统不支持时是静音模式：对象照常创建，`play` 返回 null，程序不必判断。原因见 `lastError`。
- 声音数据常驻内存，适合音效和不太长的背景音乐（3 分钟 48kHz 立体声约 34MB）。长音乐边解码边播放可以用 [sys.mediaFoundation.streamingMp3Player](https://www.aardio.com/zh-cn/docs/library/sys/mediaFoundation/streamingMp3Player/)。
- 所有方法在创建对象的线程里调用，对象不要传给其他线程。

## 快速开始

```aardio
import sys.soundPool;

var sounds = sys.soundPool();
sounds.add("ding","C:\Windows\Media\Windows Ding.wav");
sounds.add("coin","/res/coin.wav"); //支持资源路径
sounds.add("bgm","/res/music.mp3",0.5); //参数 3 是这个声音的基础音量

sounds.play("coin");
sounds.play("coin",0.5,1.2); //音量 0.5，音高 1.2 倍
var bgm = sounds.loop("bgm"); //循环播放，直到 stop
sounds.stop(bgm);
```

## 声音来源

`add(名字,来源,音量)` 的来源可以是：

- WAV 文件路径或 WAV 数据：支持 8/16/24/32 位整数 PCM 与 32 位浮点，按原格式播放，不转换。支持发布后的内嵌资源路径。
- MP3、M4A、WMA 等文件路径：用系统 Media Foundation 一次解码为 16 位 PCM，必须是硬盘文件。
- PCM 表：`{pcm=buffer;sampleRate=48000;channels=2;bitsPerSample=16}`，浮点数据加 `float=true`。
- 提供 `pcm16` 方法与 `sampleRate` 字段的声音对象，例如 [av.audio](https://www.aardio.com/zh-cn/docs/library/av/audio/) 生成的音效，不需要素材文件：

```aardio
import av.audio.sfx;
sounds.add("done",av.audio.sfx("success").trim(-60)); //trim 去掉混响尾部的静音
sounds.add("pop",av.audio.sfx("pop"));
```

同名重复添加会替换原来的声音。`add` 成功返回时长（秒），失败返回 `null,错误信息`。

## 播放控制

- `play(名字,音量,音高)` 返回播放编号。同一个声音可以重叠播放。音量与 `add` 的基础音量相乘。音高范围 0.01~4，同时改变速度。
- 重复的音效（脚步、射击、金币）每次把音高在 1 附近随机取值，听起来不单调：`sounds.play("coin",1,0.95 + math.random() * 0.1)`。
- `loop(名字,音量,音高)` 循环播放，直到 `stop`。
- `stop(编号)` 停止一次播放，`stop(名字)` 停止这个声音的所有播放，`stop()` 停止全部。
- `isPlaying(编号或名字)` 查询是否在播放。
- `setVolume(音量)` 设置总音量，影响正在播放和之后的所有声音，设为 0 即静音。

每种音频格式最多同时播放 32 个声音（`sys.soundPool({maxVoices=64})` 可以改），超出时打断最早开始的声音，循环播放的声音不会被打断。

## 关闭

`close()` 停止所有声音、关闭音频设备并释放声音数据。对象被回收或线程结束时也会自动关闭，一般不必手动调用。
