Spaces:
Running
Running
package audio | |
import ( | |
"bytes" | |
"encoding/binary" | |
"errors" | |
"fmt" | |
"io" | |
"math" | |
"os" | |
"path/filepath" | |
) | |
/* | |
-----RIFF----- | |
RIFF 4 标头字母 | |
CHUNK_SIZE 4 整个RIFF文件的长度(不包含RIFF和CHUNK_SIZE这两个字段的长度) | |
FORMAT 4 格式,WAVE代表是wav文件,WAVE格式要求带有标头为fmt和data的子chunk | |
-----FMT ----- | |
SUB_CHUNK_ID 4 子chunk的标头字母,此处为"fmt "(注意,fmt后面是带一个空格的),其相当于wav的属性字段 | |
SUB_CHUNK_SIZE 4 此subchunk的长度(不包含SUB_CHUNK_ID和SUB_CHUNK_SIZE两个字段的长度) | |
AUDIO_FORMAT 2 音频格式,pcm为1 | |
NUM_CHANNELS 2 声道数量,理论上可以n声道,一般我们用单声道mono,或者双声道stereo(双声道也叫立体声) | |
SAMPLE_RATE 4 采样率,每秒采样多少次,通常都有固定的采样选择(8000, 11025,12000,16000,22050,24000,32000,44100,48000) | |
BYTE_RATE 4 码率,即每秒播放多少byte数据,计算公式=SAMPLE_RATE*NUM_CHANNELS*BITS_PER_SAMPLE/8(不明白为何需要这个字段) | |
BLOCK_ALIGN 2 块对其,其值=BITS_PER_SAMPLE*NUM_CHANNELS/8 | |
BITS_PER_SAMPLE 2 每个采样多少bit,通常为8,16,32(为8时候代表的是uint8,16代表的是int16,32代表float32) | |
-----DATA----- | |
SUB_CHUNK_ID 4 子chunk的标头字母,此处为"data" | |
SUB_CHUNK_SIZE 4 此subchunk的长度(不包含SUB_CHUNK_ID和SUB_CHUNK_SIZE两个字段的长度) | |
DATA pcm的数据 | |
------------- | |
*/ | |
//MaxChannelNum 最大声道数量(此处只允许2) | |
const MaxChannelNum = 2 | |
const ( | |
LeftChannel = 0 | |
RightChannel =1 | |
) | |
//tag tag定义 | |
type tag [4]byte | |
//一些变量 | |
var ( | |
tagRIFF = tag{'R', 'I', 'F', 'F'} // "RIFF" | |
tagWAVE = tag{'W', 'A', 'V', 'E'} // "WAVE" | |
tagFmt = tag{'f', 'm', 't', ' '} // "fmt " | |
tagData = tag{'d', 'a', 't', 'a'} // "data" | |
) | |
//WavHeaderType wav子部头结构 | |
type WavHeaderType struct { | |
ID tag | |
Size uint32 | |
} | |
//String 打印 | |
func (wavHeader *WavHeaderType) String() string { | |
return fmt.Sprintf("ID=%s,Size=%d", string(wavHeader.ID[:]), wavHeader.Size) | |
} | |
//头部size | |
var ( | |
sizeHeader = binary.Size(WavHeaderType{}) | |
) | |
//chunkLoc ... | |
type chunkLoc struct { | |
pos int64 | |
size int64 | |
} | |
//RiffType ... | |
type RiffType struct { | |
WavHeaderType | |
Fmt tag | |
} | |
//String ... | |
func (riff *RiffType) String() string { | |
return fmt.Sprintf("ID=%s,Size=%d,Fmt=%s", string(riff.ID[:]), riff.Size, string(riff.Fmt[:])) | |
} | |
//WavFmtType wav格式结构(头部) | |
type WavFmtType struct { | |
WavHeaderType | |
AudioFormat uint16 | |
Channels uint16 | |
SampleRate uint32 | |
BytesPerSec uint32 | |
BytesPerBlock uint16 | |
BitsPerSample uint16 | |
} | |
//String ... | |
func (wavFmt *WavFmtType) String() string { | |
return fmt.Sprintf( | |
"ID=%s,Size=%d,AudioFormat=%d,Channels=%d,SampleRate=%d,BytesPerSec=%d,BytesPerBlock=%d,BitsPerSample=%d", | |
string(wavFmt.ID[:]), wavFmt.Size, wavFmt.AudioFormat, wavFmt.Channels, wavFmt.SampleRate, | |
wavFmt.BytesPerSec, wavFmt.BytesPerBlock, wavFmt.BitsPerSample) | |
} | |
//SampleType 采样结构 | |
type SampleType struct { | |
val8s [MaxChannelNum]uint8 | |
val16s [MaxChannelNum]int16 | |
val32s [MaxChannelNum]float32 | |
} | |
//WavDataType wav整体结构(头部+采样数据结构) | |
type WavDataType struct { | |
WavHeaderType | |
Sample []SampleType | |
} | |
//String ... | |
func (wavData *WavDataType) String() string { | |
blockNum := len(wavData.Sample) | |
return fmt.Sprintf("ID=%s,Size=%d,BlockNum=%d", string(wavData.ID[:]), wavData.Size, blockNum) | |
} | |
//WavInfoType wav操作实例 | |
type WavInfoType struct { | |
Riff RiffType | |
Fmt WavFmtType | |
Data WavDataType | |
//create info | |
createMs int64 | |
} | |
//String ... | |
func (wavInfo *WavInfoType) String() string { | |
f := &wavInfo.Fmt | |
blockNum := len(wavInfo.Data.Sample) | |
return fmt.Sprintf("SampleRate=%d,BitsPerSample=%d,Channels=%d,BlockNum=%d", | |
f.SampleRate, f.BitsPerSample, f.Channels, blockNum) | |
} | |
//SetCreateTs ... | |
func (wavInfo *WavInfoType) SetCreateTs(timestampMs int64) { | |
wavInfo.createMs = timestampMs | |
} | |
//CopyFormat 复制头部结构 | |
func (wavInfo *WavInfoType) CopyFormat(w *WavInfoType) (err error) { | |
wavInfo.Riff.ID = w.Riff.ID | |
wavInfo.Riff.Size = w.Riff.Size | |
wavInfo.Riff.Fmt = w.Riff.Fmt | |
wavInfo.Fmt.ID = w.Fmt.ID | |
wavInfo.Fmt.Size = w.Fmt.Size | |
wavInfo.Fmt.AudioFormat = w.Fmt.AudioFormat | |
wavInfo.Fmt.Channels = w.Fmt.Channels | |
wavInfo.Fmt.SampleRate = w.Fmt.SampleRate | |
wavInfo.Fmt.BytesPerSec = w.Fmt.BytesPerSec | |
wavInfo.Fmt.BytesPerBlock = w.Fmt.BytesPerBlock | |
wavInfo.Fmt.BitsPerSample = w.Fmt.BitsPerSample | |
wavInfo.Data.ID = w.Data.ID | |
wavInfo.Data.Size = w.Data.Size | |
return | |
} | |
//ParseFromFile 从文件中导入 | |
func (wavInfo *WavInfoType) ParseFromFile(absFile string) (err error) { | |
absFile, err = filepath.Abs(absFile) //#nosec | |
if err != nil { | |
return err | |
} | |
fileHandler, err := os.Open(absFile) //#nosec | |
if err != nil { | |
return err | |
} | |
defer fileHandler.Close() | |
_, err = fileHandler.Seek(0, os.SEEK_SET) | |
if err != nil { | |
return err | |
} | |
var pos int64 | |
var ch WavHeaderType | |
//----------------------------------------------------- | |
// RIFF header | |
err = binary.Read(fileHandler, binary.LittleEndian, &wavInfo.Riff) | |
if err != nil { | |
return err | |
} | |
pos += int64(sizeHeader) + int64(len(tagWAVE)) | |
if wavInfo.Riff.ID != tagRIFF { | |
return errors.New("File Format Not Riff") | |
} | |
if wavInfo.Riff.Fmt != tagWAVE { | |
return errors.New("File Format Not Wave") | |
} | |
fileSize := int64(sizeHeader) + int64(wavInfo.Riff.Size) | |
_ = fileSize | |
//r := &wavInfo.Riff | |
//----------------------------------------------------- | |
// read all chunks | |
var chunks = make(map[tag]*chunkLoc) | |
for { | |
err = binary.Read(fileHandler, binary.LittleEndian, &ch) | |
if err != nil { | |
if err == io.EOF { | |
break | |
} | |
return err | |
} | |
pos += int64(sizeHeader) | |
loc := chunkLoc{ | |
pos: pos, | |
size: int64(ch.Size), | |
} | |
_, err = fileHandler.Seek(loc.size, os.SEEK_CUR) | |
if err != nil { | |
return err | |
} | |
pos += loc.size // chunk data | |
chunks[ch.ID] = &loc | |
} | |
// check fileHandler size | |
if pos != fileSize { | |
return errors.New("pos != fileSize") | |
} | |
//----------------------------------------------------- | |
// chunk fmt_ | |
loc, ok := chunks[tagFmt] | |
if !ok { | |
return errors.New("wav: has not chunk \"fmt \"") | |
} | |
_, err = fileHandler.Seek(loc.pos-int64(sizeHeader), os.SEEK_SET) | |
if err != nil { | |
return err | |
} | |
err = binary.Read(fileHandler, binary.LittleEndian, &wavInfo.Fmt) | |
if err != nil { | |
return err | |
} | |
//----------------------------------------------------- | |
// chunk data | |
loc, ok = chunks[tagData] | |
if !ok { | |
return errors.New("wav: has not chunk \"data\"") | |
} | |
_, err = fileHandler.Seek(loc.pos, os.SEEK_SET) | |
if err != nil { | |
return err | |
} | |
channel := wavInfo.Fmt.Channels | |
bytePerSample := wavInfo.Fmt.BitsPerSample / 8 | |
blockSize := channel * bytePerSample | |
wavInfo.Data.ID = tagData | |
wavInfo.Data.Size = uint32(loc.size) | |
blockNum := wavInfo.Data.Size / uint32(blockSize) | |
wavInfo.Data.Sample = make([]SampleType, blockNum) | |
_, err = fileHandler.Seek(loc.pos, os.SEEK_SET) | |
if err != nil { | |
return err | |
} | |
blockIdx := 0 | |
for i := 0; i < int(wavInfo.Data.Size); i += int(blockSize) { | |
sample := &wavInfo.Data.Sample[blockIdx] | |
blockIdx++ | |
for c := 0; c < int(channel); c++ { | |
switch bytePerSample { | |
case 1: | |
var val uint8 | |
err = binary.Read(fileHandler, binary.LittleEndian, &val) | |
//sample.val8s = append(sample.val8s,val) | |
sample.val8s[c] = val | |
case 2: | |
var val int16 | |
err = binary.Read(fileHandler, binary.LittleEndian, &val) | |
//sample.val16s = append(sample.val16s,val) | |
sample.val16s[c] = val | |
//fmt.Printf("pos=%d,val=%d\n",i,sample.val16s) | |
case 4: | |
var val float32 | |
err = binary.Read(fileHandler, binary.LittleEndian, &val) | |
//sample.val32s = append(sample.val32s,val) | |
sample.val32s[c] = val | |
} | |
if err != nil { | |
return err | |
} | |
} | |
//wavInfo.Data.Sample = append(wavInfo.Data.Sample,sample) | |
} | |
//for i:=0;i<len(wavInfo.Data.Sample);i++{ | |
// sample := &wavInfo.Data.Sample[i] | |
// fmt.Printf("idx=%d,left=%d,right=%d\n",i,sample.val16s[0],sample.val16s[1]) | |
//} | |
for id, chunk := range chunks { | |
if id != tagData && id != tagFmt { | |
//util.MainLogger.Error(fmt.Sprintf("unkonw chunk=%s,size=%d\n", string(id[:]), chunk.size)) | |
fmt.Printf("unkonw chunk=%s,size=%d\n", string(id[:]), chunk.size) | |
} | |
} | |
return | |
} | |
//ParseFromBuffer 从buffer中导入 | |
func (wavInfo *WavInfoType) ParseFromBuffer(buffer []byte) (err error) { | |
var ch WavHeaderType | |
bufferReader := bytes.NewBuffer(buffer) | |
//----------------------------------------------------- | |
// RIFF header | |
err = binary.Read(bufferReader, binary.LittleEndian, &wavInfo.Riff) | |
if err != nil { | |
return err | |
} | |
//pos := int64(sizeHeader) + int64(len(tagWAVE)) | |
if wavInfo.Riff.ID != tagRIFF { | |
return errors.New("File Format Not Riff") | |
} | |
if wavInfo.Riff.Fmt != tagWAVE { | |
return errors.New("File Format Not Wave") | |
} | |
fileSize := int64(sizeHeader) + int64(wavInfo.Riff.Size) | |
_ = fileSize | |
//r := &wavInfo.Riff | |
//----------------------------------------------------- | |
// read all chunks | |
for { | |
err = binary.Read(bufferReader, binary.LittleEndian, &ch) | |
if err != nil { | |
if err == io.EOF { | |
//文件读取结束 | |
err = nil | |
return | |
} | |
return err | |
} | |
//fmt格式的chunk | |
if ch.ID == tagFmt { | |
wavInfo.Fmt.WavHeaderType = ch | |
err = binary.Read(bufferReader, binary.LittleEndian, &wavInfo.Fmt.AudioFormat) | |
err = binary.Read(bufferReader, binary.LittleEndian, &wavInfo.Fmt.Channels) | |
err = binary.Read(bufferReader, binary.LittleEndian, &wavInfo.Fmt.SampleRate) | |
err = binary.Read(bufferReader, binary.LittleEndian, &wavInfo.Fmt.BytesPerSec) | |
err = binary.Read(bufferReader, binary.LittleEndian, &wavInfo.Fmt.BytesPerBlock) | |
err = binary.Read(bufferReader, binary.LittleEndian, &wavInfo.Fmt.BitsPerSample) | |
if err != nil { | |
return err | |
} | |
continue | |
} | |
//data格式的chunk | |
if ch.ID == tagData { | |
channel := wavInfo.Fmt.Channels | |
bytePerSample := wavInfo.Fmt.BitsPerSample / 8 | |
blockSize := channel * bytePerSample | |
wavInfo.Data.ID = tagData | |
wavInfo.Data.Size = ch.Size | |
blockNum := wavInfo.Data.Size / uint32(blockSize) | |
wavInfo.Data.Sample = make([]SampleType, blockNum) | |
blockIdx := 0 | |
for i := 0; i < int(wavInfo.Data.Size); i += int(blockSize) { | |
sample := &wavInfo.Data.Sample[blockIdx] | |
blockIdx++ | |
for c := 0; c < int(channel); c++ { | |
switch bytePerSample { | |
case 1: | |
var val uint8 | |
err = binary.Read(bufferReader, binary.LittleEndian, &val) | |
//sample.val8s = append(sample.val8s,val) | |
sample.val8s[c] = val | |
case 2: | |
var val int16 | |
err = binary.Read(bufferReader, binary.LittleEndian, &val) | |
//sample.val16s = append(sample.val16s,val) | |
sample.val16s[c] = val | |
//fmt.Printf("pos=%d,val=%d\n",i,sample.val16s) | |
case 4: | |
var val float32 | |
err = binary.Read(bufferReader, binary.LittleEndian, &val) | |
//sample.val32s = append(sample.val32s,val) | |
sample.val32s[c] = val | |
} | |
if err != nil { | |
return err | |
} | |
} | |
//wavInfo.Data.Sample = append(wavInfo.Data.Sample,sample) | |
} | |
continue | |
} | |
//其他格式的chunk | |
byteData := make([]byte, ch.Size) | |
err = binary.Read(bufferReader, binary.LittleEndian, byteData) | |
} | |
return | |
} | |
//SaveToFile 保存到文件中 | |
func (wavInfo *WavInfoType) SaveToFile(absFile string) (err error) { | |
file, err := os.Create(absFile) | |
if err != nil { | |
return | |
} | |
defer file.Close() | |
_, err = file.Seek(0, os.SEEK_SET) | |
if err != nil { | |
return | |
} | |
//calc size | |
wavInfo.Data.Size = (uint32(wavInfo.Fmt.Channels) * uint32(wavInfo.Fmt.BitsPerSample) / 8) * | |
uint32(len(wavInfo.Data.Sample)) | |
wavInfo.Fmt.Size = uint32(binary.Size(wavInfo.Fmt)) - uint32(sizeHeader) | |
wavInfo.Riff.Size = uint32(len(wavInfo.Riff.Fmt)) + | |
uint32(sizeHeader) + | |
wavInfo.Fmt.Size + | |
uint32(sizeHeader) + | |
wavInfo.Data.Size | |
//---------------------------------------- | |
// RIFF header | |
err = binary.Write(file, binary.LittleEndian, wavInfo.Riff) | |
if err != nil { | |
return err | |
} | |
//---------------------------------------- | |
// chunk fmt_ | |
wavInfo.Fmt.Size = uint32(binary.Size(wavInfo.Fmt)) - uint32(sizeHeader) | |
err = binary.Write(file, binary.LittleEndian, wavInfo.Fmt) | |
if err != nil { | |
return err | |
} | |
//---------------------------------------- | |
// chunk data | |
ch := WavHeaderType{ | |
ID: tagData, | |
Size: wavInfo.Data.Size, | |
} | |
err = binary.Write(file, binary.LittleEndian, ch) | |
if err != nil { | |
return err | |
} | |
channel := wavInfo.Fmt.Channels | |
bytePerSample := wavInfo.Fmt.BitsPerSample / 8 | |
//blockSize := channel*bytePerSample | |
for i := 0; i < len(wavInfo.Data.Sample); i++ { | |
sample := &wavInfo.Data.Sample[i] | |
switch bytePerSample { | |
case 1: | |
for c := 0; c < int(channel); c++ { | |
err = binary.Write(file, binary.LittleEndian, sample.val8s[c]) | |
if err != nil { | |
return | |
} | |
} | |
case 2: | |
for c := 0; c < int(channel); c++ { | |
err = binary.Write(file, binary.LittleEndian, sample.val16s[c]) | |
if err != nil { | |
return | |
} | |
} | |
case 4: | |
for c := 0; c < int(channel); c++ { | |
err = binary.Write(file, binary.LittleEndian, sample.val32s[c]) | |
if err != nil { | |
return | |
} | |
} | |
} | |
} | |
return | |
} | |
//SaveToBuffer 保存到buffer中 | |
func (wavInfo *WavInfoType) SaveToBuffer() (buffer []byte, err error) { | |
//var bufferWriter bytes.Buffer | |
buffer = make([]byte, 0) | |
bufferWriter := bytes.NewBuffer(buffer) | |
//calc size | |
wavInfo.Data.Size = (uint32(wavInfo.Fmt.Channels) * uint32(wavInfo.Fmt.BitsPerSample) / 8) * | |
uint32(len(wavInfo.Data.Sample)) | |
wavInfo.Fmt.Size = uint32(binary.Size(wavInfo.Fmt)) - uint32(sizeHeader) | |
wavInfo.Riff.Size = uint32(len(wavInfo.Riff.Fmt)) + | |
uint32(sizeHeader) + | |
wavInfo.Fmt.Size + | |
uint32(sizeHeader) + | |
wavInfo.Data.Size | |
//---------------------------------------- | |
// RIFF header | |
err = binary.Write(bufferWriter, binary.LittleEndian, wavInfo.Riff) | |
if err != nil { | |
return | |
} | |
//---------------------------------------- | |
// chunk fmt_ | |
wavInfo.Fmt.Size = uint32(binary.Size(wavInfo.Fmt)) - uint32(sizeHeader) | |
err = binary.Write(bufferWriter, binary.LittleEndian, wavInfo.Fmt) | |
if err != nil { | |
return | |
} | |
//---------------------------------------- | |
// chunk data | |
ch := WavHeaderType{ | |
ID: tagData, | |
Size: wavInfo.Data.Size, | |
} | |
err = binary.Write(bufferWriter, binary.LittleEndian, ch) | |
if err != nil { | |
return | |
} | |
channel := wavInfo.Fmt.Channels | |
bytePerSample := wavInfo.Fmt.BitsPerSample / 8 | |
//blockSize := channel*bytePerSample | |
for i := 0; i < len(wavInfo.Data.Sample); i++ { | |
sample := &wavInfo.Data.Sample[i] | |
switch bytePerSample { | |
case 1: | |
for c := 0; c < int(channel); c++ { | |
err = binary.Write(bufferWriter, binary.LittleEndian, sample.val8s[c]) | |
if err != nil { | |
return | |
} | |
} | |
case 2: | |
for c := 0; c < int(channel); c++ { | |
err = binary.Write(bufferWriter, binary.LittleEndian, sample.val16s[c]) | |
if err != nil { | |
return | |
} | |
} | |
case 4: | |
for c := 0; c < int(channel); c++ { | |
err = binary.Write(bufferWriter, binary.LittleEndian, sample.val32s[c]) | |
if err != nil { | |
return | |
} | |
} | |
} | |
} | |
buffer = bufferWriter.Bytes() | |
return | |
} | |
//SaveToBuffer 保存到buffer中 | |
func (wavInfo *WavInfoType) SaveToBufferWithChannel(voiceChannel int) (buffer []byte, err error) { | |
//var bufferWriter bytes.Buffer | |
buffer = make([]byte, 0) | |
bufferWriter := bytes.NewBuffer(buffer) | |
//calc size | |
channel := wavInfo.Fmt.Channels | |
wavInfo.Fmt.Channels = 1 | |
wavInfo.Data.Size = (uint32(wavInfo.Fmt.Channels) * uint32(wavInfo.Fmt.BitsPerSample) / 8) * | |
uint32(len(wavInfo.Data.Sample)) | |
wavInfo.Fmt.Size = uint32(binary.Size(wavInfo.Fmt)) - uint32(sizeHeader) | |
wavInfo.Riff.Size = uint32(len(wavInfo.Riff.Fmt)) + | |
uint32(sizeHeader) + | |
wavInfo.Fmt.Size + | |
uint32(sizeHeader) + | |
wavInfo.Data.Size | |
//---------------------------------------- | |
// RIFF header | |
err = binary.Write(bufferWriter, binary.LittleEndian, wavInfo.Riff) | |
if err != nil { | |
return | |
} | |
//---------------------------------------- | |
// chunk fmt_ | |
wavInfo.Fmt.Size = uint32(binary.Size(wavInfo.Fmt)) - uint32(sizeHeader) | |
err = binary.Write(bufferWriter, binary.LittleEndian, wavInfo.Fmt) | |
if err != nil { | |
return | |
} | |
//---------------------------------------- | |
// chunk data | |
ch := WavHeaderType{ | |
ID: tagData, | |
Size: wavInfo.Data.Size, | |
} | |
err = binary.Write(bufferWriter, binary.LittleEndian, ch) | |
if err != nil { | |
return | |
} | |
bytePerSample := wavInfo.Fmt.BitsPerSample / 8 | |
//blockSize := channel*bytePerSample | |
for i := 0; i < len(wavInfo.Data.Sample); i++ { | |
sample := &wavInfo.Data.Sample[i] | |
switch bytePerSample { | |
case 1: | |
for c := 0; c < int(channel); c++ { | |
if voiceChannel == c { | |
err = binary.Write(bufferWriter, binary.LittleEndian, sample.val8s[c]) | |
if err != nil { | |
return | |
} | |
} | |
} | |
case 2: | |
for c := 0; c < int(channel); c++ { | |
if voiceChannel == c { | |
err = binary.Write(bufferWriter, binary.LittleEndian, sample.val16s[c]) | |
if err != nil { | |
return | |
} | |
} | |
} | |
case 4: | |
for c := 0; c < int(channel); c++ { | |
if voiceChannel == c { | |
err = binary.Write(bufferWriter, binary.LittleEndian, sample.val32s[c]) | |
if err != nil { | |
return | |
} | |
} | |
} | |
} | |
} | |
buffer = bufferWriter.Bytes() | |
wavInfo.Fmt.Channels = 2 | |
return | |
} | |
//SaveToFile 保存到文件中 | |
func (wavInfo *WavInfoType) SaveToFileWithChannel(absFile string, voiceChannel int) (err error) { | |
file, err := os.Create(absFile) | |
if err != nil { | |
return | |
} | |
defer file.Close() | |
_, err = file.Seek(0, io.SeekStart) | |
if err != nil { | |
return | |
} | |
//calc size | |
channel := wavInfo.Fmt.Channels | |
wavInfo.Fmt.Channels = 1 | |
wavInfo.Data.Size = (uint32(wavInfo.Fmt.Channels) * uint32(wavInfo.Fmt.BitsPerSample) / 8) * | |
uint32(len(wavInfo.Data.Sample)) | |
wavInfo.Fmt.Size = uint32(binary.Size(wavInfo.Fmt)) - uint32(sizeHeader) | |
wavInfo.Riff.Size = uint32(len(wavInfo.Riff.Fmt)) + | |
uint32(sizeHeader) + | |
wavInfo.Fmt.Size + | |
uint32(sizeHeader) + | |
wavInfo.Data.Size | |
//---------------------------------------- | |
// RIFF header | |
err = binary.Write(file, binary.LittleEndian, wavInfo.Riff) | |
if err != nil { | |
return err | |
} | |
//---------------------------------------- | |
// chunk fmt_ | |
wavInfo.Fmt.Size = uint32(binary.Size(wavInfo.Fmt)) - uint32(sizeHeader) | |
err = binary.Write(file, binary.LittleEndian, wavInfo.Fmt) | |
if err != nil { | |
return err | |
} | |
//---------------------------------------- | |
// chunk data | |
ch := WavHeaderType{ | |
ID: tagData, | |
Size: wavInfo.Data.Size, | |
} | |
err = binary.Write(file, binary.LittleEndian, ch) | |
if err != nil { | |
return err | |
} | |
bytePerSample := wavInfo.Fmt.BitsPerSample / 8 | |
//blockSize := channel*bytePerSample | |
for i := 0; i < len(wavInfo.Data.Sample); i++ { | |
sample := &wavInfo.Data.Sample[i] | |
switch bytePerSample { | |
case 1: | |
for c := 0; c < int(channel); c++ { | |
if voiceChannel == c { | |
err = binary.Write(file, binary.LittleEndian, sample.val8s[c]) | |
if err != nil { | |
return | |
} | |
} | |
} | |
case 2: | |
for c := 0; c < int(channel); c++ { | |
if voiceChannel == c { | |
err = binary.Write(file, binary.LittleEndian, sample.val16s[c]) | |
if err != nil { | |
return | |
} | |
} | |
} | |
case 4: | |
for c := 0; c < int(channel); c++ { | |
if voiceChannel == c { | |
err = binary.Write(file, binary.LittleEndian, sample.val32s[c]) | |
if err != nil { | |
return | |
} | |
} | |
} | |
} | |
} | |
wavInfo.Fmt.Channels = 2 | |
return | |
} | |
//AdjusterVolume 调整音量 | |
func (wavInfo *WavInfoType) AdjusterVolume(rateAsRaw float32) { | |
channel := wavInfo.Fmt.Channels | |
bytePerSample := wavInfo.Fmt.BitsPerSample / 8 | |
//blockSize := channel*bytePerSample | |
const MaxUint8 = math.MaxUint8 | |
const MinUint8 = 0 | |
const MaxInt16 = math.MaxInt16 | |
const MinInt16 = math.MinInt16 | |
for i := 0; i < len(wavInfo.Data.Sample); i++ { | |
switch bytePerSample { | |
case 1: | |
for c := 0; c < int(channel); c++ { | |
val := wavInfo.Data.Sample[i].val8s[c] | |
clip := float64(val) * float64(rateAsRaw) | |
if clip < MinUint8 { | |
clip = MinUint8 | |
} | |
if clip > MaxUint8 { | |
clip = MaxUint8 | |
} | |
//wavInfo.Data.Sample[i].val8s[c] = uint8((float32(val) * rateAsRaw)) | |
wavInfo.Data.Sample[i].val8s[c] = uint8(clip) | |
} | |
case 2: | |
for c := 0; c < int(channel); c++ { | |
//val := wavInfo.Data.Sample[i].val16s[c] | |
//wavInfo.Data.Sample[i].val16s[c] = int16((float32(val) * rateAsRaw)) | |
val := wavInfo.Data.Sample[i].val16s[c] | |
clip := float64(val) * float64(rateAsRaw) | |
if clip < MinInt16 { | |
clip = MinInt16 | |
} | |
if clip > MaxInt16 { | |
clip = MaxInt16 | |
} | |
wavInfo.Data.Sample[i].val16s[c] = int16(clip) | |
} | |
case 4: | |
for c := 0; c < int(channel); c++ { | |
val := wavInfo.Data.Sample[i].val32s[c] | |
wavInfo.Data.Sample[i].val32s[c] = val * rateAsRaw | |
} | |
} | |
} | |
} | |
//Resample 重置采样率 | |
func (wavInfo *WavInfoType) Resample(resampleRate uint32) { | |
sampleRate := wavInfo.Fmt.SampleRate | |
rate := float64(sampleRate) / float64(resampleRate) | |
channel := wavInfo.Fmt.Channels | |
bytePerSample := wavInfo.Fmt.BitsPerSample / 8 | |
rawLen := len(wavInfo.Data.Sample) | |
resampleData := make([]SampleType, 0) | |
resampleIdx := 0 | |
for { | |
rawIdx := int(float64(resampleIdx) * rate) | |
if rawIdx < rawLen { | |
sample := SampleType{} | |
for c := 0; c < int(channel); c++ { | |
switch bytePerSample { | |
case 1: | |
val := wavInfo.Data.Sample[rawIdx].val8s[c] | |
//sample.val8s = append(sample.val8s,val) | |
sample.val8s[c] = val | |
case 2: | |
val := wavInfo.Data.Sample[rawIdx].val16s[c] | |
//sample.val16s = append(sample.val16s,val) | |
sample.val16s[c] = val | |
case 4: | |
val := wavInfo.Data.Sample[rawIdx].val32s[c] | |
//sample.val32s = append(sample.val32s,val) | |
sample.val32s[c] = val | |
} | |
} | |
resampleData = append(resampleData, sample) | |
} else { | |
break | |
} | |
resampleIdx++ | |
} | |
wavInfo.Data.Sample = resampleData | |
wavInfo.Fmt.SampleRate = resampleRate | |
} | |
//ConvertToFloat32 将采样值转换到 0 到 1 之间 | |
func (wavInfo *WavInfoType) GetFloat32Samples(channel int, bytePerSample int) []float32 { | |
//fmt.Println(wavInfo.cha) | |
var floatSamples []float32 | |
var point float32 | |
for i := 0; i < len(wavInfo.Data.Sample); i++ { | |
sample := &wavInfo.Data.Sample[i] | |
switch bytePerSample { | |
case 1: | |
point = float32(sample.val8s[channel]) / (1 << 8) | |
case 2: | |
point = float32(sample.val16s[channel]) / (1 << 15) | |
case 4: | |
point = sample.val32s[channel] | |
} | |
floatSamples = append(floatSamples, point) | |
} | |
return floatSamples | |
} | |
//Trim 切头切尾 | |
func (wavInfo *WavInfoType) Trim(dbPercent float32) { | |
channel := wavInfo.Fmt.Channels | |
bytePerSample := wavInfo.Fmt.BitsPerSample / 8 | |
//blockSize := channel*bytePerSample | |
const MaxUint8 = math.MaxUint8 | |
const MinUint8 = 0 | |
const MaxInt16 = math.MaxInt16 | |
const MinInt16 = math.MinInt16 | |
//trim head | |
silenceHeadIdx := 0 //头部静音截止位置 | |
done := false | |
for i := 0; i < len(wavInfo.Data.Sample); i++ { | |
switch bytePerSample { | |
case 1: | |
for c := 0; c < int(channel); c++ { | |
val := wavInfo.Data.Sample[i].val8s[c] | |
if float32(val) > MaxUint8*dbPercent { | |
silenceHeadIdx = i | |
done = true | |
break | |
} | |
} | |
case 2: | |
for c := 0; c < int(channel); c++ { | |
val := wavInfo.Data.Sample[i].val16s[c] | |
if float32(val) > MaxInt16*dbPercent { | |
silenceHeadIdx = i | |
done = true | |
break | |
} | |
if float32(val) < MinInt16*dbPercent { | |
silenceHeadIdx = i | |
done = true | |
break | |
} | |
} | |
case 4: | |
for c := 0; c < int(channel); c++ { | |
val := wavInfo.Data.Sample[i].val32s[c] | |
if float32(val) > math.MaxFloat32*dbPercent { | |
silenceHeadIdx = i | |
done = true | |
break | |
} | |
if val < 0 && (-val > math.MaxFloat32*dbPercent) { | |
silenceHeadIdx = i | |
done = true | |
break | |
} | |
} | |
} | |
if done { | |
break | |
} | |
} | |
//trim tail,截断尾部 | |
silenceTailIdx := len(wavInfo.Data.Sample) - 1 //尾部静音截止位置 | |
done = false | |
for i := len(wavInfo.Data.Sample) - 1; i >= 0; i-- { | |
switch bytePerSample { | |
case 1: | |
for c := 0; c < int(channel); c++ { | |
val := wavInfo.Data.Sample[i].val8s[c] | |
if float32(val) > MaxUint8*dbPercent { | |
silenceTailIdx = i | |
done = true | |
break | |
} | |
} | |
case 2: | |
for c := 0; c < int(channel); c++ { | |
val := wavInfo.Data.Sample[i].val16s[c] | |
if val >= 0 && float32(val) > float32(MaxInt16*dbPercent) { | |
silenceTailIdx = i | |
done = true | |
break | |
} | |
if val < 0 && float32(val) < float32(MinInt16*dbPercent) { | |
silenceTailIdx = i | |
done = true | |
break | |
} | |
} | |
case 4: | |
for c := 0; c < int(channel); c++ { | |
val := wavInfo.Data.Sample[i].val32s[c] | |
if float32(val) > math.MaxFloat32*dbPercent { | |
silenceTailIdx = i | |
done = true | |
break | |
} | |
if val < 0 && (-val > math.MaxFloat32*dbPercent) { | |
silenceTailIdx = i | |
done = true | |
break | |
} | |
} | |
} | |
if done { | |
break | |
} | |
} | |
wavInfo.Data.Sample = wavInfo.Data.Sample[:silenceTailIdx] | |
wavInfo.Data.Sample = wavInfo.Data.Sample[silenceHeadIdx:] | |
} | |
func (wavInfo *WavInfoType) TrimFirstWithTime(milliseconds int64) error{ | |
sampleRate := wavInfo.Fmt.SampleRate | |
sizeToTrim := int64(sampleRate) * milliseconds / 1000 | |
if int(sizeToTrim) >= len(wavInfo.Data.Sample) { | |
return errors.New("check time err") | |
} | |
wavInfo.Data.Sample = wavInfo.Data.Sample[sizeToTrim:] | |
return nil | |
} | |
func (wavInfo *WavInfoType) GetWavTime() int { | |
return int(math.Ceil(float64(len(wavInfo.Data.Sample)) / float64(wavInfo.Fmt.SampleRate))) | |
} | |
//NewWavInfo 新建一个wav操作实例 | |
func NewWavInfo() *WavInfoType { | |
w := &WavInfoType{} | |
return w | |
} | |