9
15

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Last updated at Posted at 2016-11-13

はじめに

本稿は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使っているのがあれですがなんとかストリーミング再生できるようになりました。

参考にしたサイト

9
15
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
15

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?