Naudio Read throws InvalidCast Exception when reading mp4 but not always. Why?

0

I am making a windows forms that previews waveforms. I use AudioFileReader.Read in the following:
A...When I first load the file and read through to save peak files for zoom preview
B...When I calculate FFT for the whole audio
C...When I draw the waveform

The wav/mp3 works fine, but when I load mp4, the Read in A and B works fine but C throws System.InvalidCastException.
Why does it happen only in C?

The exception and the codes are below. Thank you.

Exception:

例外がスローされました: '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

Code: (Edited; simplified for easy copy paste)

public partial class Form1 : Form
    {
        List<AudioFileReader> streams = new List<AudioFileReader>();
        int ch;
        long fullsamples;
        WaveFormat waveformat;
        long fullms;
        int block = 128;
        int fftLength = 128;

        public Form1()
        {
            InitializeComponent();

            streams.Add(new AudioFileReader(/*Media file path*/));
            ch = streams[0].WaveFormat.Channels;
            fullsamples = (int)(streams[0].Length / streams[0].BlockAlign);
            waveformat = streams[0].WaveFormat;
            fullms = (int)(fullsamples * 1000 / waveformat.SampleRate);

            backgroundWorker1.RunWorkerAsync();
        }

        //A
        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            BackgroundWorker control = (BackgroundWorker)sender;

            float[] write = new float[ch * 2];

            int block = this.block * 2;
            float[] sample = new float[block * ch];

            for (int x = 0; x < 3; x++)
            {
                var writer = new WaveFileWriter("tempshrink" + x + ".wav", streams[x].WaveFormat);
                streams[x].Position = 0;

                int read = streams[x].Read(sample, 0, block * ch);
                while (read != 0)
                {
                    for (int b = 0; b < block; b++)
                    {
                        for (int c = 0; c < ch; c++)
                        {
                            int sampleindex = b * ch + c;
                            if (b == 0)
                            {
                                write[c] = sample[sampleindex];
                                write[c + ch] = sample[sampleindex];
                            }
                            else
                            {
                                write[c] = Math.Max(write[c], sample[sampleindex]);
                                write[c + ch] = Math.Min(write[c + ch], sample[sampleindex]);
                            }
                        }
                    }
                    //書き込み
                    for (int i = 0; i < write.Length; i++) writer.WriteSample(write[i]);

                    read = streams[x].Read(sample, 0, block * ch);
                }
                writer.Dispose();
                streams.Add(new AudioFileReader("tempshrink" + x + ".wav"));
            }
        }

        private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            backgroundWorker2.RunWorkerAsync();
        }

        //B
        private void backgroundWorker2_DoWork(object sender, DoWorkEventArgs e)
        {
            BackgroundWorker control = (BackgroundWorker)sender;

            AudioFileReader reader = streams[0];
            reader.Position = 0;

            int yoko = (int)(fullsamples / fftLength);
            int tate = fftLength / 2;
            int stride = (yoko % 4 == 0) ? yoko : (yoko / 4 + 1) * 4;
            byte[] result = new byte[stride * tate];
            byte[][] spec = new byte[yoko][];
            int m = (int)Math.Log(fftLength, 2);
            int bundle = 10;
            Complex[] buffer = new Complex[fftLength];
            int count = 0;

            int readoncelength = fftLength * ch * bundle;
            float[] sample = new float[readoncelength];

            while (reader.Read(sample, 0, readoncelength) != 0)
            {
                for (int i = 0; i < bundle; i++)
                {
                    int timex = count * bundle + i;
                    if (timex >= yoko) break;
                    for (int r = 0; r < fftLength; r++)
                    {
                        int index = (i * fftLength + r) * ch;
                        if (ch == 2) sample[index] = (sample[index] + sample[index + 1]) / 2;

                        buffer[r].X = sample[index] * (float)FastFourierTransform.HammingWindow(r, fftLength);
                        buffer[r].Y = 0.0f;
                    }

                    FastFourierTransform.FFT(true, m, buffer);

                    spec[timex] = new byte[tate];

                    for (int k = 0; k < tate; k++)
                    {
                        double diagonal = Math.Sqrt(buffer[k].X * buffer[k].X + buffer[k].Y * buffer[k].Y);
                        double intensityDB = 10.0 * Math.Log10(diagonal);

                        const double minDB = -50.0;
                        double percent = (intensityDB < minDB) ? 1.0 : intensityDB / minDB;

                        result[timex + (tate - 1 - k) * stride] = (byte)(255f * percent / 16);
                        spec[timex][k] = (byte)(255f * percent);
                    }
                }

                count++;
            }

            e.Result = result;
        }

        private void backgroundWorker2_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            timer1.Enabled = true;
        }

        //
        private void timer1_Tick(object sender, EventArgs e)
        {
            float[] samples = new float[2];

            //Throws Exception Here
            while (streams[0].Read(samples, 0, 2)!=0)
            {

            }
        }
    }
c#
naudio
asked on Stack Overflow Jan 24, 2021 by HIBINO • edited Jan 26, 2021 by HIBINO

1 Answer

0

Most likely because you are accessing it from different threads. The underlying APIs that NAudio is calling require access from the same thread a COM object was created on.

answered on Stack Overflow Jan 25, 2021 by Mark Heath

User contributions licensed under CC BY-SA 3.0