0

我的任务是使用 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;                      

    }
4

2 回答 2

0

目前WMAFileReader该类不支持从流中读取数据。WMA API 支持从一个读取 WMA,IStream所以这绝对是可能的。

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

您需要的第一件事是为IStream.NET 提供 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使用文件名或Stream实例 - MemoryStreamFileStream等创建修改的实例。该Stream实例需要可读且可查找。

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

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

于 2013-10-19T07:15:02.323 回答
0

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

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

在没有 MP3 编码器的系统上,我倾向于使用 LAME.exe 并通过标准输入流式传输输入音频。有关此方法的更多信息,请参阅我的文章。

于 2015-07-06T05:55:45.393 回答