Вывод звука из MemoryStream с использованием TTS в Discord Bot

Я пишу Discord Bot в VS2017, используя оболочку Discord.Net. У меня есть все, чтобы работать (парсинг / отправка текстовых команд, присоединение к голосовым каналам), кроме основной цели: использование выходного аудиопотока TTS в голосовом канале.

По сути, я использую SpeechSynthesizer для создания MemoryStream и записи его боту Discord. Проблема в том, что звука нет. Вообще. Я следил за несколькими другими ответами, а также за документацией на сайте Discord.Net и, похоже, не могу найти способ заставить это работать. Потоковое аудио через url / файл хорошо документировано, но не это.

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. Все, что я прочитал, похоже, указывает на необходимость физического источника, а не только потока памяти.

Итак, я на грани остроумия. Любая помощь будет оценена.


person Casval Zem Daikun    schedule 24.12.2018    source источник
comment
Не могли бы вы добавить немного больше контекста. Например, где вы запускаете приведенные выше фрагменты кода?   -  person MX D    schedule 28.12.2018
comment
Фрагмент находится в AudioService.cs. Это класс обслуживания, который вызывается из AudioModule.cs. Метод в функции вызывается через делегат события. При запуске я использую внедрение зависимостей и использую Discord.Net для маршрутизации к соответствующему методу в зависимости от введенной пользователем команды. Надеюсь, это помогло.   -  person Casval Zem Daikun    schedule 29.12.2018


Ответы (1)


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);
}
person Keelah    schedule 26.03.2019
comment
Спасибо за You need a single PCMStream per audio connexion or it will do some weird stuff! Совершенно не связано, но я хотел, чтобы мой бот оставался в канале, чтобы он не входил и не выходил каждый раз, когда кто-то хочет использовать голосовые возможности бота. Первый звук после подключения работает нормально, а второй вообще не работает (даже если вы видите активность бота в Discord, но звука нет). Кэширование AudioOutStream вместе с IAudioClient решило эту проблему! - person Devator; 17.09.2020