前提・実現したいこと
※スレッドに関して追記しました。
自作ツールで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
コードが長すぎるので、不具合を再現可能な必要最小限のコードに絞ってみた方がよいかと思われます。コピペで動作可能なくらい短いと、回答者が試しに実行する事も可能なので理想的です。
ファイルの種類が変わったら読み込めたということは、mp4の読み込みが上手くいってないようですが、単純にファイルを読み込んでwavで書き出すだけのコードを書いてみて、オーディオフォーマットや長さが正常に取得出来ているか確認してみた方がよいのではないでしょうか。
>ファイルの種類が変わったら読み込めたということは、mp4の読み込みが上手くいってないようですが
ファイルをAudioFileReaderに読み込み後にいずれもReadしているA、B、Cを実行しており、AとBで例外が発生していないので読み込み自体はできているという認識ですがいかがでしょうか。WaveFormatのサンプルレート・チャンネル数も取得できております。
後ほど時間ができましたら同じ現象を再現できるもっとシンプルなコードがお見せできるか確認いたします、アドバイスありがとうございます。
ファイルを実際にオープンしている部分や、各処理がどのように呼び出されているかが提示されているコードからは不明なので何とも言えないのですが、A・BとCは別スレッドで動作させたりしていないでしょうか?
回答1件
あなたの回答
tips
プレビュー