数据格式
[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); } }
java封装工具:
import com.fasterxml.jackson.databind.ObjectMapper; import com.sun.media.sound.WaveFileWriter; import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.ArrayUtils; import javax.sound.sampled.AudioFileFormat; import javax.sound.sampled.AudioFormat; import javax.sound.sampled.AudioInputStream; import java.io.ByteArrayInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.util.Arrays; @Slf4j public final class WavFileGenerator { //采样率16kHz private float SAMPLING_RATE = 16000; private float sampleSizeBits = 16; private int HeaderSize = 44; //frequency or sampling rate private int Hz = 16_000; private byte[] values; public WavFileGenerator(String ctx) { this(convert(ctx)); } public WavFileGenerator(String []values) { convert(toPrimitive(Arrays.asList(values).stream().map(Float::valueOf).toArray(size -> new Float[size]))); } public WavFileGenerator(short[] values) { /*sizeof(short) was 2 bytes in java platform*/ byte[] result = new byte[values.length * 2]; // 1short = 2bytes int iter = 0; for (short sample : values) { result[iter++] = (byte)(sample & 0x00ff); result[iter++] = (byte)((sample & 0xff00) >>> 8); } this.values = result; } public WavFileGenerator(float[] floatValues) { this(convert(floatValues)); } static float[] toPrimitive(Float[] floats) { return ArrayUtils.toPrimitive(floats); } public static final short[] convert(float[] values) { //PCM 16bit little endian short[] shortValues = new short[values.length]; for (int i = 0; i < values.length; i++) { float value = values[i]; shortValues[i] = (short) (value < 0 ? value* 0x8000 : value*0x7fff); } return shortValues; } public static final float[] convert(String ctx) { ObjectMapper objectMapper = new ObjectMapper(); try { Float[] data = objectMapper.readValue(ctx, Float[].class); return toPrimitive(data); } catch (IOException e) { log.error("转换出错:{}",e); } return new float[0]; } public byte[] getFmtChunk() { return null; } public byte[] getDataChunk() { return values; } void builder() { } public ByteBuffer getAudioStream() { return null; } public void saveFile(String dest) throws IOException { WaveFileWriter writer = new WaveFileWriter(); FileOutputStream outStream = new FileOutputStream(dest); AudioFormat format = new AudioFormat(SAMPLING_RATE,16,1,true,false); int frames = values.length / 2; AudioInputStream audioInputStream = new AudioInputStream(new ByteArrayInputStream(values), format, frames); writer.write(audioInputStream, AudioFileFormat.Type.WAVE, outStream); audioInputStream.close(); outStream.close(); } }
运行以上两个文件,最终会成功wav文件。