201 lines
5.6 KiB
Go
201 lines
5.6 KiB
Go
package libebur128
|
|
|
|
/*
|
|
#cgo pkg-config: libebur128
|
|
#include <ebur128.h>
|
|
|
|
*/
|
|
import "C"
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
)
|
|
|
|
type Ebur128Mode int
|
|
|
|
const (
|
|
LoudnessMomentary Ebur128Mode = C.EBUR128_MODE_M
|
|
LoudnessShortTerm Ebur128Mode = C.EBUR128_MODE_S
|
|
LoudnessGlobalMomentary Ebur128Mode = C.EBUR128_MODE_I
|
|
LoudnessRange Ebur128Mode = C.EBUR128_MODE_LRA
|
|
SamplePeak Ebur128Mode = C.EBUR128_MODE_SAMPLE_PEAK
|
|
TruePeak Ebur128Mode = C.EBUR128_MODE_TRUE_PEAK
|
|
Histogram Ebur128Mode = C.EBUR128_MODE_HISTOGRAM
|
|
)
|
|
|
|
func GetVersion() (major, minor, patch int) {
|
|
var i, j, k C.int
|
|
C.ebur128_get_version(&i, &j, &k)
|
|
return int(i), int(j), int(k)
|
|
}
|
|
|
|
type State struct {
|
|
p *C.ebur128_state
|
|
channels int
|
|
}
|
|
|
|
func NewState(channels, sampleRate int, mode Ebur128Mode) *State {
|
|
p := C.ebur128_init(C.uint(channels), C.ulong(sampleRate), C.int(mode))
|
|
if p == nil {
|
|
return nil
|
|
}
|
|
return &State{
|
|
p: p,
|
|
channels: channels,
|
|
}
|
|
}
|
|
|
|
//GetSamplePeak Get maximum sample peak from all frames that have been processed.
|
|
func (s *State) GetSamplePeak() (out []float64, err error) {
|
|
var j C.double
|
|
for i := 0; i < s.channels; i++ {
|
|
ret := C.ebur128_sample_peak(s.p, C.uint(i), &j)
|
|
if ret != C.EBUR128_SUCCESS {
|
|
return nil, fmt.Errorf("error calculating peak: %d", ret)
|
|
}
|
|
out = append(out, float64(j))
|
|
}
|
|
return
|
|
}
|
|
|
|
//GetPreviousSamplePeak Get maximum sample peak from the last call to Add*()
|
|
func (s *State) GetPreviousSamplePeak() (out []float64, err error) {
|
|
var j C.double
|
|
for i := 0; i < s.channels; i++ {
|
|
ret := C.ebur128_prev_sample_peak(s.p, C.uint(i), &j)
|
|
if ret != C.EBUR128_SUCCESS {
|
|
return nil, fmt.Errorf("error calculating peak: %d", ret)
|
|
}
|
|
out = append(out, float64(j))
|
|
}
|
|
return
|
|
}
|
|
|
|
//GetTruePeak Get maximum true peak from all frames that have been processed.
|
|
func (s *State) GetTruePeak() (out []float64, err error) {
|
|
var j C.double
|
|
for i := 0; i < s.channels; i++ {
|
|
ret := C.ebur128_true_peak(s.p, C.uint(i), &j)
|
|
if ret != C.EBUR128_SUCCESS {
|
|
return nil, fmt.Errorf("error calculating peak: %d", ret)
|
|
}
|
|
out = append(out, float64(j))
|
|
}
|
|
return
|
|
}
|
|
|
|
//GetPreviousTruePeak Get maximum true peak from the last call to Add*()
|
|
func (s *State) GetPreviousTruePeak() (out []float64, err error) {
|
|
var j C.double
|
|
for i := 0; i < s.channels; i++ {
|
|
ret := C.ebur128_prev_true_peak(s.p, C.uint(i), &j)
|
|
if ret != C.EBUR128_SUCCESS {
|
|
return nil, fmt.Errorf("error calculating peak: %d", ret)
|
|
}
|
|
out = append(out, float64(j))
|
|
}
|
|
return
|
|
}
|
|
|
|
//GetLoudnessGlobal Get global integrated loudness in LUFS.
|
|
func (s *State) GetLoudnessGlobal() (out float64, err error) {
|
|
var i C.double
|
|
ret := C.ebur128_loudness_global(s.p, &i)
|
|
if ret != C.EBUR128_SUCCESS {
|
|
return float64(i), fmt.Errorf("error calculating loudness: %d", ret)
|
|
}
|
|
return float64(i), nil
|
|
}
|
|
|
|
//GetLoudnessGlobalMultiple Get global integrated loudness in LUFS across multiple instances.
|
|
func GetLoudnessGlobalMultiple(states []*State) (out float64, err error) {
|
|
slice := make([]*C.ebur128_state, len(states))
|
|
for i, s := range states {
|
|
slice[i] = s.p
|
|
}
|
|
var i C.double
|
|
ret := C.ebur128_loudness_global_multiple(&slice[0], C.ulong(len(slice)), &i)
|
|
if ret != C.EBUR128_SUCCESS {
|
|
return float64(i), fmt.Errorf("error calculating loudness: %d", ret)
|
|
}
|
|
return float64(i), nil
|
|
}
|
|
|
|
//SetMaxWindow Set the maximum window duration.
|
|
func (s *State) SetMaxWindow(window time.Duration) (err error) {
|
|
ret := C.ebur128_set_max_window(s.p, C.ulong(window.Milliseconds()))
|
|
if ret != C.EBUR128_SUCCESS {
|
|
return fmt.Errorf("error calculating loudness: %d", ret)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
//GetLoudnessWindow Get loudness of the specified window in LUFS.
|
|
func (s *State) GetLoudnessWindow(window time.Duration) (out float64, err error) {
|
|
var i C.double
|
|
ret := C.ebur128_loudness_window(s.p, C.ulong(window.Milliseconds()), &i)
|
|
if ret != C.EBUR128_SUCCESS {
|
|
return float64(i), fmt.Errorf("error calculating loudness: %d", ret)
|
|
}
|
|
return float64(i), nil
|
|
}
|
|
|
|
//GetLoudnessMomentary Get momentary loudness (last 400ms) in LUFS.
|
|
func (s *State) GetLoudnessMomentary() (out float64, err error) {
|
|
var i C.double
|
|
ret := C.ebur128_loudness_momentary(s.p, &i)
|
|
if ret != C.EBUR128_SUCCESS {
|
|
return float64(i), fmt.Errorf("error calculating loudness: %d", ret)
|
|
}
|
|
return float64(i), nil
|
|
}
|
|
|
|
//GetLoudnessShortTerm Get short-term loudness (last 3s) in LUFS.
|
|
func (s *State) GetLoudnessShortTerm() (out float64, err error) {
|
|
var i C.double
|
|
ret := C.ebur128_loudness_shortterm(s.p, &i)
|
|
if ret != C.EBUR128_SUCCESS {
|
|
return float64(i), fmt.Errorf("error calculating loudness: %d", ret)
|
|
}
|
|
return float64(i), nil
|
|
}
|
|
|
|
func (s *State) AddFloat(src []float32) error {
|
|
ret := C.ebur128_add_frames_float(s.p, (*C.float)(&src[0]), C.size_t(len(src)/s.channels))
|
|
if ret != C.EBUR128_SUCCESS {
|
|
return fmt.Errorf("error adding frames: %d", ret)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *State) AddDouble(src []float64) error {
|
|
ret := C.ebur128_add_frames_double(s.p, (*C.double)(&src[0]), C.size_t(len(src)/s.channels))
|
|
if ret != C.EBUR128_SUCCESS {
|
|
return fmt.Errorf("error adding frames: %d", ret)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *State) AddShort(src []int16) error {
|
|
ret := C.ebur128_add_frames_short(s.p, (*C.short)(&src[0]), C.size_t(len(src)/s.channels))
|
|
if ret != C.EBUR128_SUCCESS {
|
|
return fmt.Errorf("error adding frames: %d", ret)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *State) AddInt(src []int32) error {
|
|
ret := C.ebur128_add_frames_int(s.p, (*C.int)(&src[0]), C.size_t(len(src)/s.channels))
|
|
if ret != C.EBUR128_SUCCESS {
|
|
return fmt.Errorf("error adding frames: %d", ret)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *State) Close() {
|
|
if s.p != nil {
|
|
C.ebur128_destroy(&s.p)
|
|
s.p = nil
|
|
}
|
|
}
|