56205


現状、Naudioライブラリを使用してネットワークドライブに保存されたWavファイルを再生しようとしていました。  
しかし、再生中にネットワークが切断された場合、UIスレッドが停止します。  
おそらく原因は、Naudio内部でファイル読込が完了しないため発生していると思われます。  
そこで、StreamのReadTimeoutが設定できないかと試しましたが無理でした。  
以上により、ストリーミング(逐次読込)のWavファイル再生ができ、かつ上記のファイル読込異常が発生した場合はTimeOut値が設定できる、C#用のライブラリを探しております。ご存知でしょうか。

追記  
ネットワーク切断によって、Naudio内部で使用されるFilestreamのRead()で応答が返らない(System.IO.IOException
)ため、UIスレッドがストールされていると思っています。  
ReadtimeOutを実施することで、UIスレッドのストールを回避しタイムアウト時には何らかの処理をして再生自体を中止しようと考えていました。  
タイムアウトは1秒程度を想定しています。


CC BY-SA 4.0
2019-06-28T07:42:30.923
56179
2019-06-30T12:42:46.647
2019-06-28T09:40:57.090
32228
32228
question
c# audio-streaming
ストリーミング(逐次読込)のWavファイル再生のC#用のライブラリはありませんか


今のところ無いのでは？ C#では [NAudio](https://github.com/naudio/NAudio) と
[CSCore](https://github.com/filoe/cscore) が主なようですが、両方ともそうした機能は無さそうですし。

[Popular C# audio Projects -
Libraries.io](https://libraries.io/search?keywords=audio&languages=C%23) や
[Free Audio / Sound Libraries and Source
Code](https://www.thefreecountry.com/sourcecode/audio.shtml)
とかで探すか、それらに掲載されているライブラリのソースを元に自分で機能を追加する等を考えてみてはどうでしょう？

ライブラリのソースに手を入れるなら、例えば [FileStream Class](https://docs.microsoft.com/ja-
jp/dotnet/api/system.io.filestream?view=netframework-4.8) の [Stream.CanTimeout
Property](https://docs.microsoft.com/ja-
jp/dotnet/api/system.io.stream.cantimeout?view=netframework-4.8#System_IO_Stream_CanTimeout),
[Stream.ReadTimeout Property](https://docs.microsoft.com/ja-
jp/dotnet/api/system.io.stream.readtimeout?view=netframework-4.8) を使って
Timeout検出可能か判断して処理したり、使われている同期型の [FileStream.Read
Method](https://docs.microsoft.com/ja-
jp/dotnet/api/system.io.filestream.read?view=netframework-4.8) を非同期型の
[FileStream.ReadAsync Method](https://docs.microsoft.com/ja-
jp/dotnet/api/system.io.filestream.readasync?view=netframework-4.8)
に変更する等が出来るでしょう。

* * *

ちょうどこの2019年春に開発・保守終了となってしまいましたが、DirectXやMedia
FoundationのAPIを.NETで使えるようにしたSharpDXというWrapperライブラリを見つけました。  
終了したプロジェクトですが、ソースやサンプルアプリは残っているので、これを試してみる手も考えられます。

[sharpdx/SharpDX](https://github.com/sharpdx/SharpDX)  
[Class Library API | SharpDX](http://sharpdx.org/wiki/class-library-api/)  
[sharpdx/SharpDX-Samples](https://github.com/sharpdx/SharpDX-Samples)  
[SharpDXを使ってDirectXで音声ファイルを再生するには？](https://www.atmarkit.co.jp/ait/articles/1407/17/news113.html)


CC BY-SA 4.0
2019-06-28T23:45:53.007
56205
2019-06-30T12:42:46.647
2019-06-30T12:42:46.647
26370
26370
56179
answer


> タイムアウトは1秒程度を想定しています。

ネットワーク処理において１秒は短すぎて切れまくってしまいます。逆にUI処理において１秒の停止は遅過ぎで利用者はストレスが溜まることでしょう。結局、UIスレッドでネットワーク処理・オーディオ処理を行うこと自体が非現実的です。ですので質問者さんの希望を満たすようなライブラリは（kunifさんも回答されているように）無いのかもしれません。

* * *

一般論としては、ネットワーク処理においてはノンブロッキングI/Oを使います。ノンブロッキングI/Oであれば、バッファから受信済みのデータのみを扱うため処理がブロックされることはありません。オーディオ再生に追いつく程度のネットワーク速度があれば途切れることなく再生できることでしょう。これはあくまでネットワークダウンロード部分に限定した話であり、ダウンロード後、音声ファイルのデコード等の処理を考慮すればやはりUIスレッドで処理することは非現実的という結論が変わることはありません。

* * *

あくまでUIスレッドでの処理にこだわる質問者さんには関係ありませんが、参考情報として。

実はWindows Vista以降には[Media Foundation](https://docs.microsoft.com/en-
us/windows/desktop/medfound/microsoft-media-foundation-
sdk)といって、メディアプレーヤーを構成する各種機能がAPIとして提供されています。具体的には

  * 各種ネットワーク処理（HTTPやRTSP、それらのバッファリングやネットワーク再接続など）
  * 各種コーデックによるデコード
  * VideoやAudioの出力、もしくは各種コーデックでの再エンコード
  * これらをスムーズに処理するためのスレッドプール

などの機能を備えています。C/C++言語向けのAPIしか用意されていませんが、Interopを頑張って書けばC#言語からも呼び出せます。自由度が高い分、APIがそこそこ複雑なためInteropコードを書くのには挫けるため、C++言語での呼び出しサンプルを載せておきます。

[code]

    #include <chrono>
    #include <string>
    #include <thread>
    #include <assert.h>
    #include <wrl/client.h>
    #include <mfapi.h>
    #include <mfidl.h>
    void check(HRESULT hr) { if (FAILED(hr)) throw hr; }
    #include "get.h"    // https://github.com/sayurin/get
    #pragma comment(lib, "mf.lib")
    #pragma comment(lib, "mfplat.lib")
    using namespace std::literals;
    
    struct MF {
        MF() { check(MFStartup(MF_VERSION, MFSTARTUP_FULL)); }
        ~MF() { check(MFShutdown()); }
    };
    
    auto CreateSession(std::wstring const& url) {
        auto mediaSession = sayuri::get(MFCreateMediaSession, nullptr);
        auto topology = sayuri::get(MFCreateTopology);
    
        // create source node.
        auto sourceNode = sayuri::get(MFCreateTopologyNode, MF_TOPOLOGY_SOURCESTREAM_NODE);
        auto sourceResolver = sayuri::get(MFCreateSourceResolver);
        MF_OBJECT_TYPE objectType;
        auto object = GET(sourceResolver, CreateObjectFromURL, url.c_str(), MF_RESOLUTION_MEDIASOURCE, nullptr, &objectType);
        assert(objectType == MF_OBJECT_MEDIASOURCE);
        Microsoft::WRL::ComPtr<IMFMediaSource> mediaSource;
        check(object.As(&mediaSource));
        auto presentationDescriptor = GET(mediaSource, CreatePresentationDescriptor);
        auto count = GET(presentationDescriptor, GetStreamDescriptorCount);
        assert(count == 1);
        BOOL selected;
        auto streamDescriptor = GET(presentationDescriptor, GetStreamDescriptorByIndex, 0, &selected);
        check(sourceNode->SetUnknown(MF_TOPONODE_SOURCE, mediaSource.Get()));
        check(sourceNode->SetUnknown(MF_TOPONODE_PRESENTATION_DESCRIPTOR, presentationDescriptor.Get()));
        check(sourceNode->SetUnknown(MF_TOPONODE_STREAM_DESCRIPTOR, streamDescriptor.Get()));
        check(topology->AddNode(sourceNode.Get()));
    
        // create output node.
        auto activate = sayuri::get(MFCreateAudioRendererActivate);
        auto outputNode = sayuri::get(MFCreateTopologyNode, MF_TOPOLOGY_OUTPUT_NODE);
        check(outputNode->SetObject(activate.Get()));
        check(outputNode->SetUINT32(MF_TOPONODE_NOSHUTDOWN_ON_REMOVE, TRUE));
        check(topology->AddNode(outputNode.Get()));
    
        check(sourceNode->ConnectOutput(0, outputNode.Get(), 0));
        check(mediaSession->SetTopology(0, topology.Get()));
        return std::move(mediaSession);
    }
    
    int wmain() {
        MF mf;
    
        auto session = CreateSession(L"http://www.example.co.jp/sample.mp3"s);
    
        auto startPosition = sayuri::get(PropVariantInit);
        check(session->Start(&GUID_NULL, &startPosition));
        PropVariantClear(&startPosition);
    
        // すべての処理は別スレッドで動作しているため、呼び出しスレッドは停止可能
        std::this_thread::sleep_for(20s);
    
        check(session->Stop());
    }
    
[/code]


CC BY-SA 4.0
2019-06-29T04:37:28.187
56211
2019-06-29T04:37:28.187
4236
56179
answer