ebiten 学习(9)-decode.go 解码

本文深入解析了Go语言中音频解码库audio.wav的decode.go源码,详细介绍了WAV(RIFF)格式的解码过程,包括检查文件头、按块读取、格式转换和重采样等关键步骤,帮助读者理解音频解码的实现细节。
摘要由CSDN通过智能技术生成

2021SC@SDUSC


一、前言

1.说明

audio 说明文档地址

audio package - github.com/hajimehoshi/ebiten/v2/audio - pkg.go.dev

本文中所有截图和代码均来自说明文档或 ebiten 源文件

笔者负责 audio 音频相关的代码分析

2.梗概

阅读 decode.go 的代码,分析大概的逻辑和功能。


二、解码

首先 decode.go 位于 audio.wav 包内
wav 包提供 wav(RIFF)解码器。

stream 结构体是一个解码的音频流。

在这里插入图片描述

Read 是 io.Reader 的 Read 方法的实现。

在这里插入图片描述
在这里插入图片描述

Seek 是 io.Seeker 的 Seek 方法的实现。

在这里插入图片描述
在这里插入图片描述

Length返回解码流的大小(以字节为单位)。

在这里插入图片描述

DecodeWithSampleRate 将 WAV(RIFF)数据解码为可播放流。
格式必须为1或2通道、8位或16位小端PCM。
格式转换为2个通道和16位。
解码失败或发生IO错误时,DecodeWithSampleRate 返回错误。
DecodeWithSampleRate会在必要时自动对流进行重新采样,以适合采样器。
只有当 src 是 io.Seeker 时,返回流的 Seek 方法才可用。
即使 src 实现了 io.Closer ,流也不会关闭。
关闭资源是 src 所有者的责任。

func DecodeWithSampleRate(sampleRate int, src io.Reader) (*Stream, error) {

	首先是一些报错条件
	buf := make([]byte, 12)
	n, err := io.ReadFull(src, buf)
	if n != len(buf) {
		return nil, fmt.Errorf("wav: invalid header")
	}
	if err != nil {
		return nil, err
	}
	if !bytes.Equal(buf[0:4], []byte("RIFF")) {
		return nil, fmt.Errorf("wav: invalid header: 'RIFF' not found")
	}
	if !bytes.Equal(buf[8:12], []byte("WAVE")) {
		return nil, fmt.Errorf("wav: invalid header: 'WAVE' not found")
	}

	按块读取
	dataSize := int64(0)
	headerSize := int64(len(buf))
	sampleRateFrom := 0
	sampleRateTo := 0
	mono := false
	bitsPerSample := 0
chunks:
	for {
		buf := make([]byte, 8)
		n, err := io.ReadFull(src, buf)
		if n != len(buf) {
			return nil, fmt.Errorf("wav: invalid header")
		}
		if err != nil {
			return nil, err
		}
		headerSize += 8
		size := int64(buf[4]) | int64(buf[5])<<8 | int64(buf[6])<<16 | int64(buf[7])<<24
		switch {
		case bytes.Equal(buf[0:4], []byte("fmt ")):
			// Size of 'fmt' header is usually 16, but can be more than 16.
			if size < 16 {
				return nil, fmt.Errorf("wav: invalid header: maybe non-PCM file?")
			}
			buf := make([]byte, size)
			n, err := io.ReadFull(src, buf)
			if n != len(buf) {
				return nil, fmt.Errorf("wav: invalid header")
			}
			if err != nil {
				return nil, err
			}
			format := int(buf[0]) | int(buf[1])<<8
			if format != 1 {
				return nil, fmt.Errorf("wav: format must be linear PCM")
			}
			channelNum := int(buf[2]) | int(buf[3])<<8
			switch channelNum {
			case 1:
				mono = true
			case 2:
				mono = false
			default:
				return nil, fmt.Errorf("wav: channel num must be 1 or 2 but was %d", channelNum)
			}
			bitsPerSample = int(buf[14]) | int(buf[15])<<8
			if bitsPerSample != 8 && bitsPerSample != 16 {
				return nil, fmt.Errorf("wav: bits per sample must be 8 or 16 but was %d", bitsPerSample)
			}
			origSampleRate := int64(buf[4]) | int64(buf[5])<<8 | int64(buf[6])<<16 | int64(buf[7])<<24
			if int64(sampleRate) != origSampleRate {
				sampleRateFrom = int(origSampleRate)
				sampleRateTo = sampleRate
			}
			headerSize += size
		case bytes.Equal(buf[0:4], []byte("data")):
			dataSize = size
			break chunks
		default:
			buf := make([]byte, size)
			n, err := io.ReadFull(src, buf)
			if n != len(buf) {
				return nil, fmt.Errorf("wav: invalid header")
			}
			if err != nil {
				return nil, err
			}
			headerSize += size
		}
	}
	var s io.ReadSeeker = &stream{
		src:        src,
		headerSize: headerSize,
		dataSize:   dataSize,
		remaining:  dataSize,
	}

	if mono || bitsPerSample != 16 {
		s = convert.NewStereo16(s, mono, bitsPerSample != 16)
		if mono {
			dataSize *= 2
		}
		if bitsPerSample != 16 {
			dataSize *= 2
		}
	}
	if sampleRateFrom != sampleRateTo {
		r := convert.NewResampling(s, dataSize, sampleRateFrom, sampleRateTo)
		s = r
		dataSize = r.Length()
	}
	ss := &Stream{inner: s, size: dataSize}
	return ss, nil
}

Decode 根据上下文采样率自动设置。

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值