Added MP3 encoding via LAME

This commit is contained in:
DataHoarder 2022-02-27 16:43:50 +01:00
parent 9c27f76805
commit 41d0fdde5d
7 changed files with 183 additions and 1 deletions

View file

@ -13,7 +13,7 @@ steps:
image: golang:1.18rc1-bullseye
commands:
- DEBIAN_FRONTEND=noninteractive apt update
- DEBIAN_FRONTEND=noninteractive apt install -y libflac-dev libopus-dev libopusfile-dev libsamplerate0-dev
- DEBIAN_FRONTEND=noninteractive apt install -y libflac-dev libopus-dev libopusfile-dev libsamplerate0-dev libmp3lame-dev
- go test -cover -coverpkg=git.gammaspectra.live/S.O.N.G/Kirika/hasher,git.gammaspectra.live/S.O.N.G/Kirika/audio,git.gammaspectra.live/S.O.N.G/Kirika/audio/format,git.gammaspectra.live/S.O.N.G/Kirika/audio/format/flac,git.gammaspectra.live/S.O.N.G/Kirika/audio/format/mp3,git.gammaspectra.live/S.O.N.G/Kirika/audio/format/opus,git.gammaspectra.live/S.O.N.G/Kirika/audio/format/tta -v
...

View file

@ -262,6 +262,71 @@ func TestFilterChain(t *testing.T) {
}
}
func TestEncodeFLAC(t *testing.T) {
t.Parallel()
fp, err := os.Open(TestSingleSample24)
if err != nil {
t.Error(err)
return
}
defer fp.Close()
source, err := flac.NewFormat().Open(fp)
if err != nil {
t.Error(err)
return
}
target, err := os.CreateTemp("/tmp", "encode_test_*.flac")
if err != nil {
t.Error(err)
return
}
defer func() {
name := target.Name()
target.Close()
os.Remove(name)
}()
err = flac.NewFormat().Encode(source, target, nil)
if err != nil {
t.Error(err)
return
}
}
func TestEncodeMP3(t *testing.T) {
t.Parallel()
fp, err := os.Open(TestSingleSample24)
if err != nil {
t.Error(err)
return
}
defer fp.Close()
source, err := flac.NewFormat().Open(fp)
if err != nil {
t.Error(err)
return
}
target, err := os.CreateTemp("/tmp", "encode_test_*.mp3")
if err != nil {
t.Error(err)
return
}
defer func() {
name := target.Name()
target.Close()
os.Remove(name)
}()
err = mp3.NewFormat().Encode(source, target, nil)
if err != nil {
t.Error(err)
return
}
}
func TestQueue(t *testing.T) {
t.Parallel()

View file

@ -24,6 +24,11 @@ sudo apt install libflac-dev
sudo apt install libopus-dev libopusfile-dev
```
### [LAME](https://lame.sourceforge.io/) (required by [go-lame](hhttps://github.com/viert/go-lame))
```shell
sudo apt install libmp3lame-dev
```
### [libsamplerate](https://github.com/libsndfile/libsamplerate) (required by [gosamplerate](https://github.com/dh1tw/gosamplerate))
```shell
sudo apt install libsamplerate0-dev

View file

@ -6,9 +6,12 @@ package mp3
*/
import "C"
import (
"errors"
"fmt"
"git.gammaspectra.live/S.O.N.G/Kirika/audio"
"git.gammaspectra.live/S.O.N.G/Kirika/audio/format"
mp3Lib "github.com/kvark128/minimp3"
"github.com/viert/go-lame"
"io"
"unsafe"
)
@ -17,6 +20,7 @@ const BlockSize = 1024 * 128
type Format struct {
format.Format
format.Encoder
}
func NewFormat() Format {
@ -61,6 +65,98 @@ func (f Format) Open(r io.ReadSeekCloser) (audio.Source, error) {
}, nil
}
func (f Format) Encode(source audio.Source, writer io.WriteCloser, options map[string]interface{}) error {
var vbr = true
var bitrate = 0
if options != nil {
var val interface{}
var ok bool
var intVal int
var strVal string
if val, ok = options["bitrate"]; ok {
if strVal, ok = val.(string); ok {
switch strVal {
case "v0":
vbr = true
bitrate = 0
case "v1":
vbr = true
bitrate = 1
case "v2":
vbr = true
bitrate = 2
case "v3":
vbr = true
bitrate = 3
case "320k":
vbr = false
bitrate = 320
case "256k":
vbr = false
bitrate = 256
case "192k":
vbr = false
bitrate = 192
case "128k":
vbr = false
bitrate = 128
default:
return fmt.Errorf("unknown setting bitrate=%s", strVal)
}
} else if intVal, ok = val.(int); ok {
vbr = false
bitrate = intVal
}
}
}
encoder := lame.NewEncoder(writer)
if encoder == nil {
return errors.New("could not create encoder")
}
defer encoder.Close()
if err := encoder.SetNumChannels(source.Channels); err != nil {
return err
}
if err := encoder.SetInSamplerate(source.SampleRate); err != nil {
return err
}
encoder.SetWriteID3TagAutomatic(false)
if vbr {
if err := encoder.SetVBR(lame.VBRMTRH); err != nil {
return err
}
if err := encoder.SetVBRQuality(float64(bitrate)); err != nil {
return err
}
} else {
if err := encoder.SetVBR(lame.VBROff); err != nil {
return err
}
if err := encoder.SetBrate(bitrate); err != nil {
return err
}
}
for block := range source.Blocks {
samples := make([]int16, len(block))
C.audio_float32_to_int16((*C.float)(&block[0]), C.size_t(len(block)), (*C.int16_t)(&samples[0]))
_, err := encoder.Write(unsafe.Slice((*byte)(unsafe.Pointer(&samples[0])), len(samples)*2))
if err != nil {
return err
}
}
return nil
}
func (f Format) Identify(peek []byte, extension string) bool {
return /*bytes.Compare(peek[:4], []byte{'f', 'L', 'a', 'C'}) == 0 || */ extension == "mp3"
}

View file

@ -185,4 +185,17 @@ __attribute__((weak)) void audio_float32_to_int32(float* restrict data, size_t d
}
buffer[i] = (int32_t)(f * BITS_TO_DIV(bitDepth));
}
}
__attribute__((weak)) void audio_float32_to_int16(float* restrict data, size_t data_len, int16_t* restrict buffer){
for (int i = 0; i < data_len; ++i){
float f = data[i];
if (f < -1.0) {
f = -1.0;
}
if (f > 1.0) {
f = 1.0;
}
buffer[i] = (int16_t)(f * BITS_TO_DIV(16));
}
}

1
go.mod
View file

@ -8,6 +8,7 @@ require (
git.gammaspectra.live/S.O.N.G/goflac v0.0.0-20220223152921-827e6c3f729f
github.com/dh1tw/gosamplerate v0.1.2
github.com/kvark128/minimp3 v0.0.0-20211109174940-101188771a65
github.com/viert/go-lame v0.0.0-20201108052322-bb552596b11d
)
require github.com/klauspost/cpuid v1.3.1 // indirect

2
go.sum
View file

@ -11,3 +11,5 @@ github.com/klauspost/cpuid v1.3.1 h1:5JNjFYYQrZeKRJ0734q51WCEEn2huer72Dc7K+R/b6s
github.com/klauspost/cpuid v1.3.1/go.mod h1:bYW4mA6ZgKPob1/Dlai2LviZJO7KGI3uoWLd42rAQw4=
github.com/kvark128/minimp3 v0.0.0-20211109174940-101188771a65 h1:8qfVQv7MSACDXadEwl1yjUKJ68yC9B7nR4cioEoCfH0=
github.com/kvark128/minimp3 v0.0.0-20211109174940-101188771a65/go.mod h1:hIq9nAqNcwTySvnFhCe1C8xC/STIr2Fe5vJ52zk1jkE=
github.com/viert/go-lame v0.0.0-20201108052322-bb552596b11d h1:LptdD7GTUZeklomtW5vZ1AHwBvDBUCZ2Ftpaz7uEI7g=
github.com/viert/go-lame v0.0.0-20201108052322-bb552596b11d/go.mod h1:EqTcYM7y4JlSfeTI47pmNu3EZQuCuLQefsQyg1Imlz8=