Java 有人能解释一下.wav(WAVE)文件头吗?
好的,我正在尝试制作一个可以处理.wav文件的程序,我已经看到了,但是我不能完全确定头中的每一条数据是指什么。例如,“块”指的是什么?这是一个特定的位/字节数吗 如果有人能告诉我,至少在这个问题中使用的格式中,除了常量字符串文本和“数据”数组之外,每个写入.wav的数据是指什么?特别是,我想知道什么是“块”,以及所有通道的采样率、字节率、每个采样的字节数和每个采样的字节数之间的关系?(我怀疑字节率是采样率*每个采样的字节数,但“所有通道”的情况如何?)Java 有人能解释一下.wav(WAVE)文件头吗?,java,audio,header,byte,wav,Java,Audio,Header,Byte,Wav,好的,我正在尝试制作一个可以处理.wav文件的程序,我已经看到了,但是我不能完全确定头中的每一条数据是指什么。例如,“块”指的是什么?这是一个特定的位/字节数吗 如果有人能告诉我,至少在这个问题中使用的格式中,除了常量字符串文本和“数据”数组之外,每个写入.wav的数据是指什么?特别是,我想知道什么是“块”,以及所有通道的采样率、字节率、每个采样的字节数和每个采样的字节数之间的关系?(我怀疑字节率是采样率*每个采样的字节数,但“所有通道”的情况如何?) 非常感谢您的帮助。仅发布链接是违反董事会规
非常感谢您的帮助。仅发布链接是违反董事会规则的,因此这是我从中获取的表格
定位样本值描述
1-4“RIFF”将文件标记为RIFF文件。每个字符都是1。字节长。
5-8文件大小(整数)整个文件的大小-8字节,以字节为单位(32位整数)。通常,您会在创建后填写此信息。
9-12“WAVE”文件类型标题。就我们而言,它总是等于“波”。
13-16“fmt”格式块标记。包括尾随null
17-20 16上述格式数据的长度
21-22 1种格式(1为PCM)-2字节整数
23-24 2个通道数-2字节整数
25-28 44100采样率-32位整数。常用值为44100(CD)和48000(DAT)。采样率=每秒采样数,或赫兹。
29-32 176400(采样率*比特采样*通道)/8。
33-34 4(位采样*通道)/8.1-8位单声道2-8位立体声/16位单声道4-16位立体声
35-36每个样本16位
37-40“数据”“数据”块头。标记数据节的开头。
41-44数据段的文件大小(数据),即文件大小-44字节头。
上面给出了16位立体声源的样本值
更新/提醒
标头整数都是最低有效字节顺序,
因此,双字节通道信息0x01 0x00实际上是0x00001,例如单声道。
我知道OP将这个问题标记为Java,但下面是完整的Kotlin代码,用于读取可以传递给Java的头。阅读《小恩迪安》可能很棘手,但谢天谢地,我们不必这么做
class WaveHeader(bytes: ByteArray) {
init {
require(bytes.size >= SIZE) { "Input size is must be at least $SIZE bytes" }
}
private var start = 0
private val riff = RiffChunk(
String(bytes.copyOfRange(start, start + 4))
.also {
require(it == "RIFF") { "$it must be 'RIFF'" }
start += it.length
},
ByteBuffer.wrap(bytes.copyOfRange(start, start + 4)).order(ByteOrder.LITTLE_ENDIAN)
.also { start += it.capacity() }.int,
String(bytes.copyOfRange(start, start + 4))
.also {
require(it == "WAVE") { "$it must be 'WAVE'" }
start += it.length
}
)
private val format = FormatChunk(
// null terminated
String(bytes.copyOfRange(start, start + 3))
.also {
require(it == "fmt") { "$it must be 'fmt'" }
start += 4
},
ByteBuffer.wrap(bytes.copyOfRange(start, start + 4)).order(ByteOrder.LITTLE_ENDIAN)
.also { start += it.capacity() }.int,
ByteBuffer.wrap(bytes.copyOfRange(start, start + 2)).order(ByteOrder.LITTLE_ENDIAN)
.also { start += it.capacity() }
.let { if (it.short == 1.toShort()) "PCM" else "OTHER (${it.short})" },
ByteBuffer.wrap(bytes.copyOfRange(start, start + 2)).order(ByteOrder.LITTLE_ENDIAN)
.also { start += it.capacity() }.short,
ByteBuffer.wrap(bytes.copyOfRange(start, start + 4)).order(ByteOrder.LITTLE_ENDIAN)
.also { start += it.capacity() }.int,
ByteBuffer.wrap(bytes.copyOfRange(start, start + 4)).order(ByteOrder.LITTLE_ENDIAN)
.also { start += it.capacity() }.int,
ByteBuffer.wrap(bytes.copyOfRange(start, start + 2)).order(ByteOrder.LITTLE_ENDIAN)
.also { start += it.capacity() }.short,
ByteBuffer.wrap(bytes.copyOfRange(start, start + 2)).order(ByteOrder.LITTLE_ENDIAN)
.also { start += it.capacity() }.short
)
private val `data` = DataChunk(
String(bytes.copyOfRange(start, start + 4))
// remove all null chars
.replace("\u0000", "")
.also { start += it.length },
ByteBuffer.wrap(bytes.copyOfRange(start, start + 4)).order(ByteOrder.LITTLE_ENDIAN)
.also { start += it.capacity() }.int
)
init {
assert(start == 44) { "Illegal state" }
}
data class RiffChunk(val id: String, val size: Int, val format: String)
data class FormatChunk(
val id: String, val size: Int, val format: String, val numChannels: Short,
val sampleRate: Int, val byteRate: Int, val blockAlign: Short, val bitsPerSample: Short
)
data class DataChunk(val id: String, val size: Int)
override fun toString(): String {
val ls = System.lineSeparator()
return "WaveHeader($ls\t$riff}$ls\t$format$ls\t$`data`$ls)"
}
companion object {
const val SIZE = 44
fun fromPath(path: String): WaveHeader = fromInputStream(WaveHeader::class.java.getResourceAsStream(path))
fun fromUrl(url: String): WaveHeader = fromInputStream(URL(url).openStream())
private fun fromInputStream(input: InputStream): WaveHeader {
val bytes = input.use {
it.readNBytes(SIZE)
}
return WaveHeader(bytes)
}
}
}
fun main(args: Array<String>) {
if (args.isEmpty()) {
System.err.println("Argument is missing")
}
println(WaveHeader.fromUrl(args[0]))
}
那么
文件大小
值是32位整数
,分别包含整个文件和数据部分中8位字节的数量
?是采样率32位
或32字节
,因为如果我没有弄错的话,后者在128位时比需要的大得多。32位采样就是答案。精确地说是4字节的头空间。你可能会找到一些库来为你处理所有的头内容,你所需要做的就是找到它们。如果你使用这些库,你所要担心的就是如何实际操作音频,而不仅仅是如何编码或解码它们。
WaveHeader(
RiffChunk(id=RIFF, size=168050, format=WAVE)}
FormatChunk(id=fmt, size=18, format=PCM, numChannels=1, sampleRate=16000, byteRate=32000, blockAlign=2, bitsPerSample=16)
DataChunk(id=fa, size=1952670054)
)