SocialMSDN

SocialMSDN
Форум Разработчиков Mirosoft

суббота, 8 марта 2014 г.

Интернет радио приложение для Winforms

Есть отличная библиотека bass.NET которая может проигрывать любые аудио файлы, она почти бесплатна
Для начало определим что будет в приложении. 1 - кнопка проигрывания, 2 - визуальный эффект, ну как в Windows Media Player, 3 - кнопка смены качества. Ну и на последок сделаем его красивым, добавив MetroUI,



Шаг 1. Добавим нужные библиотеки
Из bass.NET нужно только 2 файла - bass.dll, и bass.Net.dll
из MetroUI нужен Metroframework.dll
Шаг 2. Я не буду описывать работать с MetroUI, но скажу только что достаточно его подключить через References из студии, и в главной форме Form1 написать
public partial class Form1 : MetroFramework.Forms.MetroForm
Шаг 3. Маленька обертка для упрощения обращения с Bass.NET, в основном нам понадобятся методы Play() ну и так понятно что для плейнга сонга, GetSpectrum() для получения изображении спектра. Звучит заманчиво? Басс нет предоставляет куча-вар-иантов спектрума, воспользуемся всеми. + еще одни хандлер для перебора спектрумов (пусть это будет PictureBox Click event handler)
Шаг 4. Метод Play() принимает несколько важных аргументов, точнее всего 1, но оочень важный, угадаете** ? - это ссылка на файл.
public void Play()
        {
            if (Bass.BASS_Init(-1, 44100, BASSInit.BASS_DEVICE_DEFAULT, IntPtr.Zero))
            {
                //stream = Bass.BASS_StreamCreateFile("test.mp3", 0, 0, BASSFlag.BASS_DEFAULT);
                stream = Bass.BASS_StreamCreateURL(_url, 0, BASSFlag.BASS_DEFAULT, null, IntPtr.Zero);
                if (stream != 0)
                {
                    Bass.BASS_ChannelPlay(stream, false);
                    vs = new Visuals();
                }
                else
                {
                    throw new Exception(string.Format("Stream error: {0}", Bass.BASS_ErrorGetCode()));
                }
            }
        }
Здесь как видно, Bass имеет два метода, из файла, или из ссылки, остальные аргумены трогать не стоит, аргумент _url определен как строка с адресом на интернет ресурс
Шаг 5. Спектрум
Тут выбор очень хороший, будем использовать всё что есть

public enum Waves
    {
        Line = 0,
        Bean = 1,
        Dot = 2,
        //Elipse = 3,
        LinePeak = 4,
        Wave = 5
    }
Теперь метод для получения того самого спектрума ** странное слово
public Image GetSpectrum(Waves waveType)
        {
            if (stream != 0)
            {
                if (vs != null)
                {
                    switch (waveType)
                    {
                        case Waves.Line:
                            return (Image)vs.CreateSpectrumLine(stream, _width, _height, _colorBase, _colorPeak, _colorBackground, _thickness, _distance, false, true, true);
                        case Waves.Bean:
                            return (Image)vs.CreateSpectrumBean(stream, _width, _height, _colorBase, _colorPeak, _colorBackground, _thickness, false, true, true);
                        case Waves.Dot:
                            return (Image)vs.CreateSpectrumDot(stream, _width, _height, _colorBase, _colorPeak, _colorBackground, _thickness, _distance, false, true, true);
                        //case Waves.Elipse:
                        //    return (Image)vs.CreateSpectrumEllipse(stream, _width, _height, _colorBase, _colorPeak, _colorBackground, _thickness, _distance, false, true, true);
                        case Waves.LinePeak:
                            return (Image)vs.CreateSpectrumLinePeak(stream, _width, _height, _colorBase, Color.White, _colorBase, _colorBackground, _thickness, _thickness, _distance, 5, false, true, true);
                        case Waves.Wave:
                            return (Image)vs.CreateSpectrumWave(stream, _width, _height, _colorBase, _colorPeak, _colorBackground, _thickness, false, true, true);
                        default:
                            return (Image)vs.CreateSpectrumLine(stream, _width, _height, _colorBase, _colorPeak, _colorBackground, _thickness, _distance, false, true, true);
                    }
                }
            }
            return null;
        }
Откуда все эти vs? _widht? _colorBase? это просто поля которые мы настраиваем под наше приложение, width height означают высоту ширину картинки, дада, метод возвращает картинку, которые мы будем ежесекундно перезавать в наш picturebox, и понадобится таймер, ну и что бы приложение было красивым я использовал metroui в синем-белов цвете, и что бы спектрум соответсвтовал цветовой гамме, мы и передаем _colorBase и прочее, ниже полный список
private Un4seen.Bass.Misc.Visuals vs;
        private int stream;

        private string _url;
        public string URL { get { return _url; } set { _url = value; } }

        private Timer timer1;

        private Color _colorBase = Color.Black;
        public Color ColorBase { get { return _colorBase; } set { _colorBase = value; } }
        private Color _colorPeak = Color.Gray;
        public Color ColorPeak { get { return _colorPeak; } set { _colorPeak = value; } }
        private Color _colorBackground = Color.White;
        public Color ColorBackground { get { return _colorBackground; } set { _colorBackground = value; } }

        private int _width = 200;
        public int Width { get { return _width; } set { _width = value; } }
        private int _height = 50;
        public int Height { get { return _height; } set { _height = value; } }

        private int _thickness = 1;
        public int Thickness { get { return _thickness; } set { _thickness = value; } }
        private int _distance = 3;
        public int Distance { get { return _distance; } set { _distance = value; } }
vs - объект передающий спектрумы, ColorBase - основной цвет ColorPeak - цвет при пике этой пригающей полоски ColorBackground - тут все понятно timer1 - получаем картинки height, width - размеры спектрума thickness размер полоски спектрума distance - дистанция между полосками спектрума Как видите настрайвать тут много чего можно, ну можно и дефолтные использовать
Шаг .. какойта там
Обернем весь код, что бы было проще обращаться, и в отдельную библиотеку

using Un4seen.Bass;
using Un4seen.Bass.Misc;
namespace EuropaPlus
{
    public enum Waves
    {
        Line = 0,
        Bean = 1,
        Dot = 2,
        //Elipse = 3,
        LinePeak = 4,
        Wave = 5
       
    }
   
    public class Engine : IDisposable
    {
        private Un4seen.Bass.Misc.Visuals vs;
        private int stream;

        private string _url;
        public string URL { get { return _url; } set { _url = value; } }

        private Timer timer1;

        private Color _colorBase = Color.Black;
        public Color ColorBase { get { return _colorBase; } set { _colorBase = value; } }
        private Color _colorPeak = Color.Gray;
        public Color ColorPeak { get { return _colorPeak; } set { _colorPeak = value; } }
        private Color _colorBackground = Color.White;
        public Color ColorBackground { get { return _colorBackground; } set { _colorBackground = value; } }

        private int _width = 200;
        public int Width { get { return _width; } set { _width = value; } }
        private int _height = 50;
        public int Height { get { return _height; } set { _height = value; } }

        private int _thickness = 1;
        public int Thickness { get { return _thickness; } set { _thickness = value; } }
        private int _distance = 3;
        public int Distance { get { return _distance; } set { _distance = value; } }


        public Engine(string Url, Color Base, Color Peak, Color Background, int width, int height, int thickness, int distance)
        {
            this._url = Url;
            this._colorBase = Base;
            this._colorPeak = Peak;
            this._colorBackground = Background;
            this._width = width;
            this._height = height;
            this._thickness = thickness;
            this._distance = distance;
        }
        public Engine(string Url, Color Base, Color Peak, Color Background, int width, int height)
        {
            this._url = Url;
            this._colorBase = Base;
            this._colorPeak = Peak;
            this._colorBackground = Background;
            this._width = width;
            this._height = height;
        }
        public Engine(string Url, int width, int height)
        {
            this._url = Url;
            this._width = width;
            this._height = height;
        }
        public Engine(string Url)
        {
            this._url = Url;
        }
        /// 
        /// Custom spectrum wave from 'url'
        /// 
        /// Image
        public void Play()
        {
            if (Bass.BASS_Init(-1, 44100, BASSInit.BASS_DEVICE_DEFAULT, IntPtr.Zero))
            {
                //stream = Bass.BASS_StreamCreateFile("test.mp3", 0, 0, BASSFlag.BASS_DEFAULT);
                stream = Bass.BASS_StreamCreateURL(_url, 0, BASSFlag.BASS_DEFAULT, null, IntPtr.Zero);
                if (stream != 0)
                {
                    Bass.BASS_ChannelPlay(stream, false);
                    vs = new Visuals();
                }
                else
                {
                    throw new Exception(string.Format("Stream error: {0}", Bass.BASS_ErrorGetCode()));
                }
            }
        }
        public void FlushSpectrum()
        {
            vs = new Visuals();
        }
        public Image GetSpectrum(Waves waveType)
        {
            if (stream != 0)
            {
                if (vs != null)
                {
                    switch (waveType)
                    {
                        case Waves.Line:
                            return (Image)vs.CreateSpectrumLine(stream, _width, _height, _colorBase, _colorPeak, _colorBackground, _thickness, _distance, false, true, true);
                        case Waves.Bean:
                            return (Image)vs.CreateSpectrumBean(stream, _width, _height, _colorBase, _colorPeak, _colorBackground, _thickness, false, true, true);
                        case Waves.Dot:
                            return (Image)vs.CreateSpectrumDot(stream, _width, _height, _colorBase, _colorPeak, _colorBackground, _thickness, _distance, false, true, true);
                        //case Waves.Elipse:
                        //    return (Image)vs.CreateSpectrumEllipse(stream, _width, _height, _colorBase, _colorPeak, _colorBackground, _thickness, _distance, false, true, true);
                        case Waves.LinePeak:
                            return (Image)vs.CreateSpectrumLinePeak(stream, _width, _height, _colorBase, Color.White, _colorBase, _colorBackground, _thickness, _thickness, _distance, 5, false, true, true);
                        case Waves.Wave:
                            return (Image)vs.CreateSpectrumWave(stream, _width, _height, _colorBase, _colorPeak, _colorBackground, _thickness, false, true, true);
                        default:
                            return (Image)vs.CreateSpectrumLine(stream, _width, _height, _colorBase, _colorPeak, _colorBackground, _thickness, _distance, false, true, true);
                    }
                   
                }
            }
            return null;
        }
        public void Dispose()
        {
            Bass.BASS_Free();
            this._url = null;
            this.stream = 0;
            vs = null;
        }
    }
}

Настало время использовать все это добро
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows.Forms;
using Un4seen.Bass;
using Un4seen.Bass.Misc;
using MetroFramework;

namespace EuropaPlus
{
    public partial class Form1 : MetroFramework.Forms.MetroForm
    {
        public Form1()
        {
            InitializeComponent();
            //this.Size = new Size(190, 173);
            this.metroButton1.Size = new Size(150, 80);
            this.metroButton1.Location = new Point(20, 60);
            //this.pictureBox1.Size = new Size(150, 80);
            //this.pictureBox1.Location = new Point(20, 60);
            this.metroButton1.Text = "Play";
            this.metroToggle1.Visible = false;
            this.metroToggle2.Visible = false;
            wvv = Waves.Line;
            rnd = new Random();
        }
        private bool animDone = false;
        private bool animDone2 = false;

        private Random rnd;
        private Waves wvv;
        private Engine eng;
        private const string music = "http://ep256.streamr.ru";
        private const string music2 = "http://ep128.streamr.ru";
        private MetroFramework.Animation.MoveAnimation mvAnimBut;
        //private MetroFramework.Animation.ExpandAnimation exAnimForm;
        private MetroFramework.Animation.ExpandAnimation exAnimBut;

        private void StartGrabbing()
        {
            eng.FlushSpectrum();
            timer1.Stop();
            timer1.Interval = 100;
            timer1.Start();
        }
        private void timer1_Tick(object sender, EventArgs e)
        {
            GetImages();
        }
        private void GetImages()
        {
            this.pictureBox1.Image = eng.GetSpectrum(wvv);
        }
        private void metroButton1_Click(object sender, EventArgs e)
        {
            if (animDone2 == true)
            {

                eng.Dispose();
                if (metroToggle2.CheckState == CheckState.Checked)
                    eng = new Engine(music, this.pictureBox1.Width, this.pictureBox1.Height);
                else
                    eng = new Engine(music2, this.pictureBox1.Width, this.pictureBox1.Height);
                eng.Thickness = 1;
                eng.Distance = 0;
                eng.ColorBase = Color.DeepSkyBlue;
                if (metroStyleManager1.Theme == MetroThemeStyle.Light) eng.ColorBackground = Color.White;
                else eng.ColorBackground = Color.FromArgb(19, 19, 19);
                eng.ColorPeak = Color.DeepSkyBlue;
                eng.Play();
                StartGrabbing();

            }
            else
            {
                try
                {
                    if (metroToggle2.CheckState == CheckState.Checked)
                        eng = new Engine(music, this.pictureBox1.Width, this.pictureBox1.Height);
                    else
                        eng = new Engine(music2, this.pictureBox1.Width, this.pictureBox1.Height);
                    eng.Thickness = 1;
                    eng.Distance = 0;
                    eng.ColorBase = Color.DeepSkyBlue;
                    if (metroStyleManager1.Theme == MetroThemeStyle.Light) eng.ColorBackground = Color.White;
                    else eng.ColorBackground = Color.FromArgb(19, 19, 19);
                    eng.ColorPeak = Color.DeepSkyBlue;
                    eng.Play();
                    StartGrabbing();

                    DoAnim();
                    animDone2 = true;
                }
                catch (Exception ee)
                {
                    MessageBox.Show(ee.Message);
                    return;
                }
            }
            

        }
        private void DoAnim()
        {
            if (animDone == false)
            {
                mvAnimBut = new MetroFramework.Animation.MoveAnimation();
                exAnimBut = new MetroFramework.Animation.ExpandAnimation();
                //exAnimForm = new MetroFramework.Animation.ExpandAnimation();

                exAnimBut.Start(metroButton1, new Size(48, 17), MetroFramework.Animation.TransitionType.EaseOutExpo, 50);
                //exAnimForm.Start(this, new Size(600, 200), MetroFramework.Animation.TransitionType.EaseOutExpo, 50);
                mvAnimBut.Start(this.metroButton1, new Point(74, 128), MetroFramework.Animation.TransitionType.EaseOutCubic, 50);
                this.metroToggle1.Visible = true;
                this.metroToggle2.Visible = true;
                animDone = true;
                this.metroButton1.Text = "Playing";
            }
        }

        private void Form1_Load(object sender, EventArgs e)
        {

        }

        private void metroToggle1_CheckedChanged(object sender, EventArgs e)
        {
            if (this.metroStyleManager1.Theme == MetroThemeStyle.Light)
            {
                this.metroStyleManager1.Theme = MetroThemeStyle.Dark;
                eng.ColorBackground = Color.FromArgb(19,19,19);
            }
            else
            {
                this.metroStyleManager1.Theme = MetroThemeStyle.Light;
                eng.ColorBackground = Color.White;
            }
        }

        private void metroToggle2_CheckedChanged(object sender, EventArgs e)
        {
            if (metroToggle2.CheckState == CheckState.Checked)
            {
                eng.Dispose();
                eng = new Engine(music, this.pictureBox1.Width, this.pictureBox1.Height);
                eng.Thickness = 1;
                eng.Distance = 0;
                eng.ColorBase = Color.DeepSkyBlue;
                if (metroStyleManager1.Theme == MetroThemeStyle.Light) eng.ColorBackground = Color.White;
                else eng.ColorBackground = Color.FromArgb(19, 19, 19);
                eng.ColorPeak = Color.DeepSkyBlue;
                eng.Play(); StartGrabbing();
            }
            else
            {
                eng.Dispose();
                eng = new Engine(music2, this.pictureBox1.Width, this.pictureBox1.Height);
                eng.Thickness = 1;
                eng.Distance = 0;
                eng.ColorBase = Color.DeepSkyBlue;
                if (metroStyleManager1.Theme == MetroThemeStyle.Light) eng.ColorBackground = Color.White;
                else eng.ColorBackground = Color.FromArgb(19, 19, 19);
                eng.ColorPeak = Color.DeepSkyBlue;
                eng.Play(); StartGrabbing();
            }
        }
        //http://ep128.streamr.ru
        private void pictureBox1_Click(object sender, EventArgs e)
        {
            wvv = (Waves)rnd.Next(5);
            StartGrabbing();
        }
    }
}
Теперь по порядку, приложение запускается, появляется милинькое окошко
при нажатии Play приложение вызывает
private void metroButton1_Click(object sender, EventArgs e)
        {
            if (animDone2 == true)
            {

                eng.Dispose();
                if (metroToggle2.CheckState == CheckState.Checked)
                    eng = new Engine(music, this.pictureBox1.Width, this.pictureBox1.Height);
                else
                    eng = new Engine(music2, this.pictureBox1.Width, this.pictureBox1.Height);
                eng.Thickness = 1;
                eng.Distance = 0;
                eng.ColorBase = Color.DeepSkyBlue;
                if (metroStyleManager1.Theme == MetroThemeStyle.Light) eng.ColorBackground = Color.White;
                else eng.ColorBackground = Color.FromArgb(19, 19, 19);
                eng.ColorPeak = Color.DeepSkyBlue;
                eng.Play();
                StartGrabbing();

            }
            else
            {
                try
                {
                    if (metroToggle2.CheckState == CheckState.Checked)
                        eng = new Engine(music, this.pictureBox1.Width, this.pictureBox1.Height);
                    else
                        eng = new Engine(music2, this.pictureBox1.Width, this.pictureBox1.Height);
                    eng.Thickness = 1;
                    eng.Distance = 0;
                    eng.ColorBase = Color.DeepSkyBlue;
                    if (metroStyleManager1.Theme == MetroThemeStyle.Light) eng.ColorBackground = Color.White;
                    else eng.ColorBackground = Color.FromArgb(19, 19, 19);
                    eng.ColorPeak = Color.DeepSkyBlue;
                    eng.Play();
                    StartGrabbing();

                    DoAnim();
                    animDone2 = true;
                }
                catch (Exception ee)
                {
                    MessageBox.Show(ee.Message);
                    return;
                }
            }
        }
в котором описано поведение, то есть кнопка уменьшается, движется в середину, и изменяется текст, и в соответсви с выбранной темой, все преобразуется в некую гамму, ну чесно говоря в коде все итак понятно, думаю никчему больше слов, )) Исходник на Github