我正在使用Discord.Net包装器在VS2017中编写Discord Bot。除了主要目标:在语音通道中使用TTS音频输出流之外,我已经可以进行所有工作(解析/发送文本命令,加入语音通道)。
基本上,我正在使用SpeechSynthesizer创建MemoryStream并将其写入Discord机器人。问题是,没有音频。完全没有我一直在关注其他几个答案以及Discord.Net网站上的文档,但似乎找不到找到使之起作用的方法。通过url / file进行音频流传输的文档很丰富,但事实并非如此。
var ffmpeg = CreateProcess("");
var output = ffmpeg.StandardOutput.BaseStream;
IAudioClient client;
ConnectedChannels.TryGetValue(guild.Id, out client);
var discord = client.CreatePCMStream(AudioApplication.Mixed);
await output.CopyToAsync(discord);
await discord.FlushAsync();
以上是我一直使用的示例,该示例是通过ffmpeg从文件中获取的。我看到它只是在流上复制,因此我尝试了各种方法进行以下操作:
IAudioClient client;
ConnectedChannels.TryGetValue(guild.Id, out client);
var discord = client.CreatePCMStream(AudioApplication.Mixed);
var synth = new SpeechSynthesizer();
var stream = new MemoryStream();
var synthFormat = new SpeechAudioFormatInfo(
EncodingFormat.Pcm,
8000,
16,
1,
16000,
2,
null);
synth.SetOutputToAudioStream(stream, synthFormat);
synth.Speak("this is a test");
await stream.CopyToAsync(discord);
await discord.FlushAsync();
我尝试过更改SpeechAudioFormatInfo属性,更改SpeechSynthesizer上的输出,完全删除了异步调用,几乎所有我想到的事情都没有结果。
我意识到我可以将声音输出到虚拟音频设备,并让另一个帐户/机器人来接听,但这不是本练习的目的。 我还意识到,我可以将输出写入文件并进行流传输,但这会增加处理时间。这些TTS指令很小,永远不会超过5个字,并且由于它们应该是“标注”,因此需要快速一点。
最后,我也无法完全找到使用ffmpeg进行这项工作的方法。我读过的所有内容似乎都表明需要物理资源,而不仅仅是内存流。
所以,我机智了。任何援助将不胜感激。
答案 0 :(得分:0)
Discord.NET对AudioStreams有点挑剔。每个音频连接只需要一个PCMStream,否则它会做一些奇怪的事情。您可以在语音连接时创建PCMStream,然后调用多个SendAsync发送音频。
如果我没记错的话,您应该可以将TTS流作为媒体(mp3或AAC媒体文件)输出 然后像这样播放TTS音频文件
public async Task SendAsync(float volume, string path, AudioOutStream stream)
{
_currentProcess = CreateStream(path);
while (true)
{
if (_currentProcess.HasExited)
{ break; }
int blockSize = 2880;
byte[] buffer = new byte[blockSize];
int byteCount;
byteCount = await _currentProcess.StandardOutput.BaseStream.ReadAsync(buffer, 0, blockSize);
if (byteCount == 0)
{ break; }
await stream.WriteAsync(buffer, 0, byteCount);
}
await stream.FlushAsync();
}
然后像这样调用ffmpeg:
private static Process CreateStream(string path)
{
var ffmpeg = new ProcessStartInfo
{
FileName = "ffmpeg",
Arguments = $"-hide_banner -loglevel panic -i \"{path}\" -ac 2 -f s16le -ar 48000 pipe:1",
UseShellExecute = false,
RedirectStandardOutput = true
};
return Process.Start(ffmpeg);
}