精华内容
下载资源
问答
  • wav文件读取

    千次阅读 2019-01-29 16:00:28
    想要读取wav文件必须要知道wav头文件的信息,前人已有太多解析,此处不做详解。可以参考链接:https://blog.csdn.net/vacu_um/article/details/70195761。头文件中重点要关心的信息是文件大小,单通道还是双通道,...

    想要读取wav文件必须要知道wav头文件的信息,前人已有太多解析,此处不做详解。可以参考链接:https://blog.csdn.net/vacu_um/article/details/70195761。头文件中重点要关心的信息是文件大小,单通道还是双通道,以及数据长度。

    //wav头格式
    struct WavHead{
     char RIFF[4];    //头部分那个RIFF
     long int filesize;//存的是后面所有文件大小
     char WAVE[4];
     char FMT[4];
    
     long int size1;//存的是fmt保存的大小,包含这之后,data前面几个,共16个
     short int fmttag;//1
     short int channel;//1
     long int samplespersec;//每秒采样数
     long int bytepersec;
     short int blockalign;
     short int bitpersamples;
     char DATA[4];
     long int datasize;//剩下文件大小
    
    };

    文件读取的内容是和matlab读取出来的数组是一致的:

    #include <iostream>
    #include <fstream>
    #include <string.h>
    #include<math.h>
    #include<cmath>
    #include<stdlib.h>
    #include <bitset>
    #include <iomanip>
    
    using namespace std;
    
    struct WavHead{
     char RIFF[4];    //头部分那个RIFF
     long int filesize;//存的是后面所有文件大小
     char WAVE[4];
     char FMT[4];
    
     long int size1;//存的是fmt保存的大小,包含这之后,data前面几个,共16个
     short int fmttag;//1
     short int channel;//1
     long int samplespersec;//每秒采样数,用的是11025
     long int bytepersec;
     short int blockalign;
     short int bitpersamples;
     char DATA[4];
     long int datasize;//剩下文件大小,也就是声音采样是大小,因为是一秒钟的内容,那么就是11025了。
    
    };
    
    /*******************************
     *  cosmos
     *  参数
        parth:文件路径
        data_len:获取wav 数据长度
        wavhead:获取wav 头信息
     ******************************/
    double * readwav(char* path,long unsigned *data_len,WavHead *wavhead)
    {
        fstream fs;
        fs.open(path, ios::binary | ios::in);
        fs.seekg(0x00);
        fs.read((char *)wavhead, sizeof(WavHead));
        fs.seekg(0x2c);
        unsigned char *data=(unsigned char *)malloc(sizeof(unsigned char)*wavhead->datasize) ;
        fs.read((char *)data,sizeof(char)*wavhead->datasize);
        double* wavdata=( double *)malloc(wavhead->datasize*sizeof(double));
        for (unsigned long i =0; i<wavhead->datasize; i = i + 2)
        {
            //右边为大端
            unsigned long data_low = data[i];
            unsigned long data_high = data[i + 1];
            double data_true = data_high * 256 + data_low;
            long data_complement = 0;
            //取大端的最高位(符号位)
            int my_sign = (int)(data_high / 128);
            if (my_sign == 1)
            {
                data_complement = data_true - 65536;
            }
            else
            {
                data_complement = data_true;
            }
            setprecision(4);
    
            double float_data = (double)(data_complement/(double)32768);
            wavdata[i/2]=float_data;
        }
        fs.close();
        delete[] data;
    	*data_len=wavhead->datasize/2;
    	return wavdata;
    }
    //函数调用示例
     void main(void)
    {
        double * wavdata;
        long unsigned data_len;
        WavHead head;
        wavdata=readwav("C://tes.wav",&data_len,&head);
    }
    

     

    展开全文
  • C++标准库实现WAV文件读写

    千次阅读 2018-10-22 21:51:40
    C++标准库实现WAV文件读写 在上一篇文章RIFF和WAVE音频文件格式中对WAV的文件格式做了介绍,本文将使用标准C++库实现对数据为PCM格式的WAV文件读写操作,只使用标准C++库函数,不依赖于其他的库。 WAV文件结构 ...

    C++标准库实现WAV文件读写

    在上一篇文章RIFF和WAVE音频文件格式中对WAV的文件格式做了介绍,本文将使用标准C++库实现对数据为PCM格式的WAV文件的读写操作,只使用标准C++库函数,不依赖于其他的库。

    WAV文件结构

    WAV是符合RIFF标准的多媒体文件,其文件结构可以如下:

    WAV 文件结构
    RIFF块
    WAVE FOURCC
    fmt 块
    fact 块(可选)
    data块(包含PCM数据)

    首先是一个RIFF块,有块标识RIFF,指明该文件是符合RIFF标准的文件;接着是一个FourCC,WAVE,该文件为WAV文件;fmt块包含了音频的一些属性:采样率、码率、声道等;fact 块是一个可选块,不是PCM数据格式的需要该块;最后data块,则包含了音频的PCM数据。实际上,可以将一个WAV文件看着由两部分组成:文件头和PCM数据,则WAV文件头各字段的意义如下:

    本文实现的是一个能够读取PCM数据格式的单声道或者双声道的WAV文件,是没有fact块以及扩展块。

    结构体定义

    通过上面的介绍发现,WAV的头文件所包含的内容有两种:RIFF文件格式标准中需要的数据和关于音频格式的信息。对于RIFF文件格式所需的信息,声明结构体如下:

    // The basic chunk of RIFF file format
    struct Base_chunk{
    
        FOURCC fcc;    // FourCC id
        uint32_t cb_size; // 数据域的大小
    
        Base_chunk(FOURCC fourcc)
            : fcc(fourcc)
        {
            cb_size = 0;
        }
    };

     

    chunk是RIFF文件的基本单元,首先一个4字节的标识FOURCC,用来指出该块的类型;cb_size则是改块数据域中数据的大小。

    文件头中另一个信息则是音频的格式信息,实际上是frm chunk的数据域信息,其声明如下:

    // Format chunk data field
    struct Wave_format{
    
        uint16_t format_tag;      // WAVE的数据格式,PCM数据该值为1
        uint16_t channels;        // 声道数
        uint32_t sample_per_sec;  // 采样率
        uint32_t bytes_per_sec;   // 码率,channels * sample_per_sec * bits_per_sample / 8
        uint16_t block_align;     // 音频数据块,每次采样处理的数据大小,channels * bits_per_sample / 8
        uint16_t bits_per_sample; // 量化位数,8、16、32等
        uint16_t ex_size;         // 扩展块的大小,附加块的大小
    
        Wave_format()
        {
            format_tag      = 1; // PCM format data
            ex_size         = 0; // don't use extesion field
    
            channels        = 0;
            sample_per_sec  = 0;
            bytes_per_sec   = 0;
            block_align     = 0;
            bits_per_sample = 0;
        }
    
        Wave_format(uint16_t nb_channel, uint32_t sample_rate, uint16_t sample_bits)
            :channels(nb_channel), sample_per_sec(sample_rate), bits_per_sample(sample_bits)
        {
            format_tag    = 0x01;                                            // PCM format data
            bytes_per_sec = channels * sample_per_sec * bits_per_sample / 8; // 码率
            block_align   = channels * bits_per_sample / 8;
            ex_size       = 0;                                               // don't use extension field
        }
    };

    关于各个字段的信息,在上面图中有介绍,这里主要说明两个字段:

    • format_tag表示以何种数据格式存储音频的sample值,这里设置为0x01表示用PCM格式,非压缩格式,不需要fact块。
    • ex_size表示的是扩展块的大小。有两种方法来设置不使用扩展块,一种是设置fmt中的size字段为16(无ex_size字段);或者,有ex_size,设置其值为0.在本文中,使用第二种方法,设置ex_size的值为0,不使用扩展块。

    有了上面两个结构体的定义,对于WAV的文件头,可以表示如下:

     

    /*
    
        数据格式为PCM的WAV文件头
        --------------------------------
        | Base_chunk | RIFF |
        ---------------------
        | WAVE              |
        ---------------------
        | Base_chunk | fmt  |   Header
        ---------------------
        | Wave_format|      |
        ---------------------
        | Base_chunk | data |
        --------------------------------
    */
    struct Wave_header{
    
        shared_ptr<Base_chunk> riff;
        FOURCC wave_fcc;
        shared_ptr<Base_chunk> fmt;
        shared_ptr<Wave_format>  fmt_data;
        shared_ptr<Base_chunk> data;
    
        Wave_header(uint16_t nb_channel, uint32_t sample_rate, uint16_t sample_bits)
        {
            riff      = make_shared<Base_chunk>(MakeFOURCC<'R', 'I', 'F', 'F'>::value);
            fmt       = make_shared<Base_chunk>(MakeFOURCC<'f', 'm', 't', ' '>::value);
            fmt->cb_size = 18;
    
            fmt_data  = make_shared<Wave_format>(nb_channel, sample_rate, sample_bits);
            data      = make_shared<Base_chunk>(MakeFOURCC<'d', 'a', 't', 'a'>::value);
    
            wave_fcc = MakeFOURCC<'W', 'A', 'V', 'E'>::value;
        }
    
        Wave_header()
        {
            riff         = nullptr;
            fmt          = nullptr;
    
            fmt_data     = nullptr;
            data         = nullptr;
    
            wave_fcc     = 0;
        }
    };

    在WAV的文件头中有三种chunk,分别为:RIFF,fmt,data,然后是音频的格式信息Wave_format。在RIFF chunk的后面是一个4字节非FOURCC:WAVE,表示该文件为WAV文件。另外,Wave_format的构造函数只需要三个参数:声道数、采样率和量化精度,关于音频的其他信息都可以使用这三个数值计算得到。注意,这里设置fmt chunk的size为18。

    实现

    有了上面结构体后,再对WAV文件进行读写就比较简单了。由于RIFF文件中使用FOURCC老标识chunk的类型,这里有两个FOURCC的实现方法:使用宏和使用模板,具体如下:

    #define FOURCC uint32_t 
    
    #define MAKE_FOURCC(a,b,c,d) \
    ( ((uint32_t)d) | ( ((uint32_t)c) << 8 ) | ( ((uint32_t)b) << 16 ) | ( ((uint32_t)a) << 24 ) )
    template <char ch0, char ch1, char ch2, char ch3> struct MakeFOURCC{ enum { value = (ch0 << 0) + (ch1 << 8) + (ch2 << 16) + (ch3 << 24) }; };
    

    Write WAVE file

    写WAV文件过程,首先是填充文件头信息,对于Wave_format只需要三个参数:声道数、采样率和量化精度,将文件头信息写入后,紧接这写入PCM数据就完成了WAV文件的写入。其过程如下:

    Wave_header header(1, 48000, 16);
    
        uint32_t length = header.fmt_data->sample_per_sec * 10 * header.fmt_data->bits_per_sample / 8;
        uint8_t *data = new uint8_t[length];
    
        memset(data, 0x80, length);
    
        CWaveFile::write("e:\\test1.wav", header, data, length);

    首先够着WAV文件头,然后写入文件即可。将数据写入的实现也比较简单,按照WAv的文件结构,依次将数据写入文件。在设置各个chunk的size值时要注意其不同的意义:

    • RIFF chunk 的size表示的是其数据的大小,其包含各个chunk的大小以及PCM数据的长度。该值 + 8 就是整个WAV文件的大小。
    • fmt chunk 的size是Wave_format的大小,这里为18
    • data chunk 的size 是写入的PCM数据的长度

    Read WAVE file

    知道了WAV的文件结构后,读取其数据就更为简单了。有一种直接的方法,按照PCM相对于文件起始的位置的偏移位置,直接读取PCM数据;或者是按照其文件结构依次读取信息,本文的将依次读取WAV文件的信息填充到相应的结构体中,其实现代码片段如下:

    header = make_unique<Wave_header>();
    
        // Read RIFF chunk
        FOURCC fourcc;
        ifs.read((char*)&fourcc, sizeof(FOURCC));
    
        if (fourcc != MakeFOURCC<'R', 'I', 'F', 'F'>::value) // 判断是不是RIFF
            return false;
        Base_chunk riff_chunk(fourcc);
        ifs.read((char*)&riff_chunk.cb_size, sizeof(uint32_t));
    
        header->riff = make_shared<Base_chunk>(riff_chunk);
    
        // Read WAVE FOURCC
        ifs.read((char*)&fourcc, sizeof(FOURCC));
        if (fourcc != MakeFOURCC<'W', 'A', 'V', 'E'>::value)
            return false;
        header->wave_fcc = fourcc;
        ...

     

    实例

    调用本文的实现,写入一个单声道,16位量化精度,采样率为48000Hz的10秒钟WAV文件,代码如下:

     Wave_header header(1, 48000, 16);
    
        uint32_t length = header.fmt_data->sample_per_sec * 10 * header.fmt_data->bits_per_sample / 8;
        uint8_t *data = new uint8_t[length];
    
        memset(data, 0x80, length);
    
        CWaveFile::write("e:\\test1.wav", header, data, length);

    这里将所有的sample按字节填充为0x80,以16进制打开该wav文件,结果如下:

    可以参照上图给出的WAV文件头信息,看看各个字节的意义。音频的格式信息在FOURCC fmt后面

    • 4字节 00000012 fmt数据的长度 18字节
    • 2字节 0001 数据的存储格式为PCM
    • 2字节 0001 声道个数
    • 4字节 0000BB80 采样率 48000Hz
    • 4字节 00017700 码率 96000bps
    • 2字节 0002 数据块大小
    • 2字节 0010 量化精度 16位
    • 2字节 0000 扩展块的大小
    • 4字节 FOURCC data
    • 4字节 数据长度 0x000EA600

    代码

    最后将本文的代码封装在了类CWaveFile中,使用简单。

    • 写WAV文件
    Wave_header header(1, 48000, 16);
    
        uint32_t length = header.fmt_data->sample_per_sec * 10 * header.fmt_data->bits_per_sample / 8;
        uint8_t *data = new uint8_t[length];
    
        memset(data, 0x80, length);
    
        CWaveFile::write("e:\\test1.wav", header, data, length);

     

    • 读取WAV文件
        CWaveFile wave;
        wave.read("e:\\test1.wav");
        wave.data // PCM数据
    展开全文
  • 基于VS2013,c++有关WAV文件读取和G.711编解码的源代码
  • C语言 wav文件读取头文件和数据

    热门讨论 2012-03-15 13:54:11
    wav文件读取头文件和数据,并且能根据时间选择截取的数据
  • 利用wavfile进行wav文件读取不成功的问题 当利用scipy包时: scipy.io import wavfile rate, s = wavfile.read('aud.wav') 使用wavfile进行wav文件读取千万不要直接将ma4或其他格式的文件直接通过改后缀的方式进行...

    利用wavfile进行wav文件读取不成功的问题

    当利用scipy包时:

    scipy.io import wavfile
    rate, s = wavfile.read('aud.wav')
    

    使用wavfile进行wav文件读取千万不要直接将ma4或其他格式的文件直接通过改后缀的方式进行跟新,这样会报如下错误:

    ValueError: File format b'\x00\x00\x00\x18'... not understood.
    
    

    需要进行有效的格式转换:

    import os
    
    m4a_path = "/Users/Downloads/start1/"
    
    m4a_file = os.listdir(m4a_path)
    
    for i, m4a in enumerate(m4a_file):
        os.system("ffmpeg -i "+ m4a_path + m4a 
        + " " + m4a_path + str(i) + ".wav" )# ffmpeg 可以用来处理视频和音频文件,可               #以设置一些参数,如采样频率等
    

    最后附一个wav格式的详图:
    在这里插入图片描述

    展开全文
  • 因此封装了一个 WavFile 在 PCM 前面增加 Wav 头,生成 wav 文件,可以在手机上直接播放。文件头信息见 使用方法如下: String wavSavePath = "/sdcard/Alan/audio/record.wav"; // 指定 PCM 格式(跟 AudioRecord ...

    Android 在使用 AudioRecord 录音时,采集的是 PCM 数据,有时候在录完后向听下录音效果,还需要拉到电脑端用 AU 播放,手机端没法直接播放 PCM 数据。因此封装了一个 WavFile 在 PCM 前面增加 Wav 头,生成 wav 文件,可以在手机上直接播放。文件头信息见:《Audio WAV文件头格式》

    使用方法如下:

                String wavSavePath = "/sdcard/Alan/audio/record.wav";
                // 指定 PCM 格式(跟 AudioRecord 配置一致),内部自动生成 wav 头信息
                WavFile.HeadInfo headInfo = WavFile.HeadInfo.build()
                        .setSampleRate(44100)
                        .setChannelCount(1)
                        .setBytePerSample(2);
                WavFile wavFile = new WavFile(wavSavePath, headInfo);
    
                // 录制过程中从 AudioRecord 读到 PCM 数据后写入 wavFile
                wavFile.write(byteBuffer.array(), byteBuffer.arrayOffset(), byteBuffer.limit());
    
                // 录制结束关闭文件
                wavFile.close();
    

    完整 Demo 见 GitHub

    WavFile 封装如下:

    import java.io.IOException;
    import java.io.RandomAccessFile;
    import java.nio.ByteBuffer;
    import java.nio.ByteOrder;
    
    /**
     * 封装文件头尾 44 字节长度的 WavFile,支持对 WavFile 的读、写操作,
     * 以 {@link #WavFile(String)} 方式创建时,为[读]模式,可以通过 {@link #getHeadInfo()} 获取 wav 文件的头信息
     * 以 {@link #WavFile(String, HeadInfo)} 方式创建时,为[写]模式
     *
     * Author: AlanWang4523.
     * Date: 2020/10/28 20:35.
     * Mail: alanwang4523@gmail.com
     */
    public class WavFile {
        private HeadInfo mHeadInfo;
        private RandomAccessFile mWavFile;
        private int mAudioDataLenInBytes;
        private volatile boolean isWriteMode;
        private volatile boolean isClosed;
    
        /**
         * 构建一个 WavFile,该文件已存在,以读模式打开
         * @param filePath wav file 文件路径
         * @throws IOException IOException
         */
        public WavFile(String filePath) throws IOException {
            this(filePath, null);
        }
    
        /**
         * 根据 HeadInfo 创建一个 WavFile,以写模式打开
         * @param filePath wav file 文件路径
         * @param wavHeaderInfo wavHeaderInfo
         * @throws IOException IOException
         */
        public WavFile(String filePath, HeadInfo wavHeaderInfo) throws IOException {
            mWavFile = new RandomAccessFile(filePath, "rw");
            if (wavHeaderInfo != null) {
                mHeadInfo = wavHeaderInfo;
                byte[] wavHeader = generateWavHeader(
                        wavHeaderInfo.getSampleRate(),
                        wavHeaderInfo.getChannelCount(),
                        wavHeaderInfo.getBytePerSample() * 8,
                        0);
                mWavFile.write(wavHeader);
                isWriteMode = true;
            } else {
                mHeadInfo = getWavHeader(mWavFile);
                isWriteMode = false;
            }
            mWavFile.seek(44);
            mAudioDataLenInBytes = 0;
            isClosed = false;
        }
    
        /**
         * 获取 wav 头信息
         * @return HeadInfo
         */
        public HeadInfo getHeadInfo() {
            return mHeadInfo;
        }
    
        /**
         * 读取 PCM 数据
         * @param data pcm 数据存放的位置
         * @param off offset
         * @param len 想要读取的长度,单位:字节
         * @return 读取的长度,单位:字节
         * @throws IOException IOException
         */
        public int read(byte[] data, int off, int len) throws IOException {
            if (isWriteMode) {
                throw new IOException("The current file is not read mode.");
            }
            if (isClosed) {
                return 0;
            }
            return mWavFile.read(data, off, len);
        }
    
        /**
         * 写 PCM 数据
         * @param data 音频数据
         * @param offset offset
         * @param len 数据长度,单位:字节
         */
        public void write(byte[] data, int offset, int len) throws IOException {
            if (!isWriteMode) {
                throw new IOException("The current file is not write mode.");
            }
            if (isClosed || data == null || len <= 0) {
                return;
            }
            mWavFile.write(data, offset, len);
            mAudioDataLenInBytes += len;
        }
    
        /**
         * 更新 wav 文件头信息,并关闭文件
         * @throws IOException IOException
         */
        public void close() throws IOException {
            if (isClosed) {
                return;
            }
            isClosed = true;
    
            // 如果是写入模式,则更新文件头中的数据长度信息
            if (isWriteMode) {
                int totalFileLenIncludeHeader = mAudioDataLenInBytes + 44;
                //更新wav文件头04H— 08H的数据长度:该长度 = 文件总长 - 8
                mWavFile.seek(4);
                mWavFile.write(int2ByteArray(totalFileLenIncludeHeader - 8));
    
                //更新wav文件头28H— 2CH,实际PCM采样数据长度
                mWavFile.seek(40);
                mWavFile.write(int2ByteArray(totalFileLenIncludeHeader - 44));
            }
    
            mWavFile.close();
        }
    
        /**
         * 生成 44 字节 WAV 文件头
         * @param sampleRate 采样率,如 44100
         * @param channels 通道数,如立体声为2
         * @param bitsPerSample 采样精度,即每个采样所占数据位数,如 16,表示每个采样 16bit 数据,即 2 个字节
         * @param audioDataLenInBytes 音频数据长度
         * @return 44 字节 WAV 头信息
         */
        private byte[] generateWavHeader(int sampleRate, int channels, int bitsPerSample, int audioDataLenInBytes) {
            if (bitsPerSample != 16 && bitsPerSample != 32) {
                throw new IllegalArgumentException("The bitsPerSample is not 16 or 32!");
            }
            if (audioDataLenInBytes < 0) {
                throw new IllegalArgumentException("Audio data len could not be negative!");
            }
            byte[] wavHeader = new byte[44];
    
            // 这个长度不包括"RIFF"标志(4字节)和文件长度本身所占字节(4字节),即该长度等于整个 Wav文件长度(包含44字节头) - 8
            // 也等于纯音频数据的长度 + 36
            int ckTotalSize = 36 + audioDataLenInBytes;
    
            // 生成文件头默认纯音频数据长度为 0
            int audioDataLen = audioDataLenInBytes;
    
            // 音频数据传送速率, 单位是字节。其值为采样率×每次采样大小。播放软件利用此值可以估计缓冲区的大小。
            // bytePerSecond = sampleRate * (bitsPerSample / 8) * channels
            int bytePerSecond = sampleRate * (bitsPerSample / 8) * channels;
    
            //ckid:4字节 RIFF 标志,大写
            wavHeader[0]  = 'R';
            wavHeader[1]  = 'I';
            wavHeader[2]  = 'F';
            wavHeader[3]  = 'F';
    
            //cksize:4字节文件长度,这个长度不包括"RIFF"标志(4字节)和文件长度本身所占字节(4字节),即该长度等于整个文件长度 - 8
            wavHeader[4]  = (byte)(ckTotalSize & 0xff);
            wavHeader[5]  = (byte)((ckTotalSize >> 8) & 0xff);
            wavHeader[6]  = (byte)((ckTotalSize >> 16) & 0xff);
            wavHeader[7]  = (byte)((ckTotalSize >> 24) & 0xff);
    
            //fcc type:4字节 "WAVE" 类型块标识, 大写
            wavHeader[8]  = 'W';
            wavHeader[9]  = 'A';
            wavHeader[10] = 'V';
            wavHeader[11] = 'E';
    
            //ckid:4字节 表示"fmt" chunk的开始,此块中包括文件内部格式信息,小写, 最后一个字符是空格
            wavHeader[12] = 'f';
            wavHeader[13] = 'm';
            wavHeader[14] = 't';
            wavHeader[15] = ' ';
    
            //cksize:4字节,文件内部格式信息数据的大小,过滤字节(一般为00000010H)
            wavHeader[16] = 0x10;
            wavHeader[17] = 0;
            wavHeader[18] = 0;
            wavHeader[19] = 0;
    
            //FormatTag:2字节,音频数据的编码方式,1:表示是PCM 编码
            wavHeader[20] = 1;
            wavHeader[21] = 0;
    
            //Channels:2字节,声道数,单声道为1,双声道为2
            wavHeader[22] = (byte) channels;
            wavHeader[23] = 0;
    
            //SamplesPerSec:4字节,采样率,如44100
            wavHeader[24] = (byte)(sampleRate & 0xff);
            wavHeader[25] = (byte)((sampleRate >> 8) & 0xff);
            wavHeader[26] = (byte)((sampleRate >> 16) & 0xff);
            wavHeader[27] = (byte)((sampleRate >> 24) & 0xff);
    
            //BytesPerSec:4字节,音频数据传送速率, 单位是字节。其值为采样率×每次采样大小。播放软件利用此值可以估计缓冲区的大小;
            //bytePerSecond = sampleRate * (bitsPerSample / 8) * channels
            wavHeader[28] = (byte)(bytePerSecond & 0xff);
            wavHeader[29] = (byte)((bytePerSecond >> 8) & 0xff);
            wavHeader[30] = (byte)((bytePerSecond >> 16) & 0xff);
            wavHeader[31] = (byte)((bytePerSecond >> 24) & 0xff);
    
            //BlockAlign:2字节,每次采样的大小 = 采样精度*声道数/8(单位是字节); 这也是字节对齐的最小单位, 譬如 16bit 立体声在这里的值是 4 字节。
            //播放软件需要一次处理多个该值大小的字节数据,以便将其值用于缓冲区的调整
            wavHeader[32] = (byte)(bitsPerSample * channels / 8);
            wavHeader[33] = 0;
    
            //BitsPerSample:2字节,每个声道的采样精度; 譬如 16bit 在这里的值就是16。如果有多个声道,则每个声道的采样精度大小都一样的;
            wavHeader[34] = (byte) bitsPerSample;
            wavHeader[35] = 0;
    
            //ckid:4字节,数据标志符(data),表示 "data" chunk的开始。此块中包含音频数据,小写;
            wavHeader[36] = 'd';
            wavHeader[37] = 'a';
            wavHeader[38] = 't';
            wavHeader[39] = 'a';
    
            //cksize:音频数据的长度,4字节,audioDataLen = ckSize - 36 = fileLenIncludeHeader - 44
            wavHeader[40] = (byte)(audioDataLen & 0xff);
            wavHeader[41] = (byte)((audioDataLen >> 8) & 0xff);
            wavHeader[42] = (byte)((audioDataLen >> 16) & 0xff);
            wavHeader[43] = (byte)((audioDataLen >> 24) & 0xff);
    
            return wavHeader;
        }
    
        /**
         * 从 wav 文件中获取头信息
         * 注意:必须是 44 字节头的 wav 文件
         * @param randomAccessFile wav 文件
         * @return HeadInfo
         * @throws IOException IOException
         */
        private HeadInfo getWavHeader(RandomAccessFile randomAccessFile) throws IOException {
    
            //读取channelCount,第22~23位
            randomAccessFile.seek(22);
            byte[] channelCountArray = new byte[2];
            randomAccessFile.read(channelCountArray);
            int channelCount = byteArray2Short(channelCountArray);
    
            //读取sampleRate,第24~27位
            randomAccessFile.seek(24);
            byte[] sampleRateArray = new byte[4];
            randomAccessFile.read(sampleRateArray);
            int sampleRate = byteArray2Int(sampleRateArray);
    
            //读取BitsPerSample,第34~35位
            randomAccessFile.seek(34);
            byte[] bitsPerSampleArray = new byte[2];
            randomAccessFile.read(bitsPerSampleArray);
            int bytePerSample = byteArray2Short(bitsPerSampleArray) / 8;
    
            return HeadInfo.build().
                    setSampleRate(sampleRate).
                    setChannelCount(channelCount).
                    setBytePerSample(bytePerSample);
        }
    
        /**
         * 将 byte 数组转成short
         * @param b byte 数组
         * @return 返回 short 数值
         */
        private static short byteArray2Short(byte[] b) {
            return ByteBuffer.wrap(b).order(ByteOrder.LITTLE_ENDIAN).getShort();
        }
    
        /**
         * 将整型转成 byte 数组
         * @param data 要转换的数字
         * @return byte 数组
         */
        private static byte[] int2ByteArray(int data) {
            return ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(data).array();
        }
    
        /**
         * 将 byte 数组转成整型
         * @param b byte 数组
         * @return int 数值
         */
        private static int byteArray2Int(byte[] b) {
            return ByteBuffer.wrap(b).order(ByteOrder.LITTLE_ENDIAN).getInt();
        }
    
    
        public static class HeadInfo {
            /**
             * 采样率
             */
            private int sampleRate;
    
            /**
             * 通道数
             */
            private int channelCount;
    
            /**
             * 每个采样点的大小,
             * short 型 PCM 是 2 字节
             * float 型 PCM 是 4 字节
             * 单位:字节
             */
            private int bytePerSample = 2;
    
            /**
             * 构造 HeadInfo
             * @return HeadInfo 实例
             */
            public static HeadInfo build() {
                return new HeadInfo();
            }
    
            /**
             * 获取音频采样率,如:44100、48000
             * @return 设置参数后的 HeadInfo 实例
             */
            public int getSampleRate() {
                return sampleRate;
            }
    
            /**
             * 设置音频采样率
             * @param sampleRate 采样率,如:44100、48000
             * @return 设置参数后的 HeadInfo 实例
             */
            public HeadInfo setSampleRate(int sampleRate) {
                this.sampleRate = sampleRate;
                return this;
            }
    
            /**
             * 获取音频通道数,如:1、2
             * @return 设置参数后的 HeadInfo 实例
             */
            public int getChannelCount() {
                return channelCount;
            }
    
            /**
             * 设置音频通道数
             * @param channelCount 音频通道数
             * @return 设置参数后的 HeadInfo 实例
             */
            public HeadInfo setChannelCount(int channelCount) {
                this.channelCount = channelCount;
                return this;
            }
    
            /**
             * 获取每个采样点大小,单位:字节
             * short 型 PCM 是 2 字节
             * float 型 PCM 是 4 字节
             * @return 每个采样点占的字节数
             */
            public int getBytePerSample() {
                return bytePerSample;
            }
    
            /**
             * 设置每个采样占的字节数
             * @param bytePerSample bytePerSample
             * @return 设置参数后的 HeadInfo 实例
             */
            public HeadInfo setBytePerSample(int bytePerSample) {
                this.bytePerSample = bytePerSample;
                return this;
            }
        }
    }
    

    完整 Demo 见 GitHub

    展开全文
  • wav文件读取参考

    千次阅读 2017-09-14 16:13:52
    在实现一个音频处理功能的过程中,需要对wav文件进行处理。我们知道在matlab中有现成的函数wavread('wavtest.wav'),本文的用途就是用c++代码实现该函数的功能。 读者可以在vs中新建一个工程,再将本文的代码粘贴在...
  • 作者原来的下载链接失效了,我照着网页上代码重新建的工程,可以跑,作为一名多年的伸手党,希望能够帮助到大家! https://blog.csdn.net/qq_40006363/article/details/78655110
  • 1.wav文件头格式 typedef struct WAV_HEADER{ char RIFF[4]; // RIFF Header Magic header unsigned long ChunkSize; // RIFF Chunk Size char WAVE[4]; // WAVE Header char fmt[4]; // FMT header unsigned...
  • C# 实现wav 文件读取并绘制波形图

    千次阅读 热门讨论 2017-11-28 15:00:53
    第一次写这种音频文件转化为图像显示,遇到了好多困难,百度了一些方法都不正确,终于自己搞定了。 源码地址: http://download.csdn.net/download/qq_40006363/10131910
  • 近来了解到wav文件基本格式,文件头有44个字节的文件描述,之后的字节就是音频样本数据的信息,但是有一个问题就出来了: 假如文件中第一个样本数据信息为0E 90,那么如果要将这个数据读取到一个short类型变量中去...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 20,832
精华内容 8,332
关键字:

wav文件读取