feat: loudnorm

This commit is contained in:
Justyer
2024-08-28 15:56:58 +08:00
parent e90d2ef0c8
commit 0232df4ab2
3 changed files with 121 additions and 2 deletions

View File

@@ -0,0 +1,75 @@
package main
import (
"context"
"fmt"
"github.com/fxkt-tech/liv/ffmpeg"
"github.com/fxkt-tech/liv/ffmpeg/codec"
"github.com/fxkt-tech/liv/ffmpeg/filter"
"github.com/fxkt-tech/liv/ffmpeg/input"
"github.com/fxkt-tech/liv/ffmpeg/output"
"github.com/fxkt-tech/liv/internal/encoding/json"
)
// ffmpeg -i voice.wav -filter_complex "loudnorm=I=-16:TP=-1:LRA=11:print_format=json" -f null -
func main() {
var (
ctx = context.Background()
// inputs
iMain = input.WithSimple("voice.wav")
// filters
fLoudnorm = filter.Loudnorm(-12, 7, -1.5).Use(iMain.A())
)
ln, err := ffmpeg.New(
ffmpeg.WithDebug(true),
ffmpeg.WithDry(true),
ffmpeg.WithLogLevel(""),
).AddInput(
iMain,
).AddFilter(
fLoudnorm,
).AddOutput(
output.New(
output.Map(fLoudnorm),
output.Format("null"),
output.File("-"),
),
).ExtractLoudnorm(ctx)
if err != nil {
fmt.Println(err)
}
fmt.Println(json.ToString(ln))
var (
// filters
fLoudnorm2 = filter.LoudnormDoublePass(
-12, 7, -1.5,
ln.InputI, ln.InputLRA, ln.InputTP, ln.InputThresh,
).Use(iMain.A())
// output
oOnly = output.New(
output.Map(fLoudnorm2),
output.AudioCodec(codec.PCMS16LE),
output.File("voice_ln.wav"),
)
)
err = ffmpeg.New(
ffmpeg.WithDebug(true),
ffmpeg.WithDry(true),
).AddInput(
iMain,
).AddFilter(
fLoudnorm2,
).AddOutput(
oOnly,
).Run(ctx)
if err != nil {
fmt.Println(err)
}
}

View File

@@ -5,11 +5,13 @@ import (
"errors"
"fmt"
"os/exec"
"regexp"
"strings"
"github.com/fxkt-tech/liv/ffmpeg/filter"
"github.com/fxkt-tech/liv/ffmpeg/input"
"github.com/fxkt-tech/liv/ffmpeg/output"
"github.com/fxkt-tech/liv/internal/encoding/json"
"github.com/fxkt-tech/liv/internal/sugar"
)
@@ -90,3 +92,35 @@ func (ff *FFmpeg) Run(ctx context.Context) (err error) {
}
return
}
type LoudnormParms struct {
InputI float32 `json:"input_i,string"`
InputTP float32 `json:"input_tp,string"`
InputLRA float32 `json:"input_lra,string"`
InputThresh float32 `json:"input_thresh,string"`
OutputI float32 `json:"output_i,string"`
OutputTP float32 `json:"output_tp,string"`
OutputLRA float32 `json:"output_lra,string"`
OutputThresh float32 `json:"output_thresh,string"`
NormalizationType string `json:"normalization_type,string"`
TargetOffset float32 `json:"target_offset,string"`
}
func (ff *FFmpeg) ExtractLoudnorm(ctx context.Context) (*LoudnormParms, error) {
if ff.debug {
ff.DryRun()
}
cc := exec.CommandContext(ctx, ff.bin, ff.Params()...)
retbytes, err := cc.CombinedOutput()
retstr := string(retbytes)
if err != nil {
return nil, errors.New(retstr)
}
reg := regexp.MustCompile("(?s)({.*})")
matches := reg.FindStringSubmatch(retstr)
if len(matches) < 2 {
return nil, errors.New(retstr)
}
return json.ToV[*LoudnormParms]([]byte(matches[1])), nil
}

View File

@@ -50,10 +50,20 @@ func AFade(t string, st, d float32) *SingleFilter {
}
}
func Loudnorm(i, tp int32) *SingleFilter {
func Loudnorm(i, lra, tp float32) *SingleFilter {
return &SingleFilter{
name: naming.Default.Gen(),
content: fmt.Sprintf("loudnorm=I=%d:TP=%d", i, tp),
content: fmt.Sprintf("loudnorm=i=%f:lra=%f:tp=%f:print_format=json", i, lra, tp),
}
}
func LoudnormDoublePass(i, lra, tp float32, mi, mlra, mtp, mthres float32) *SingleFilter {
return &SingleFilter{
name: naming.Default.Gen(),
content: fmt.Sprintf(
"loudnorm=i=%f:lra=%f:tp=%f:measured_i=%f:measured_lra=%f:measured_tp=%f:measured_thresh=%f:print_format=json",
i, lra, tp, mi, mlra, mtp, mthres,
),
}
}