Help us shape the future of Go! Take the 2026 Go Developer Survey

audio

package module
v0.0.0-...-16b7191 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 21, 2026 License: MIT Imports: 12 Imported by: 0

README

SoundKit Go

SoundKit Go 是一个纯 Go 编写的服务端音频文件处理库。它可以按文件内容读取 WAV 和 MP3,将音频解码为有界大小的交错 PCM16 数据块,并写出 PCM16 WAV;无需 FFmpeg、CGO 或本机原生库。

支持范围

输入格式 是否支持 输出格式
RIFF/WAVE PCM 16-bit 支持 PCM16 WAV
MPEG1/MPEG2 Layer III MP3 支持 PCM16 WAV
WAV 浮点、压缩 WAV 和其他位深 不支持 —
MP3 编码 V1 暂不支持 —

MP3 解码器输出双声道 PCM,包括单声道 MP3 输入。AudioInfo.Channels 表示解码后的 PCM 声道数。库按文件内容识别格式,不依赖文件扩展名。V1 支持 MPEG1/MPEG2 Layer III、开头的 ID3v2 标签和末尾的 ID3v1 标签;不支持 MPEG2.5、其他尾部元数据和非帧数据,也不会跳过任意字节重新同步 MP3 帧。

WAV 输入必须声明有效的 RIFF 和 data 块大小。对于录音器尚未完成、仍使用占位大小的 WAV 文件头,库不会据此推断 PCM 数据长度。

WAV 解码器需要 seek;因此对于不可 seek 的 WAV 输入,库会先将其暂存到临时文件。RemoveEdgeSilence、SplitOnSilence、FadeIn 和 FadeOut 也可能暂存不可 seek 的输入,以便扫描时长后再次解码。库不会关闭调用方传入的输入 reader、输出 writer,也不会关闭 OutputFactory 返回的 writer。如果操作在开始写入后返回错误,输出可能不完整,请丢弃该输出。

各项操作以固定大小的 PCM 数据块流式处理。Concat 只保留配置的交叉淡化边缘;静音检测只保留分析窗口和返回的静音区间。FadeIn 和 FadeOut 会先统计帧数,再通过第二遍解码应用限幅后的淡化包络,因此工作内存不会随淡化时长增长。

OpenDecoder 返回一个 PCMReader。ReadFrames 要求 len(dst) 能被声道数整除,返回值表示帧数而不是单个采样数。即使同时返回 io.EOF,也要先处理本次读取到的帧。MP3 的帧数和时长会随着解码更新;如果需要在处理前获得完整元数据,请使用 Inspect。

转换和检查音频信息

package example

import (
    "context"
    "fmt"
    "io"
    "os"

    audio "github.com/onlymonday/soundkit-go"
)

func convertAndInspect(ctx context.Context) error {
    input, err := os.Open("voice.mp3")
    if err != nil {
        return err
    }
    defer input.Close()

    info, err := audio.Inspect(ctx, input)
    if err != nil {
        return err
    }
    fmt.Printf("%d Hz, %d channels, %s\n", info.SampleRate, info.Channels, info.Duration)
    if _, err := input.Seek(0, io.SeekStart); err != nil {
        return err
    }

    output, err := os.Create("voice.wav")
    if err != nil {
        return err
    }
    defer output.Close()
    return audio.ConvertToWAV(ctx, input, output)
}

Inspect 会完整解码音频以统计帧数并计算精确时长,但不会将整段录音保留在内存中。

音频处理操作

  • Trim(ctx, src, dst, start, end) 按左闭右开的范围裁剪。负起始时间、空区间或反向区间、以及起点在文件末尾或之后都会返回错误;超出文件长度的结束时间会被截断。时间转换为帧索引时,会向下取整小数帧。
  • SplitByDuration(ctx, src, output, segmentLength) 按指定时长分段,写出完整分段和最后一个不足时长的分段。
  • Concat(ctx, sources, dst, crossfade) 拼接兼容的音频。交叉淡化时长为零表示直接连接;大于零时进行线性混合,且不能超过最短输入的时长。采样率或声道数不同会返回 ErrIncompatibleAudio。
  • AdjustGain(ctx, src, dst, gainDB) 按分贝调整增益,并将结果限制在有符号 16 位整数范围内。
  • FadeIn 和 FadeOut 分别应用线性淡入和淡出。负时长会被拒绝,超过音频长度的时长会截断到音频长度。
  • DetectSilence(ctx, src, options) 返回左闭右开的静音区间。
  • RemoveEdgeSilence(ctx, src, dst, options) 删除符合条件的开头和结尾静音。
  • SplitOnSilence(ctx, src, output, options) 在符合条件的内部静音区间处分段。KeepPadding 会在静音间隔两侧分配,并限制在不会造成输出片段重叠的范围内。

静音检测默认参数为:-40 dBFS 阈值、500 ms 最短静音时长、10 ms 分析窗口和 100 ms 保留边缘。建议从 DefaultSilenceOptions() 开始,再按需覆盖字段。全静音输入调用 SplitOnSilence 时不会产生输出;调用 RemoveEdgeSilence 时会写出一个空 WAV 文件。

package example

import (
    "context"
    "fmt"
    "os"

    audio "github.com/onlymonday/soundkit-go"
)

func printSilence(ctx context.Context) error {
    input, err := os.Open("voice.mp3")
    if err != nil {
        return err
    }
    defer input.Close()

    intervals, err := audio.DetectSilence(ctx, input, audio.DefaultSilenceOptions())
    if err != nil {
        return err
    }
    for _, interval := range intervals {
        fmt.Printf("静音:%s–%s\n", interval.Start, interval.End)
    }
    return nil
}

分段输出与流所有权

库不会关闭 OutputFactory 返回的 writer;请由调用方保存并关闭每个文件。

package example

import (
    "context"
    "fmt"
    "io"
    "os"
    "time"

    audio "github.com/onlymonday/soundkit-go"
)

func split(ctx context.Context) error {
    input, err := os.Open("voice.wav")
    if err != nil {
        return err
    }
    defer input.Close()

    var outputs []*os.File
    defer func() {
        for _, file := range outputs {
            _ = file.Close()
        }
    }()
    return audio.SplitByDuration(ctx, input, func(index int) (io.WriteSeeker, error) {
        file, err := os.Create(fmt.Sprintf("part-%03d.wav", index))
        if err == nil {
            outputs = append(outputs, file)
        }
        return file, err
    }, 30*time.Second)
}

V1 仅写出 PCM16 WAV;MP3 编码和 MP3 输出暂不支持。

许可证

项目代码采用 MIT 许可证,详见 LICENSE。testdata/ 中的音频样例按各自许可条款提供,来源与说明见 testdata/README.md。

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrUnsupportedFormat     = errors.New("audio: unsupported format")
	ErrMalformedAudio        = errors.New("audio: malformed audio")
	ErrInvalidPCMBuffer      = errors.New("audio: PCM buffer length must align to channel count")
	ErrInvalidTimeRange      = errors.New("audio: invalid time range")
	ErrTrimStartOutOfRange   = errors.New("audio: trim start is at or beyond EOF")
	ErrInvalidDuration       = errors.New("audio: duration must include at least one frame")
	ErrIncompatibleAudio     = errors.New("audio: incompatible sample rate or channel count")
	ErrInvalidCrossfade      = errors.New("audio: invalid crossfade duration")
	ErrInvalidGain           = errors.New("audio: gain must be finite")
	ErrInvalidSilenceOptions = errors.New("audio: invalid silence options")
)

Functions

func AdjustGain

func AdjustGain(ctx context.Context, src io.Reader, dst io.WriteSeeker, gainDB float64) (resultErr error)

func Concat

func Concat(ctx context.Context, sources []io.Reader, dst io.WriteSeeker, crossfade time.Duration) (resultErr error)

func ConvertToWAV

func ConvertToWAV(ctx context.Context, src io.Reader, dst io.WriteSeeker) (resultErr error)

func FadeIn

func FadeIn(ctx context.Context, src io.Reader, dst io.WriteSeeker, duration time.Duration) (resultErr error)

func FadeOut

func FadeOut(ctx context.Context, src io.Reader, dst io.WriteSeeker, duration time.Duration) (resultErr error)

func RemoveEdgeSilence

func RemoveEdgeSilence(ctx context.Context, src io.Reader, dst io.WriteSeeker, options SilenceOptions) (resultErr error)

func SplitByDuration

func SplitByDuration(ctx context.Context, src io.Reader, output OutputFactory, segmentLength time.Duration) (resultErr error)

func SplitOnSilence

func SplitOnSilence(ctx context.Context, src io.Reader, output OutputFactory, options SilenceOptions) (resultErr error)

func Trim

func Trim(ctx context.Context, src io.Reader, dst io.WriteSeeker, start, end time.Duration) (resultErr error)

Types

type AudioInfo

type AudioInfo struct {
	Format     Format
	SampleRate int
	Channels   int
	Frames     int64
	Duration   time.Duration
}

func Inspect

func Inspect(ctx context.Context, src io.Reader) (info AudioInfo, resultErr error)

type Format

type Format string
const (
	FormatWAV Format = "wav"
	FormatMP3 Format = "mp3"
)

type Interval

type Interval struct {
	Start time.Duration
	End   time.Duration
}

func DetectSilence

func DetectSilence(ctx context.Context, src io.Reader, options SilenceOptions) (intervals []Interval, resultErr error)

type OutputFactory

type OutputFactory func(index int) (io.WriteSeeker, error)

type PCMReader

type PCMReader interface {
	Info() AudioInfo
	ReadFrames(dst []int16) (int, error)
	Close() error
}

func OpenDecoder

func OpenDecoder(ctx context.Context, src io.Reader) (PCMReader, error)

type SilenceOptions

type SilenceOptions struct {
	ThresholdDBFS float64
	MinSilence    time.Duration
	Window        time.Duration
	KeepPadding   time.Duration
}

func DefaultSilenceOptions

func DefaultSilenceOptions() SilenceOptions

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL