wma audio stream to mp3 stream using NAudio c#

后端 未结 2 432
礼貌的吻别
礼貌的吻别 2021-01-27 09:50

My task is to convert wma audio stream to mp3 stream using NAudio and Lame. The below code is working fine with file name but I want it to be done with memory stream. I search i

相关标签:
2条回答
  • 2021-01-27 10:40

    Currently the WMAFileReader class doesn't support reading data from a stream. The WMA APIs support reading WMA from an IStream so it is definitely possible.

    If you want to implement streaming yourself you'll need to grab the source code for WmaFileReader and WmaStream from CodePlex, and use them as templates for your modified classes.

    First thing you'll need is a wrapper class that provides a COM IStream interface to a .NET Stream. Here's a simple one:

    public class InteropStream : IStream, IDisposable
    {
        public readonly Stream intern;
    
        public InteropStream(Stream strm)
        {
            intern = strm;
        }
    
        ~InteropStream()
        {
            Dispose(true);
        }
    
        public void Dispose()
        {
            Dispose(false);
        }
    
        protected void Dispose(bool final)
        {
            if (final)
                intern.Dispose();
        }
    
        #region IStream Members
        public void Clone(out IStream ppstm)
        {
            ppstm = null;
        }
    
        public void Commit(int grfCommitFlags)
        {
            intern.Flush();
        }
    
        readonly byte[] buffer = new byte[4096];
    
        public void CopyTo(IStream pstm, long cb, IntPtr pcbRead, IntPtr pcbWritten)
        {
            if (pcbRead != IntPtr.Zero)
                Marshal.WriteInt32(pcbRead, 0);
            if (pcbWritten != IntPtr.Zero)
                Marshal.WriteInt32(pcbWritten, 0);
        }
    
        public void LockRegion(long libOffset, long cb, int dwLockType)
        { }
    
        public void Read(byte[] pv, int cb, IntPtr pcbRead)
        {
            int rc = intern.Read(pv, 0, cb);
            if (pcbRead != IntPtr.Zero)
                Marshal.WriteInt32(pcbRead, rc);
        }
    
        public void Revert()
        { }
    
        public void Seek(long dlibMove, int dwOrigin, IntPtr plibNewPosition)
        {
            long origin = 0;
            if (dwOrigin == 1) // STREAM_SEEK_CUR
                origin = intern.Position;
            else if (dwOrigin == 2) // STREAM_SEEK_END
                origin = intern.Length;
    
            long pos = origin + dlibMove;
            intern.Position = pos;
    
            if (plibNewPosition != IntPtr.Zero)
                Marshal.WriteInt64(plibNewPosition, pos);
        }
    
        public void SetSize(long libNewSize)
        { }
    
        public void Stat(out System.Runtime.InteropServices.ComTypes.STATSTG pstatstg, int grfStatFlag)
        {
            var res = new System.Runtime.InteropServices.ComTypes.STATSTG();
    
            res.type = 2; // STGTY_STREAM
            res.cbSize = intern.Length;
    
            pstatstg = res;
        }
    
        public void UnlockRegion(long libOffset, long cb, int dwLockType)
        { }
    
        public void Write(byte[] pv, int cb, IntPtr pcbWritten)
        { }
        #endregion
    }
    

    Next copy the WmaStream code to a new namespace in your project and add the following code to the top of the class:

        InteropStream interopStrm = null;
    
        public WmaStream(Stream fileStream)
            : this(fileStream, null)
        { }
    
        public WmaStream(Stream fileStream, WaveFormat OutputFormat)
        {
            interopStrm = new InteropStream(fileStream);
            m_reader = WM.CreateSyncReader(WMT_RIGHTS.WMT_RIGHT_NO_DRM);
            try
            {
                IWMSyncReader2 rdr = m_reader as IWMSyncReader2;
                rdr.OpenStream(interopStrm);
                Init(OutputFormat);
            }
            catch
            {
                try
                {
                    m_reader.Close();
                }
                catch
                {
                }
                m_reader = null;
                throw;
            }
        }
    

    Do the same with WmaFileReader and the following code:

        public WMAFileReader(Stream wmaStream)
        {
            m_wmaStream = new WmaStream2(wmaStream);
            m_waveFormat = m_wmaStream.Format;
        }
    

    Now you can create an instance of your modified WmaFileReader using either a filename or a Stream instance - MemoryStream, FileStream, etc. The Stream instance needs to be readable and seekable.

    I've tried the above on a few random WMA files I found on my computer, loaded into MemoryStream or using FileStream, and it works as I expected it to.

    Presumably Mark is working on adding this functionality to the NAudio.Wma package, so consider this an interim fix until NAudio supports it.

    0 讨论(0)
  • 2021-01-27 10:53

    The easiest way to convert WMA to MP3 with NAudio is to use the Media Foundation based classes. If you are on Windows 8 and above, and MP3 encoder should be available.

    using (var reader = new MediaFoundationReader("test.wma"))
    {
        MediaFoundationEncoder.EncodeToMp3(reader, "test.mp3");
    }
    

    On systems without an MP3 encoder, I tend to use LAME.exe and stream the input audio through stdin. See my article here for more info on this approach.

    0 讨论(0)
提交回复
热议问题