WAV相关:从PCM16 Little Endian数据转WAV文件

Posted passedbylove

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了WAV相关:从PCM16 Little Endian数据转WAV文件相关的知识,希望对你有一定的参考价值。

数据格式

[0.0, -0.0, -0.0, 0.0, 0.0, 0.0, 5.960464477539063e-08, 5.960464477539063e-08, 1.1920928955078125e-07, 1.7881393432617188e-07, 2.384185791015625e-07,.....]

转换方法:c#

using System;
using System.IO;
using System.Text;

namespace deserialfromPCMData
{
 public static class BinaryWriterExtensions
{
   private const int HeaderSize = 44;

   private const int Hz = 16000; //frequency or sampling rate

   private const float RescaleFactor = 32767; //to convert float to Int16

   public static void AppendWaveData<T>(this T stream, float[] buffer)
      where T : Stream
   {
      if (stream.Length > HeaderSize)
      {
         stream.Seek(0, SeekOrigin.End);
      }
      else
      {
         stream.SetLength(HeaderSize);
         stream.Position = HeaderSize;
      }

      // rescale
      var floats = Array.ConvertAll(buffer, x => (short)(x * RescaleFactor));

      // Copy to bytes
      var result = new byte[floats.Length * sizeof(short)];
      Buffer.BlockCopy(floats, 0, result, 0, result.Length);

      // write to stream
      stream.Write(result, 0, result.Length);

      // Update Header
      UpdateHeader(stream);
   }

   public static void UpdateHeader(Stream stream)
   {
      var writer = new BinaryWriter(stream);

      writer.Seek(0, SeekOrigin.Begin);

      writer.Write(Encoding.ASCII.GetBytes("RIFF")); //RIFF marker. Marks the file as a riff file. Characters are each 1 byte long. 
      writer.Write((int)(writer.BaseStream.Length - 8)); //file-size (equals file-size - 8). Size of the overall file - 8 bytes, in bytes (32-bit integer). Typically, you‘d fill this in after creation.
      writer.Write(Encoding.ASCII.GetBytes("WAVE")); //File Type Header. For our purposes, it always equals "WAVE".
      writer.Write(Encoding.ASCII.GetBytes("fmt ")); //Mark the format section. Format chunk marker. Includes trailing null. 
      writer.Write(16); //Length of format data.  Always 16. 
      writer.Write((short)1); //Type of format (1 is PCM, other number means compression) . 2 byte integer. Wave type PCM
      writer.Write((short)1); //Number of Channels - 2 byte integer
      writer.Write(Hz); //Sample Rate - 32 byte integer. Sample Rate = Number of Samples per second, or Hertz.
      writer.Write(Hz * 2 * 1); // sampleRate * bytesPerSample * number of channels, here 16000*2*1.
      writer.Write((short)(1 * 2)); //channels * bytesPerSample, here 1 * 2  // Bytes Per Sample: 1=8 bit Mono,  2 = 8 bit Stereo or 16 bit Mono, 4 = 16 bit Stereo
      writer.Write((short)16); //Bits per sample (BitsPerSample * Channels) ?? should be 8???
      writer.Write(Encoding.ASCII.GetBytes("data")); //"data" chunk header. Marks the beginning of the data section.    
      writer.Write((int)(writer.BaseStream.Length - HeaderSize)); //Size of the data section. data-size (equals file-size - 44). or NumSamples * NumChannels * bytesPerSample ??        
   }
} //end of class
}
using System;
using System.IO;
using System.Text;

namespace deserialfromPCMData
{
    class SaveAudiostreamToWav
    {
        static void Main(string[] args)
        {
         
            Stream instream = File.OpenRead(@"g:sample.txt");
           
            BufferedStream bfs = new BufferedStream(instream);
            byte[] array = new byte[bfs.Length];
            instream.Read(array, 0, array.Length);

            string str = Encoding.Default.GetString(array);

            var StreamSample = str.Substring(1, str.Length - 2).Split(,);


            var floatsArray = new float[StreamSample.Length];
            floatsArray = Array.ConvertAll(StreamSample, x => (float)Convert.ToDouble(x));



            using (var stream = new FileStream(@"g:sample2.wav", FileMode.Create, FileAccess.ReadWrite))
            {
               stream.AppendWaveData(floatsArray);
            }
        }       
    }
}

 

 

Java方法:

 

package com;


import com.sun.media.sound.WaveFileWriter;
import org.junit.Test;

import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import java.io.*;
import java.util.Arrays;

//https://stackoverflow.com/questions/3599378/java-read-wav-file-as-a-float-array
//
//https://stackoverflow.com/questions/26824663/how-do-i-use-audio-sample-data-from-java-sound
//
//https://stackoverflow.com/questions/4440015/java-pcm-to-wav
public class TestWavFile {

    //采样率16kHz
    private float SAMPLING_RATE = 16000;
    private float sampleSizeBits = 16;


    //把short(2字节)拆解成字节流byte[2]
    public byte[] get16BitPcm(short[] data) {

        byte[] resultData = new byte[2 * data.length];
        int iter = 0;
        for (short sample : data) {
            resultData[iter++] = (byte)(sample & 0x00ff);
            resultData[iter++] = (byte)((sample & 0xff00) >>> 8);
        }
        return resultData;
    }

    @Test
    public void test() throws IOException{

        BufferedReader bufferedReader = new BufferedReader(
                new InputStreamReader(
                        new FileInputStream("g:/sample.txt")));

        StringBuffer buffer = new StringBuffer();
        String line = null;

        while ( (line = bufferedReader.readLine()) != null)
        {
            buffer.append(line);
        }

        int len = buffer.length();

        String raw = buffer.substring(1,len-1);

        Short[] data = Arrays.stream(raw.split(","))
                .map(track->( Float.valueOf(track) * 0x7fff ))
                .map(item->item.shortValue()).toArray(Short[]::new);

        short [] frameData = new short[data.length];

        for (int i = 0;  i< data.length; i++) {
            frameData[i] = data[i];
        }

        WaveFileWriter writer = new WaveFileWriter();

        FileOutputStream outStream = new FileOutputStream("g:/sample.wav");
        //(采样率,比特位,通道,是否有符号,大小端)
        //比特位:short 2个字节  2*8 = 16
        //是否有符号:是否有负数
        //
        AudioFormat format = new AudioFormat(SAMPLING_RATE,16,1,true,false);

        AudioInputStream audioInputStream = new AudioInputStream(new ByteArrayInputStream(get16BitPcm(frameData)), format, frameData.length);

        writer.write(audioInputStream, AudioFileFormat.Type.WAVE, outStream);

    }
}

 

运行以上两个文件,最终会成功wav文件。

样本数据

以上是关于WAV相关:从PCM16 Little Endian数据转WAV文件的主要内容,如果未能解决你的问题,请参考以下文章

正确读取 .wav 文件中的样本

Microsoft 认知服务 SST 支持哪些音频格式?为啥 16 位 PCM x.wav 成功而 32 位 PCM y.wav 不成功?

写入 PCM 数据时的文件 (.wav) 持续时间 @16KBps

将 PCM 16 位 LE 转换为 WAV

在 HTC One 上以 16khz 单声道 PCM (WAV) 录制时出现断断续续的音频

从 PCM 转换为 WAV。是不是可以?