Discord.net can't stream audio with NAudio

北城以北 提交于 2021-02-05 09:12:10

问题


I want to play an mp3 file in a voice channel. The BOT successfully can connect, but doesn't play anything, and throws an exception.

Code

public async Task SendAudioAsync(IGuild guild, IMessageChannel channel, string path)
{
    try
    {
        if (!File.Exists(path))
        {
            await channel.SendMessageAsync("File does not exist.");
            return;
        }
        IAudioClient client;
        if (ConnectedChannels.TryGetValue(guild.Id, out client))
        {
            await Log.d($"Starting playback of {path} in {guild.Name}", src);
            using (var reader = new Mp3FileReader(path))
            using (var output = client.CreateOpusStream())
            {
                try
                {
                    reader.CopyTo(output);//<- AudioService.cs, line: 70
                }
                catch(Exception e)
                {
                    await Log.e(e.ToString(), src);
                }
                finally { await output.FlushAsync(); }
            }
        }
    }
    catch (Exception e)
    {
        await Log.e(e.ToString(), src);
    }
}

Exception

System.ArgumentException: Shift and length outside the array boundaries or the number of elements is greater than the number of items in the source collection from the index to the end of the collection. 
System.Buffer.BlockCopy(Array src, Int32 srcOffset , Array dst, Int32 dstOffset, Int32 count)
at Discord.Audio.Streams.BufferedWriteStream.<WriteAsync>d__21.MoveNext() 
--- Trigger end tracking from the previous occurrence of the exception --- 
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess (Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Discord.Audio.AudioStream.Write(Byte [] buffer, Int32 offset, Int32 count)
at System.IO.Stream.InternalCopyTo(Stream destination, Int32 <SendAudioAsync> d__4.MoveNext() location: C:\Users\noel\source\repos\DiscordBot\Bot\Core\Voice\AudioService.cs, line: 70 

This version of the exception, translated from hungarian with google translator..

Update

I did more research and found this example, because it's outdated I rewrote a bit and now looks like this:

public async Task SendAudioAsync(IGuild guild, IMessageChannel channel, string path)
{
    try
    {
        if (!File.Exists(path))
        {
            await channel.SendMessageAsync("File does not exist.");
            return;
        }
        IAudioClient client;
        if (ConnectedChannels.TryGetValue(guild.Id, out client))
        {
            await Log.d($"Starting playback of \"{path}\" in \"{guild.Name}\"", src);
            var OutFormat = new WaveFormat(48000, 16, 2);

            using (var MP3Reader = new Mp3FileReader(path)) // Create a new Disposable MP3FileReader, to read audio from the filePath parameter
            using (var resampler = new MediaFoundationResampler(MP3Reader, OutFormat)) // Create a Disposable Resampler, which will convert the read MP3 data to PCM, using our Output Format
            {
                resampler.ResamplerQuality = 60; // Set the quality of the resampler to 60, the highest quality
                int blockSize = OutFormat.AverageBytesPerSecond / 50; // Establish the size of our AudioBuffer
                byte[] buffer = new byte[blockSize];
                int byteCount;
                while ((byteCount = resampler.Read(buffer, 0, blockSize)) > 0) // Read audio into our buffer, and keep a loop open while data is present
                {
                    if (byteCount < blockSize)
                    {
                        // Incomplete Frame
                        for (int i = byteCount; i < blockSize; i++)
                            buffer[i] = 0;
                    }
                    using(var output = client.CreatePCMStream(AudioApplication.Mixed))
                    await output.WriteAsync(buffer, 0, blockSize); // Send the buffer to Discord
                }
            }
        }
    }
    catch (Exception e)
    {
        await Log.e(e.ToString(), src);
    }
}

Now it doesn't do anything and doesn't throw any exception.


回答1:


I have come up with this Code, which does work for me. As this is considered to be a test program I did not really care about synchronous/asynchrounos programming. It should be easy to adapt this. I hope this helps.

class Program
{
    private static string mytoken = "";
    private static CancellationTokenSource cancellationToken = new CancellationTokenSource();
    private static bool playing = false;
    private static AudioOutStream dstream = null;
    private static IAudioClient client = null;

    static void Main(string[] args)
    {
        var Client = new DiscordSocketClient();
        Client.LoginAsync(TokenType.Bot, mytoken).Wait();
        Client.StartAsync().Wait();

        var path = "path\\testfile.mp3";

        Client.Ready += async () =>
        {
            var guild = Client.Guilds.First();
            var channel = guild.Channels.Where(x => x is SocketVoiceChannel).Select(x => x as SocketVoiceChannel).First();

            try
            {
                client = await channel.ConnectAsync();

                var reader = new Mp3FileReader(path);
                var naudio = WaveFormatConversionStream.CreatePcmStream(reader);

                dstream = client.CreatePCMStream(AudioApplication.Music);
                byte[] buffer = new byte[naudio.Length];

                int rest = (int)(naudio.Length - naudio.Position);
                await naudio.ReadAsync(buffer, 0, rest);
                playing = true;
                await dstream.WriteAsync(buffer, 0, rest, cancellationToken.Token);
            }
            catch (Exception e)
            {
                Debug.WriteLine(e.Message);
                if(e.InnerException != null)
                    Debug.WriteLine(e.InnerException.Message);
            }

        };

        while (!playing) ;
        Console.ReadLine();
        cancellationToken.Cancel();
        Debug.WriteLine("Pre-Flush");
        dstream.Flush();
        Debug.WriteLine("POST-FLUSH");
        client.StopAsync().Wait();

        Client.StopAsync().Wait();
        Client.LogoutAsync().Wait();
    }
}

It also worked, when instead of using a byte[] to buffer the audio, I just called naudio.CopyToAsync(dstream);



来源:https://stackoverflow.com/questions/51611921/discord-net-cant-stream-audio-with-naudio

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!