/
lame_encoder.go
138 lines (112 loc) · 2.85 KB
/
lame_encoder.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
package broadcast
/*
#cgo LDFLAGS: -lmp3lame
#include "lame/lame.h"
*/
import "C"
import (
"errors"
"io"
"math"
"runtime"
"unsafe"
)
const (
STEREO = C.STEREO
JOINT_STEREO = C.JOINT_STEREO
MONO = C.MONO
NOT_SET = C.NOT_SET
MAX_INDICATOR = C.MAX_INDICATOR
)
type LameEncoder struct {
Quality float32
BitRate int
Mode string
ChannelCount int
SampleRate int
Writer io.Writer
handle *C.lame_global_flags
}
func (encoder *LameEncoder) Init() error {
if encoder.ChannelCount == 0 {
encoder.ChannelCount = 2
}
if encoder.SampleRate == 0 {
encoder.SampleRate = 44100
}
if encoder.Mode == "" {
encoder.Mode = "vbr"
}
handle := C.lame_init()
if handle == nil {
return errors.New("Can't initialize lame")
}
C.lame_set_num_channels(handle, C.int(encoder.ChannelCount))
C.lame_set_in_samplerate(handle, C.int(encoder.SampleRate))
// Use "recommended" quality, used by default for VBR
C.lame_set_quality(handle, 2)
C.lame_set_mode(handle, (C.MPEG_mode)(encoder.LameMode()))
switch {
case encoder.Mode == "vbr":
C.lame_set_VBR(handle, C.vbr_mtrh)
C.lame_set_VBR_q(handle, C.int(encoder.LameQuality()))
case encoder.Mode == "cbr":
C.lame_set_brate(handle, C.int(encoder.BitRate))
case encoder.Mode == "abr":
C.lame_set_VBR(handle, C.vbr_abr)
C.lame_set_VBR_mean_bitrate_kbps(handle, C.int(encoder.BitRate))
}
initResults := C.lame_init_params(handle)
if initResults == -1 {
return errors.New("Can't setup lame")
}
encoder.handle = handle
runtime.SetFinalizer(encoder, finalizeLameEncoder)
return nil
}
func (encoder *LameEncoder) AudioOut(audio *Audio) {
if encoder.Writer == nil {
return
}
estimatedSize := int(1.25*float64(audio.SampleCount()) + 7200)
encodedBytes := make([]byte, estimatedSize)
encodedByteCount := C.int(C.lame_encode_buffer_ieee_float(
encoder.handle,
(*C.float)(unsafe.Pointer(&audio.Samples(0)[0])),
(*C.float)(unsafe.Pointer(&audio.Samples(1)[0])),
C.int(audio.SampleCount()),
(*C.uchar)(unsafe.Pointer(&encodedBytes[0])),
C.int(estimatedSize),
))
encoder.Writer.Write(encodedBytes[0:encodedByteCount])
}
func (encoder *LameEncoder) Flush() {
estimatedSize := 7200
encodedBytes := make([]byte, estimatedSize)
encodedByteCount := C.int(C.lame_encode_flush(
encoder.handle,
(*C.uchar)(unsafe.Pointer(&encodedBytes[0])),
C.int(estimatedSize),
))
encoder.Writer.Write(encodedBytes[0:encodedByteCount])
}
func (encoder *LameEncoder) LameQuality() int {
quality := (1.0 - (float64)(encoder.Quality)) * 10.0
return (int)(math.Min(quality, 9))
}
func (encoder *LameEncoder) LameMode() int {
if encoder.ChannelCount == 1 {
return MONO
} else {
return JOINT_STEREO
}
}
func (encoder *LameEncoder) Close() {
if encoder.handle != nil {
C.lame_close(encoder.handle)
encoder.handle = nil
}
}
func finalizeLameEncoder(encoder *LameEncoder) {
encoder.Close()
}