当前位置: 首页 > wzjs >正文

国家网站备案查询系统临沂网站seo

国家网站备案查询系统,临沂网站seo,网站建设课程体会,手机网站如何做营销pcm转wav PCM 格式校验pcm 添加 wav 头信息WAVWAV 格式检验小端序? 参考地址 PCM 格式校验 /*** 专业PCM文件验证(支持动态参数与多格式)* param silenceThreshold 静音检测阈值(0.0~1.0),默认90%零值为静…

pcm转wav

    • PCM 格式校验
    • pcm 添加 wav 头信息
    • WAV
    • WAV 格式检验
    • 小端序?
  • 参考地址

PCM 格式校验

/*** 专业PCM文件验证(支持动态参数与多格式)* @param silenceThreshold 静音检测阈值(0.0~1.0),默认90%零值为静音* @return false表示文件无效(自动打印错误日志)*/
fun validatePcmFile(file: File,sampleRate: Int,channelConfig: Int,audioFormat: Int,silenceThreshold: Float = 0.9f
): Boolean {// 基础参数校验require(silenceThreshold in 0.0f..1.0f) { "静音阈值必须在0~1之间" }require(audioFormat == AudioFormat.ENCODING_PCM_8BIT || audioFormat == AudioFormat.ENCODING_PCM_16BIT || audioFormat == AudioFormat.ENCODING_PCM_FLOAT) {"不支持的音频格式: $audioFormat"}// 基础检查if (!file.exists()) {Log.e(TAG, "PCM文件不存在: ${file.absolutePath}")return false}if (file.length() == 0L) {Log.e(TAG, "PCM文件为空: ${file.absolutePath}")return false}// 调试日志Log.d(TAG, "开始校验PCM文件: ${file.name} [大小=${file.length()} bytes]")try {// 计算位深度和字节对齐val bytesPerSample = when (audioFormat) {AudioFormat.ENCODING_PCM_8BIT -> 1AudioFormat.ENCODING_PCM_16BIT -> 2AudioFormat.ENCODING_PCM_FLOAT -> 4else -> 2 // 不会执行(已校验)}// 1. 最小帧检查val minFrameSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat)val minRequiredSize = minFrameSize * MIN_FRAME_MULTIPLIERif (file.length() < minRequiredSize) {Log.e(TAG, "PCM文件过小: ${file.length()} bytes (至少需要 $minRequiredSize bytes)")return false}// 2. 数据有效性检查val buffer = ByteArray(1024)var zeroCount = 0var totalSamples = 0FileInputStream(file).use { stream ->var bytesRead: Intwhile (true) {bytesRead = stream.read(buffer)if (bytesRead == -1) breakval samples = bytesRead / bytesPerSampleval byteBuffer = ByteBuffer.wrap(buffer, 0, bytesRead).apply {order(ByteOrder.LITTLE_ENDIAN)}when (bytesPerSample) {1 -> { // 8-bit unsignedfor (i in 0 until samples) {val value = byteBuffer.get().toInt() and 0xFFif (value == 128) zeroCount++ // 静音中点为128}}2 -> { // 16-bit signedfor (i in 0 until samples) {if (byteBuffer.short == 0.toShort()) zeroCount++}}4 -> { // 32-bit floatfor (i in 0 until samples) {if (abs(byteBuffer.float - 0.0f) < 1e-6) zeroCount++}}}totalSamples += samples}}Log.d(TAG, "样本统计: 总数=$totalSamples, 零值=$zeroCount")// 静音检测if (totalSamples > 0) {val zeroRatio = zeroCount.toFloat() / totalSamplesif (zeroRatio > silenceThreshold) {Log.e(TAG, "静音数据过多: ${"%.1f%%".format(zeroRatio * 100)} ≥ ${"%.0f%%".format(silenceThreshold * 100)}")return false}} else {Log.e(TAG, "文件无有效样本数据")return false}Log.i(TAG, "PCM文件验证通过")return true} catch (e: Exception) {Log.e(TAG, "验证异常: ${e.javaClass.simpleName} - ${e.message}", e)return false}
}

pcm 添加 wav 头信息

 /*** 头部信息共44字节* @param sampleRate* @param channels* @param bitDepth* @param dataSize* @return* @throws IOException*/fun getWavHeader(sampleRate: Int, channels: Int, bitDepth: Int, dataSize: Long): ByteArray {val header = ByteArray(44)// ChunkID,RIFF标识header[0] = 'R'.code.toByte()header[1] = 'I'.code.toByte()header[2] = 'F'.code.toByte()header[3] = 'F'.code.toByte()// ChunkSize,文件长度val totalSize = dataSize + 36header[4] = (totalSize and 0xffL).toByte()header[5] = ((totalSize shr 8) and 0xffL).toByte()header[6] = ((totalSize shr 16) and 0xffL).toByte()header[7] = ((totalSize shr 24) and 0xffL).toByte()// Format,WAVE标识header[8] = 'W'.code.toByte()header[9] = 'A'.code.toByte()header[10] = 'V'.code.toByte()header[11] = 'E'.code.toByte()// Subchunk1ID,fmt标识header[12] = 'f'.code.toByte()header[13] = 'm'.code.toByte()header[14] = 't'.code.toByte()header[15] = ' '.code.toByte()// Subchunk1Size,格式信息长度header[16] = 16header[17] = 0header[18] = 0header[19] = 0// AudioFormat,音频格式(PCM为1)header[20] = 1header[21] = 0// NumChannels,声道数header[22] = channels.toByte()header[23] = 0// SampleRate,采样率header[24] = (sampleRate and 0xff).toByte()header[25] = ((sampleRate shr 8) and 0xff).toByte()header[26] = ((sampleRate shr 16) and 0xff).toByte()header[27] = ((sampleRate shr 24) and 0xff).toByte()// ByteRate,比特率val byteRate = sampleRate * channels * bitDepth / 8header[28] = (byteRate and 0xff).toByte()header[29] = ((byteRate shr 8) and 0xff).toByte()header[30] = ((byteRate shr 16) and 0xff).toByte()header[31] = ((byteRate shr 24) and 0xff).toByte()// BlockAlign,块对齐val blockAlign = channels * bitDepth / 8header[32] = blockAlign.toByte()header[33] = 0// BitsPerSample,采样位深度header[34] = bitDepth.toByte()header[35] = 0// Subchunk2ID,data标识header[36] = 'd'.code.toByte()header[37] = 'a'.code.toByte()header[38] = 't'.code.toByte()header[39] = 'a'.code.toByte()// Subchunk2Size,音频数据长度 dataHdrLengthheader[40] = (dataSize and 0xffL).toByte()header[41] = ((dataSize shr 8) and 0xffL).toByte()header[42] = ((dataSize shr 16) and 0xffL).toByte()header[43] = ((dataSize shr 24) and 0xffL).toByte()return header}

WAV

DEPSEK

偏移地址字段名称说明
00-03ChunkId固定值 "RIFF" (ASCII编码)
04-07ChunkSize文件总字节数 - 8(从地址08开始到文件末尾的总字节数,小端存储)
08-11fccType固定值 "WAVE" (ASCII编码)
12-15SubChunkId1固定值 "fmt "(包含末尾空格)
16-19SubChunkSize1fmt块数据大小(标准PCM为16,扩展格式可能为1840,小端存储)
20-21FormatTag编码格式:1=PCM,3=IEEE浮点(小端存储)
22-23Channels声道数:1=单声道,2=双声道(小端存储)
24-27SamplesPerSec采样率(Hz,如44100,小端存储)
28-31BytesPerSec字节率 = 采样率 × 声道数 × 位深/8(小端存储)
32-33BlockAlign每帧字节数 = 声道数 × 位深/8(小端存储)
34-35BitsPerSample位深:8/16/24/32(小端存储)
36-39SubChunkId2固定值 "data"
40-43SubChunkSize2音频数据长度(字节数 = 采样总数 × 声道数 × 位深/8,小端存储)
44-…Data音频原始数据(二进制格式,与FormatTag和BitsPerSample匹配)

WAV 格式检验

/*** 专业WAV文件验证(增强版)* 注意:WAV_HEADER_SIZE常量在此版本中已不再需要,因采用动态块遍历机制* @return false表示文件无效(自动打印错误日志)*/
fun validateWavFile(file: File): Boolean {// 基础文件检查if (!file.exists()) {Log.e(TAG, "❌ WAV文件不存在: ${file.absolutePath}")return false}if (file.length() < MIN_WAV_FILE_SIZE) { // 至少需要包含RIFF头、WAVE标识和一个子块Log.e(TAG, "❌ 文件过小: ${file.length()} bytes (至少需要 $MIN_WAV_FILE_SIZE bytes)")return false}try {RandomAccessFile(file, "r").use { raf ->/* ------------------------- RIFF头验证 ------------------------- */// 读取前4字节验证RIFF标识val riffHeader = ByteArray(4).apply { raf.read(this) }if (String(riffHeader) != "RIFF") {Log.e(TAG, "❌ 无效的RIFF头: ${String(riffHeader)} (应为\"RIFF\")")return false}Log.d(TAG, "✅ RIFF头验证通过")/* ----------------------- RIFF块大小验证 ----------------------- */// 读取小端序的RIFF块大小(文件总大小-8)val riffChunkSize = raf.readLittleEndianInt()val expectedRiffSize = file.length() - 8if (riffChunkSize != expectedRiffSize.toInt()) {Log.e(TAG, "❌ RIFF大小不匹配 | 声明:$riffChunkSize | 实际:$expectedRiffSize")return false}Log.d(TAG, "✅ RIFF块大小验证通过 (${riffChunkSize}bytes)")/* ----------------------- WAVE标识验证 ------------------------ */val waveHeader = ByteArray(4).apply { raf.read(this) }if (String(waveHeader) != "WAVE") {Log.e(TAG, "❌ 无效的WAVE标识: ${String(waveHeader)} (应为\"WAVE\")")return false}Log.d(TAG, "✅ WAVE标识验证通过")/* --------------------- 子块遍历验证 --------------------- */var fmtVerified = falsevar dataSize = 0LchunkLoop@ while (raf.filePointer < file.length()) {// 读取当前块头信息val chunkId = ByteArray(4).apply { raf.read(this) }.toString(Charsets.US_ASCII)val chunkSize = raf.readLittleEndianInt().toLong() and 0xFFFFFFFFL // 转为无符号when (chunkId) {"fmt " -> { // 格式块验证/* --------------- 基本格式块验证 --------------- */if (chunkSize < 16) {Log.e(TAG, "❌ fmt块过小: $chunkSize bytes (至少需要16 bytes)")return false}// 读取音频格式(1=PCM)val formatTag = raf.readLittleEndianShort()if (formatTag != 1.toShort()) {Log.e(TAG, "❌ 非PCM格式 | 格式码:$formatTag (应为1)")return false}Log.d(TAG, "✅ PCM格式验证通过")// 跳过声道数、采样率等参数(此处不验证具体音频参数)raf.skipBytes(6) // 跳过2(short)+4(int)// 验证位深是否为整数字节val bitsPerSample = raf.readLittleEndianShort()if (bitsPerSample % 8 != 0) {Log.e(TAG, "❌ 非常规位深: ${bitsPerSample}bits (应为8的倍数)")return false}Log.d(TAG, "✅ 位深验证通过 (${bitsPerSample}bits)")fmtVerified = trueraf.skipBytes(chunkSize.toInt() - 16) // 跳过剩余格式数据}"data" -> { // 数据块验证/* --------------- 数据块大小验证 --------------- */dataSize = chunkSizeval declaredDataEnd = raf.filePointer + chunkSizeval actualDataEnd = file.length()if (declaredDataEnd > actualDataEnd) {Log.e(TAG, "❌ 数据块越界 | 声明结束位置:$declaredDataEnd | 文件大小:$actualDataEnd")return false}Log.d(TAG, "✅ 数据块大小验证通过 (${chunkSize}bytes)")break@chunkLoop // 找到数据块后终止遍历}else -> { // 其他块处理Log.d(TAG, "⏭ 跳过块: $chunkId (${chunkSize}bytes)")raf.skipBytes(chunkSize.toInt())}}}/* ------------------- 最终完整性检查 ------------------- */if (!fmtVerified) {Log.e(TAG, "❌ 缺少必需的fmt块")return false}if (dataSize == 0L) {Log.e(TAG, "❌ 缺少data块")return false}return true}} catch (e: Exception) {Log.e(TAG, "❌ 文件解析异常: ${e.javaClass.simpleName} - ${e.message}")return false}
}/* ------------------------- 扩展函数:小端序读取 ------------------------- */
private fun RandomAccessFile.readLittleEndianInt(): Int {return ByteArray(4).apply { read(this) }.let {(it[3].toInt() shl 24) or (it[2].toInt() shl 16) or (it[1].toInt() shl 8) or it[0].toInt()}
}private fun RandomAccessFile.readLittleEndianShort(): Short {return ByteArray(2).apply { read(this) }.let {((it[1].toInt() shl 8) or it[0].toInt()).toShort()}
}companion object {private const val TAG = "WavValidator"private const val MIN_WAV_FILE_SIZE = 44L // RIFF头(12) + fmt块(24) + data块头(8)
}

小端序?

在 Android 中,AudioRecord 录制的音频数据默认是 PCM 格式,且字节序(Endianness)为 小端序(Little-Endian)。这是 Android 音频系统的默认行为,与大多数移动设备和 x86/ARM 平台的处理器架构一致。

大 2 小

    /**************** 字节序转换实现 ****************/private fun convertEndian(inputFile: File): File? {return try {val outputFile = createTempPcmFile("converted_")FileInputStream(inputFile).use { input ->FileOutputStream(outputFile).use { output ->val buffer = ByteArray(4096) // 4KB缓冲区var bytesRead: Intwhile (input.read(buffer).also { bytesRead = it } != -1) {// 确保读取的是完整shortval validLength = bytesRead - (bytesRead % 2)if (validLength == 0) continue// 转换字节序convertByteOrder(buffer, validLength)output.write(buffer, 0, validLength)}}}outputFile} catch (e: Exception) {Log.e(TAG, "Endian conversion failed", e)null}}private fun convertByteOrder(data: ByteArray, length: Int) {val byteBuffer = ByteBuffer.wrap(data, 0, length)val shortBuffer = byteBuffer.order(ByteOrder.BIG_ENDIAN).asShortBuffer()val shorts = ShortArray(shortBuffer.remaining())shortBuffer.get(shorts)ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().put(shorts)}

参考地址

PCM 2 WAV:
https://blog.csdn.net/qq_36451275/article/details/135057683

PCM 2 WAV:
https://blog.csdn.net/m0_54198552/article/details/145653031

depsek

转:Android音频开发(4):PCM转WAV格式音频
https://www.jianshu.com/p/90c77197f1d4

http://www.dtcms.com/wzjs/223889.html

相关文章:

  • 学生做的网站成品百度快照入口
  • 林州网站建设哪家好seo优化推广专员招聘
  • 怎么做销售网站网络营销招聘岗位有哪些
  • 移动端网站宽度做多大南昌百度快速排名提升
  • cpa广告联盟平台seo兼职外包
  • 广州 网站建设国家提供的免费网课平台
  • 搜狗收录查询武安百度seo
  • 湖北省建设厅网站证件什么公司适合做seo优化
  • 网站备案在哪里办理新闻早知道
  • 做电脑网站步骤酒店推广渠道有哪些
  • 青浦网站制作su35搜索引擎优化的定义
  • 建设网站需要花费免费网址注册
  • 漂亮的企业网站源码深圳搜索竞价账户托管
  • 做网站和网页新手做网络销售难吗
  • 包头市建设局网站阿里云盘资源搜索引擎
  • 内蒙古微网站建设创意设计
  • wordpress upload 加密seo博客
  • 网站下载怎么做软件开发交易平台
  • 做任务刷王者皮肤网站海南seo排名优化公司
  • 网站备案收费吗seo顾问能赚钱吗
  • 淄博淄川最新疫情seo诊断工具
  • 做足球原创短视频网站今日微博热搜榜前十名
  • 云主机怎么做网站推广引流方法有哪些推广方法
  • 网络营销推广体系外贸seo优化
  • 怎么建免费论坛网站凯里seo排名优化
  • 秦皇岛网站推广联系电话整站优化的公司
  • 苏宁易购b2b平台seo推广顾问
  • wordpress 搭网站网站外贸推广
  • 做mod游戏下载网站详细的营销推广方案
  • 做网站时空间的选择磁力引擎