Android 在使用 AudioRecord 录音时,采集的是 PCM 数据,有时候在录完后向听下录音效果,还需要拉到电脑端用 AU 播放,手机端没法直接播放 PCM 数据。因此封装了一个 WavFile 在 PCM 前面增加 Wav 头,生成 wav 文件,可以在手机上直接播放。文件头信息见:
《Audio WAV文件头格式》
使用方法如下:
String wavSavePath = "/sdcard/Alan/audio/record.wav";
WavFile.HeadInfo headInfo = WavFile.HeadInfo.build()
.setSampleRate(44100)
.setChannelCount(1)
.setBytePerSample(2);
WavFile wavFile = new WavFile(wavSavePath, headInfo);
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;
mWavFile.seek(4);
mWavFile.write(int2ByteArray(totalFileLenIncludeHeader - 8));
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];
int ckTotalSize = 36 + audioDataLenInBytes;
int audioDataLen = audioDataLenInBytes;
int bytePerSecond = sampleRate * (bitsPerSample / 8) * channels;
wavHeader[0] = 'R';
wavHeader[1] = 'I';
wavHeader[2] = 'F';
wavHeader[3] = 'F';
wavHeader[4] = (byte)(ckTotalSize & 0xff);
wavHeader[5] = (byte)((ckTotalSize >> 8) & 0xff);
wavHeader[6] = (byte)((ckTotalSize >> 16) & 0xff);
wavHeader[7] = (byte)((ckTotalSize >> 24) & 0xff);
wavHeader[8] = 'W';
wavHeader[9] = 'A';
wavHeader[10] = 'V';
wavHeader[11] = 'E';
wavHeader[12] = 'f';
wavHeader[13] = 'm';
wavHeader[14] = 't';
wavHeader[15] = ' ';
wavHeader[16] = 0x10;
wavHeader[17] = 0;
wavHeader[18] = 0;
wavHeader[19] = 0;
wavHeader[20] = 1;
wavHeader[21] = 0;
wavHeader[22] = (byte) channels;
wavHeader[23] = 0;
wavHeader[24] = (byte)(sampleRate & 0xff);
wavHeader[25] = (byte)((sampleRate >> 8) & 0xff);
wavHeader[26] = (byte)((sampleRate >> 16) & 0xff);
wavHeader[27] = (byte)((sampleRate >> 24) & 0xff);
wavHeader[28] = (byte)(bytePerSecond & 0xff);
wavHeader[29] = (byte)((bytePerSecond >> 8) & 0xff);
wavHeader[30] = (byte)((bytePerSecond >> 16) & 0xff);
wavHeader[31] = (byte)((bytePerSecond >> 24) & 0xff);
wavHeader[32] = (byte)(bitsPerSample * channels / 8);
wavHeader[33] = 0;
wavHeader[34] = (byte) bitsPerSample;
wavHeader[35] = 0;
wavHeader[36] = 'd';
wavHeader[37] = 'a';
wavHeader[38] = 't';
wavHeader[39] = 'a';
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 {
randomAccessFile.seek(22);
byte[] channelCountArray = new byte[2];
randomAccessFile.read(channelCountArray);
int channelCount = byteArray2Short(channelCountArray);
randomAccessFile.seek(24);
byte[] sampleRateArray = new byte[4];
randomAccessFile.read(sampleRateArray);
int sampleRate = byteArray2Int(sampleRateArray);
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
Android 在使用 AudioRecord 录音时,采集的是 PCM 数据,有时候在录完后向听下录音效果,还需要拉到电脑端用 AU 播放,手机端没法直接播放 PCM 数据。因此封装了一个 WavFile 在 PCM 前面增加 Wav 头,生成 wav 文件,可以在手机上直接播放。文件头信息见使用方法如下:String wavSavePath = "/sdcard/Alan/audio/record.wav"; // 指定 PCM 格式(跟 AudioRecord 配置一致),
AEQ-
Android AudioEffect EQ
一种将
Android强大的音频增强功能与任何音乐(音频)应用程序结合使用的简便方法。 适用于Google Nexus和Pixel设备。
功能要求和错误:请。
AEQ使用:
该
文件是AEQ的一部分。
AEQ是免费软件:您可以根据自由软件基金会发布的GNU通用公共许可的条款(许可的版本3)或(根据您的判断)任何更高版本来重新分发和/或修改它。
分发AEQ的目的是希望它会有用,但不作任何担保; 甚至没有对适销性或特定用途适用性的暗示保证。 有关更多详细信息,请参见GNU通用公共许可证。
您应该已经与AEQ一起收到了GNU通用公共许可证的副本。 如果没有,请参阅 。
Android AudioRecord录制wav格式的音频package com.example.chezi008.recorderdemo;import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.os.Environm
前端通过浏览器识别解决IE浏览器中
audio标签无法播放.
wav文件
HTML5 提供了播放音频
文件的标准。H5新标签
audio可以很好地在浏览器中播放语音
文件。
但是,根据官方文档显示,IE浏览器下的
audio标签却无法播放.
wav格式的语音
文件。具体浏览器对语音
文件的支持情况在下表中给出:
Android音频(10)——多App同时录音实现
转自:https://www.cnblogs.com/hellokitty2/p/10947364.html
一、使用c++编写录音程序
PCM音频数据是原始音频数据,无法使用播放器播放,需要给它加上一个头部,表明声音有几个通道,采样率是多少等等。将
PCM音频数据转换为WAV格式,这样其它播放器才能播放出来。
录音时要确定3个参数
(1)采样率:一秒钟对声波采样的次数。常用的采样率有8000,11025,22050,32000,44100.