"Must be already floating point" 将音频文件转换为 wav 文件时

"Must be already floating point" when converting an audio file into wav file

这里我有一个代码可以将音频文件转换为 wav 格式以获得更好的质量并减小文件大小。这里我使用的是 naudio 文件压缩源代码,当我尝试转换该文件时出现异常。

Must be already floating point

public string ConvertToWAV(string tempFilePath, string tempFileName, string audioType)
{
    //Try to transform the file, if it fails use the original file            
    FileInfo fileInfo = new FileInfo(tempFilePath + tempFileName);
    byte[] fileData = new byte[fileInfo.Length];
    fileData = File.ReadAllBytes(tempFilePath + tempFileName);
    ISampleProvider sampleProvider;
    try
    {
        if (audioType.ToLower().Contains("wav"))
        {
            try
            {
                using (MemoryStream wav = new MemoryStream(fileData))
                {
                    WaveStream stream = new WaveFileReader(wav);

                    WaveFormat target = new WaveFormat();

                    var s = new RawSourceWaveStream(new MemoryStream(), new WaveFormat(8000, 16, 1));
                    var c = new WaveFormatConversionStream(WaveFormat.CreateALawFormat(8000, 1), s);

                    sampleProvider = new WaveToSampleProvider(c);

                    WaveFileWriter.CreateWaveFile16(tempFilePath + tempFileName, sampleProvider);

                    wav.Close();
                }
            }
            catch (Exception ex)
            {
                //We couldn't convert the file, continue with the original file.                        
            }
        }
    }
    catch (Exception ex)
    {
        throw ex;
    }
    return Convert.ToBase64String(fileData);
}

总体而言,代码和概念存在一些问题。

首先,您忽略了输入文件的 WaveFormat。我猜你假设它是 8K、16 位、1 个通道,基于你创建的行 var s,但这不是保证。

其次,您不需要 MemoryStreamRawSourceWaveStreamWaveFileReaderWaveStream,适用于任何 "next-stage" NAudio 波形处理器。

第三(这很可能是您的例外):NAudio Wave 处理器和转换器不喜欢 WaveFormat 中的 A-Law(或 u-Law)。 A-Law(和 u-Law)在技术上不是 PCM 数据。因此,它们不是 NAudio 喜欢使用的 "wave" 数据。

好了,综上所述,这里有一些建议。 NAudio.Codecs 命名空间中有非常特殊的 A-Law 和 u-Law 编码器。奇怪的是,它们被命名为 ALawEncoderMuLawEncoder。这些东西流兼容,所以我们想让它们兼容。

我在此处的末尾添加了一个 class 来做到这一点:创建一个 IWaveProvider 实际上吐出流 A-Law 或 u-Law。这是使用新 class 的测试代码。测试代码执行以下操作:

  1. 使用 MediaFoundationReader 读取输入文件(我喜欢这个)
  2. 使用 MediaFoundationResampler 将任何输入格式转换为 16 位 PCM(同时保持通道数)。请注意,这意味着您的输入文件不必具有与 A-law 输出相同的格式,因此它几乎可以转换任何内容。
  3. 将新的 16 位 PCM 流提供给自定义 "ALaw-to-IWaveProvider" 转换器 class。
  4. IWaveProvider 兼容的 A-Law 输出写入波形文件。

我在这里使用 MediaFoundation classes 是因为它们似乎不像基于 ACM 的波形格式那么特别。

    static void ConversionTest( string _outfilename, string _infilename )
    {
        try
        {
            using( var reader = new MediaFoundationReader(_infilename) )
            {
                // Create a wave format for 16-bit pcm at 8000 samples per second.
                int channels = reader.WaveFormat.Channels;
                int rate = 8000;
                int rawsize = 2;
                int blockalign = rawsize * channels; // this is the size of one sample.
                int bytespersecond = rate * blockalign;
                var midformat =
                    WaveFormat.CreateCustomFormat( WaveFormatEncoding.Pcm,
                                                   rate,
                                                   channels,
                                                   bytespersecond,
                                                   blockalign,
                                                   rawsize * 8 );

                // And a conversion stream to turn input into 16-bit PCM.
                var midstream = new MediaFoundationResampler(reader, midformat);
                //var midstream = new WaveFormatConversionStream(midformat, reader);

                // The output stream is our custom stream.
                var outstream = new PcmToALawConversionStream(midstream);


                WaveFileWriter.CreateWaveFile(_outfilename, outstream);
            }
        }
        catch( Exception _ex )
        {
        }
    }

这里是将 16 位 PCM 转换为 A-Law 或 u-Law 的 class。最后是 A-Law 或 u-Law 的特化:

    /// <summary>
    /// Encodes 16-bit PCM input into A- or u-Law, presenting the output
    /// as an IWaveProvider.
    /// </summary>
    public class PcmToG711ConversionStream : IWaveProvider
    {
        /// <summary>Gets the local a-law or u-law format.</summary>
        public WaveFormat WaveFormat { get { return waveFormat; } }

        /// <summary>Returns <paramref name="count"/> encoded bytes.</summary>
        /// <remarks>
        /// Note that <paramref name="count"/> is raw bytes.  It doesn't consider
        /// channel counts, etc.
        /// </remarks>
        /// <param name="buffer">The output buffer.</param>
        /// <param name="offset">The starting position in the output buffer.</param>
        /// <param name="count">The number of bytes to read.</param>
        /// <returns>The total number of bytes encoded into <paramref name="buffer"/>.</returns>
        public int Read(byte[] buffer, int offset, int count)
        {
            // We'll need a source buffer, twice the size of 'count'.
            int shortcount = count*2;
            byte [] rawsource = new byte [shortcount];
            int sourcecount = Provider.Read(rawsource, 0, shortcount);
            int bytecount = sourcecount / 2;
            for( int index = 0; index < bytecount; ++index )
            {
                short source = BitConverter.ToInt16(rawsource, index*2);
                buffer[offset+index] = Encode(source);
            }
            return bytecount;
        }


        /// <summary>
        /// Initializes and A-Law or u-Law "WaveStream".  The source stream
        /// must be 16-bit PCM!
        /// </summary>
        /// <param name="_encoding">ALaw or MuLaw only.</param>
        /// <param name="_sourcestream">The input PCM stream.</param>
        public PcmToG711ConversionStream( WaveFormatEncoding _encoding,
                                          IWaveProvider _provider )
        {
            Provider = _provider;
            WaveFormat sourceformat = Provider.WaveFormat;
            if( (sourceformat.Encoding != WaveFormatEncoding.Pcm) &&
                (sourceformat.BitsPerSample != 16) )
            {
                throw new NotSupportedException("Input must be 16-bit PCM.  Try using a conversion stream.");
            }

            if( _encoding == WaveFormatEncoding.ALaw )
            {
                Encode = this.EncodeALaw;
                waveFormat = WaveFormat.CreateALawFormat( _provider.WaveFormat.SampleRate,
                                                          _provider.WaveFormat.Channels) ;

            }
            else if( _encoding == WaveFormatEncoding.MuLaw )
            {
                Encode = this.EncodeMuLaw;
                waveFormat = WaveFormat.CreateMuLawFormat( _provider.WaveFormat.SampleRate,
                                                           _provider.WaveFormat.Channels) ;
            }
            else
            {
                throw new NotSupportedException("Encoding must be A-Law or u-Law");
            }
        }


        /// <summary>The a-law or u-law encoder delegate.</summary>
        EncodeHandler Encode;
        /// <summary>a-law or u-law wave format.</summary>
        WaveFormat waveFormat;
        /// <summary>The input stream.</summary>
        IWaveProvider Provider;

        /// <summary>A-Law or u-Law encoder delegate.</summary>
        /// <param name="_sample">The 16-bit PCM sample to encode.</param>
        /// <returns>The encoded value.</returns>
        delegate byte EncodeHandler( short _sample );

        byte EncodeALaw( short _sample )
        {
            return ALawEncoder.LinearToALawSample(_sample);
        }
        byte EncodeMuLaw( short _sample )
        {
            return MuLawEncoder.LinearToMuLawSample(_sample);
        }
    }


    public class PcmToALawConversionStream : PcmToG711ConversionStream
    {
        public PcmToALawConversionStream( IWaveProvider _provider )
          : base(WaveFormatEncoding.ALaw, _provider)
        {
        }
    }

    public class PcmToMuLawConversionStream : PcmToG711ConversionStream
    {
        public PcmToMuLawConversionStream( IWaveProvider _provider )
          : base(WaveFormatEncoding.MuLaw, _provider)
        {
        }
    }
}

最后我找到了这个问题的解决方案,即需要添加一种名为 Media Foundation 的附加功能,以便在 Windows Server 2012 中更好地工作。

使用服务器管理器中的添加角色和功能向导。跳至功能和 select 媒体基础