使用NAudio c将wma音频流转换为mp3流

时间:2013-10-17 08:15:52

标签: c#-4.0 naudio lame wma

我的任务是使用NAudio和Lame将wma音频流转换为mp3流。下面的代码与文件名工作正常,但我希望它与内存流完成。我在NAudio搜索没有读取wma音频流的方法。 NAudio有可能吗?

    public static byte[] ConvertWmaToMp3(uint bitrate = 128)
    {

        FileStream fs = new FileStream("..\\sample.wma", FileMode.Open, FileAccess.Read);            
        var ws = new NAudio.WindowsMediaFormat.WMAFileReader(fs.Name);                                    

        // Setup encoder configuration
        WaveLib.WaveFormat fmt = new WaveLib.WaveFormat(ws.WaveFormat.SampleRate, 16, ws.WaveFormat.Channels);
        Yeti.Lame.BE_CONFIG beconf = new Yeti.Lame.BE_CONFIG(fmt, bitrate);

        // Encode WAV to MP3
        int blen = ws.WaveFormat.AverageBytesPerSecond;
        byte[] buffer = new byte[blen];
        byte[] mp3data = null;

        using (MemoryStream mp3strm = new MemoryStream())
        using (Mp3Writer mp3wri = new Mp3Writer(mp3strm, fmt, beconf))
        {
            int rc;
            while ((rc = ws.Read(buffer, 0, blen)) > 0)
            {
                mp3wri.Write(buffer, 0, rc);
            }

            mp3data = mp3strm.ToArray();
        }

        return mp3data;                      

    }

2 个答案:

答案 0 :(得分:0)

目前WMAFileReader类不支持从流中读取数据。 WMA API支持从IStream读取WMA,因此绝对可以。

如果您想自己实现流式传输,则需要从CodePlex获取WmaFileReaderWmaStream的源代码,并将其用作修改后的类的模板。

您首先需要的是一个包装类,它为.NET IStream提供COM Stream接口。这是一个简单的:

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
}

接下来将WmaStream代码复制到项目中的新命名空间,并将以下代码添加到类的顶部:

    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;
        }
    }

WmaFileReader和以下代码执行相同操作:

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

现在,您可以使用文件名或WmaFileReader实例 - StreamMemoryStream等创建已修改FileStream的实例。Stream实例需要具有可读性和可搜索性。

我已经在我的计算机上找到的一些随机WMA文件上尝试了上述操作,加载到MemoryStream或使用FileStream,并且它按预期工作。

据推测,Mark正在努力将此功能添加到NAudio.Wma包中,因此在NAudio支持之前,请考虑这是一个临时修复。

答案 1 :(得分:0)

使用NAudio将WMA转换为MP3的最简单方法是使用基于Media Foundation的类。如果您使用的是Windows 8及以上版本,则应提供MP3编码器。

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

在没有MP3编码器的系统上,我倾向于使用LAME.exe并通过stdin传输输入音频。有关此方法的详细信息,请参阅my article here