使用go lang获取caf音频文件的持续时间

使用go lang获取caf音频文件的持续时间,go,audio,caf,Go,Audio,Caf,我想使用go获取.caf音频文件的持续时间。我发现了一些解码器,但它们的Duration()方法只返回0,其中的注释可能建议了计算持续时间的方法,有人知道这些注释是否合法吗?如果是,我将如何计算持续时间?如果没有简单的解决办法,我会接受“这不可能”作为答案 func (d *Decoder) Duration() time.Duration { //duration := time.Duration((float64(p.Size) / float64(p.AvgBytesPerSec)

我想使用go获取.caf音频文件的持续时间。我发现了一些解码器,但它们的Duration()方法只返回0,其中的注释可能建议了计算持续时间的方法,有人知道这些注释是否合法吗?如果是,我将如何计算持续时间?如果没有简单的解决办法,我会接受“这不可能”作为答案

func (d *Decoder) Duration() time.Duration {
    //duration := time.Duration((float64(p.Size) / float64(p.AvgBytesPerSec)) * float64(time.Second))
    //duration := time.Duration(float64(p.NumSampleFrames) / float64(p.SampleRate) * float64(time.Second))

    return 0
}

一个实现示例,尽管我很乐意使用任何易于安装的实现:

直接获取链接文件中的文档注释。在这些文档中,您会发现以下两件重要的事情:

“文件中音频的持续时间是[有效帧数]除以文件音频描述块中指定的采样率。”

好的,很酷,但是有多少有效帧?有两种可能的了解方法:

  • 如果CAF有一个数据包表,它必须包括有效帧的数量。太好了
  • 唯一不允许有数据包表的CAF是具有恒定数据包大小的CAF:
请注意,只要格式的每个数据包具有恒定的帧数,就可以通过将mSampleRate[每秒帧数]值除以mFramesPerPacket值来计算每个数据包的持续时间

这会告诉您每个数据包的持续时间,但由于数据包的大小是恒定的,因此数据包的数量只是
audioDataSize/bytesPerPacket
。后一个值包含在音频描述中。前者通常直接嵌入到文件中,但允许它是
-1
,音频数据作为最后一个块,在这种情况下,它的大小是
totalFileSize-startOfAudioData

它分解如下:

  • 如果有数据包表块,请使用它和音频描述:
    seconds=validFrames/sampleRate
  • 否则,数据包必须具有恒定的大小:
    • framesPerByte=framesPerPacket/bytesPerPacket
    • seconds=framesPerByte*audioDataSize
您拥有的库读取音频描述块,但我认为它不会读取数据包表。此外,如果区块为-1,我不确定它会计算音频数据大小。也许两者都有,在这种情况下,你可以使用上面的信息

如果没有,您可以自己解析文件,特别是如果您只关心持续时间的话。该文件以一个短头开始,然后被分成“块”(又名TLV)。 以下是一个示例实现,您可以将其用作起点或修改链接的库:



func readCAF(){
buf:=[]字节{
//文件头
'c','a','f','f',//文件类型
0x0,0x1,0x0,0x0,//文件版本,标志
//音频描述
'd','e','s','c',//块类型
0x0,0x0,0x0,0x0,
0x0,0x0,0x0,0x20,//格式大小
0x40、0xe5、0x88、0x80、,
0x00,0x00,0x00,0x00,//采样率
'l','p','c','m',//fmt id
0x0,0x0,0x0,0x0,//fmt标志
0x0,0x0,0x0,0x1,//每个数据包的字节数
0x0,0x0,0x0,0x1,//每个数据包的帧数
0x0,0x0,0x0,0x2,//每帧通道数
0x0,0x0,0x0,0x3,//每个通道位
//音频数据
'd','a','t','a',//块类型
0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,//数据段的大小(-1=til EOF)
//实际音频数据包(无论如何,理论上)
0x0,
0x0,
0x0,
0x0,
0x0,
0x0,
}
文件大小:=len(buf)
br:=bufio.NewReader(bytes.NewBuffer(buf))
类型cafHdr结构{
类型[4]字节
版本uint16
_uint16
}
类型chunkHdr struct{
类型[4]字节
Sz int64
}
类型audioDescription结构{
FramesPerSec浮动64
FmtId uint32
FmtFlags uint32
字节数Peruint32
帧分组uint32
信道帧uint32
比特通道uint32
}
类型packetTable结构{
n数据包,NValidFrames,NPrimingFr,nRepainingFR int64
}
常数FileHeaderSz=8
常量ChunkHeaderSz=12
常数AudioDescSz=32
常数PacketHdrSz=24
fileHdr:=cafHdr{}
如果错误:=binary.Read(br,binary.BigEndian,&fileHdr);错误!=nil{
恐慌(错误)
}
如果fileHdr.Typ!=[4]字节{'c','a','f','f'}| | fileHdr.Version!=1{
恐慌(“未知文件格式”)
}
剩余:=int64(文件大小)-FileHeaderSz
audioDesc:=audioDescription{}
packetTab:=packetTable{}
var audioDataSz int64
读块:
为了{
hdr:=chunkHdr{}
如果错误:=binary.Read(br,binary.BigEndian,&hdr);错误!=nil{
恐慌(错误)
}
剩余-=ChunkHeaderSz
开关hdr.Typ{
案例[4]字节{'d','e','s','c'}://音频描述
如果错误:=binary.Read(br,binary.BigEndian,&audioDesc);错误!=nil{
恐慌(错误)
}
hdr.Sz-=AudioDescSz
剩余-=AudioDescSz
案例[4]字节{'p','a','k','t'}://数据包表
如果错误:=binary.Read(br,binary.BigEndian,&packetTab);错误!=nil{
恐慌(错误)
}
hdr.Sz-=PacketHdrSz
剩余-=包装HDRSZ
案例[4]字节{'d','a','t','a'}://音频数据
如果hdr.Sz>0{
audioDataSz=hdr.Sz
}否则,如果hdr.Sz==-1{
//如果需要,读取EOF以确定字节大小
audioDataSz=剩余
断开读块
}
}
如果hdr.Sz<0{
死机(“无效的头大小”)
}
剩余-=hdr.Sz
//跳到下一块。在32位机器上,Sz可能溢出,
//所以你应该检查一下(如果你是