mirror of
https://github.com/fxkt-tech/liv
synced 2025-09-26 20:11:20 +08:00
feat: loudnorm
This commit is contained in:
75
examples/ffmpeg/loudnorm/main.go
Normal file
75
examples/ffmpeg/loudnorm/main.go
Normal 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)
|
||||
}
|
||||
}
|
@@ -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
|
||||
}
|
||||
|
@@ -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,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user