Help us understand the problem. What is going on with this article?

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

More than 3 years have passed since last update.

はじめに

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

再生するMP3ファイル

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

View

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

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ですね。
毎回テキストボックスに入力するのが面倒だったのでコンストラクタで設定しています。

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を設定し先頭に戻すようにしています。
これを忘れると停止ではなくて一時停止の動きになります。

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

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away