Refactor media (#401)

* Encapsulate media
* Write audio by 4 bytes instead 2
* Update deps
This commit is contained in:
sergystepanov 2023-05-27 17:34:35 +03:00 committed by GitHub
parent 2ed6e8724f
commit 7668ef7bd8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 120 additions and 128 deletions

9
go.mod
View file

@ -13,7 +13,7 @@ require (
github.com/knadh/koanf/v2 v2.0.1
github.com/pion/interceptor v0.1.17
github.com/pion/logging v0.2.2
github.com/pion/webrtc/v3 v3.2.5
github.com/pion/webrtc/v3 v3.2.8
github.com/rs/xid v1.5.0
github.com/rs/zerolog v1.29.1
github.com/veandco/go-sdl2 v0.4.35
@ -26,13 +26,13 @@ require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.18 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/pion/datachannel v1.5.5 // indirect
github.com/pion/dtls/v2 v2.2.7 // indirect
github.com/pion/ice/v2 v2.3.4 // indirect
github.com/pion/ice/v2 v2.3.6 // indirect
github.com/pion/mdns v0.0.7 // indirect
github.com/pion/randutil v0.1.0 // indirect
github.com/pion/rtcp v1.2.10 // indirect
@ -40,10 +40,9 @@ require (
github.com/pion/sctp v1.8.7 // indirect
github.com/pion/sdp/v3 v3.0.6 // indirect
github.com/pion/srtp/v2 v2.0.15 // indirect
github.com/pion/stun v0.5.2 // indirect
github.com/pion/stun v0.6.0 // indirect
github.com/pion/transport/v2 v2.2.1 // indirect
github.com/pion/turn/v2 v2.1.0 // indirect
github.com/pion/udp/v2 v2.0.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/stretchr/testify v1.8.3 // indirect
github.com/valyala/fastrand v1.1.0 // indirect

34
go.sum
View file

@ -1,5 +1,3 @@
github.com/VictoriaMetrics/metrics v1.23.1 h1:/j8DzeJBxSpL2qSIdqnRFLvQQhbJyJbbEi22yMm7oL0=
github.com/VictoriaMetrics/metrics v1.23.1/go.mod h1:rAr/llLpEnAdTehiNlUxKgnjcOuROSzpw0GvjpEbvFc=
github.com/VictoriaMetrics/metrics v1.24.0 h1:ILavebReOjYctAGY5QU2F9X0MYvkcrG3aEn2RKa1Zkw=
github.com/VictoriaMetrics/metrics v1.24.0/go.mod h1:eFT25kvsTidQFHb6U0oa0rTrDRdz4xTYjpL8+UPohys=
github.com/cavaliergopher/grab/v3 v3.0.1 h1:4z7TkBfmPjmLAAmkkAZNX/6QJ1nNFdv3SdIHXju0Fr4=
@ -50,8 +48,8 @@ github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxec
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98=
github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
@ -69,16 +67,10 @@ github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1y
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
github.com/pion/datachannel v1.5.5 h1:10ef4kwdjije+M9d7Xm9im2Y3O6A6ccQb0zcqZcJew8=
github.com/pion/datachannel v1.5.5/go.mod h1:iMz+lECmfdCMqFRhXhcA/219B0SQlbpoR2V118yimL0=
github.com/pion/dtls/v2 v2.2.6 h1:yXMxKr0Skd+Ub6A8UqXTRLSywskx93ooMRHsQUtd+Z4=
github.com/pion/dtls/v2 v2.2.6/go.mod h1:t8fWJCIquY5rlQZwA2yWxUS1+OCrAdXrhVKXB5oD/wY=
github.com/pion/dtls/v2 v2.2.7 h1:cSUBsETxepsCSFSxC3mc/aDo14qQLMSL+O6IjG28yV8=
github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s=
github.com/pion/ice/v2 v2.3.2 h1:vh+fi4RkZ8H5fB4brZ/jm3j4BqFgMmNs+aB3X52Hu7M=
github.com/pion/ice/v2 v2.3.2/go.mod h1:AMIpuJqcpe+UwloocNebmTSWhCZM1TUCo9v7nW50jX0=
github.com/pion/ice/v2 v2.3.4 h1:tjYjTLpWyZzUjpDnzk6T1y3oQyhyY2DiM2t095iDhyQ=
github.com/pion/ice/v2 v2.3.4/go.mod h1:jVbxqPWQDK5+/V/YqpinUcP0YtDGYqd24n2lusVdX80=
github.com/pion/interceptor v0.1.16 h1:0GDZrfNO+BmVNWymS31fMlVtPO2IJVBzy2Qq5XCYMIg=
github.com/pion/interceptor v0.1.16/go.mod h1:SY8kpmfVBvrbUzvj2bsXz7OJt5JvmVNZ+4Kjq7FcwrI=
github.com/pion/ice/v2 v2.3.6 h1:Jgqw36cAud47iD+N6rNX225uHvrgWtAlHfVyOQc3Heg=
github.com/pion/ice/v2 v2.3.6/go.mod h1:9/TzKDRwBVAPsC+YOrKH/e3xDrubeTRACU9/sHQarsU=
github.com/pion/interceptor v0.1.17 h1:prJtgwFh/gB8zMqGZoOgJPHivOwVAp61i2aG61Du/1w=
github.com/pion/interceptor v0.1.17/go.mod h1:SY8kpmfVBvrbUzvj2bsXz7OJt5JvmVNZ+4Kjq7FcwrI=
github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY=
@ -96,31 +88,24 @@ github.com/pion/sctp v1.8.7 h1:JnABvFakZueGAn4KU/4PSKg+GWbF6QWbKTWZOSGJjXw=
github.com/pion/sctp v1.8.7/go.mod h1:g1Ul+ARqZq5JEmoFy87Q/4CePtKnTJ1QCL9dBBdN6AU=
github.com/pion/sdp/v3 v3.0.6 h1:WuDLhtuFUUVpTfus9ILC4HRyHsW6TdugjEX/QY9OiUw=
github.com/pion/sdp/v3 v3.0.6/go.mod h1:iiFWFpQO8Fy3S5ldclBkpXqmWy02ns78NOKoLLL0YQw=
github.com/pion/srtp/v2 v2.0.12/go.mod h1:C3Ep44hlOo2qEYaq4ddsmK5dL63eLehXFbHaZ9F5V9Y=
github.com/pion/srtp/v2 v2.0.13 h1:GJQNMCqbYrNIBt1f3maX+E+woREVh2ilhAafBh0vqmk=
github.com/pion/srtp/v2 v2.0.13/go.mod h1:FA7u5fWpVITMYNL70TA3csQuMQJA5/+6ZMajGxveHgM=
github.com/pion/srtp/v2 v2.0.15 h1:+tqRtXGsGwHC0G0IUIAzRmdkHvriF79IHVfZGfHrQoA=
github.com/pion/srtp/v2 v2.0.15/go.mod h1:b/pQOlDrbB0HEH5EUAQXzSYxikFbNcNuKmF8tM0hCtw=
github.com/pion/stun v0.4.0/go.mod h1:QPsh1/SbXASntw3zkkrIk3ZJVKz4saBY2G7S10P3wCw=
github.com/pion/stun v0.5.2 h1:J/8glQnDV91dfk2+ZnGN0o9bUJgABhTNljwfQWByoXE=
github.com/pion/stun v0.5.2/go.mod h1:TNo1HjyjaFVpMZsvowqPeV8TfwRytympQC0//neaksA=
github.com/pion/stun v0.6.0 h1:JHT/2iyGDPrFWE8NNC15wnddBN8KifsEDw8swQmrEmU=
github.com/pion/stun v0.6.0/go.mod h1:HPqcfoeqQn9cuaet7AOmB5e5xkObu9DwBdurwLKO9oA=
github.com/pion/transport v0.14.1 h1:XSM6olwW+o8J4SCmOBb/BpwZypkHeyM0PGFCxNQBr40=
github.com/pion/transport v0.14.1/go.mod h1:4tGmbk00NeYA3rUa9+n+dzCCoKkcy3YlYb99Jn2fNnI=
github.com/pion/transport/v2 v2.0.0/go.mod h1:HS2MEBJTwD+1ZI2eSXSvHJx/HnzQqRy2/LXxt6eVMHc=
github.com/pion/transport/v2 v2.0.2/go.mod h1:vrz6bUbFr/cjdwbnxq8OdDDzHf7JJfGsIRkxfpZoTA0=
github.com/pion/transport/v2 v2.1.0/go.mod h1:AdSw4YBZVDkZm8fpoz+fclXyQwANWmZAlDuQdctTThQ=
github.com/pion/transport/v2 v2.2.0 h1:u5lFqFHkXLMXMzai8tixZDfVjb8eOjH35yCunhPeb1c=
github.com/pion/transport/v2 v2.2.0/go.mod h1:AdSw4YBZVDkZm8fpoz+fclXyQwANWmZAlDuQdctTThQ=
github.com/pion/transport/v2 v2.2.1 h1:7qYnCBlpgSJNYMbLCKuSY9KbQdBFoETvPNETv0y4N7c=
github.com/pion/transport/v2 v2.2.1/go.mod h1:cXXWavvCnFF6McHTft3DWS9iic2Mftcz1Aq29pGcU5g=
github.com/pion/turn/v2 v2.1.0 h1:5wGHSgGhJhP/RpabkUb/T9PdsAjkGLS6toYz5HNzoSI=
github.com/pion/turn/v2 v2.1.0/go.mod h1:yrT5XbXSGX1VFSF31A3c1kCNB5bBZgk/uu5LET162qs=
github.com/pion/udp/v2 v2.0.1 h1:xP0z6WNux1zWEjhC7onRA3EwwSliXqu1ElUZAQhUP54=
github.com/pion/udp/v2 v2.0.1/go.mod h1:B7uvTMP00lzWdyMr/1PVZXtV3wpPIxBRd4Wl6AksXn8=
github.com/pion/webrtc/v3 v3.2.1 h1:eehbYzkM6xWoH3LXoIBnZTb4TOrjwmVzI78JO1+5kgQ=
github.com/pion/webrtc/v3 v3.2.1/go.mod h1:sQVqop5YhZezvKyyz6Nywvf15LhlXUWiXWdN5DV4zHs=
github.com/pion/webrtc/v3 v3.2.5 h1:WA38+a1T3/EP55k5IYQFLH3ORaNpRTcElW6UL1CwNeA=
github.com/pion/webrtc/v3 v3.2.5/go.mod h1:8+GhDtUqfKmnZkj+aT2kjvV9B2/nhSTqINEXbVQEGSo=
github.com/pion/webrtc/v3 v3.2.8 h1:RmDEz7wjK3k0sAuCSMptfxp095pBYSkSSm5ySiJYIHI=
github.com/pion/webrtc/v3 v3.2.8/go.mod h1:6/7wF1P86AQAw4iTmKIgdzaevaQ8qh9SfrFyypqmN6w=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
@ -137,7 +122,6 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
@ -153,8 +137,6 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU=
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g=
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=

View file

@ -27,7 +27,7 @@ func buildConnQuery(id com.Uid, conf config.Worker, address string) (string, err
func (c *coordinator) HandleWebrtcInit(rq api.WebrtcInitRequest[com.Uid], w *Worker, connApi *webrtc.ApiFactory) api.Out {
peer := webrtc.New(c.log, connApi)
localSDP, err := peer.NewCall(w.conf.Encoder.Video.Codec, audioCodec, func(data any) {
localSDP, err := peer.NewCall(w.conf.Encoder.Video.Codec, "opus", func(data any) {
candidate, err := toBase64Json(data)
if err != nil {
c.log.Error().Err(err).Msgf("ICE candidate encode fail for [%v]", data)

View file

@ -3,6 +3,7 @@ package worker
import (
"sync"
"time"
"unsafe"
"github.com/giongto35/cloud-game/v3/pkg/config"
"github.com/giongto35/cloud-game/v3/pkg/worker/emulator"
@ -13,51 +14,55 @@ import (
webrtc "github.com/pion/webrtc/v3/pkg/media"
)
const (
dstHz = 48000
sampleBufLen = 1024 * 4
)
// buffer is a simple non-concurrent safe ring buffer for audio samples.
type (
buffer struct {
s samples
wi int
dst int
stretch bool
}
samples []int16
)
var (
encoderOnce = sync.Once{}
opusCoder *opus.Encoder
samplePool sync.Pool
audioPool = sync.Pool{New: func() any { b := make([]int16, 3000); return &b }}
audioPool = sync.Pool{New: func() any { b := make([]int16, sampleBufLen); return &b }}
)
const (
audioChannels = 2
audioCodec = "opus"
audioFrequency = 48000
)
func newBuffer(srcLen int) buffer { return buffer{s: make(samples, srcLen)} }
// Buffer is a simple non-thread safe ring buffer for audio samples.
// It should be used for 16bit PCM (LE interleaved) data.
type (
Buffer struct {
s Samples
wi int
}
OnFull func(s Samples)
Samples []int16
)
// enableStretch adds a simple stretching of buffer to a desired size before
// the onFull callback call.
func (b *buffer) enableStretch(l int) { b.stretch = true; b.dst = l }
func NewBuffer(numSamples int) Buffer { return Buffer{s: make(Samples, numSamples)} }
// Write fills the buffer with data calling a callback function when
// the internal buffer fills out.
// write fills the buffer until it's full and then passes the gathered data into a callback.
//
// Consider two cases:
//
// 1. Underflow, when the length of written data is less than the buffer's available space.
// There are two cases to consider:
// 1. Underflow, when the length of the written data is less than the buffer's available space.
// 2. Overflow, when the length exceeds the current available buffer space.
// In the both cases we overwrite any previous values in the buffer and move the internal
// write pointer on the length of written data.
// In the first case we won't call the callback, but it will be called every time
//
// We overwrite any previous values in the buffer and move the internal write pointer
// by the length of the written data.
// In the first case, we won't call the callback, but it will be called every time
// when the internal buffer overflows until all samples are read.
func (b *Buffer) Write(s Samples, onFull OnFull) (r int) {
func (b *buffer) write(s samples, onFull func(samples)) (r int) {
for r < len(s) {
w := copy(b.s[b.wi:], s[r:])
r += w
b.wi += w
if b.wi == len(b.s) {
b.wi = 0
if onFull != nil {
if b.stretch {
onFull(b.s.stretch(b.dst))
} else {
onFull(b.s)
}
}
@ -65,18 +70,29 @@ func (b *Buffer) Write(s Samples, onFull OnFull) (r int) {
return
}
// GetFrameSizeFor calculates audio frame size, i.e. 48k*frame/1000*2
func GetFrameSizeFor(hz int, frame int) int { return hz * frame / 1000 * audioChannels }
// frame calculates an audio stereo frame size, i.e. 48k*frame/1000*2
func frame(hz int, frame int) int { return hz * frame / 1000 * 2 }
func (r *Room) initAudio(frequency int, conf config.Audio) {
buf := NewBuffer(GetFrameSizeFor(frequency, conf.Frame))
resample, frameLen := frequency != audioFrequency, 0
if resample {
frameLen = GetFrameSizeFor(audioFrequency, conf.Frame)
// stretch does a simple stretching of audio samples.
// something like: [1,2,3,4,5,6] -> [1,2,x,x,3,4,x,x,5,6,x,x] -> [1,2,1,2,3,4,3,4,5,6,5,6]
func (s samples) stretch(size int) []int16 {
out := (*audioPool.Get().(*[]int16))[:size]
n := len(s)
ratio := float32(size) / float32(n)
sPtr := unsafe.Pointer(&s[0])
for i, l, r := 0, 0, 0; i < n; i += 2 {
l, r = r, int(float32((i+2)>>1)*ratio)<<1 // index in src * ratio -> approximated index in dst *2 due to int16
for j := l; j < r; j += 2 {
*(*int32)(unsafe.Pointer(&out[j])) = *(*int32)(sPtr) // out[j] = s[i]; out[j+1] = s[i+1]
}
sPtr = unsafe.Add(sPtr, uintptr(4))
}
return out
}
func (r *Room) initAudio(srcHz int, conf config.Audio) {
encoderOnce.Do(func() {
enc, err := opus.NewEncoder(audioFrequency)
enc, err := opus.NewEncoder(dstHz)
if err != nil {
r.log.Fatal().Err(err).Msg("couldn't create audio encoder")
}
@ -87,23 +103,28 @@ func (r *Room) initAudio(frequency int, conf config.Audio) {
}
r.log.Debug().Msgf("Opus: %v", opusCoder.GetInfo())
dur := time.Duration(conf.Frame) * time.Millisecond
buf := newBuffer(frame(srcHz, conf.Frame))
if srcHz != dstHz {
buf.enableStretch(frame(dstHz, conf.Frame))
r.log.Debug().Msgf("Resample %vHz -> %vHz", srcHz, dstHz)
}
frameDur := time.Duration(conf.Frame) * time.Millisecond
fn := func(s Samples) {
if resample {
s = ResampleStretchNew(s, frameLen)
}
f, err := opusCoder.Encode(s)
audioPool.Put((*[]int16)(&s))
if err == nil {
r.handleSample(f, dur, func(u *Session, s *webrtc.Sample) {
r.emulator.SetAudio(func(raw *emulator.GameAudio) {
buf.write(*raw.Data, func(pcm samples) {
data, err := opusCoder.Encode(pcm)
audioPool.Put((*[]int16)(&pcm))
if err != nil {
r.log.Error().Err(err).Msgf("opus encode fail")
return
}
r.handleSample(data, frameDur, func(u *Session, s *webrtc.Sample) {
if err := u.SendAudio(s); err != nil {
r.log.Error().Err(err).Send()
}
})
}
}
r.emulator.SetAudio(func(samples *emulator.GameAudio) { buf.Write(*samples.Data, fn) })
})
})
}
// initVideo processes videoFrames images with an encoder (codec) then pushes the result to WebRTC.
@ -160,19 +181,3 @@ func (r *Room) handleSample(b []byte, d time.Duration, fn func(*Session, *webrtc
})
samplePool.Put(sample)
}
// ResampleStretchNew does a simple stretching of audio samples.
// something like: [1,2,3,4,5,6] -> [1,2,x,x,3,4,x,x,5,6,x,x] -> [1,2,1,2,3,4,3,4,5,6,5,6]
func ResampleStretchNew(pcm []int16, size int) []int16 {
out := (*audioPool.Get().(*[]int16))[:size]
n := len(pcm)
ratio := float32(size) / float32(n)
for i, l, r := 0, 0, 0; i < n; i += 2 {
l, r = r, int(float32((i+2)>>1)*ratio)<<1
for j := l; j < r-1; j += 2 {
out[j] = pcm[i]
out[j+1] = pcm[i+1]
}
}
return out
}

View file

@ -1,7 +1,6 @@
package worker
import (
"fmt"
"image"
"math/rand"
"reflect"
@ -86,7 +85,7 @@ func genTestImage(w, h int, seed float32) *image.RGBA {
func TestResampleStretch(t *testing.T) {
type args struct {
pcm []int16
pcm samples
size int
}
tests := []struct {
@ -106,7 +105,7 @@ func TestResampleStretch(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
rez2 := ResampleStretchNew(tt.args.pcm, tt.args.size)
rez2 := tt.args.pcm.stretch(tt.args.size)
if rez2[0] != tt.args.pcm[0] || rez2[1] != tt.args.pcm[1] ||
rez2[len(rez2)-1] != tt.args.pcm[len(tt.args.pcm)-1] ||
@ -119,20 +118,10 @@ func TestResampleStretch(t *testing.T) {
}
func BenchmarkResampler(b *testing.B) {
tests := []struct {
name string
fn func(pcm []int16, size int) []int16
}{
{name: "new", fn: ResampleStretchNew},
}
pcm := gen(1764)
pcm := samples(gen(1764))
size := 1920
for _, bn := range tests {
b.Run(fmt.Sprintf("%v", bn.name), func(b *testing.B) {
for i := 0; i < b.N; i++ {
bn.fn(pcm, size)
}
})
for i := 0; i < b.N; i++ {
pcm.stretch(size)
}
}
@ -141,10 +130,6 @@ func gen(l int) []int16 {
for i := range nums {
nums[i] = int16(rand.Intn(10))
}
//for i := len(nums) / 2; i < len(nums)/2+42; i++ {
// nums[i] = 0
//}
return nums
}
@ -157,7 +142,7 @@ func TestBufferWrite(t *testing.T) {
tests := []struct {
bufLen int
writes []bufWrite
expect Samples
expect samples
}{
{
bufLen: 20,
@ -166,7 +151,7 @@ func TestBufferWrite(t *testing.T) {
{sample: 2, len: 20},
{sample: 3, len: 30},
},
expect: Samples{3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3},
expect: samples{3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3},
},
{
bufLen: 11,
@ -175,15 +160,15 @@ func TestBufferWrite(t *testing.T) {
{sample: 2, len: 18},
{sample: 3, len: 2},
},
expect: Samples{3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3},
expect: samples{3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3},
},
}
for _, test := range tests {
var lastResult Samples
buf := NewBuffer(test.bufLen)
var lastResult samples
buf := newBuffer(test.bufLen)
for _, w := range test.writes {
buf.Write(samplesOf(w.sample, w.len), func(s Samples) { lastResult = s })
buf.write(samplesOf(w.sample, w.len), func(s samples) { lastResult = s })
}
if !reflect.DeepEqual(test.expect, lastResult) {
t.Errorf("not expted buffer, %v != %v", lastResult, test.expect)
@ -192,21 +177,42 @@ func TestBufferWrite(t *testing.T) {
}
func BenchmarkBufferWrite(b *testing.B) {
fn := func(_ Samples) {}
fn := func(_ samples) {}
l := 1920
buf := NewBuffer(l)
buf := newBuffer(l)
samples1 := samplesOf(1, l/2)
samples2 := samplesOf(2, l*2)
for i := 0; i < b.N; i++ {
buf.Write(samples1, fn)
buf.Write(samples2, fn)
buf.write(samples1, fn)
buf.write(samples2, fn)
}
}
func samplesOf(v int16, len int) (s Samples) {
s = make(Samples, len)
func samplesOf(v int16, len int) (s samples) {
s = make(samples, len)
for i := range s {
s[i] = v
}
return
}
func Test_frame(t *testing.T) {
type args struct {
hz int
frame int
}
tests := []struct {
name string
args args
want int
}{
{name: "mGBA", args: args{hz: 32768, frame: 10}, want: 654},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := frame(tt.args.hz, tt.args.frame); got != tt.want {
t.Errorf("frame() = %v, want %v", got, tt.want)
}
})
}
}