🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Q&A

解決済

1回答

2515閲覧

NaudioのMediaFoundationReader.Readで起こる例外について

hibin0bin0

総合スコア2

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

0グッド

0クリップ

投稿2021/01/24 05:06

編集2021/01/26 11:33

前提・実現したいこと

※スレッドに関して追記しました。

自作ツールでNaudioを使って音声データを扱っています。
メインスレッドで音声を読み込んでから、
A・音声データの読み込み直後(別スレッド)
B・Aの後にFFTの計算時(別スレッド)
C・波形データの描画(メインスレッド)
主にこの3つのタイミングでAudioFileReader.Readを行っています。
wav/mp3などの音声データでは問題ないのですが
mp4のような動画データから音声を読む際に、
A、BのReadは問題ないのにCのReadのタイミングで例外が発生してしまいます。
おそらくMediaFoundationReaderの扱いにどこか問題があるのではと思いますが
何なのか見当がつかないので、ヒントでもありましたら幸いです。

発生している問題・エラーメッセージ

例外がスローされました: 'System.InvalidCastException' (NAudio.dll の中) System.InvalidCastException: 型 'System.__ComObject' の COM オブジェクトをインターフェイス型 'NAudio.MediaFoundation.IMFSourceReader' にキャストできません。IID '{70AE66F2-C809-4E4F-8915-BDCB406B7993}' が指定されたインターフェイスの COM コンポーネント上での QueryInterface 呼び出しのときに次のエラーが発生したため、この操作に失敗しました: インターフェイスがサポートされていません (HRESULT からの例外:0x80004002 (E_NOINTERFACE))。 場所 System.StubHelpers.StubHelpers.GetCOMIPFromRCW(Object objSrc, IntPtr pCPCMD, IntPtr& ppTarget, Boolean& pfNeedsRelease) 場所 NAudio.MediaFoundation.IMFSourceReader.SetCurrentPosition(Guid guidTimeFormat, IntPtr varPosition) 場所 NAudio.Wave.MediaFoundationReader.Reposition(Int64 desiredPosition) 場所 NAudio.Wave.MediaFoundationReader.Read(Byte[] buffer, Int32 offset, Int32 count) 場所 NAudio.Wave.SampleProviders.Pcm16BitToSampleProvider.Read(Single[] buffer, Int32 offset, Int32 count) 場所 NAudio.Wave.SampleProviders.MeteringSampleProvider.Read(Single[] buffer, Int32 offset, Int32 count) 場所 NAudio.Wave.SampleProviders.VolumeSampleProvider.Read(Single[] buffer, Int32 offset, Int32 sampleCount) 場所 NAudio.Wave.AudioFileReader.Read(Single[] buffer, Int32 offset, Int32 count) 場所 SengiriWave.SoundStream.GetNextPeak(Int32 id, Int64 samplesperpeak, Boolean withrms) 場所 D:\VisualStudio\SengiriWave\SoundStream.cs:行 303 場所 SengiriWave.DrawWave.PaintPeaksLines(SoundStream sound, Graphics graphics, Int32 skippx, Boolean withRMS) 場所 D:\VisualStudio\SengiriWave\DrawWave.cs:行 105 場所 SengiriWave.MainWindow.wave_Paint(Object sender, PaintEventArgs e) 場所 D:\VisualStudio\SengiriWave\MainWindow - wave.cs:行 194

該当のソースコード

※追記 Winforms Form1クラスにコピペできる簡易版のスクリプトはこちらです:

C#

1 public partial class Form1 : Form 2 { 3 List<AudioFileReader> streams = new List<AudioFileReader>(); 4 int ch; 5 long fullsamples; 6 WaveFormat waveformat; 7 long fullms; 8 int block = 128; 9 int fftLength = 128; 10 11 public Form1() 12 { 13 InitializeComponent(); 14 15 streams.Add(new AudioFileReader(/*任意のメディアファイル*/)); 16 ch = streams[0].WaveFormat.Channels; 17 fullsamples = (int)(streams[0].Length / streams[0].BlockAlign); 18 waveformat = streams[0].WaveFormat; 19 fullms = (int)(fullsamples * 1000 / waveformat.SampleRate); 20 21 backgroundWorker1.RunWorkerAsync(); 22 } 23 24 //Aの処理 25 private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) 26 { 27 BackgroundWorker control = (BackgroundWorker)sender; 28 29 float[] write = new float[ch * 2]; 30 31 int block = this.block * 2; 32 float[] sample = new float[block * ch]; 33 34 for (int x = 0; x < 3; x++) 35 { 36 var writer = new WaveFileWriter("tempshrink" + x + ".wav", streams[x].WaveFormat); 37 streams[x].Position = 0; 38 39 int read = streams[x].Read(sample, 0, block * ch); 40 while (read != 0) 41 { 42 for (int b = 0; b < block; b++) 43 { 44 for (int c = 0; c < ch; c++) 45 { 46 int sampleindex = b * ch + c; 47 if (b == 0) 48 { 49 write[c] = sample[sampleindex]; 50 write[c + ch] = sample[sampleindex]; 51 } 52 else 53 { 54 write[c] = Math.Max(write[c], sample[sampleindex]); 55 write[c + ch] = Math.Min(write[c + ch], sample[sampleindex]); 56 } 57 } 58 } 59 //書き込み 60 for (int i = 0; i < write.Length; i++) writer.WriteSample(write[i]); 61 62 read = streams[x].Read(sample, 0, block * ch); 63 } 64 writer.Dispose(); 65 streams.Add(new AudioFileReader("tempshrink" + x + ".wav")); 66 } 67 } 68 69 private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 70 { 71 backgroundWorker2.RunWorkerAsync(); 72 } 73 74 //Bの処理 75 private void backgroundWorker2_DoWork(object sender, DoWorkEventArgs e) 76 { 77 BackgroundWorker control = (BackgroundWorker)sender; 78 79 AudioFileReader reader = streams[0]; 80 reader.Position = 0; 81 82 int yoko = (int)(fullsamples / fftLength); 83 int tate = fftLength / 2; 84 int stride = (yoko % 4 == 0) ? yoko : (yoko / 4 + 1) * 4; 85 byte[] result = new byte[stride * tate]; 86 byte[][] spec = new byte[yoko][]; 87 int m = (int)Math.Log(fftLength, 2); 88 int bundle = 10; 89 Complex[] buffer = new Complex[fftLength]; 90 int count = 0; 91 92 int readoncelength = fftLength * ch * bundle; 93 float[] sample = new float[readoncelength]; 94 95 while (reader.Read(sample, 0, readoncelength) != 0) 96 { 97 for (int i = 0; i < bundle; i++) 98 { 99 int timex = count * bundle + i; 100 if (timex >= yoko) break; 101 for (int r = 0; r < fftLength; r++) 102 { 103 int index = (i * fftLength + r) * ch; 104 if (ch == 2) sample[index] = (sample[index] + sample[index + 1]) / 2; 105 106 buffer[r].X = sample[index] * (float)FastFourierTransform.HammingWindow(r, fftLength); 107 buffer[r].Y = 0.0f; 108 } 109 110 FastFourierTransform.FFT(true, m, buffer); 111 112 spec[timex] = new byte[tate]; 113 114 for (int k = 0; k < tate; k++) 115 { 116 double diagonal = Math.Sqrt(buffer[k].X * buffer[k].X + buffer[k].Y * buffer[k].Y); 117 double intensityDB = 10.0 * Math.Log10(diagonal); 118 119 const double minDB = -50.0; 120 double percent = (intensityDB < minDB) ? 1.0 : intensityDB / minDB; 121 122 result[timex + (tate - 1 - k) * stride] = (byte)(255f * percent / 16); 123 spec[timex][k] = (byte)(255f * percent); 124 } 125 } 126 127 count++; 128 } 129 130 e.Result = result; 131 } 132 133 private void backgroundWorker2_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 134 { 135 timer1.Enabled = true; 136 } 137 138 //Tickで描画させているという体 139 private void timer1_Tick(object sender, EventArgs e) 140 { 141 float[] samples = new float[2]; 142 143 //ここで例外発生 144 while (streams[0].Read(samples, 0, 2)!=0) 145 { 146 147 } 148 } 149 }

補足情報(FW/ツールのバージョンなど)

Visual Studio 2019
Naudio

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2021/01/25 06:20 編集

コードが長すぎるので、不具合を再現可能な必要最小限のコードに絞ってみた方がよいかと思われます。コピペで動作可能なくらい短いと、回答者が試しに実行する事も可能なので理想的です。 ファイルの種類が変わったら読み込めたということは、mp4の読み込みが上手くいってないようですが、単純にファイルを読み込んでwavで書き出すだけのコードを書いてみて、オーディオフォーマットや長さが正常に取得出来ているか確認してみた方がよいのではないでしょうか。
hibin0bin0

2021/01/25 06:46

>ファイルの種類が変わったら読み込めたということは、mp4の読み込みが上手くいってないようですが ファイルをAudioFileReaderに読み込み後にいずれもReadしているA、B、Cを実行しており、AとBで例外が発生していないので読み込み自体はできているという認識ですがいかがでしょうか。WaveFormatのサンプルレート・チャンネル数も取得できております。 後ほど時間ができましたら同じ現象を再現できるもっとシンプルなコードがお見せできるか確認いたします、アドバイスありがとうございます。
退会済みユーザー

退会済みユーザー

2021/01/25 07:50 編集

ファイルを実際にオープンしている部分や、各処理がどのように呼び出されているかが提示されているコードからは不明なので何とも言えないのですが、A・BとCは別スレッドで動作させたりしていないでしょうか?
guest

回答1

0

ベストアンサー

確証はないですが、Media Foundationを内部で使用しているのであれば、Media Foundationの一部のコンポーネントはCOMで実装されているので、スレッドのアパートメントが影響しているかもしれません。Media Foundation and COM より抜粋すると、

Media Foundation components cannot be STA objects. Many Media Foundation objects do not need to be COM objects at all. But if they are, they cannot run in the STA.

との事です。COMは複雑で私も完全に理解している訳ではないのですが、COMを使用するスレッドは、STAかMTAいずれかのアパートメントに属する必要があり、STAスレッドに属するCOMオブジェクトを他のスレッドからは操作出来なかったと思います。
アパートメントモデルと、.NETのSTAThreadAttribute
COMのアパートメント (6) アパートメントの種類はどのように決まるのか
Thread.SetApartmentState(ApartmentState) メソッド

投稿2021/01/26 02:31

編集2021/01/26 02:57
退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hibin0bin0

2021/01/26 11:45

異なるスレッドからのアクセスについて考えるに至りませんでした。 いただいた情報についてかみ砕くにはもう少し時間はかかりそうですが、ヒントをいただき A→B→Cで実行していたものを順番を入れ替えて C→A→Bとしてみたところ 今度はAで例外が発生するようになったので 少なくとも今回の件に関しては最初にどのスレッドでデータをReadするかにポイントがありそうに見えました。 (本来ならこういうことを避けるために異なるスレッドで同じデータを参照するのは避けたほうがよさそう?なので自作ツールでは考慮して作り直してみることにします) スレッドは最近触れたものなので勉強になりました、解決の糸口をありがとうございます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問