这里只介绍思路,具体代码可以指路
如果有更好的方法,欢迎指出,这里仅当抛砖引玉
原理很简单,找到一个可以同时可读可写的流对象而且不必担心写入时流的位置对播放的影响就完事大吉,因为两个解码的位置和读取流的位置必定不一样(也许可以通过锁来解决),一开始博主想到的是TCP UDP这些的,的确是可以的,服务端发送数据,客户端读取数据;其实还有一个更好的,就是MemoryMappedFile,它是读写毫不相干,那么我发送端只需要发送就可以了,另外一端直接读取,非常方便
//全局
static MemoryMappedFile mmf;
static Imouto.Audio.WaveFormat waveFormat;
//解码WavPack格式无损音频
//WavPackCodec:https://www.wavpack.com/downloads.html -->C# Wrapper
static void tesk_server(object o)
{
using (WavPack wavPack = new WavPack(@"E:\Audio\LosslessAudio\[EAC] [MGRT][WavPack][ANZX-6718]「白金ディスコ」&「偽物語」劇伴音楽集 其ノ參(WavPack+CUE)\ANZX-6718.wv"))
{
waveFormat = wavPack.WaveFormat;
using (MemoryMappedViewStream stream = mmf.CreateViewStream())
{
BinaryWriter writer = new BinaryWriter(stream);
Byte[] buffer = new Byte[1024 * 16];
for (Int32 bytesRead; (0 != (bytesRead = wavPack.Read(buffer)));)
{
writer.Write(buffer, 0, bytesRead);
}
}
}
}
//用NAudio的BufferedWaveProvider播放PCM数据
//https://github.com/hha1501/NanoBot/tree/8d7051ccc184718ce2960f30cfb0e45e4d172dc7
static void tesk_client(object o)
{
using (MemoryMappedViewStream stream = mmf.CreateViewStream())
{
BufferedWavePlayer dd = new BufferedWavePlayer(stream, waveFormat.SamplesPerSecond, waveFormat.BitsPerSample, waveFormat.Channels);
System.Threading.Tasks.Task task = dd.PlayAsync();
task.GetAwaiter().GetResult();
}
}
主函数:
PS:这里只要关心产生的数据大小会不会爆内存,这里博主设置了1G大小
一般音频也没有那么大。
创建一个解码线程,一个播放线程,思路其实和SDL音频播放接口异曲同工
static void Main(string[] args)
{
mmf = MemoryMappedFile.CreateNew("test", 1024*1024*1024, MemoryMappedFileAccess.ReadWrite);
ThreadPool.QueueUserWorkItem(tesk_server, null);
System.Threading.Thread.Sleep(1000);
ThreadPool.QueueUserWorkItem(tesk_client, null);
Console.ReadKey();
}
PS:以上函数仅供参考,在内存效率使用上还是有一点点缺陷,一般有可能出现内存溢出等问题,可以参考“https://www.codenong.com/15662455/” 去解决和优化
知道了这个的话,其实播放Wav格式的音频也一样,只要读取Wav的头,大小44个字节,里面包括了sampleRate(一般为44100),channels(一般为2),和bitperSample(一般为16)这三个关键数据,就可以直接截取44字节后面的PCM数据了,一模一样,可以Git找一下Wav的解析类