mirror of
https://github.com/pion/mediadevices.git
synced 2025-10-04 00:06:32 +08:00
Compare commits
12 Commits
update
...
copy-audio
Author | SHA1 | Date | |
---|---|---|---|
![]() |
25a1bf5a16 | ||
![]() |
a44240be5f | ||
![]() |
70f7360b92 | ||
![]() |
30d49e1fd3 | ||
![]() |
0cd870fd4b | ||
![]() |
13e6dcc437 | ||
![]() |
366885e01c | ||
![]() |
86e3a3f14c | ||
![]() |
b4c11d5a0c | ||
![]() |
18da7ff1c6 | ||
![]() |
f7068296d3 | ||
![]() |
6d07cc2a58 |
4
.github/workflows/ci.yaml
vendored
4
.github/workflows/ci.yaml
vendored
@@ -12,7 +12,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
go: [ '1.14', '1.13' ]
|
||||
go: [ '1.15', '1.14' ]
|
||||
name: Linux Go ${{ matrix.go }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
@@ -47,7 +47,7 @@ jobs:
|
||||
runs-on: macos-latest
|
||||
strategy:
|
||||
matrix:
|
||||
go: [ '1.14', '1.13' ]
|
||||
go: [ '1.15', '1.14' ]
|
||||
name: Darwin Go ${{ matrix.go }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
|
8
go.mod
8
go.mod
@@ -4,10 +4,10 @@ go 1.13
|
||||
|
||||
require (
|
||||
github.com/blackjack/webcam v0.0.0-20200313125108-10ed912a8539
|
||||
github.com/jfreymuth/pulse v0.0.0-20200804114219-7d61c4938214
|
||||
github.com/lherman-cs/opus v0.0.0-20200223204610-6a4b98199ea4
|
||||
github.com/pion/webrtc/v2 v2.2.23
|
||||
github.com/jfreymuth/pulse v0.0.0-20200817093420-a82ccdb5e8aa
|
||||
github.com/lherman-cs/opus v0.0.0-20200925064139-8edf1852fd1f
|
||||
github.com/pion/webrtc/v2 v2.2.26
|
||||
github.com/satori/go.uuid v1.2.0
|
||||
golang.org/x/image v0.0.0-20200801110659-972c09e46d76
|
||||
golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1
|
||||
golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a
|
||||
)
|
||||
|
24
go.sum
24
go.sum
@@ -15,15 +15,15 @@ github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/jfreymuth/pulse v0.0.0-20200804114219-7d61c4938214 h1:2xVJKIumEUWeV3vczQwn61SHjNZ94Bwk+4CTjmcePxk=
|
||||
github.com/jfreymuth/pulse v0.0.0-20200804114219-7d61c4938214/go.mod h1:cpYspI6YljhkUf1WLXLLDmeaaPFc3CnGLjDZf9dZ4no=
|
||||
github.com/jfreymuth/pulse v0.0.0-20200817093420-a82ccdb5e8aa h1:qUZIj5+D3UDgfshNe8Cz/9maOxe8ddt43qwQH9vEEC8=
|
||||
github.com/jfreymuth/pulse v0.0.0-20200817093420-a82ccdb5e8aa/go.mod h1:cpYspI6YljhkUf1WLXLLDmeaaPFc3CnGLjDZf9dZ4no=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/lherman-cs/opus v0.0.0-20200223204610-6a4b98199ea4 h1:2ydMA2KbxRkYmIw3R8Me8dn90bejxBR4MKYXJ5THK3I=
|
||||
github.com/lherman-cs/opus v0.0.0-20200223204610-6a4b98199ea4/go.mod h1:v9KQvlDYMuvlwniumBVMlrB0VHQvyTgxNvaXjPmTmps=
|
||||
github.com/lherman-cs/opus v0.0.0-20200925064139-8edf1852fd1f h1:xZKyjUoki95rRDQl3mDf20j2WZ+jZaFVzPZO72Jdi4A=
|
||||
github.com/lherman-cs/opus v0.0.0-20200925064139-8edf1852fd1f/go.mod h1:v9KQvlDYMuvlwniumBVMlrB0VHQvyTgxNvaXjPmTmps=
|
||||
github.com/lucas-clemente/quic-go v0.7.1-0.20190401152353-907071221cf9 h1:tbuodUh2vuhOVZAdW3NEUvosFHUMJwUNl7jk/VSEiwc=
|
||||
github.com/lucas-clemente/quic-go v0.7.1-0.20190401152353-907071221cf9/go.mod h1:PpMmPfPKO9nKJ/psF49ESTAGQSdfXxlg1otPbEB2nOw=
|
||||
github.com/marten-seemann/qtls v0.2.3 h1:0yWJ43C62LsZt08vuQJDK1uC1czUc3FJeCLPoNAI4vA=
|
||||
@@ -33,8 +33,8 @@ github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs=
|
||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU=
|
||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/pion/datachannel v1.4.19 h1:IcOmm5fdDzJVCMgFYDCMtFC+lrjG78KcMYXH+gOo6ys=
|
||||
github.com/pion/datachannel v1.4.19/go.mod h1:JzKF/zzeWgkOYwQ+KFb8JzbrUt8s63um+Qunu8VqTyw=
|
||||
github.com/pion/datachannel v1.4.21 h1:3ZvhNyfmxsAqltQrApLPQMhSFNA+aT87RqyCq4OXmf0=
|
||||
github.com/pion/datachannel v1.4.21/go.mod h1:oiNyP4gHx2DIwRzX/MFyH0Rz/Gz05OgBlayAI2hAWjg=
|
||||
github.com/pion/dtls/v2 v2.0.1 h1:ddE7+V0faYRbyh4uPsRZ2vLdRrjVZn+wmCfI7jlBfaA=
|
||||
github.com/pion/dtls/v2 v2.0.1/go.mod h1:uMQkz2W0cSqY00xav7WByQ4Hb+18xeQh2oH2fRezr5U=
|
||||
github.com/pion/dtls/v2 v2.0.2 h1:FHCHTiM182Y8e15aFTiORroiATUI16ryHiQh8AIOJ1E=
|
||||
@@ -54,8 +54,8 @@ github.com/pion/rtcp v1.2.3 h1:2wrhKnqgSz91Q5nzYTO07mQXztYPtxL8a0XOss4rJqA=
|
||||
github.com/pion/rtcp v1.2.3/go.mod h1:zGhIv0RPRF0Z1Wiij22pUt5W/c9fevqSzT4jje/oK7I=
|
||||
github.com/pion/rtp v1.6.0 h1:4Ssnl/T5W2LzxHj9ssYpGVEQh3YYhQFNVmSWO88MMwk=
|
||||
github.com/pion/rtp v1.6.0/go.mod h1:QgfogHsMBVE/RFNno467U/KBqfUywEH+HK+0rtnwsdI=
|
||||
github.com/pion/sctp v1.7.8 h1:tEWel2BKXLZitU+LxY3GDeQXoKeTafYasiu/X+XBKNM=
|
||||
github.com/pion/sctp v1.7.8/go.mod h1:EhpTUQu1/lcK3xI+eriS6/96fWetHGCvBi9MSsnaBN0=
|
||||
github.com/pion/sctp v1.7.10 h1:o3p3/hZB5Cx12RMGyWmItevJtZ6o2cpuxaw6GOS4x+8=
|
||||
github.com/pion/sctp v1.7.10/go.mod h1:EhpTUQu1/lcK3xI+eriS6/96fWetHGCvBi9MSsnaBN0=
|
||||
github.com/pion/sdp/v2 v2.4.0 h1:luUtaETR5x2KNNpvEMv/r4Y+/kzImzbz4Lm1z8eQNQI=
|
||||
github.com/pion/sdp/v2 v2.4.0/go.mod h1:L2LxrOpSTJbAns244vfPChbciR/ReU1KWfG04OpkR7E=
|
||||
github.com/pion/srtp v1.5.1 h1:9Q3jAfslYZBt+C69SI/ZcONJh9049JUHZWYRRf5KEKw=
|
||||
@@ -73,8 +73,8 @@ github.com/pion/turn/v2 v2.0.4 h1:oDguhEv2L/4rxwbL9clGLgtzQPjtuZwCdoM7Te8vQVk=
|
||||
github.com/pion/turn/v2 v2.0.4/go.mod h1:1812p4DcGVbYVBTiraUmP50XoKye++AMkbfp+N27mog=
|
||||
github.com/pion/udp v0.1.0 h1:uGxQsNyrqG3GLINv36Ff60covYmfrLoxzwnCsIYspXI=
|
||||
github.com/pion/udp v0.1.0/go.mod h1:BPELIjbwE9PRbd/zxI/KYBnbo7B6+oA6YuEaNE8lths=
|
||||
github.com/pion/webrtc/v2 v2.2.23 h1:rZdOC95fwUCoQFVjHooPAayx/vhs3SLHFz8J/iRkAuk=
|
||||
github.com/pion/webrtc/v2 v2.2.23/go.mod h1:1lN/3EcATkQxc7GJSQbISCGC2l64Xu2VSLpwEG3c/tM=
|
||||
github.com/pion/webrtc/v2 v2.2.26 h1:01hWE26pL3LgqfxvQ1fr6O4ZtyRFFJmQEZK39pHWfFc=
|
||||
github.com/pion/webrtc/v2 v2.2.26/go.mod h1:XMZbZRNHyPDe1gzTIHFcQu02283YO45CbiwFgKvXnmc=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
@@ -119,8 +119,8 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c h1:UIcGWL6/wpCfyGuJnRFJRurA+yj8RrW7Q6x2YMCXt6c=
|
||||
golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1 h1:sIky/MyNRSHTrdxfsiUSS4WIAMvInbeXljJz+jDjeYE=
|
||||
golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a h1:i47hUS795cOydZI4AwJQCKXOr4BvxzvikwDoDtHhP2Y=
|
||||
golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
|
@@ -3,6 +3,7 @@ package mediadevices
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"strings"
|
||||
|
||||
"github.com/pion/mediadevices/pkg/driver"
|
||||
"github.com/pion/mediadevices/pkg/prop"
|
||||
@@ -214,7 +215,23 @@ func selectBestDriver(filter driver.FilterFn, constraints MediaTrackConstraints)
|
||||
}
|
||||
|
||||
if bestDriver == nil {
|
||||
return nil, MediaTrackConstraints{}, errNotFound
|
||||
var foundProperties []string
|
||||
for _, props := range driverProperties {
|
||||
for _, p := range props {
|
||||
foundProperties = append(foundProperties, fmt.Sprint(&p))
|
||||
}
|
||||
}
|
||||
|
||||
err := fmt.Errorf(`%w:
|
||||
============ Found Properties ============
|
||||
|
||||
%s
|
||||
|
||||
=============== Constraints ==============
|
||||
|
||||
%s
|
||||
`, errNotFound, strings.Join(foundProperties, "\n\n"), &constraints)
|
||||
return nil, MediaTrackConstraints{}, err
|
||||
}
|
||||
|
||||
constraints.selectedMedia = prop.Media{}
|
||||
|
@@ -80,13 +80,13 @@ func (e *encoder) Read(p []byte) (int, error) {
|
||||
|
||||
switch b := buff.(type) {
|
||||
case *wave.Int16Interleaved:
|
||||
n, err := e.engine.Encode(b.Data, p)
|
||||
n, err := e.engine.EncodeBytes(b.Data, p, false)
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
return n, nil
|
||||
case *wave.Float32Interleaved:
|
||||
n, err := e.engine.EncodeFloat32(b.Data, p)
|
||||
n, err := e.engine.EncodeBytes(b.Data, p, true)
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
|
@@ -57,10 +57,11 @@ func init() {
|
||||
|
||||
func newCamera(path string) *camera {
|
||||
formats := map[webcam.PixelFormat]frame.Format{
|
||||
webcam.PixelFormat(C.V4L2_PIX_FMT_YUYV): frame.FormatYUYV,
|
||||
webcam.PixelFormat(C.V4L2_PIX_FMT_UYVY): frame.FormatUYVY,
|
||||
webcam.PixelFormat(C.V4L2_PIX_FMT_NV12): frame.FormatNV21,
|
||||
webcam.PixelFormat(C.V4L2_PIX_FMT_MJPEG): frame.FormatMJPEG,
|
||||
webcam.PixelFormat(C.V4L2_PIX_FMT_YUV420): frame.FormatI420,
|
||||
webcam.PixelFormat(C.V4L2_PIX_FMT_YUYV): frame.FormatYUYV,
|
||||
webcam.PixelFormat(C.V4L2_PIX_FMT_UYVY): frame.FormatUYVY,
|
||||
webcam.PixelFormat(C.V4L2_PIX_FMT_NV12): frame.FormatNV21,
|
||||
webcam.PixelFormat(C.V4L2_PIX_FMT_MJPEG): frame.FormatMJPEG,
|
||||
}
|
||||
|
||||
reversedFormats := make(map[frame.Format]webcam.PixelFormat)
|
||||
|
@@ -3,8 +3,6 @@ package frame
|
||||
type Format string
|
||||
|
||||
const (
|
||||
// YUV Formats
|
||||
|
||||
// FormatI420 https://www.fourcc.org/pixel-format/yuv-i420/
|
||||
FormatI420 Format = "I420"
|
||||
// FormatI444 is a YUV format without sub-sampling
|
||||
@@ -16,18 +14,11 @@ const (
|
||||
// FormatUYVY https://www.fourcc.org/pixel-format/yuv-uyvy/
|
||||
FormatUYVY = "UYVY"
|
||||
|
||||
// RGB Formats
|
||||
|
||||
// FormatRGBA https://www.fourcc.org/pixel-format/rgb-rgba/
|
||||
FormatRGBA Format = "RGBA"
|
||||
|
||||
// Compressed Formats
|
||||
|
||||
// FormatMJPEG https://www.fourcc.org/mjpg/
|
||||
FormatMJPEG = "MJPEG"
|
||||
)
|
||||
|
||||
// YUV aliases
|
||||
|
||||
// FormatYUYV is an alias of FormatYUY2
|
||||
const FormatYUYV = FormatYUY2
|
||||
|
@@ -5,7 +5,7 @@ import (
|
||||
)
|
||||
|
||||
func NewDecoder(f Format) (Decoder, error) {
|
||||
var decoder DecoderFunc
|
||||
var decoder decoderFunc
|
||||
|
||||
switch f {
|
||||
case FormatI420:
|
||||
|
@@ -7,8 +7,8 @@ type Decoder interface {
|
||||
}
|
||||
|
||||
// DecoderFunc is a proxy type for Decoder
|
||||
type DecoderFunc func(frame []byte, width, height int) (image.Image, error)
|
||||
type decoderFunc func(frame []byte, width, height int) (image.Image, error)
|
||||
|
||||
func (f DecoderFunc) Decode(frame []byte, width, height int) (image.Image, error) {
|
||||
func (f decoderFunc) Decode(frame []byte, width, height int) (image.Image, error) {
|
||||
return f(frame, width, height)
|
||||
}
|
||||
|
@@ -66,8 +66,8 @@ func NewBuffer(nSamples int) TransformFunc {
|
||||
case *wave.Int16Interleaved:
|
||||
ibCopy := *ib
|
||||
ibCopy.Size.Len = nSamples
|
||||
n := nSamples * ib.Size.Channels
|
||||
ibCopy.Data = make([]int16, n)
|
||||
n := nSamples * ib.Size.Channels * 2
|
||||
ibCopy.Data = make([]uint8, n)
|
||||
copy(ibCopy.Data, ib.Data)
|
||||
ib.Data = ib.Data[n:]
|
||||
ib.Size.Len -= nSamples
|
||||
@@ -76,8 +76,8 @@ func NewBuffer(nSamples int) TransformFunc {
|
||||
case *wave.Float32Interleaved:
|
||||
ibCopy := *ib
|
||||
ibCopy.Size.Len = nSamples
|
||||
n := nSamples * ib.Size.Channels
|
||||
ibCopy.Data = make([]float32, n)
|
||||
n := nSamples * ib.Size.Channels * 4
|
||||
ibCopy.Data = make([]uint8, n)
|
||||
copy(ibCopy.Data, ib.Data)
|
||||
ib.Data = ib.Data[n:]
|
||||
ib.Size.Len -= nSamples
|
||||
|
@@ -9,41 +9,83 @@ import (
|
||||
)
|
||||
|
||||
func TestBuffer(t *testing.T) {
|
||||
input1 := wave.NewInt16Interleaved(wave.ChunkInfo{Len: 1, Channels: 2, SamplingRate: 1234})
|
||||
input1.SetInt16(0, 0, 1)
|
||||
input1.SetInt16(0, 1, 2)
|
||||
|
||||
input2 := wave.NewInt16Interleaved(wave.ChunkInfo{Len: 3, Channels: 2, SamplingRate: 1234})
|
||||
input2.SetInt16(0, 0, 3)
|
||||
input2.SetInt16(0, 1, 4)
|
||||
input2.SetInt16(1, 0, 5)
|
||||
input2.SetInt16(1, 1, 6)
|
||||
input2.SetInt16(2, 0, 7)
|
||||
input2.SetInt16(2, 1, 8)
|
||||
|
||||
input3 := wave.NewInt16Interleaved(wave.ChunkInfo{Len: 2, Channels: 2, SamplingRate: 1234})
|
||||
input3.SetInt16(0, 0, 9)
|
||||
input3.SetInt16(0, 1, 10)
|
||||
input3.SetInt16(1, 0, 11)
|
||||
input3.SetInt16(1, 1, 12)
|
||||
|
||||
input4 := wave.NewInt16Interleaved(wave.ChunkInfo{Len: 7, Channels: 2, SamplingRate: 1234})
|
||||
input4.SetInt16(0, 0, 13)
|
||||
input4.SetInt16(0, 1, 14)
|
||||
input4.SetInt16(1, 0, 15)
|
||||
input4.SetInt16(1, 1, 16)
|
||||
input4.SetInt16(2, 0, 17)
|
||||
input4.SetInt16(2, 1, 18)
|
||||
input4.SetInt16(3, 0, 19)
|
||||
input4.SetInt16(3, 1, 20)
|
||||
input4.SetInt16(4, 0, 21)
|
||||
input4.SetInt16(4, 1, 22)
|
||||
input4.SetInt16(5, 0, 23)
|
||||
input4.SetInt16(5, 1, 24)
|
||||
input4.SetInt16(6, 0, 25)
|
||||
input4.SetInt16(6, 1, 26)
|
||||
|
||||
expected1 := wave.NewInt16Interleaved(wave.ChunkInfo{Len: 3, Channels: 2, SamplingRate: 1234})
|
||||
expected1.SetInt16(0, 0, 1)
|
||||
expected1.SetInt16(0, 1, 2)
|
||||
expected1.SetInt16(1, 0, 3)
|
||||
expected1.SetInt16(1, 1, 4)
|
||||
expected1.SetInt16(2, 0, 5)
|
||||
expected1.SetInt16(2, 1, 6)
|
||||
|
||||
expected2 := wave.NewInt16Interleaved(wave.ChunkInfo{Len: 3, Channels: 2, SamplingRate: 1234})
|
||||
expected2.SetInt16(0, 0, 7)
|
||||
expected2.SetInt16(0, 1, 8)
|
||||
expected2.SetInt16(1, 0, 9)
|
||||
expected2.SetInt16(1, 1, 10)
|
||||
expected2.SetInt16(2, 0, 11)
|
||||
expected2.SetInt16(2, 1, 12)
|
||||
|
||||
expected3 := wave.NewInt16Interleaved(wave.ChunkInfo{Len: 3, Channels: 2, SamplingRate: 1234})
|
||||
expected3.SetInt16(0, 0, 13)
|
||||
expected3.SetInt16(0, 1, 14)
|
||||
expected3.SetInt16(1, 0, 15)
|
||||
expected3.SetInt16(1, 1, 16)
|
||||
expected3.SetInt16(2, 0, 17)
|
||||
expected3.SetInt16(2, 1, 18)
|
||||
|
||||
expected4 := wave.NewInt16Interleaved(wave.ChunkInfo{Len: 3, Channels: 2, SamplingRate: 1234})
|
||||
expected4.SetInt16(0, 0, 19)
|
||||
expected4.SetInt16(0, 1, 20)
|
||||
expected4.SetInt16(1, 0, 21)
|
||||
expected4.SetInt16(1, 1, 22)
|
||||
expected4.SetInt16(2, 0, 23)
|
||||
expected4.SetInt16(2, 1, 24)
|
||||
|
||||
input := []wave.Audio{
|
||||
&wave.Int16Interleaved{
|
||||
Size: wave.ChunkInfo{Len: 1, Channels: 2, SamplingRate: 1234},
|
||||
Data: []int16{1, 2},
|
||||
},
|
||||
&wave.Int16Interleaved{
|
||||
Size: wave.ChunkInfo{Len: 3, Channels: 2, SamplingRate: 1234},
|
||||
Data: []int16{3, 4, 5, 6, 7, 8},
|
||||
},
|
||||
&wave.Int16Interleaved{
|
||||
Size: wave.ChunkInfo{Len: 2, Channels: 2, SamplingRate: 1234},
|
||||
Data: []int16{9, 10, 11, 12},
|
||||
},
|
||||
&wave.Int16Interleaved{
|
||||
Size: wave.ChunkInfo{Len: 7, Channels: 2, SamplingRate: 1234},
|
||||
Data: []int16{13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26},
|
||||
},
|
||||
input1,
|
||||
input2,
|
||||
input3,
|
||||
input4,
|
||||
}
|
||||
expected := []wave.Audio{
|
||||
&wave.Int16Interleaved{
|
||||
Size: wave.ChunkInfo{Len: 3, Channels: 2, SamplingRate: 1234},
|
||||
Data: []int16{1, 2, 3, 4, 5, 6},
|
||||
},
|
||||
&wave.Int16Interleaved{
|
||||
Size: wave.ChunkInfo{Len: 3, Channels: 2, SamplingRate: 1234},
|
||||
Data: []int16{7, 8, 9, 10, 11, 12},
|
||||
},
|
||||
&wave.Int16Interleaved{
|
||||
Size: wave.ChunkInfo{Len: 3, Channels: 2, SamplingRate: 1234},
|
||||
Data: []int16{13, 14, 15, 16, 17, 18},
|
||||
},
|
||||
&wave.Int16Interleaved{
|
||||
Size: wave.ChunkInfo{Len: 3, Channels: 2, SamplingRate: 1234},
|
||||
Data: []int16{19, 20, 21, 22, 23, 24},
|
||||
},
|
||||
expected1,
|
||||
expected2,
|
||||
expected3,
|
||||
expected4,
|
||||
}
|
||||
|
||||
trans := NewBuffer(3)
|
||||
|
@@ -10,25 +10,33 @@ import (
|
||||
)
|
||||
|
||||
func TestMixer(t *testing.T) {
|
||||
input1 := wave.NewInt16Interleaved(wave.ChunkInfo{Len: 1, Channels: 2, SamplingRate: 1234})
|
||||
input1.SetInt16(0, 0, 1)
|
||||
input1.SetInt16(0, 1, 3)
|
||||
|
||||
input2 := wave.NewInt16Interleaved(wave.ChunkInfo{Len: 3, Channels: 2, SamplingRate: 1234})
|
||||
input2.SetInt16(0, 0, 2)
|
||||
input2.SetInt16(0, 1, 4)
|
||||
input2.SetInt16(1, 0, 3)
|
||||
input2.SetInt16(1, 1, 5)
|
||||
input2.SetInt16(2, 0, 4)
|
||||
input2.SetInt16(2, 1, 6)
|
||||
|
||||
expected1 := wave.NewInt16Interleaved(wave.ChunkInfo{Len: 1, Channels: 1, SamplingRate: 1234})
|
||||
expected1.SetInt16(0, 0, 2)
|
||||
|
||||
expected2 := wave.NewInt16Interleaved(wave.ChunkInfo{Len: 3, Channels: 1, SamplingRate: 1234})
|
||||
expected2.SetInt16(0, 0, 3)
|
||||
expected2.SetInt16(1, 0, 4)
|
||||
expected2.SetInt16(2, 0, 5)
|
||||
|
||||
input := []wave.Audio{
|
||||
&wave.Int16Interleaved{
|
||||
Size: wave.ChunkInfo{Len: 1, Channels: 2, SamplingRate: 1234},
|
||||
Data: []int16{1, 3},
|
||||
},
|
||||
&wave.Int16Interleaved{
|
||||
Size: wave.ChunkInfo{Len: 3, Channels: 2, SamplingRate: 1234},
|
||||
Data: []int16{2, 4, 3, 5, 4, 6},
|
||||
},
|
||||
input1,
|
||||
input2,
|
||||
}
|
||||
expected := []wave.Audio{
|
||||
&wave.Int16Interleaved{
|
||||
Size: wave.ChunkInfo{Len: 1, Channels: 1, SamplingRate: 1234},
|
||||
Data: []int16{2},
|
||||
},
|
||||
&wave.Int16Interleaved{
|
||||
Size: wave.ChunkInfo{Len: 3, Channels: 1, SamplingRate: 1234},
|
||||
Data: []int16{3, 4, 5},
|
||||
},
|
||||
expected1,
|
||||
expected2,
|
||||
}
|
||||
|
||||
trans := NewChannelMixer(1, &mixer.MonoMixer{})
|
||||
|
214
pkg/io/video/framebuffer.go
Normal file
214
pkg/io/video/framebuffer.go
Normal file
@@ -0,0 +1,214 @@
|
||||
package video
|
||||
|
||||
import (
|
||||
"image"
|
||||
)
|
||||
|
||||
// FrameBuffer is a buffer that can store any image format.
|
||||
type FrameBuffer struct {
|
||||
buffer []uint8
|
||||
tmp image.Image
|
||||
}
|
||||
|
||||
// NewFrameBuffer creates a new FrameBuffer instance and initialize internal buffer
|
||||
// with initialSize
|
||||
func NewFrameBuffer(initialSize int) *FrameBuffer {
|
||||
return &FrameBuffer{
|
||||
buffer: make([]uint8, initialSize),
|
||||
}
|
||||
}
|
||||
|
||||
func (buff *FrameBuffer) storeInOrder(srcs ...[]uint8) {
|
||||
var neededSize int
|
||||
|
||||
for _, src := range srcs {
|
||||
neededSize += len(src)
|
||||
}
|
||||
|
||||
if len(buff.buffer) < neededSize {
|
||||
if cap(buff.buffer) >= neededSize {
|
||||
buff.buffer = buff.buffer[:neededSize]
|
||||
} else {
|
||||
buff.buffer = make([]uint8, neededSize)
|
||||
}
|
||||
}
|
||||
|
||||
var currentLen int
|
||||
for _, src := range srcs {
|
||||
copy(buff.buffer[currentLen:], src)
|
||||
currentLen += len(src)
|
||||
}
|
||||
}
|
||||
|
||||
// Load loads the current owned image
|
||||
func (buff *FrameBuffer) Load() image.Image {
|
||||
return buff.tmp
|
||||
}
|
||||
|
||||
// StoreCopy makes a copy of src and store its copy. StoreCopy will reuse as much memory as it can
|
||||
// from the previous copies. For example, if StoreCopy is given an image that has the same resolution
|
||||
// and format from the previous call, StoreCopy will not allocate extra memory and only copy the content
|
||||
// from src to the previous buffer.
|
||||
func (buff *FrameBuffer) StoreCopy(src image.Image) {
|
||||
switch src := src.(type) {
|
||||
case *image.Alpha:
|
||||
clone, ok := buff.tmp.(*image.Alpha)
|
||||
if ok {
|
||||
*clone = *src
|
||||
} else {
|
||||
copied := *src
|
||||
clone = &copied
|
||||
}
|
||||
|
||||
buff.storeInOrder(src.Pix)
|
||||
clone.Pix = buff.buffer[:len(src.Pix)]
|
||||
|
||||
buff.tmp = clone
|
||||
case *image.Alpha16:
|
||||
clone, ok := buff.tmp.(*image.Alpha16)
|
||||
if ok {
|
||||
*clone = *src
|
||||
} else {
|
||||
copied := *src
|
||||
clone = &copied
|
||||
}
|
||||
|
||||
buff.storeInOrder(src.Pix)
|
||||
clone.Pix = buff.buffer[:len(src.Pix)]
|
||||
|
||||
buff.tmp = clone
|
||||
case *image.CMYK:
|
||||
clone, ok := buff.tmp.(*image.CMYK)
|
||||
if ok {
|
||||
*clone = *src
|
||||
} else {
|
||||
copied := *src
|
||||
clone = &copied
|
||||
}
|
||||
|
||||
buff.storeInOrder(src.Pix)
|
||||
clone.Pix = buff.buffer[:len(src.Pix)]
|
||||
|
||||
buff.tmp = clone
|
||||
case *image.Gray:
|
||||
clone, ok := buff.tmp.(*image.Gray)
|
||||
if ok {
|
||||
*clone = *src
|
||||
} else {
|
||||
copied := *src
|
||||
clone = &copied
|
||||
}
|
||||
|
||||
buff.storeInOrder(src.Pix)
|
||||
clone.Pix = buff.buffer[:len(src.Pix)]
|
||||
|
||||
buff.tmp = clone
|
||||
case *image.Gray16:
|
||||
clone, ok := buff.tmp.(*image.Gray16)
|
||||
if ok {
|
||||
*clone = *src
|
||||
} else {
|
||||
copied := *src
|
||||
clone = &copied
|
||||
}
|
||||
|
||||
buff.storeInOrder(src.Pix)
|
||||
clone.Pix = buff.buffer[:len(src.Pix)]
|
||||
|
||||
buff.tmp = clone
|
||||
case *image.NRGBA:
|
||||
clone, ok := buff.tmp.(*image.NRGBA)
|
||||
if ok {
|
||||
*clone = *src
|
||||
} else {
|
||||
copied := *src
|
||||
clone = &copied
|
||||
}
|
||||
|
||||
buff.storeInOrder(src.Pix)
|
||||
clone.Pix = buff.buffer[:len(src.Pix)]
|
||||
|
||||
buff.tmp = clone
|
||||
case *image.NRGBA64:
|
||||
clone, ok := buff.tmp.(*image.NRGBA64)
|
||||
if ok {
|
||||
*clone = *src
|
||||
} else {
|
||||
copied := *src
|
||||
clone = &copied
|
||||
}
|
||||
|
||||
buff.storeInOrder(src.Pix)
|
||||
clone.Pix = buff.buffer[:len(src.Pix)]
|
||||
|
||||
buff.tmp = clone
|
||||
case *image.RGBA:
|
||||
clone, ok := buff.tmp.(*image.RGBA)
|
||||
if ok {
|
||||
*clone = *src
|
||||
} else {
|
||||
copied := *src
|
||||
clone = &copied
|
||||
}
|
||||
|
||||
buff.storeInOrder(src.Pix)
|
||||
clone.Pix = buff.buffer[:len(src.Pix)]
|
||||
|
||||
buff.tmp = clone
|
||||
case *image.RGBA64:
|
||||
clone, ok := buff.tmp.(*image.RGBA64)
|
||||
if ok {
|
||||
*clone = *src
|
||||
} else {
|
||||
copied := *src
|
||||
clone = &copied
|
||||
}
|
||||
|
||||
buff.storeInOrder(src.Pix)
|
||||
clone.Pix = buff.buffer[:len(src.Pix)]
|
||||
|
||||
buff.tmp = clone
|
||||
case *image.NYCbCrA:
|
||||
clone, ok := buff.tmp.(*image.NYCbCrA)
|
||||
if ok {
|
||||
*clone = *src
|
||||
} else {
|
||||
copied := *src
|
||||
clone = &copied
|
||||
}
|
||||
|
||||
var currentLen int
|
||||
buff.storeInOrder(src.Y, src.Cb, src.Cr, src.A)
|
||||
clone.Y = buff.buffer[currentLen : currentLen+len(src.Y) : currentLen+len(src.Y)]
|
||||
currentLen += len(src.Y)
|
||||
clone.Cb = buff.buffer[currentLen : currentLen+len(src.Cb) : currentLen+len(src.Cb)]
|
||||
currentLen += len(src.Cb)
|
||||
clone.Cr = buff.buffer[currentLen : currentLen+len(src.Cr) : currentLen+len(src.Cr)]
|
||||
currentLen += len(src.Cr)
|
||||
clone.A = buff.buffer[currentLen : currentLen+len(src.A) : currentLen+len(src.A)]
|
||||
|
||||
buff.tmp = clone
|
||||
case *image.YCbCr:
|
||||
clone, ok := buff.tmp.(*image.YCbCr)
|
||||
if ok {
|
||||
*clone = *src
|
||||
} else {
|
||||
copied := *src
|
||||
clone = &copied
|
||||
}
|
||||
|
||||
var currentLen int
|
||||
buff.storeInOrder(src.Y, src.Cb, src.Cr)
|
||||
clone.Y = buff.buffer[currentLen : currentLen+len(src.Y) : currentLen+len(src.Y)]
|
||||
currentLen += len(src.Y)
|
||||
clone.Cb = buff.buffer[currentLen : currentLen+len(src.Cb) : currentLen+len(src.Cb)]
|
||||
currentLen += len(src.Cb)
|
||||
clone.Cr = buff.buffer[currentLen : currentLen+len(src.Cr) : currentLen+len(src.Cr)]
|
||||
|
||||
buff.tmp = clone
|
||||
default:
|
||||
var converted image.RGBA
|
||||
imageToRGBA(&converted, src)
|
||||
buff.StoreCopy(&converted)
|
||||
}
|
||||
}
|
195
pkg/io/video/framebuffer_test.go
Normal file
195
pkg/io/video/framebuffer_test.go
Normal file
@@ -0,0 +1,195 @@
|
||||
package video
|
||||
|
||||
import (
|
||||
"image"
|
||||
"math/rand"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func randomize(arr []uint8) {
|
||||
for i := range arr {
|
||||
arr[i] = uint8(rand.Uint32())
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkFrameBufferCopyOptimized(b *testing.B) {
|
||||
frameBuffer := NewFrameBuffer(0)
|
||||
resolution := image.Rect(0, 0, 1920, 1080)
|
||||
src := image.NewYCbCr(resolution, image.YCbCrSubsampleRatio420)
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
frameBuffer.StoreCopy(src)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkFrameBufferCopyNaive(b *testing.B) {
|
||||
resolution := image.Rect(0, 0, 1920, 1080)
|
||||
src := image.NewYCbCr(resolution, image.YCbCrSubsampleRatio420)
|
||||
var dst image.Image
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
clone := *src
|
||||
clone.Cb = make([]uint8, len(src.Cb))
|
||||
clone.Cr = make([]uint8, len(src.Cr))
|
||||
clone.Y = make([]uint8, len(src.Y))
|
||||
|
||||
copy(clone.Cb, src.Cb)
|
||||
copy(clone.Cr, src.Cr)
|
||||
copy(clone.Y, src.Y)
|
||||
dst = &clone
|
||||
_ = dst
|
||||
}
|
||||
}
|
||||
|
||||
func TestFrameBufferStoreCopyAndLoad(t *testing.T) {
|
||||
resolution := image.Rect(0, 0, 16, 8)
|
||||
rgbaLike := image.NewRGBA64(resolution)
|
||||
randomize(rgbaLike.Pix)
|
||||
testCases := map[string]struct {
|
||||
New func() image.Image
|
||||
Update func(image.Image)
|
||||
}{
|
||||
"Alpha": {
|
||||
New: func() image.Image {
|
||||
return (*image.Alpha)(rgbaLike)
|
||||
},
|
||||
Update: func(src image.Image) {
|
||||
img := src.(*image.Alpha)
|
||||
randomize(img.Pix)
|
||||
},
|
||||
},
|
||||
"Alpha16": {
|
||||
New: func() image.Image {
|
||||
return (*image.Alpha16)(rgbaLike)
|
||||
},
|
||||
Update: func(src image.Image) {
|
||||
img := src.(*image.Alpha16)
|
||||
randomize(img.Pix)
|
||||
},
|
||||
},
|
||||
"CMYK": {
|
||||
New: func() image.Image {
|
||||
return (*image.CMYK)(rgbaLike)
|
||||
},
|
||||
Update: func(src image.Image) {
|
||||
img := src.(*image.CMYK)
|
||||
randomize(img.Pix)
|
||||
},
|
||||
},
|
||||
"Gray": {
|
||||
New: func() image.Image {
|
||||
return (*image.Gray)(rgbaLike)
|
||||
},
|
||||
Update: func(src image.Image) {
|
||||
img := src.(*image.Gray)
|
||||
randomize(img.Pix)
|
||||
},
|
||||
},
|
||||
"Gray16": {
|
||||
New: func() image.Image {
|
||||
return (*image.Gray16)(rgbaLike)
|
||||
},
|
||||
Update: func(src image.Image) {
|
||||
img := src.(*image.Gray16)
|
||||
randomize(img.Pix)
|
||||
},
|
||||
},
|
||||
"NRGBA": {
|
||||
New: func() image.Image {
|
||||
return (*image.NRGBA)(rgbaLike)
|
||||
},
|
||||
Update: func(src image.Image) {
|
||||
img := src.(*image.NRGBA)
|
||||
randomize(img.Pix)
|
||||
},
|
||||
},
|
||||
"NRGBA64": {
|
||||
New: func() image.Image {
|
||||
return (*image.NRGBA64)(rgbaLike)
|
||||
},
|
||||
Update: func(src image.Image) {
|
||||
img := src.(*image.NRGBA64)
|
||||
randomize(img.Pix)
|
||||
},
|
||||
},
|
||||
"RGBA": {
|
||||
New: func() image.Image {
|
||||
return (*image.RGBA)(rgbaLike)
|
||||
},
|
||||
Update: func(src image.Image) {
|
||||
img := src.(*image.RGBA)
|
||||
randomize(img.Pix)
|
||||
},
|
||||
},
|
||||
"RGBA64": {
|
||||
New: func() image.Image {
|
||||
return (*image.RGBA64)(rgbaLike)
|
||||
},
|
||||
Update: func(src image.Image) {
|
||||
img := src.(*image.RGBA64)
|
||||
randomize(img.Pix)
|
||||
},
|
||||
},
|
||||
"NYCbCrA": {
|
||||
New: func() image.Image {
|
||||
img := image.NewNYCbCrA(resolution, image.YCbCrSubsampleRatio420)
|
||||
randomize(img.Y)
|
||||
randomize(img.Cb)
|
||||
randomize(img.Cr)
|
||||
randomize(img.A)
|
||||
img.CStride = 10
|
||||
img.YStride = 5
|
||||
return img
|
||||
},
|
||||
Update: func(src image.Image) {
|
||||
img := src.(*image.NYCbCrA)
|
||||
randomize(img.Y)
|
||||
randomize(img.Cb)
|
||||
randomize(img.Cr)
|
||||
randomize(img.A)
|
||||
img.CStride = 3
|
||||
img.YStride = 2
|
||||
},
|
||||
},
|
||||
"YCbCr": {
|
||||
New: func() image.Image {
|
||||
img := image.NewYCbCr(resolution, image.YCbCrSubsampleRatio420)
|
||||
randomize(img.Y)
|
||||
randomize(img.Cb)
|
||||
randomize(img.Cr)
|
||||
img.CStride = 10
|
||||
img.YStride = 5
|
||||
return img
|
||||
},
|
||||
Update: func(src image.Image) {
|
||||
img := src.(*image.YCbCr)
|
||||
randomize(img.Y)
|
||||
randomize(img.Cb)
|
||||
randomize(img.Cr)
|
||||
img.CStride = 3
|
||||
img.YStride = 2
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
frameBuffer := NewFrameBuffer(0)
|
||||
|
||||
for name, testCase := range testCases {
|
||||
// Since the test also wants to make sure that Copier can convert from 1 type to another,
|
||||
// t.Run is not ideal since it'll run the tests separately
|
||||
t.Log("Testing", name)
|
||||
|
||||
src := testCase.New()
|
||||
frameBuffer.StoreCopy(src)
|
||||
if !reflect.DeepEqual(frameBuffer.Load(), src) {
|
||||
t.Fatal("Expected the copied image to be identical with the source")
|
||||
}
|
||||
|
||||
testCase.Update(src)
|
||||
frameBuffer.StoreCopy(src)
|
||||
if !reflect.DeepEqual(frameBuffer.Load(), src) {
|
||||
t.Fatal("Expected the copied image to be identical with the source after an update in source")
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,5 +1,7 @@
|
||||
package prop
|
||||
|
||||
import "fmt"
|
||||
|
||||
// BoolConstraint is an interface to represent bool value constraint.
|
||||
type BoolConstraint interface {
|
||||
Compare(bool) (float64, bool)
|
||||
@@ -20,6 +22,11 @@ func (b BoolExact) Compare(o bool) (float64, bool) {
|
||||
// Value implements BoolConstraint.
|
||||
func (b BoolExact) Value() bool { return bool(b) }
|
||||
|
||||
// String implements Stringify
|
||||
func (b BoolExact) String() string {
|
||||
return fmt.Sprintf("%t (exact)", b)
|
||||
}
|
||||
|
||||
// Bool specifies ideal bool value.
|
||||
type Bool BoolExact
|
||||
|
||||
|
@@ -1,7 +1,9 @@
|
||||
package prop
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -23,6 +25,11 @@ func (d Duration) Compare(a time.Duration) (float64, bool) {
|
||||
// Value implements DurationConstraint.
|
||||
func (d Duration) Value() (time.Duration, bool) { return time.Duration(d), true }
|
||||
|
||||
// String implements Stringify
|
||||
func (d Duration) String() string {
|
||||
return fmt.Sprintf("%v (ideal)", time.Duration(d))
|
||||
}
|
||||
|
||||
// DurationExact specifies exact duration value.
|
||||
type DurationExact time.Duration
|
||||
|
||||
@@ -37,6 +44,11 @@ func (d DurationExact) Compare(a time.Duration) (float64, bool) {
|
||||
// Value implements DurationConstraint.
|
||||
func (d DurationExact) Value() (time.Duration, bool) { return time.Duration(d), true }
|
||||
|
||||
// String implements Stringify
|
||||
func (d DurationExact) String() string {
|
||||
return fmt.Sprintf("%v (exact)", time.Duration(d))
|
||||
}
|
||||
|
||||
// DurationOneOf specifies list of expected duration values.
|
||||
type DurationOneOf []time.Duration
|
||||
|
||||
@@ -53,6 +65,16 @@ func (d DurationOneOf) Compare(a time.Duration) (float64, bool) {
|
||||
// Value implements DurationConstraint.
|
||||
func (DurationOneOf) Value() (time.Duration, bool) { return 0, false }
|
||||
|
||||
// String implements Stringify
|
||||
func (d DurationOneOf) String() string {
|
||||
var opts []string
|
||||
for _, v := range d {
|
||||
opts = append(opts, fmt.Sprint(v))
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s (one of values)", strings.Join(opts, ","))
|
||||
}
|
||||
|
||||
// DurationRanged specifies range of expected duration value.
|
||||
// If Ideal is non-zero, closest value to Ideal takes priority.
|
||||
type DurationRanged struct {
|
||||
@@ -96,3 +118,8 @@ func (d DurationRanged) Compare(a time.Duration) (float64, bool) {
|
||||
|
||||
// Value implements DurationConstraint.
|
||||
func (DurationRanged) Value() (time.Duration, bool) { return 0, false }
|
||||
|
||||
// String implements Stringify
|
||||
func (d DurationRanged) String() string {
|
||||
return fmt.Sprintf("%s - %s (range), %s (ideal)", d.Min, d.Max, d.Ideal)
|
||||
}
|
||||
|
@@ -1,7 +1,9 @@
|
||||
package prop
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// FloatConstraint is an interface to represent float value constraint.
|
||||
@@ -22,6 +24,11 @@ func (f Float) Compare(a float32) (float64, bool) {
|
||||
// Value implements FloatConstraint.
|
||||
func (f Float) Value() (float32, bool) { return float32(f), true }
|
||||
|
||||
// String implements Stringify
|
||||
func (f Float) String() string {
|
||||
return fmt.Sprintf("%.2f (ideal)", f)
|
||||
}
|
||||
|
||||
// FloatExact specifies exact float value.
|
||||
type FloatExact float32
|
||||
|
||||
@@ -36,6 +43,11 @@ func (f FloatExact) Compare(a float32) (float64, bool) {
|
||||
// Value implements FloatConstraint.
|
||||
func (f FloatExact) Value() (float32, bool) { return float32(f), true }
|
||||
|
||||
// String implements Stringify
|
||||
func (f FloatExact) String() string {
|
||||
return fmt.Sprintf("%.2f (exact)", f)
|
||||
}
|
||||
|
||||
// FloatOneOf specifies list of expected float values.
|
||||
type FloatOneOf []float32
|
||||
|
||||
@@ -52,6 +64,16 @@ func (f FloatOneOf) Compare(a float32) (float64, bool) {
|
||||
// Value implements FloatConstraint.
|
||||
func (FloatOneOf) Value() (float32, bool) { return 0, false }
|
||||
|
||||
// String implements Stringify
|
||||
func (f FloatOneOf) String() string {
|
||||
var opts []string
|
||||
for _, v := range f {
|
||||
opts = append(opts, fmt.Sprintf("%.2f", v))
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s (one of values)", strings.Join(opts, ","))
|
||||
}
|
||||
|
||||
// FloatRanged specifies range of expected float value.
|
||||
// If Ideal is non-zero, closest value to Ideal takes priority.
|
||||
type FloatRanged struct {
|
||||
@@ -95,3 +117,8 @@ func (f FloatRanged) Compare(a float32) (float64, bool) {
|
||||
|
||||
// Value implements FloatConstraint.
|
||||
func (FloatRanged) Value() (float32, bool) { return 0, false }
|
||||
|
||||
// String implements Stringify
|
||||
func (f FloatRanged) String() string {
|
||||
return fmt.Sprintf("%.2f - %.2f (range), %.2f (ideal)", f.Min, f.Max, f.Ideal)
|
||||
}
|
||||
|
@@ -1,7 +1,9 @@
|
||||
package prop
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/pion/mediadevices/pkg/frame"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// FrameFormatConstraint is an interface to represent frame format constraint.
|
||||
@@ -25,6 +27,11 @@ func (f FrameFormat) Compare(a frame.Format) (float64, bool) {
|
||||
// Value implements FrameFormatConstraint.
|
||||
func (f FrameFormat) Value() (frame.Format, bool) { return frame.Format(f), true }
|
||||
|
||||
// String implements Stringify
|
||||
func (f FrameFormat) String() string {
|
||||
return fmt.Sprintf("%s (ideal)", frame.Format(f))
|
||||
}
|
||||
|
||||
// FrameFormatExact specifies exact frame format.
|
||||
type FrameFormatExact frame.Format
|
||||
|
||||
@@ -39,6 +46,11 @@ func (f FrameFormatExact) Compare(a frame.Format) (float64, bool) {
|
||||
// Value implements FrameFormatConstraint.
|
||||
func (f FrameFormatExact) Value() (frame.Format, bool) { return frame.Format(f), true }
|
||||
|
||||
// String implements Stringify
|
||||
func (f FrameFormatExact) String() string {
|
||||
return fmt.Sprintf("%s (exact)", frame.Format(f))
|
||||
}
|
||||
|
||||
// FrameFormatOneOf specifies list of expected frame format.
|
||||
type FrameFormatOneOf []frame.Format
|
||||
|
||||
@@ -54,3 +66,13 @@ func (f FrameFormatOneOf) Compare(a frame.Format) (float64, bool) {
|
||||
|
||||
// Value implements FrameFormatConstraint.
|
||||
func (FrameFormatOneOf) Value() (frame.Format, bool) { return "", false }
|
||||
|
||||
// String implements Stringify
|
||||
func (f FrameFormatOneOf) String() string {
|
||||
var opts []string
|
||||
for _, v := range f {
|
||||
opts = append(opts, fmt.Sprint(v))
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s (one of values)", strings.Join(opts, ","))
|
||||
}
|
||||
|
@@ -1,7 +1,9 @@
|
||||
package prop
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// IntConstraint is an interface to represent integer value constraint.
|
||||
@@ -22,6 +24,11 @@ func (i Int) Compare(a int) (float64, bool) {
|
||||
// Value implements IntConstraint.
|
||||
func (i Int) Value() (int, bool) { return int(i), true }
|
||||
|
||||
// String implements Stringify
|
||||
func (i Int) String() string {
|
||||
return fmt.Sprintf("%d (ideal)", i)
|
||||
}
|
||||
|
||||
// IntExact specifies exact int value.
|
||||
type IntExact int
|
||||
|
||||
@@ -33,6 +40,11 @@ func (i IntExact) Compare(a int) (float64, bool) {
|
||||
return 1.0, false
|
||||
}
|
||||
|
||||
// String implements Stringify
|
||||
func (i IntExact) String() string {
|
||||
return fmt.Sprintf("%d (exact)", i)
|
||||
}
|
||||
|
||||
// Value implements IntConstraint.
|
||||
func (i IntExact) Value() (int, bool) { return int(i), true }
|
||||
|
||||
@@ -52,6 +64,16 @@ func (i IntOneOf) Compare(a int) (float64, bool) {
|
||||
// Value implements IntConstraint.
|
||||
func (IntOneOf) Value() (int, bool) { return 0, false }
|
||||
|
||||
// String implements Stringify
|
||||
func (i IntOneOf) String() string {
|
||||
var opts []string
|
||||
for _, v := range i {
|
||||
opts = append(opts, fmt.Sprint(v))
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s (one of values)", strings.Join(opts, ","))
|
||||
}
|
||||
|
||||
// IntRanged specifies range of expected int value.
|
||||
// If Ideal is non-zero, closest value to Ideal takes priority.
|
||||
type IntRanged struct {
|
||||
@@ -95,3 +117,8 @@ func (i IntRanged) Compare(a int) (float64, bool) {
|
||||
|
||||
// Value implements IntConstraint.
|
||||
func (IntRanged) Value() (int, bool) { return 0, false }
|
||||
|
||||
// String implements Stringify
|
||||
func (i IntRanged) String() string {
|
||||
return fmt.Sprintf("%d - %d (range), %d (ideal)", i.Min, i.Max, i.Ideal)
|
||||
}
|
||||
|
@@ -1,7 +1,9 @@
|
||||
package prop
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/pion/mediadevices/pkg/frame"
|
||||
@@ -15,6 +17,10 @@ type MediaConstraints struct {
|
||||
AudioConstraints
|
||||
}
|
||||
|
||||
func (m *MediaConstraints) String() string {
|
||||
return prettifyStruct(m)
|
||||
}
|
||||
|
||||
// Media stores single set of media propaties.
|
||||
type Media struct {
|
||||
DeviceID string
|
||||
@@ -22,6 +28,33 @@ type Media struct {
|
||||
Audio
|
||||
}
|
||||
|
||||
func (m *Media) String() string {
|
||||
return prettifyStruct(m)
|
||||
}
|
||||
|
||||
func prettifyStruct(i interface{}) string {
|
||||
var rows []string
|
||||
var addRows func(int, reflect.Value)
|
||||
addRows = func(level int, obj reflect.Value) {
|
||||
typeOf := obj.Type()
|
||||
for i := 0; i < obj.NumField(); i++ {
|
||||
field := typeOf.Field(i)
|
||||
value := obj.Field(i)
|
||||
|
||||
padding := strings.Repeat(" ", level)
|
||||
if value.Kind() == reflect.Struct {
|
||||
rows = append(rows, fmt.Sprintf("%s%v:", padding, field.Name))
|
||||
addRows(level+1, value)
|
||||
} else {
|
||||
rows = append(rows, fmt.Sprintf("%s%v: %v", padding, field.Name, value))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
addRows(0, reflect.ValueOf(i).Elem())
|
||||
return strings.Join(rows, "\n")
|
||||
}
|
||||
|
||||
// setterFn is a callback function to set value from fieldB to fieldA
|
||||
type setterFn func(fieldA, fieldB reflect.Value)
|
||||
|
||||
|
@@ -309,3 +309,60 @@ func TestMergeConstraintsNested(t *testing.T) {
|
||||
t.Error("expected a.Width to be 100, but got 0")
|
||||
}
|
||||
}
|
||||
|
||||
func TestString(t *testing.T) {
|
||||
t.Run("IdealValues", func(t *testing.T) {
|
||||
t.Log("\n", &MediaConstraints{
|
||||
DeviceID: String("one"),
|
||||
VideoConstraints: VideoConstraints{
|
||||
Width: Int(1920),
|
||||
FrameRate: Float(30.0),
|
||||
FrameFormat: FrameFormat(frame.FormatI420),
|
||||
},
|
||||
AudioConstraints: AudioConstraints{
|
||||
Latency: Duration(time.Millisecond * 20),
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("ExactValues", func(t *testing.T) {
|
||||
t.Log("\n", &MediaConstraints{
|
||||
DeviceID: StringExact("one"),
|
||||
VideoConstraints: VideoConstraints{
|
||||
Width: IntExact(1920),
|
||||
FrameRate: FloatExact(30.0),
|
||||
FrameFormat: FrameFormatExact(frame.FormatI420),
|
||||
},
|
||||
AudioConstraints: AudioConstraints{
|
||||
Latency: DurationExact(time.Millisecond * 20),
|
||||
IsBigEndian: BoolExact(true),
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("OneOfValues", func(t *testing.T) {
|
||||
t.Log("\n", &MediaConstraints{
|
||||
DeviceID: StringOneOf{"one", "two"},
|
||||
VideoConstraints: VideoConstraints{
|
||||
Width: IntOneOf{1920, 1080},
|
||||
FrameRate: FloatOneOf{30.0, 60.1234},
|
||||
FrameFormat: FrameFormatOneOf{frame.FormatI420, frame.FormatI444},
|
||||
},
|
||||
AudioConstraints: AudioConstraints{
|
||||
Latency: DurationOneOf{time.Millisecond * 20, time.Millisecond * 40},
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("RangedValues", func(t *testing.T) {
|
||||
t.Log("\n", &MediaConstraints{
|
||||
VideoConstraints: VideoConstraints{
|
||||
Width: &IntRanged{Min: 1080, Max: 1920, Ideal: 1500},
|
||||
FrameRate: &FloatRanged{Min: 30.123, Max: 60.12321312, Ideal: 45.12312312},
|
||||
},
|
||||
AudioConstraints: AudioConstraints{
|
||||
Latency: &DurationRanged{Min: time.Millisecond * 20, Max: time.Millisecond * 40, Ideal: time.Millisecond * 30},
|
||||
},
|
||||
})
|
||||
})
|
||||
}
|
||||
|
@@ -1,5 +1,10 @@
|
||||
package prop
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// StringConstraint is an interface to represent string constraint.
|
||||
type StringConstraint interface {
|
||||
Compare(string) (float64, bool)
|
||||
@@ -21,6 +26,11 @@ func (f String) Compare(a string) (float64, bool) {
|
||||
// Value implements StringConstraint.
|
||||
func (f String) Value() (string, bool) { return string(f), true }
|
||||
|
||||
// String implements Stringify
|
||||
func (f String) String() string {
|
||||
return fmt.Sprintf("%s (ideal)", string(f))
|
||||
}
|
||||
|
||||
// StringExact specifies exact string.
|
||||
type StringExact string
|
||||
|
||||
@@ -35,6 +45,11 @@ func (f StringExact) Compare(a string) (float64, bool) {
|
||||
// Value implements StringConstraint.
|
||||
func (f StringExact) Value() (string, bool) { return string(f), true }
|
||||
|
||||
// String implements Stringify
|
||||
func (f StringExact) String() string {
|
||||
return fmt.Sprintf("%s (exact)", string(f))
|
||||
}
|
||||
|
||||
// StringOneOf specifies list of expected string.
|
||||
type StringOneOf []string
|
||||
|
||||
@@ -50,3 +65,8 @@ func (f StringOneOf) Compare(a string) (float64, bool) {
|
||||
|
||||
// Value implements StringConstraint.
|
||||
func (StringOneOf) Value() (string, bool) { return "", false }
|
||||
|
||||
// String implements Stringify
|
||||
func (f StringOneOf) String() string {
|
||||
return fmt.Sprintf("%s (one of values)", strings.Join([]string(f), ","))
|
||||
}
|
||||
|
@@ -134,18 +134,15 @@ func TestDecodeInt16Interleaved(t *testing.T) {
|
||||
decoder, _ := newInt16InterleavedDecoder()
|
||||
|
||||
t.Run("BigEndian", func(t *testing.T) {
|
||||
expected := &Int16Interleaved{
|
||||
Data: []int16{
|
||||
int16(binary.BigEndian.Uint16([]byte{0x01, 0x02})),
|
||||
int16(binary.BigEndian.Uint16([]byte{0x03, 0x04})),
|
||||
int16(binary.BigEndian.Uint16([]byte{0x05, 0x06})),
|
||||
int16(binary.BigEndian.Uint16([]byte{0x07, 0x08})),
|
||||
},
|
||||
Size: ChunkInfo{
|
||||
Len: 2,
|
||||
Channels: 2,
|
||||
},
|
||||
}
|
||||
expected := NewInt16Interleaved(ChunkInfo{
|
||||
Len: 2,
|
||||
Channels: 2,
|
||||
})
|
||||
|
||||
expected.SetInt16(0, 0, Int16Sample(binary.BigEndian.Uint16([]byte{0x01, 0x02})))
|
||||
expected.SetInt16(0, 1, Int16Sample(binary.BigEndian.Uint16([]byte{0x03, 0x04})))
|
||||
expected.SetInt16(1, 0, Int16Sample(binary.BigEndian.Uint16([]byte{0x05, 0x06})))
|
||||
expected.SetInt16(1, 1, Int16Sample(binary.BigEndian.Uint16([]byte{0x07, 0x08})))
|
||||
actual, err := decoder.Decode(binary.BigEndian, raw, 2)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -157,18 +154,15 @@ func TestDecodeInt16Interleaved(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("LittleEndian", func(t *testing.T) {
|
||||
expected := &Int16Interleaved{
|
||||
Data: []int16{
|
||||
int16(binary.LittleEndian.Uint16([]byte{0x01, 0x02})),
|
||||
int16(binary.LittleEndian.Uint16([]byte{0x03, 0x04})),
|
||||
int16(binary.LittleEndian.Uint16([]byte{0x05, 0x06})),
|
||||
int16(binary.LittleEndian.Uint16([]byte{0x07, 0x08})),
|
||||
},
|
||||
Size: ChunkInfo{
|
||||
Len: 2,
|
||||
Channels: 2,
|
||||
},
|
||||
}
|
||||
expected := NewInt16Interleaved(ChunkInfo{
|
||||
Len: 2,
|
||||
Channels: 2,
|
||||
})
|
||||
|
||||
expected.SetInt16(0, 0, Int16Sample(binary.LittleEndian.Uint16([]byte{0x02, 0x01})))
|
||||
expected.SetInt16(0, 1, Int16Sample(binary.LittleEndian.Uint16([]byte{0x04, 0x03})))
|
||||
expected.SetInt16(1, 0, Int16Sample(binary.LittleEndian.Uint16([]byte{0x06, 0x05})))
|
||||
expected.SetInt16(1, 1, Int16Sample(binary.LittleEndian.Uint16([]byte{0x08, 0x07})))
|
||||
actual, err := decoder.Decode(binary.LittleEndian, raw, 2)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -190,16 +184,15 @@ func TestDecodeInt16NonInterleaved(t *testing.T) {
|
||||
decoder, _ := newInt16NonInterleavedDecoder()
|
||||
|
||||
t.Run("BigEndian", func(t *testing.T) {
|
||||
expected := &Int16NonInterleaved{
|
||||
Data: [][]int16{
|
||||
{int16(binary.BigEndian.Uint16([]byte{0x01, 0x02})), int16(binary.BigEndian.Uint16([]byte{0x03, 0x04}))},
|
||||
{int16(binary.BigEndian.Uint16([]byte{0x05, 0x06})), int16(binary.BigEndian.Uint16([]byte{0x07, 0x08}))},
|
||||
},
|
||||
Size: ChunkInfo{
|
||||
Len: 2,
|
||||
Channels: 2,
|
||||
},
|
||||
}
|
||||
expected := NewInt16NonInterleaved(ChunkInfo{
|
||||
Len: 2,
|
||||
Channels: 2,
|
||||
})
|
||||
|
||||
expected.SetInt16(0, 0, Int16Sample(binary.BigEndian.Uint16([]byte{0x01, 0x02})))
|
||||
expected.SetInt16(0, 1, Int16Sample(binary.BigEndian.Uint16([]byte{0x05, 0x06})))
|
||||
expected.SetInt16(1, 0, Int16Sample(binary.BigEndian.Uint16([]byte{0x03, 0x04})))
|
||||
expected.SetInt16(1, 1, Int16Sample(binary.BigEndian.Uint16([]byte{0x07, 0x08})))
|
||||
actual, err := decoder.Decode(binary.BigEndian, raw, 2)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -211,16 +204,15 @@ func TestDecodeInt16NonInterleaved(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("LittleEndian", func(t *testing.T) {
|
||||
expected := &Int16NonInterleaved{
|
||||
Data: [][]int16{
|
||||
{int16(binary.LittleEndian.Uint16([]byte{0x01, 0x02})), int16(binary.LittleEndian.Uint16([]byte{0x03, 0x04}))},
|
||||
{int16(binary.LittleEndian.Uint16([]byte{0x05, 0x06})), int16(binary.LittleEndian.Uint16([]byte{0x07, 0x08}))},
|
||||
},
|
||||
Size: ChunkInfo{
|
||||
Len: 2,
|
||||
Channels: 2,
|
||||
},
|
||||
}
|
||||
expected := NewInt16NonInterleaved(ChunkInfo{
|
||||
Len: 2,
|
||||
Channels: 2,
|
||||
})
|
||||
|
||||
expected.SetInt16(0, 0, Int16Sample(binary.LittleEndian.Uint16([]byte{0x02, 0x01})))
|
||||
expected.SetInt16(0, 1, Int16Sample(binary.LittleEndian.Uint16([]byte{0x06, 0x05})))
|
||||
expected.SetInt16(1, 0, Int16Sample(binary.LittleEndian.Uint16([]byte{0x04, 0x03})))
|
||||
expected.SetInt16(1, 1, Int16Sample(binary.LittleEndian.Uint16([]byte{0x08, 0x07})))
|
||||
actual, err := decoder.Decode(binary.LittleEndian, raw, 2)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -242,18 +234,15 @@ func TestDecodeFloat32Interleaved(t *testing.T) {
|
||||
decoder, _ := newFloat32InterleavedDecoder()
|
||||
|
||||
t.Run("BigEndian", func(t *testing.T) {
|
||||
expected := &Float32Interleaved{
|
||||
Data: []float32{
|
||||
math.Float32frombits(binary.BigEndian.Uint32([]byte{0x01, 0x02, 0x03, 0x04})),
|
||||
math.Float32frombits(binary.BigEndian.Uint32([]byte{0x05, 0x06, 0x07, 0x08})),
|
||||
math.Float32frombits(binary.BigEndian.Uint32([]byte{0x09, 0x0a, 0x0b, 0x0c})),
|
||||
math.Float32frombits(binary.BigEndian.Uint32([]byte{0x0d, 0x0e, 0x0f, 0x10})),
|
||||
},
|
||||
Size: ChunkInfo{
|
||||
Len: 2,
|
||||
Channels: 2,
|
||||
},
|
||||
}
|
||||
expected := NewFloat32Interleaved(ChunkInfo{
|
||||
Len: 2,
|
||||
Channels: 2,
|
||||
})
|
||||
|
||||
expected.SetFloat32(0, 0, Float32Sample(math.Float32frombits(binary.BigEndian.Uint32([]byte{0x01, 0x02, 0x03, 0x04}))))
|
||||
expected.SetFloat32(0, 1, Float32Sample(math.Float32frombits(binary.BigEndian.Uint32([]byte{0x05, 0x06, 0x07, 0x08}))))
|
||||
expected.SetFloat32(1, 0, Float32Sample(math.Float32frombits(binary.BigEndian.Uint32([]byte{0x09, 0x0a, 0x0b, 0x0c}))))
|
||||
expected.SetFloat32(1, 1, Float32Sample(math.Float32frombits(binary.BigEndian.Uint32([]byte{0x0d, 0x0e, 0x0f, 0x10}))))
|
||||
actual, err := decoder.Decode(binary.BigEndian, raw, 2)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -265,18 +254,15 @@ func TestDecodeFloat32Interleaved(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("LittleEndian", func(t *testing.T) {
|
||||
expected := &Float32Interleaved{
|
||||
Data: []float32{
|
||||
math.Float32frombits(binary.LittleEndian.Uint32([]byte{0x01, 0x02, 0x03, 0x04})),
|
||||
math.Float32frombits(binary.LittleEndian.Uint32([]byte{0x05, 0x06, 0x07, 0x08})),
|
||||
math.Float32frombits(binary.LittleEndian.Uint32([]byte{0x09, 0x0a, 0x0b, 0x0c})),
|
||||
math.Float32frombits(binary.LittleEndian.Uint32([]byte{0x0d, 0x0e, 0x0f, 0x10})),
|
||||
},
|
||||
Size: ChunkInfo{
|
||||
Len: 2,
|
||||
Channels: 2,
|
||||
},
|
||||
}
|
||||
expected := NewFloat32Interleaved(ChunkInfo{
|
||||
Len: 2,
|
||||
Channels: 2,
|
||||
})
|
||||
|
||||
expected.SetFloat32(0, 0, Float32Sample(math.Float32frombits(binary.LittleEndian.Uint32([]byte{0x04, 0x03, 0x02, 0x01}))))
|
||||
expected.SetFloat32(0, 1, Float32Sample(math.Float32frombits(binary.LittleEndian.Uint32([]byte{0x08, 0x07, 0x06, 0x05}))))
|
||||
expected.SetFloat32(1, 0, Float32Sample(math.Float32frombits(binary.LittleEndian.Uint32([]byte{0x0c, 0x0b, 0x0a, 0x09}))))
|
||||
expected.SetFloat32(1, 1, Float32Sample(math.Float32frombits(binary.LittleEndian.Uint32([]byte{0x10, 0x0f, 0x0e, 0x0d}))))
|
||||
actual, err := decoder.Decode(binary.LittleEndian, raw, 2)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -298,22 +284,15 @@ func TestDecodeFloat32NonInterleaved(t *testing.T) {
|
||||
decoder, _ := newFloat32NonInterleavedDecoder()
|
||||
|
||||
t.Run("BigEndian", func(t *testing.T) {
|
||||
expected := &Float32NonInterleaved{
|
||||
Data: [][]float32{
|
||||
{
|
||||
math.Float32frombits(binary.BigEndian.Uint32([]byte{0x01, 0x02, 0x03, 0x04})),
|
||||
math.Float32frombits(binary.BigEndian.Uint32([]byte{0x05, 0x06, 0x07, 0x08})),
|
||||
},
|
||||
{
|
||||
math.Float32frombits(binary.BigEndian.Uint32([]byte{0x09, 0x0a, 0x0b, 0x0c})),
|
||||
math.Float32frombits(binary.BigEndian.Uint32([]byte{0x0d, 0x0e, 0x0f, 0x10})),
|
||||
},
|
||||
},
|
||||
Size: ChunkInfo{
|
||||
Len: 2,
|
||||
Channels: 2,
|
||||
},
|
||||
}
|
||||
expected := NewFloat32NonInterleaved(ChunkInfo{
|
||||
Len: 2,
|
||||
Channels: 2,
|
||||
})
|
||||
|
||||
expected.SetFloat32(0, 0, Float32Sample(math.Float32frombits(binary.BigEndian.Uint32([]byte{0x01, 0x02, 0x03, 0x04}))))
|
||||
expected.SetFloat32(0, 1, Float32Sample(math.Float32frombits(binary.BigEndian.Uint32([]byte{0x09, 0x0a, 0x0b, 0x0c}))))
|
||||
expected.SetFloat32(1, 0, Float32Sample(math.Float32frombits(binary.BigEndian.Uint32([]byte{0x05, 0x06, 0x07, 0x08}))))
|
||||
expected.SetFloat32(1, 1, Float32Sample(math.Float32frombits(binary.BigEndian.Uint32([]byte{0x0d, 0x0e, 0x0f, 0x10}))))
|
||||
actual, err := decoder.Decode(binary.BigEndian, raw, 2)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -325,22 +304,15 @@ func TestDecodeFloat32NonInterleaved(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("LittleEndian", func(t *testing.T) {
|
||||
expected := &Float32NonInterleaved{
|
||||
Data: [][]float32{
|
||||
{
|
||||
math.Float32frombits(binary.LittleEndian.Uint32([]byte{0x01, 0x02, 0x03, 0x04})),
|
||||
math.Float32frombits(binary.LittleEndian.Uint32([]byte{0x05, 0x06, 0x07, 0x08})),
|
||||
},
|
||||
{
|
||||
math.Float32frombits(binary.LittleEndian.Uint32([]byte{0x09, 0x0a, 0x0b, 0x0c})),
|
||||
math.Float32frombits(binary.LittleEndian.Uint32([]byte{0x0d, 0x0e, 0x0f, 0x10})),
|
||||
},
|
||||
},
|
||||
Size: ChunkInfo{
|
||||
Len: 2,
|
||||
Channels: 2,
|
||||
},
|
||||
}
|
||||
expected := NewFloat32NonInterleaved(ChunkInfo{
|
||||
Len: 2,
|
||||
Channels: 2,
|
||||
})
|
||||
|
||||
expected.SetFloat32(0, 0, Float32Sample(math.Float32frombits(binary.LittleEndian.Uint32([]byte{0x04, 0x03, 0x02, 0x01}))))
|
||||
expected.SetFloat32(0, 1, Float32Sample(math.Float32frombits(binary.LittleEndian.Uint32([]byte{0x0c, 0x0b, 0x0a, 0x09}))))
|
||||
expected.SetFloat32(1, 0, Float32Sample(math.Float32frombits(binary.LittleEndian.Uint32([]byte{0x08, 0x07, 0x06, 0x05}))))
|
||||
expected.SetFloat32(1, 1, Float32Sample(math.Float32frombits(binary.LittleEndian.Uint32([]byte{0x10, 0x0f, 0x0e, 0x0d}))))
|
||||
actual, err := decoder.Decode(binary.LittleEndian, raw, 2)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
@@ -1,5 +1,7 @@
|
||||
package wave
|
||||
|
||||
import "math"
|
||||
|
||||
// Float32Sample is a 32-bits float audio sample.
|
||||
type Float32Sample float32
|
||||
|
||||
@@ -9,7 +11,7 @@ func (s Float32Sample) Int() int64 {
|
||||
|
||||
// Float32Interleaved multi-channel interlaced Audio.
|
||||
type Float32Interleaved struct {
|
||||
Data []float32
|
||||
Data []uint8
|
||||
Size ChunkInfo
|
||||
}
|
||||
|
||||
@@ -23,22 +25,36 @@ func (a *Float32Interleaved) SampleFormat() SampleFormat {
|
||||
}
|
||||
|
||||
func (a *Float32Interleaved) At(i, ch int) Sample {
|
||||
return Float32Sample(a.Data[i*a.Size.Channels+ch])
|
||||
loc := 4 * (a.Size.Channels*i + ch)
|
||||
|
||||
var v uint32
|
||||
v |= uint32(a.Data[loc]) << 24
|
||||
v |= uint32(a.Data[loc+1]) << 16
|
||||
v |= uint32(a.Data[loc+2]) << 8
|
||||
v |= uint32(a.Data[loc+3])
|
||||
|
||||
return Float32Sample(math.Float32frombits(v))
|
||||
}
|
||||
|
||||
func (a *Float32Interleaved) Set(i, ch int, s Sample) {
|
||||
a.Data[i*a.Size.Channels+ch] = float32(Float32SampleFormat.Convert(s).(Float32Sample))
|
||||
a.SetFloat32(i, ch, Float32SampleFormat.Convert(s).(Float32Sample))
|
||||
}
|
||||
|
||||
func (a *Float32Interleaved) SetFloat32(i, ch int, s Float32Sample) {
|
||||
a.Data[i*a.Size.Channels+ch] = float32(s)
|
||||
loc := 4 * (a.Size.Channels*i + ch)
|
||||
|
||||
v := math.Float32bits(float32(s))
|
||||
a.Data[loc] = uint8(v >> 24)
|
||||
a.Data[loc+1] = uint8(v >> 16)
|
||||
a.Data[loc+2] = uint8(v >> 8)
|
||||
a.Data[loc+3] = uint8(v)
|
||||
}
|
||||
|
||||
// SubAudio returns part of the original audio sharing the buffer.
|
||||
func (a *Float32Interleaved) SubAudio(offsetSamples, nSamples int) *Float32Interleaved {
|
||||
ret := *a
|
||||
offset := offsetSamples * a.Size.Channels
|
||||
n := nSamples * a.Size.Channels
|
||||
offset := 4 * offsetSamples * a.Size.Channels
|
||||
n := 4 * nSamples * a.Size.Channels
|
||||
ret.Data = ret.Data[offset : offset+n]
|
||||
ret.Size.Len = nSamples
|
||||
return &ret
|
||||
@@ -46,14 +62,14 @@ func (a *Float32Interleaved) SubAudio(offsetSamples, nSamples int) *Float32Inter
|
||||
|
||||
func NewFloat32Interleaved(size ChunkInfo) *Float32Interleaved {
|
||||
return &Float32Interleaved{
|
||||
Data: make([]float32, size.Channels*size.Len),
|
||||
Data: make([]uint8, size.Channels*size.Len*4),
|
||||
Size: size,
|
||||
}
|
||||
}
|
||||
|
||||
// Float32NonInterleaved multi-channel interlaced Audio.
|
||||
type Float32NonInterleaved struct {
|
||||
Data [][]float32
|
||||
Data [][]uint8
|
||||
Size ChunkInfo
|
||||
}
|
||||
|
||||
@@ -67,31 +83,48 @@ func (a *Float32NonInterleaved) SampleFormat() SampleFormat {
|
||||
}
|
||||
|
||||
func (a *Float32NonInterleaved) At(i, ch int) Sample {
|
||||
return Float32Sample(a.Data[ch][i])
|
||||
loc := i * 4
|
||||
|
||||
var v uint32
|
||||
v |= uint32(a.Data[ch][loc]) << 24
|
||||
v |= uint32(a.Data[ch][loc+1]) << 16
|
||||
v |= uint32(a.Data[ch][loc+2]) << 8
|
||||
v |= uint32(a.Data[ch][loc+3])
|
||||
|
||||
return Float32Sample(math.Float32frombits(v))
|
||||
}
|
||||
|
||||
func (a *Float32NonInterleaved) Set(i, ch int, s Sample) {
|
||||
a.Data[ch][i] = float32(Float32SampleFormat.Convert(s).(Float32Sample))
|
||||
a.SetFloat32(i, ch, Float32SampleFormat.Convert(s).(Float32Sample))
|
||||
}
|
||||
|
||||
func (a *Float32NonInterleaved) SetFloat32(i, ch int, s Float32Sample) {
|
||||
a.Data[ch][i] = float32(s)
|
||||
loc := i * 4
|
||||
|
||||
v := math.Float32bits(float32(s))
|
||||
a.Data[ch][loc] = uint8(v >> 24)
|
||||
a.Data[ch][loc+1] = uint8(v >> 16)
|
||||
a.Data[ch][loc+2] = uint8(v >> 8)
|
||||
a.Data[ch][loc+3] = uint8(v)
|
||||
}
|
||||
|
||||
// SubAudio returns part of the original audio sharing the buffer.
|
||||
func (a *Float32NonInterleaved) SubAudio(offsetSamples, nSamples int) *Float32NonInterleaved {
|
||||
ret := *a
|
||||
ret.Size.Len = nSamples
|
||||
|
||||
offsetSamples *= 4
|
||||
nSamples *= 4
|
||||
for i := range a.Data {
|
||||
ret.Data[i] = ret.Data[i][offsetSamples : offsetSamples+nSamples]
|
||||
}
|
||||
ret.Size.Len = nSamples
|
||||
return &ret
|
||||
}
|
||||
|
||||
func NewFloat32NonInterleaved(size ChunkInfo) *Float32NonInterleaved {
|
||||
d := make([][]float32, size.Channels)
|
||||
d := make([][]uint8, size.Channels)
|
||||
for i := 0; i < size.Channels; i++ {
|
||||
d[i] = make([]float32, size.Len)
|
||||
d[i] = make([]uint8, size.Len*4)
|
||||
}
|
||||
return &Float32NonInterleaved{
|
||||
Data: d,
|
||||
|
@@ -1,39 +1,51 @@
|
||||
package wave
|
||||
|
||||
import (
|
||||
"math"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func float32ToUint8(vs ...float32) []uint8 {
|
||||
var b []uint8
|
||||
|
||||
for _, v := range vs {
|
||||
s := math.Float32bits(v)
|
||||
b = append(b, uint8(s>>24), uint8(s>>16), uint8(s>>8), uint8(s))
|
||||
}
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
func TestFloat32(t *testing.T) {
|
||||
expected := [][]float32{
|
||||
{0.0, 1.0, 2.0, 3.0},
|
||||
{4.0, 5.0, 6.0, 7.0},
|
||||
}
|
||||
|
||||
cases := map[string]struct {
|
||||
in Audio
|
||||
expected [][]float32
|
||||
}{
|
||||
"Interleaved": {
|
||||
in: &Float32Interleaved{
|
||||
Data: []float32{
|
||||
0.1, -0.5, 0.2, -0.6, 0.3, -0.7, 0.4, -0.8, 0.5, -0.9, 0.6, -1.0, 0.7, -1.1, 0.8, -1.2,
|
||||
},
|
||||
Size: ChunkInfo{8, 2, 48000},
|
||||
},
|
||||
expected: [][]float32{
|
||||
{0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8},
|
||||
{-0.5, -0.6, -0.7, -0.8, -0.9, -1.0, -1.1, -1.2},
|
||||
Data: float32ToUint8(
|
||||
0.0, 4.0, 1.0, 5.0,
|
||||
2.0, 6.0, 3.0, 7.0,
|
||||
),
|
||||
Size: ChunkInfo{4, 2, 48000},
|
||||
},
|
||||
expected: expected,
|
||||
},
|
||||
"NonInterleaved": {
|
||||
in: &Float32NonInterleaved{
|
||||
Data: [][]float32{
|
||||
{0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8},
|
||||
{-0.5, -0.6, -0.7, -0.8, -0.9, -1.0, -1.1, -1.2},
|
||||
Data: [][]uint8{
|
||||
float32ToUint8(expected[0]...),
|
||||
float32ToUint8(expected[1]...),
|
||||
},
|
||||
Size: ChunkInfo{8, 2, 48000},
|
||||
},
|
||||
expected: [][]float32{
|
||||
{0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8},
|
||||
{-0.5, -0.6, -0.7, -0.8, -0.9, -1.0, -1.1, -1.2},
|
||||
Size: ChunkInfo{4, 2, 48000},
|
||||
},
|
||||
expected: expected,
|
||||
},
|
||||
}
|
||||
for name, c := range cases {
|
||||
@@ -55,38 +67,43 @@ func TestFloat32(t *testing.T) {
|
||||
func TestFloat32SubAudio(t *testing.T) {
|
||||
t.Run("Interleaved", func(t *testing.T) {
|
||||
in := &Float32Interleaved{
|
||||
Data: []float32{
|
||||
0.1, -0.5, 0.2, -0.6, 0.3, -0.7, 0.4, -0.8, 0.5, -0.9, 0.6, -1.0, 0.7, -1.1, 0.8, -1.2,
|
||||
},
|
||||
Size: ChunkInfo{8, 2, 48000},
|
||||
// Data: []uint8{
|
||||
// 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0,
|
||||
// 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0,
|
||||
// },
|
||||
Data: float32ToUint8(
|
||||
1.0, 2.0, 3.0, 4.0,
|
||||
5.0, 6.0, 7.0, 8.0,
|
||||
),
|
||||
Size: ChunkInfo{4, 2, 48000},
|
||||
}
|
||||
expected := &Float32Interleaved{
|
||||
Data: []float32{
|
||||
0.3, -0.7, 0.4, -0.8, 0.5, -0.9,
|
||||
},
|
||||
Size: ChunkInfo{3, 2, 48000},
|
||||
Data: float32ToUint8(
|
||||
5.0, 6.0, 7.0, 8.0,
|
||||
),
|
||||
Size: ChunkInfo{2, 2, 48000},
|
||||
}
|
||||
out := in.SubAudio(2, 3)
|
||||
out := in.SubAudio(2, 2)
|
||||
if !reflect.DeepEqual(expected, out) {
|
||||
t.Errorf("SubAudio differs, expected: %v, got: %v", expected, out)
|
||||
}
|
||||
})
|
||||
t.Run("NonInterleaved", func(t *testing.T) {
|
||||
in := &Float32NonInterleaved{
|
||||
Data: [][]float32{
|
||||
{0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8},
|
||||
{-0.5, -0.6, -0.7, -0.8, -0.9, -1.0, -1.1, -1.2},
|
||||
Data: [][]uint8{
|
||||
float32ToUint8(1.0, 2.0, 3.0, 4.0),
|
||||
float32ToUint8(5.0, 6.0, 7.0, 8.0),
|
||||
},
|
||||
Size: ChunkInfo{8, 2, 48000},
|
||||
Size: ChunkInfo{4, 2, 48000},
|
||||
}
|
||||
expected := &Float32NonInterleaved{
|
||||
Data: [][]float32{
|
||||
{0.3, 0.4, 0.5},
|
||||
{-0.7, -0.8, -0.9},
|
||||
Data: [][]uint8{
|
||||
float32ToUint8(3.0, 4.0),
|
||||
float32ToUint8(7.0, 8.0),
|
||||
},
|
||||
Size: ChunkInfo{3, 2, 48000},
|
||||
Size: ChunkInfo{2, 2, 48000},
|
||||
}
|
||||
out := in.SubAudio(2, 3)
|
||||
out := in.SubAudio(2, 2)
|
||||
if !reflect.DeepEqual(expected, out) {
|
||||
t.Errorf("SubAudio differs, expected: %v, got: %v", expected, out)
|
||||
}
|
||||
|
@@ -9,7 +9,7 @@ func (s Int16Sample) Int() int64 {
|
||||
|
||||
// Int16Interleaved multi-channel interlaced Audio.
|
||||
type Int16Interleaved struct {
|
||||
Data []int16
|
||||
Data []uint8
|
||||
Size ChunkInfo
|
||||
}
|
||||
|
||||
@@ -23,22 +23,29 @@ func (a *Int16Interleaved) SampleFormat() SampleFormat {
|
||||
}
|
||||
|
||||
func (a *Int16Interleaved) At(i, ch int) Sample {
|
||||
return Int16Sample(a.Data[i*a.Size.Channels+ch])
|
||||
loc := 2 * (i*a.Size.Channels + ch)
|
||||
|
||||
var s Int16Sample
|
||||
s |= Int16Sample(a.Data[loc]) << 8
|
||||
s |= Int16Sample(a.Data[loc+1])
|
||||
return s
|
||||
}
|
||||
|
||||
func (a *Int16Interleaved) Set(i, ch int, s Sample) {
|
||||
a.Data[i*a.Size.Channels+ch] = int16(Int16SampleFormat.Convert(s).(Int16Sample))
|
||||
a.SetInt16(i, ch, Int16SampleFormat.Convert(s).(Int16Sample))
|
||||
}
|
||||
|
||||
func (a *Int16Interleaved) SetInt16(i, ch int, s Int16Sample) {
|
||||
a.Data[i*a.Size.Channels+ch] = int16(s)
|
||||
loc := 2 * (i*a.Size.Channels + ch)
|
||||
a.Data[loc] = uint8(s >> 8)
|
||||
a.Data[loc+1] = uint8(s)
|
||||
}
|
||||
|
||||
// SubAudio returns part of the original audio sharing the buffer.
|
||||
func (a *Int16Interleaved) SubAudio(offsetSamples, nSamples int) *Int16Interleaved {
|
||||
ret := *a
|
||||
offset := offsetSamples * a.Size.Channels
|
||||
n := nSamples * a.Size.Channels
|
||||
offset := 2 * offsetSamples * a.Size.Channels
|
||||
n := 2 * nSamples * a.Size.Channels
|
||||
ret.Data = ret.Data[offset : offset+n]
|
||||
ret.Size.Len = nSamples
|
||||
return &ret
|
||||
@@ -46,14 +53,14 @@ func (a *Int16Interleaved) SubAudio(offsetSamples, nSamples int) *Int16Interleav
|
||||
|
||||
func NewInt16Interleaved(size ChunkInfo) *Int16Interleaved {
|
||||
return &Int16Interleaved{
|
||||
Data: make([]int16, size.Channels*size.Len),
|
||||
Data: make([]uint8, size.Channels*size.Len*2),
|
||||
Size: size,
|
||||
}
|
||||
}
|
||||
|
||||
// Int16NonInterleaved multi-channel interlaced Audio.
|
||||
type Int16NonInterleaved struct {
|
||||
Data [][]int16
|
||||
Data [][]uint8
|
||||
Size ChunkInfo
|
||||
}
|
||||
|
||||
@@ -67,31 +74,41 @@ func (a *Int16NonInterleaved) SampleFormat() SampleFormat {
|
||||
}
|
||||
|
||||
func (a *Int16NonInterleaved) At(i, ch int) Sample {
|
||||
return Int16Sample(a.Data[ch][i])
|
||||
loc := i * 2
|
||||
|
||||
var s Int16Sample
|
||||
s |= Int16Sample(a.Data[ch][loc]) << 8
|
||||
s |= Int16Sample(a.Data[ch][loc+1])
|
||||
return s
|
||||
}
|
||||
|
||||
func (a *Int16NonInterleaved) Set(i, ch int, s Sample) {
|
||||
a.Data[ch][i] = int16(Int16SampleFormat.Convert(s).(Int16Sample))
|
||||
a.SetInt16(i, ch, Int16SampleFormat.Convert(s).(Int16Sample))
|
||||
}
|
||||
|
||||
func (a *Int16NonInterleaved) SetInt16(i, ch int, s Int16Sample) {
|
||||
a.Data[ch][i] = int16(s)
|
||||
loc := i * 2
|
||||
a.Data[ch][loc] = uint8(s >> 8)
|
||||
a.Data[ch][loc+1] = uint8(s)
|
||||
}
|
||||
|
||||
// SubAudio returns part of the original audio sharing the buffer.
|
||||
func (a *Int16NonInterleaved) SubAudio(offsetSamples, nSamples int) *Int16NonInterleaved {
|
||||
ret := *a
|
||||
ret.Size.Len = nSamples
|
||||
|
||||
nSamples *= 2
|
||||
offsetSamples *= 2
|
||||
for i := range a.Data {
|
||||
ret.Data[i] = ret.Data[i][offsetSamples : offsetSamples+nSamples]
|
||||
}
|
||||
ret.Size.Len = nSamples
|
||||
return &ret
|
||||
}
|
||||
|
||||
func NewInt16NonInterleaved(size ChunkInfo) *Int16NonInterleaved {
|
||||
d := make([][]int16, size.Channels)
|
||||
d := make([][]uint8, size.Channels)
|
||||
for i := 0; i < size.Channels; i++ {
|
||||
d[i] = make([]int16, size.Len)
|
||||
d[i] = make([]uint8, size.Len*2)
|
||||
}
|
||||
return &Int16NonInterleaved{
|
||||
Data: d,
|
||||
|
@@ -12,27 +12,28 @@ func TestInt16(t *testing.T) {
|
||||
}{
|
||||
"Interleaved": {
|
||||
in: &Int16Interleaved{
|
||||
Data: []int16{
|
||||
1, -5, 2, -6, 3, -7, 4, -8, 5, -9, 6, -10, 7, -11, 8, -12,
|
||||
Data: []uint8{
|
||||
0, 1, 1, 2, 2, 3, 3, 4,
|
||||
4, 5, 5, 6, 6, 7, 7, 8,
|
||||
},
|
||||
Size: ChunkInfo{8, 2, 48000},
|
||||
Size: ChunkInfo{4, 2, 48000},
|
||||
},
|
||||
expected: [][]int16{
|
||||
{1, 2, 3, 4, 5, 6, 7, 8},
|
||||
{-5, -6, -7, -8, -9, -10, -11, -12},
|
||||
{(0 << 8) | 1, (2 << 8) | 3, (4 << 8) | 5, (6 << 8) | 7},
|
||||
{(1 << 8) | 2, (3 << 8) | 4, (5 << 8) | 6, (7 << 8) | 8},
|
||||
},
|
||||
},
|
||||
"NonInterleaved": {
|
||||
in: &Int16NonInterleaved{
|
||||
Data: [][]int16{
|
||||
Data: [][]uint8{
|
||||
{0, 1, 2, 3, 4, 5, 6, 7},
|
||||
{1, 2, 3, 4, 5, 6, 7, 8},
|
||||
{-5, -6, -7, -8, -9, -10, -11, -12},
|
||||
},
|
||||
Size: ChunkInfo{8, 2, 48000},
|
||||
Size: ChunkInfo{4, 2, 48000},
|
||||
},
|
||||
expected: [][]int16{
|
||||
{1, 2, 3, 4, 5, 6, 7, 8},
|
||||
{-5, -6, -7, -8, -9, -10, -11, -12},
|
||||
{(0 << 8) | 1, (2 << 8) | 3, (4 << 8) | 5, (6 << 8) | 7},
|
||||
{(1 << 8) | 2, (3 << 8) | 4, (5 << 8) | 6, (7 << 8) | 8},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -55,38 +56,39 @@ func TestInt16(t *testing.T) {
|
||||
func TestInt32SubAudio(t *testing.T) {
|
||||
t.Run("Interleaved", func(t *testing.T) {
|
||||
in := &Int16Interleaved{
|
||||
Data: []int16{
|
||||
1, -5, 2, -6, 3, -7, 4, -8, 5, -9, 6, -10, 7, -11, 8, -12,
|
||||
Data: []uint8{
|
||||
1, 2, 3, 4, 5, 6, 7, 8,
|
||||
9, 10, 11, 12, 13, 14, 15, 16,
|
||||
},
|
||||
Size: ChunkInfo{8, 2, 48000},
|
||||
Size: ChunkInfo{4, 2, 48000},
|
||||
}
|
||||
expected := &Int16Interleaved{
|
||||
Data: []int16{
|
||||
3, -7, 4, -8, 5, -9,
|
||||
Data: []uint8{
|
||||
9, 10, 11, 12, 13, 14, 15, 16,
|
||||
},
|
||||
Size: ChunkInfo{3, 2, 48000},
|
||||
Size: ChunkInfo{2, 2, 48000},
|
||||
}
|
||||
out := in.SubAudio(2, 3)
|
||||
out := in.SubAudio(2, 2)
|
||||
if !reflect.DeepEqual(expected, out) {
|
||||
t.Errorf("SubAudio differs, expected: %v, got: %v", expected, out)
|
||||
}
|
||||
})
|
||||
t.Run("NonInterleaved", func(t *testing.T) {
|
||||
in := &Int16NonInterleaved{
|
||||
Data: [][]int16{
|
||||
{1, 2, 3, 4, 5, 6, 7, 8},
|
||||
{-5, -6, -7, -8, -9, -10, -11, -12},
|
||||
Data: [][]uint8{
|
||||
{1, 2, 5, 6, 9, 10, 13, 14},
|
||||
{3, 4, 7, 8, 11, 12, 15, 16},
|
||||
},
|
||||
Size: ChunkInfo{8, 2, 48000},
|
||||
Size: ChunkInfo{4, 2, 48000},
|
||||
}
|
||||
expected := &Int16NonInterleaved{
|
||||
Data: [][]int16{
|
||||
{3, 4, 5},
|
||||
{-7, -8, -9},
|
||||
Data: [][]uint8{
|
||||
{9, 10, 13, 14},
|
||||
{11, 12, 15, 16},
|
||||
},
|
||||
Size: ChunkInfo{3, 2, 48000},
|
||||
Size: ChunkInfo{2, 2, 48000},
|
||||
}
|
||||
out := in.SubAudio(2, 3)
|
||||
out := in.SubAudio(2, 2)
|
||||
if !reflect.DeepEqual(expected, out) {
|
||||
t.Errorf("SubAudio differs, expected: %v, got: %v", expected, out)
|
||||
}
|
||||
|
@@ -19,10 +19,10 @@ func TestMonoMixer(t *testing.T) {
|
||||
Len: 3,
|
||||
Channels: 3,
|
||||
},
|
||||
Data: []int16{
|
||||
0, 2, 4,
|
||||
1, -2, 1,
|
||||
3, 3, 6,
|
||||
Data: []uint8{
|
||||
0x00, 0x01, 0x00, 0x02, 0x00, 0x04,
|
||||
0x00, 0x01, 0x00, 0x02, 0x00, 0x01,
|
||||
0x00, 0x03, 0x00, 0x03, 0x00, 0x06,
|
||||
},
|
||||
},
|
||||
dst: &wave.Int16Interleaved{
|
||||
@@ -30,14 +30,14 @@ func TestMonoMixer(t *testing.T) {
|
||||
Len: 3,
|
||||
Channels: 1,
|
||||
},
|
||||
Data: make([]int16, 3),
|
||||
Data: make([]uint8, 3*2),
|
||||
},
|
||||
expected: &wave.Int16Interleaved{
|
||||
Size: wave.ChunkInfo{
|
||||
Len: 3,
|
||||
Channels: 1,
|
||||
},
|
||||
Data: []int16{2, 0, 4},
|
||||
Data: []uint8{0x00, 0x02, 0x00, 0x01, 0x00, 0x04},
|
||||
},
|
||||
},
|
||||
"MonoToStereo": {
|
||||
@@ -46,21 +46,21 @@ func TestMonoMixer(t *testing.T) {
|
||||
Len: 3,
|
||||
Channels: 1,
|
||||
},
|
||||
Data: []int16{0, 2, 4},
|
||||
Data: []uint8{0x00, 0x00, 0x00, 0x02, 0x00, 0x04},
|
||||
},
|
||||
dst: &wave.Int16Interleaved{
|
||||
Size: wave.ChunkInfo{
|
||||
Len: 3,
|
||||
Channels: 2,
|
||||
},
|
||||
Data: make([]int16, 6),
|
||||
Data: make([]uint8, 6*2),
|
||||
},
|
||||
expected: &wave.Int16Interleaved{
|
||||
Size: wave.ChunkInfo{
|
||||
Len: 3,
|
||||
Channels: 2,
|
||||
},
|
||||
Data: []int16{0, 0, 2, 2, 4, 4},
|
||||
Data: []uint8{0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x02, 0x00, 0x04, 0x00, 0x04},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
Reference in New Issue
Block a user