Example #1
0
        public void PlayFile(string filePath)
        {
            var            OutFormat     = new WaveFormat(48000, 16, 1); // Create a new Output Format, using the spec that Discord will accept, and with the number of channels that our Client supports.
            AudioOutStream currentStream = AudioClient.CreatePCMStream(AudioApplication.Mixed);

            using (var MP3Reader = new Mp3FileReader(filePath))                            // 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];
                    byte[] silence   = new byte[blockSize];
                    int    byteCount = resampler.Read(buffer, 0, blockSize);

                    while (byteCount > 0) // Read audio into our buffer, and keep a loop open while data is present
                    {
                        if (RequestStop)
                        {
                            RequestStop = false;
                            FinishedSong();
                            return;
                        }
                        if (byteCount < blockSize)
                        {
                            // Incomplete Frame
                            for (int i = byteCount; i < blockSize; i++)
                            {
                                buffer[i] = 0;
                            }
                        }
                        for (int i = 0; i < buffer.Length; i += 2)
                        {
                            short sample = (short)(buffer[i] | (buffer[i + 1] << 8));
                            short result = (short)(sample * Volume);
                            buffer[i]     = (byte)(result & 0xFF);
                            buffer[i + 1] = (byte)(result >> 8);
                        }
                        try
                        {
                            currentStream.WriteAsync(Paused ? silence : buffer, 0, blockSize).GetAwaiter(); //Send buffer to Discord
                        }
                        catch (Exception e)
                        {
                            Console.WriteLine(e.Message);
                            AudioClient  = null;
                            CurrentState = AudioState.Stopped;
                            break;
                        }
                        if (!Paused)
                        {
                            byteCount = resampler.Read(buffer, 0, blockSize);
                        }
                    }
                    FinishedSong();
                }
        }
Example #2
0
        private async Task StreamAudio(IGuild Guild, String Filename)
        {
            Console.WriteLine("Streaming");

            IAudioClient AudioClient;

            await JukeBot.DiscordClient.SetGameAsync(Filename);

            if (this.ConnectedChannels.TryGetValue(Guild.Id, out AudioClient))
            {
                var Output = this.CreateStream("4chan/" + Filename).StandardOutput.BaseStream;
                this.AudioData = new MemoryStream();
                await Output.CopyToAsync(this.AudioData);

                await Output.FlushAsync();

                Output.Dispose();
                int  read_length = 0;
                bool flipflop    = false;
                int  buffer_size = 2048;
                var  buffer      = new[] { new byte[buffer_size], new byte[buffer_size] };
                this.AudioData.Seek(0x0, SeekOrigin.Begin);
                var        DiscordStream = AudioClient.CreatePCMStream(AudioApplication.Music, 2880);
                Task       writer;
                Task <int> reader;
                while (this.AudioData.Position < this.AudioData.Length)
                {
                    if (!this.Pause)
                    {
                        writer      = DiscordStream.WriteAsync(buffer[flipflop ? 0 : 1], 0, read_length);
                        flipflop    = !flipflop;
                        reader      = this.AudioData.ReadAsync(buffer[flipflop ? 0 : 1], 0, buffer_size);
                        read_length = await reader;
                        await writer;
                    }
                    else
                    {
                        await DiscordStream.WriteAsync(new byte[512], 0, 512);

                        read_length = 0;
                    }
                }
                await this.AudioData.FlushAsync();

                //await Output.CopyToAsync(DiscordStream);
                await DiscordStream.FlushAsync();

                await JukeBot.DiscordClient.SetGameAsync("");
            }
        }
 /// <summary>
 /// Creates a new stream when a user joins the voice channel
 /// repeats all their audio
 /// </summary>
 /// <param name="arg1"></param>
 /// <param name="arg2"></param>
 /// <returns></returns>
 private async Task StreamCreated(ulong arg1, AudioInStream arg2)
 {
     try
     {
         using (var stream = AudioClient.CreatePCMStream(AudioApplication.Mixed))
         {
             if (Program.DEBUG)
             {
                 Console.WriteLine(arg1); // User ID
                 //await arg2.CopyToAsync(stream);
             }
         }
     }
     catch (Exception e)
     {
         Console.WriteLine(e);
     }
 }
        /// <summary>
        /// Stream youtube audio data to discord voice channel. Works by youtube-dl piping video data to ffmpeg,
        /// which then extracts the audio and outputs it, which is then read by a stream, which is then forced into the user's ear
        /// </summary>
        /// <param name="url">Url of the video</param>
        /// <returns></returns>
        private async Task StreamAudio(string url, CancellationToken cancelToken)
        {
            Console.WriteLine("Youtube requested");
            using (var stream = AudioClient.CreatePCMStream(application: AudioApplication.Mixed))
            {
                try
                {
                    if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Windows))
                    {
                        #if DEBUG
                        Console.WriteLine("Windows Detected");
                        #endif
                        _process = Process.Start(new ProcessStartInfo
                        {
                            // 'Direct' method using only ffmpeg and a music link

                            FileName  = "Binaries\\ffmpeg",
                            Arguments =
                                $"-i \"{url}\" " +
                                " -ac 2 -f s16le -ar 48000 pipe:1",
                            UseShellExecute        = false,
                            RedirectStandardOutput = true,
                            RedirectStandardError  = false

                                                     // 'indirect' method using both youtube-dl and ffmpeg

                                                     /*
                                                      * FileName = "cmd",
                                                      * Arguments = $"/C youtube-dl.exe --hls-prefer-native -q -o - {url} | ffmpeg.exe -i - -f s16le -ar 48000 -ac 2 -reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 10 pipe:1 -b:a 96K ",
                                                      * UseShellExecute = false,
                                                      * RedirectStandardOutput = true,
                                                      * RedirectStandardError = false,
                                                      */
                        });
                    }
                    else
                    {
                        #if DEBUG
                        Console.WriteLine("Linux Detected");
                        #endif
                        _process = Process.Start(new ProcessStartInfo
                        {
                            /*
                             * FileName = "/bin/bash",
                             * Arguments =
                             * $"-c \"ffmpeg -i \'{url}\' " +
                             * " -ac 2 -f s16le -ar 48000 -loglevel panic pipe:1 \" ",
                             * UseShellExecute = false,
                             * RedirectStandardOutput = true,
                             * RedirectStandardError = false
                             */
                            FileName               = "/bin/bash",
                            Arguments              = $"youtube-dl.exe --hls-prefer-native -q -o - {url} | ffmpeg.exe -i - -f s16le -ar 48000 -ac 2 -reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 10 pipe:1 -b:a 96K",
                            UseShellExecute        = false,
                            RedirectStandardOutput = true,
                            RedirectStandardError  = false,
                        });
                    }
                    Console.WriteLine("Starting process...");
                    int blockSize = 512;
                    var buffer    = new byte[blockSize];
                    int byteCount = 1;
                    do
                    {
                        // Don't send any data or read from the stream if the stream is supposed to be paused
                        if (Paused)
                        {
                            continue;
                        }

                        if (cancelToken.IsCancellationRequested || WillSkip)
                        {
                            break;
                        }

                        byteCount = await _process.StandardOutput.BaseStream.ReadAsync(buffer, 0, blockSize);

                        //buffer = AdjustVolume(buffer, Volume);
                        await stream.WriteAsync(buffer, 0, blockSize);
                    } while (byteCount > 0);
                    if (!WillSkip)
                    {
                        _process.WaitForExit();
                    }
                    _process.Close();
                    await stream.FlushAsync();

                    WillSkip = false;
                    Paused   = false;


                    #if DEBUG
                    Console.WriteLine("Process finished.");
                    #endif
                }
                catch (OperationCanceledException)
                {
                    Console.WriteLine("Cancelled by user.");
                    _process.Close();
                    await stream.FlushAsync();

                    WillSkip = false;
                }
                catch (FileNotFoundException)
                {
                    await _context.Channel.SendMessageAsync("Error, Youtube-dl and/or ffmpeg can not be found");
                }
                catch (Exception e)
                {
                    Console.WriteLine(e.InnerException);
                }
            }
        }
Example #5
0
        public async Task SendAudioAsync(IGuild Guild, string UserInput)
        {
            var YTC = new YoutubeClient();

            if (UserInput.ToLower().Contains("youtube.com"))
            {
                UserInput = YoutubeClient.ParseVideoId(UserInput);
            }
            else
            {
                //var SearchList = await YTC.SearchAsync( UserInput );

                HttpClient _httpClient = new HttpClient();

                string EncodedSearchQuery = WebUtility.UrlEncode(UserInput);

                string Request = $"https://www.youtube.com/search_ajax?style=xml&search_query={EncodedSearchQuery}";

                var Response = await _httpClient.GetStringAsync(Request).ConfigureAwait(false);

                var SearchResultsXml = XElement.Parse(Response).StripNamespaces();

                var VideoIds = SearchResultsXml.Descendants("encrypted_id").Select(e => ( string )e);

                UserInput = VideoIds.First();
            }

            var MediaInfo = await YTC.GetVideoMediaStreamInfosAsync(UserInput);

            var ASI = MediaInfo.Audio.OrderBy(x => x.Bitrate).Last();

            var VideoInfo = await YTC.GetVideoAsync(UserInput);

            var Title = VideoInfo.Title; //VideoInfo.ToString();            ;

            var RGX = new Regex("[^a-zA-Z0-9 -]");

            Title = RGX.Replace(Title, "");

            var Name = $"{Title}.{ASI.AudioEncoding.ToString()}";

#if DEBUG
            var Path = "bin/Debug/netcoreapp1.1/Songs/";
#else
            String Path = "Songs/";
#endif
            if (!File.Exists(Path + Name))
            {
                using (var Input = await YTC.GetMediaStreamAsync(ASI)) {
                    Directory.CreateDirectory(Path);
                    using (var Out = File.Create(Path + Name)) {
                        await Input.CopyToAsync(Out);
                    }
                }
            }

            IAudioClient AudioClient;

            await JukeBot.DiscordClient.SetGameAsync(Title);

            if (this.ConnectedChannels.TryGetValue(Guild.Id, out AudioClient))
            {
                var Output = this.CreateStream(Path + Name).StandardOutput.BaseStream;
                this.AudioData = new MemoryStream();
                await Output.CopyToAsync(this.AudioData);

                await Output.FlushAsync();

                Output.Dispose();
                int  read_length = 0;
                bool flipflop    = false;
                int  buffer_size = 2048;
                var  buffer      = new[] { new byte[buffer_size], new byte[buffer_size] };
                this.AudioData.Seek(0x0, SeekOrigin.Begin);
                var        DiscordStream = AudioClient.CreatePCMStream(AudioApplication.Music, 2880);
                Task       writer;
                Task <int> reader;
                while (this.AudioData.Position < this.AudioData.Length)
                {
                    if (!this.Pause)
                    {
                        writer      = DiscordStream.WriteAsync(buffer[flipflop ? 0 : 1], 0, read_length);
                        flipflop    = !flipflop;
                        reader      = this.AudioData.ReadAsync(buffer[flipflop ? 0 : 1], 0, buffer_size);
                        read_length = await reader;
                        await writer;
                    }
                    else
                    {
                        await DiscordStream.WriteAsync(new byte[512], 0, 512);

                        read_length = 0;
                    }
                }
                await this.AudioData.FlushAsync();

                //await Output.CopyToAsync(DiscordStream);
                await DiscordStream.FlushAsync();

                await JukeBot.DiscordClient.SetGameAsync("");
            }
        }
Example #6
0
        public async void Play()
        {
            while (Playing)
            {
                bytesSent = 0;
                AudioOutStream pcm        = null;
                Stream         songStream = null;
                Process        ffmpeg     = null;

                CancellationToken cancelToken;
                lock (locker)
                {
                    cancelToken = SongCancelSource.Token;
                }
                try
                {
                    // The size of bytes to read per frame; 1920 for mono
                    int blockSize = 3840;

                    byte[] buffer = new byte[blockSize];

                    pcm = AudioClient.CreatePCMStream(AudioApplication.Music, bufferMillis: 500);
                    int bytesRead = 0;
                    ffmpeg = FFmpegProcess(CurrentSong().AudioURI);

                    ffmpeg.Start();
                    songStream = ffmpeg.StandardOutput.BaseStream;

                    //When for some reason the given song has no data, an error will be shown.
                    if (songStream.Read(buffer, 0, 3840) == 0)
                    {
                        ShowSongNullEmbed();
                        error = true;
                        throw new OperationCanceledException();
                    }

                    ShowSongStartEmbed();

                    while ((bytesRead = songStream.Read(buffer, 0, 3840)) > 0)
                    {
                        await pcm.WriteAsync(buffer, 0, bytesRead, cancelToken).ConfigureAwait(false);

                        unchecked { bytesSent += bytesRead; }
                    }
                }
                catch (OperationCanceledException)
                {
                    Console.WriteLine("SONG CANCELED");
                    //Playing = false;
                }
                finally
                {
                    if (!error && !Skipped)
                    {
                        ShowSongEndedEmbed();
                    }
                    else
                    {
                        error   = false;
                        Skipped = false;
                    }

                    if (pcm != null)
                    {
                        var flushCancel = new CancellationTokenSource();
                        var flushToken  = flushCancel.Token;
                        var flushDelay  = Task.Delay(1000, flushToken);

                        await Task.WhenAny(flushDelay, pcm.FlushAsync(flushToken));

                        flushCancel.Cancel();

                        if (ffmpeg != null)
                        {
                            ffmpeg.Dispose();
                        }


                        pcm.Dispose();
                        songStream.Dispose();

                        //If the song list is not over yet and there is another song to be played.
                        if (NextSong())
                        {
                            CurrentSongID++;
                        }
                        else
                        {
                            //If the song list is over, but the loop setting is on (makes the list start over when
                            //it reaches its end)
                            if (LoopList)
                            {
                                CurrentSongID = 0;
                            }
                            else
                            {
                                PlaybackEndEmbed();
                                Clear();
                                Destroy();
                            }
                        }
                    }
                }
            }
        }