-
Notifications
You must be signed in to change notification settings - Fork 3
/
signal.go
222 lines (194 loc) · 6 KB
/
signal.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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
// Package signal provides functionality for manipulate digital signals and its attributes.
package signal
import (
"fmt"
"math"
"time"
)
// BitDepth is the number of bits of information in each sample.
type BitDepth uint
const (
// BitDepth8 is 8 bit depth.
BitDepth8 = BitDepth(8)
// BitDepth16 is 16 bit depth.
BitDepth16 = BitDepth(16)
// BitDepth24 is 32 bit depth.
BitDepth24 = BitDepth(24)
// BitDepth32 is 32 bit depth.
BitDepth32 = BitDepth(32)
)
func (b BitDepth) String() string {
return fmt.Sprintf("%d-bit", b)
}
// SampleRate is the number of samples obtained in one second.
type SampleRate uint
// DurationOf returns time duration of samples at this sample rate.
func (rate SampleRate) DurationOf(samples int) time.Duration {
return time.Duration(math.Round(float64(time.Second) / float64(rate) * float64(samples)))
}
// SamplesIn returns number of samples for time duration at this sample rate.
func (rate SampleRate) SamplesIn(d time.Duration) int {
return int(math.Round(float64(rate) / float64(time.Second) * float64(d)))
}
// Float64 is a non-interleaved float64 signal.
type Float64 [][]float64
// Int is a non-interleaved int signal.
type Int [][]int
// InterInt is an interleaved int signal.
type InterInt struct {
Data []int
NumChannels int
BitDepth
Unsigned bool
}
// resolution returns a half resolution for a passed bit depth.
// example: bit depth of 8 bits has resolution of (2^8)/2 -1 ie 127.
func resolution(bitDepth BitDepth) int {
if bitDepth == 0 {
return 1
}
return 1<<(bitDepth-1) - 1
}
// Size of non-interleaved data.
func (ints InterInt) Size() int {
return int(math.Ceil(float64(len(ints.Data)) / float64(ints.NumChannels)))
}
// AsFloat64 allocates new Float64 buffer of the same
// size and copies signal values there.
func (ints InterInt) AsFloat64() Float64 {
if ints.Data == nil || ints.NumChannels == 0 {
return nil
}
floats := make([][]float64, ints.NumChannels)
for i := range floats {
floats[i] = make([]float64, ints.Size())
}
ints.CopyToFloat64(floats)
return floats
}
// CopyToFloat64 buffer the values of InterInt buffer.
// If number of channels is not equal, function will panic.
func (ints InterInt) CopyToFloat64(floats Float64) {
if ints.NumChannels != floats.NumChannels() {
panic(fmt.Errorf("unexpected number of channels in destination buffer: expected %v got %v", ints.NumChannels, floats.NumChannels()))
}
// get resolution of bit depth.
res := resolution(ints.BitDepth)
// determine the divider for bit depth conversion.
divider := float64(res)
// determine the shift for signed-unsigned conversion.
shift := 0
if ints.Unsigned {
shift = res
}
for i := range floats {
for pos, j := i, 0; pos < len(ints.Data) && j < len(floats[i]); pos, j = pos+ints.NumChannels, j+1 {
floats[i][j] = float64(ints.Data[pos]-shift) / divider
}
}
}
// AsInterInt allocates new interleaved int buffer of
// the same size and copies signal values there.
// If unsigned is true, then all values are shifted
// and result will be in unsigned range.
func (floats Float64) AsInterInt(bitDepth BitDepth, unsigned bool) InterInt {
numChannels := floats.NumChannels()
if numChannels == 0 {
return InterInt{}
}
ints := InterInt{
Data: make([]int, len(floats[0])*numChannels),
NumChannels: numChannels,
BitDepth: bitDepth,
Unsigned: unsigned,
}
floats.CopyToInterInt(ints)
return ints
}
// CopyToInterInt buffer the values of Float64 buffer.
// If number of channels is not equal, function will panic.
func (floats Float64) CopyToInterInt(ints InterInt) {
if floats.NumChannels() != ints.NumChannels {
panic(fmt.Errorf("unexpected number of channels in destination buffer: expected %v got %v", floats.NumChannels(), ints.NumChannels))
}
// get resolution of bit depth
res := resolution(ints.BitDepth)
// determine the multiplier for bit depth conversion
multiplier := float64(res)
// determine the shift for signed-unsigned conversion
shift := 0
if ints.Unsigned {
shift = res
}
size := ints.Size()
for j := range floats {
for i := 0; i < len(floats[j]) && i < size; i++ {
ints.Data[i*ints.NumChannels+j] = int(floats[j][i]*multiplier) + shift
}
}
}
// Float64Buffer returns an Float64 buffer of specified dimentions.
func Float64Buffer(numChannels, bufferSize int) Float64 {
result := make([][]float64, numChannels)
for i := range result {
result[i] = make([]float64, bufferSize)
}
return result
}
// NumChannels returns number of channels in this sample slice.
func (floats Float64) NumChannels() int {
return len(floats)
}
// Size returns number of samples in single block in this sample slice.
func (floats Float64) Size() int {
if floats.NumChannels() == 0 {
return 0
}
return len(floats[0])
}
// Append buffers to existing one.
// New buffer is returned if b is nil.
func (floats Float64) Append(source Float64) Float64 {
if floats == nil {
floats = make([][]float64, source.NumChannels())
for i := range floats {
floats[i] = make([]float64, 0, source.Size())
}
}
for i := range source {
floats[i] = append(floats[i], source[i]...)
}
return floats
}
// Slice creates a new buffer that refers to floats data from start
// position with defined legth. Shorten block is returned if buffer
// doesn't have enough samples. If start is less than 0 or more than
// buffer size, nil is returned. If len goes beyond the buffer size,
// it's truncated up to length of the buffer.
func (floats Float64) Slice(start, len int) Float64 {
if floats == nil || start >= floats.Size() || start < 0 {
return nil
}
end := start + len
result := make([][]float64, floats.NumChannels())
for i := range floats {
if end > floats.Size() {
end = floats.Size()
}
result[i] = floats[i][start:end]
}
return result
}
// Sum adds values from one buffer to another.
// The lesser dimensions are used.
func (floats Float64) Sum(b Float64) Float64 {
if floats == nil {
return nil
}
for i := 0; i < len(floats) && i < len(b); i++ {
for j := 0; j < len(floats[i]) && j < len(b[i]); j++ {
floats[i][j] += b[i][j]
}
}
return floats
}