把 16 位 PCM 送往系统默认播放设备(Windows waveOut)。
write 立即返回,设备在后台按写入顺序播放。暂时没有数据时设备静音等待,继续写入就接着播。wait() 等待期间会处理窗口消息。mp3Decoder 加 sys.waveOut 实现的。import sys.waveOut;
var out,err = sys.waveOut({sampleRate=24000;channels=1});
if(!out) return print(err);
//0.5 秒 440Hz 正弦波,16 位单声道
var samples = {};
for(i=1;12000) samples[i] = math.floor(math.sin(2*math.pi*440*(i-1)/24000) * 8000);
out.write(raw.buffer({word s[] = samples}));
out.wait(); //等待播放完
out.close();
WAV 是容器格式,里面有格式块、数据块,还可能有其他块,不能把整个文件当作 PCM 写入,也不能简单跳过固定的 44 字节。可以先用 sys.mediaFoundation.reader 读出 PCM 和格式:
import sys.mediaFoundation.reader;
import sys.waveOut;
var reader,err = sys.mediaFoundation.reader("/sound.wav");
if(!reader) return print(err);
var audio = reader.info.audio;
var pcm = reader.readAudio(); //int16 交错 PCM
reader.close();
var out,err = sys.waveOut({sampleRate=audio.sampleRate;channels=audio.channels});
if(!out) return print(err);
out.write(pcm);
out.wait();
out.close();
sys.mediaFoundation.mp3Decoder 的 format 字段可以直接作为参数:
import sys.mediaFoundation.mp3Decoder;
import sys.waveOut;
var decoder,err = sys.mediaFoundation.mp3Decoder({sampleRate=24000;channels=1});
if(!decoder) return print(err);
var out,err = sys.waveOut(decoder.format);
if(!out) return print(err);
var chunks = decoder.decode(string.loadBuffer("/speech.mp3"));
if(chunks) table.append(chunks,decoder.finish());
decoder.close();
for(i=1;#chunks) out.write(chunks[i]);
out.wait();
out.close();
| 接口 | 说明 |
|---|---|
sys.waveOut(options) |
打开默认播放设备。options 必须指定 sampleRate(Hz)和 channels(1 或 2);可选 volume(0~1,默认 1),bitsPerSample 只能是 16。失败返回 null,err |
write(pcm) |
追加 16 位交错 PCM(立体声为 L,R,L,R…),字符串或 buffer,长度必须是 channels×2 的整数倍。数据会复制,返回后可以复用或修改原 buffer。成功返回 true,失败返回 null,err |
poll() |
刷新 completedBytes 和 playedSeconds。不调用也会正常播放,只是这两个属性不更新 |
wait(timeoutMs) |
等待已写入的数据全部播完,期间处理窗口消息。省略 timeoutMs 则一直等待;超时只结束等待,不停止播放 |
close() |
立即停止,释放设备和全部 PCM,可以重复调用 |
format |
{codec="pcm";sampleRate;channels;bitsPerSample=16} |
volume |
软件音量 0~1,修改后只影响之后写入的数据;不改系统音量 |
totalBytes / completedBytes |
已提交给设备、已播完的 PCM 字节数。两者相等表示已写入的数据全部播完 |
playedSeconds |
设备报告的播放位置(秒),不是扬声器的声学测量 |
handle |
waveOut 设备句柄,close 后为 null |
close,不逐块回收。设备异步读取这些内存,close 先停止设备、撤销缓冲,再释放内存。aardio 是 32 位进程,播放长音频时要留意内存:24kHz 单声道每分钟约 2.7MB,44.1kHz 立体声每分钟约 10MB。playedSeconds 正常前进却听不到声音时,用 sys.audioVolume.session() 检查本进程是否在音量合成器里被静音,详见 流式 MP3 播放 的「播放无声排查」。