WPFでストリーミング再生がしたい！！

## はじめに
本稿はWPF製デスクトップアプリからネットワーク上のMP3ファイルをストリーミング再生する方法を記載した記事になります。
WPFといえばMVVMモデルですので規模が小さいアプリですがMVVMモデルに従います。
C#でオーディオ再生といえばNAudioが有名みたいですね。本稿でもNAudioを使っています。
コードはGithubにおいているのでよかったら落としてみてください。
https://github.com/smbkrysk14/WpfStreaming

## 再生するMP3ファイル
再生するMP3ファイルはみんな大好きRebuid.fmのMP3ファイルです。

## View
Viewです。シンプルにMP3ファイルまでのURLをテキストボックスに入力してそれを「再生」「停止」「一時停止」できるようにそれぞれボタンを置いただけです。

```xml:MainWindow.xaml
<Window x:Class="WpfStreaming.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfStreaming"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <TextBox x:Name="textBox" HorizontalAlignment="Left" Height="25" Margin="10,10,0,0" TextWrapping="Wrap" Text="{Binding EnclosureUrl}" VerticalAlignment="Top" Width="500"/>
        <Button x:Name="Play" Content="再生" HorizontalAlignment="Left" Margin="10,40,0,0" VerticalAlignment="Top" Width="75" Command="{Binding PlayCommand}"/>
        <Button x:Name="Stop" Content="停止" HorizontalAlignment="Left" Margin="90,40,0,0" VerticalAlignment="Top" Width="75" Command="{Binding StopCommand}"/>
        <Button x:Name="Pause" Content="一時停止" HorizontalAlignment="Left" Margin="170,40,0,0" VerticalAlignment="Top" Width="75" Command="{Binding PauseCommand}"/>
    </Grid>
</Window>

```

## ViewModel
これも至ってシンプル。
テキストボックスに入力されたURLをもとに再生させる処理と停止、一時停止させる処理のみです。
http://cache.rebuild.fm/podcast-ep162a.mp3
が今回使うMP3ファイルまでのURLですね。
毎回テキストボックスに入力するのが面倒だったのでコンストラクタで設定しています。

```csharp:MainWindowViewModel.cs

public class MainWindowViewModel:ViewModelBase
    {

        AudioPlayer audio = new AudioPlayer();

        public MainWindowViewModel()
        {
            this.EnclosureUrl = "http://cache.rebuild.fm/podcast-ep162a.mp3";
        }

        
        private async void Play()
        {
            string enclosureUrl = this.EnclosureUrl;

            await Task.Run(() =>
            {
                audio.Play(enclosureUrl);
            });
        }

        private void Stop()
        {
            audio.Stop();
        }

        private void Pause()
        {
            audio.Pause();
        }


        private string _enclosureUrl;
        public string EnclosureUrl
        {
            get
            {
                return this._enclosureUrl;
            }
            set
            {
                this._enclosureUrl = value;
                base.RaisePropertyChanged("EnclosureUrl");
            }
        }

     // 中略
```

## Model
ありません。

## 再生用クラス
このクラスが肝です。
ダウンロードと再生を並列で処理させるためダウンロード部分は別スレッドで処理させます。
停止したときMemoryStreamのPositionに0を設定し先頭に戻すようにしています。
これを忘れると停止ではなくて一時停止の動きになります。

```csharp:AudioPlayer.cs
sealed class AudioPlayer
    {
        private IWavePlayer _waveOut;
        private Stream ms = new MemoryStream();

        /// <summary>
        /// 再生を開始します。
        /// </summary>
        public void Play(string url)
        {
            if (_waveOut == null)
            {
                new Thread(delegate (object o)
                {
                    var response = WebRequest.Create(url).GetResponse();
                    using (var stream = response.GetResponseStream())
                    {

                        byte[] buffer = new byte[65536]; // 64KB chunks

                        int read;

                        while ((read = stream.Read(buffer, 0, buffer.Length)) > 0)
                        {
                            var pos = ms.Position;
                            ms.Position = ms.Length;
                            ms.Write(buffer, 0, read);
                            ms.Position = pos;
                        }

                    }
                }).Start();
            }


            // Pre-buffering some data to allow NAudio to start playing
            while (ms.Length < 65536 * 10)
                Thread.Sleep(1000);

            
            using (Mp3FileReader reader = new Mp3FileReader(ms))
            using (WaveStream pcm = WaveFormatConversionStream.CreatePcmStream(reader))
            using (WaveStream blockAlignedStream = new BlockAlignReductionStream(pcm))
            {
                using (_waveOut = new WaveOut(WaveCallbackInfo.FunctionCallback()))
                {
                    _waveOut.Init(blockAlignedStream);

                    _waveOut.Play();
                    while (_waveOut != null && _waveOut.PlaybackState == PlaybackState.Playing)
                    {
                        System.Threading.Thread.Sleep(100);
                    }
                }
            }
        }

        /// <summary>
        /// 再生を一時停止します。
        /// </summary>
        public void Pause()
        {
            this._waveOut.Pause();
        }

        /// <summary>
        /// 再生を停止します。
        /// </summary>
        public void Stop()
        {
            this._waveOut.Stop();
            this.ms.Position = 0;
        }
    }

```

## 最後に
Thread使っているのがあれですがなんとかストリーミング再生できるようになりました。


### 参考にしたサイト
http://stackoverflow.com/questions/184683/play-audio-from-a-stream-using-c-sharp