mirror of
https://github.com/make-42/xyosc
synced 2025-01-18 18:57:10 +01:00
might be more efficient
This commit is contained in:
parent
5d3a570c29
commit
ff40ee8f63
4
go.mod
4
go.mod
@ -3,10 +3,10 @@ module xyosc
|
|||||||
go 1.23.2
|
go 1.23.2
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/MicahParks/peakdetect v0.1.2
|
|
||||||
github.com/chewxy/math32 v1.11.1
|
github.com/chewxy/math32 v1.11.1
|
||||||
github.com/fsnotify/fsnotify v1.8.0
|
github.com/fsnotify/fsnotify v1.8.0
|
||||||
github.com/gen2brain/malgo v0.11.23
|
github.com/gen2brain/malgo v0.11.23
|
||||||
|
github.com/goccmack/godsp v0.1.1
|
||||||
github.com/godbus/dbus v4.1.0+incompatible
|
github.com/godbus/dbus v4.1.0+incompatible
|
||||||
github.com/hajimehoshi/ebiten/v2 v2.8.6
|
github.com/hajimehoshi/ebiten/v2 v2.8.6
|
||||||
github.com/kirsle/configdir v0.0.0-20170128060238-e45d2f54772f
|
github.com/kirsle/configdir v0.0.0-20170128060238-e45d2f54772f
|
||||||
@ -21,8 +21,10 @@ require (
|
|||||||
github.com/ebitengine/hideconsole v1.0.0 // indirect
|
github.com/ebitengine/hideconsole v1.0.0 // indirect
|
||||||
github.com/ebitengine/purego v0.8.2 // indirect
|
github.com/ebitengine/purego v0.8.2 // indirect
|
||||||
github.com/go-text/typesetting v0.2.1 // indirect
|
github.com/go-text/typesetting v0.2.1 // indirect
|
||||||
|
github.com/goccmack/goutil v0.4.0 // indirect
|
||||||
github.com/godbus/dbus/v5 v5.1.0 // indirect
|
github.com/godbus/dbus/v5 v5.1.0 // indirect
|
||||||
github.com/jezek/xgb v1.1.1 // indirect
|
github.com/jezek/xgb v1.1.1 // indirect
|
||||||
|
github.com/mjibson/go-dsp v0.0.0-20180508042940-11479a337f12 // indirect
|
||||||
golang.org/x/image v0.23.0 // indirect
|
golang.org/x/image v0.23.0 // indirect
|
||||||
golang.org/x/sync v0.10.0 // indirect
|
golang.org/x/sync v0.10.0 // indirect
|
||||||
golang.org/x/sys v0.29.0 // indirect
|
golang.org/x/sys v0.29.0 // indirect
|
||||||
|
27
go.sum
27
go.sum
@ -1,5 +1,4 @@
|
|||||||
github.com/MicahParks/peakdetect v0.1.2 h1:DYQXgBzfl/kkuTKErM/4/2iSkk63okzTka6haTQFK5Y=
|
github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
|
||||||
github.com/MicahParks/peakdetect v0.1.2/go.mod h1:78d4YnCFxrVbu1Calxc3LIOqN/xtcr7a8lmSwPRylts=
|
|
||||||
github.com/chewxy/math32 v1.11.1 h1:b7PGHlp8KjylDoU8RrcEsRuGZhJuz8haxnKfuMMRqy8=
|
github.com/chewxy/math32 v1.11.1 h1:b7PGHlp8KjylDoU8RrcEsRuGZhJuz8haxnKfuMMRqy8=
|
||||||
github.com/chewxy/math32 v1.11.1/go.mod h1:dOB2rcuFrCn6UHrze36WSLVPKtzPMRAQvBvUwkSsLqs=
|
github.com/chewxy/math32 v1.11.1/go.mod h1:dOB2rcuFrCn6UHrze36WSLVPKtzPMRAQvBvUwkSsLqs=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
@ -10,30 +9,41 @@ github.com/ebitengine/hideconsole v1.0.0 h1:5J4U0kXF+pv/DhiXt5/lTz0eO5ogJ1iXb8Yj
|
|||||||
github.com/ebitengine/hideconsole v1.0.0/go.mod h1:hTTBTvVYWKBuxPr7peweneWdkUwEuHuB3C1R/ielR1A=
|
github.com/ebitengine/hideconsole v1.0.0/go.mod h1:hTTBTvVYWKBuxPr7peweneWdkUwEuHuB3C1R/ielR1A=
|
||||||
github.com/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I=
|
github.com/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I=
|
||||||
github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
||||||
|
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
|
||||||
github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
|
github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
|
||||||
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||||
github.com/gen2brain/malgo v0.11.23 h1:3/VAI8DP9/Wyx1CUDNlUQJVdWUvGErhjHDqYcHVk9ME=
|
github.com/gen2brain/malgo v0.11.23 h1:3/VAI8DP9/Wyx1CUDNlUQJVdWUvGErhjHDqYcHVk9ME=
|
||||||
github.com/gen2brain/malgo v0.11.23/go.mod h1:f9TtuN7DVrXMiV/yIceMeWpvanyVzJQMlBecJFVMxww=
|
github.com/gen2brain/malgo v0.11.23/go.mod h1:f9TtuN7DVrXMiV/yIceMeWpvanyVzJQMlBecJFVMxww=
|
||||||
|
github.com/go-audio/audio v1.0.0/go.mod h1:6uAu0+H2lHkwdGsAY+j2wHPNPpPoeg5AaEFh9FlA+Zs=
|
||||||
github.com/go-text/typesetting v0.2.1 h1:x0jMOGyO3d1qFAPI0j4GSsh7M0Q3Ypjzr4+CEVg82V8=
|
github.com/go-text/typesetting v0.2.1 h1:x0jMOGyO3d1qFAPI0j4GSsh7M0Q3Ypjzr4+CEVg82V8=
|
||||||
github.com/go-text/typesetting v0.2.1/go.mod h1:mTOxEwasOFpAMBjEQDhdWRckoLLeI/+qrQeBCTGEt6M=
|
github.com/go-text/typesetting v0.2.1/go.mod h1:mTOxEwasOFpAMBjEQDhdWRckoLLeI/+qrQeBCTGEt6M=
|
||||||
github.com/go-text/typesetting-utils v0.0.0-20241103174707-87a29e9e6066 h1:qCuYC+94v2xrb1PoS4NIDe7DGYtLnU2wWiQe9a1B1c0=
|
github.com/go-text/typesetting-utils v0.0.0-20241103174707-87a29e9e6066 h1:qCuYC+94v2xrb1PoS4NIDe7DGYtLnU2wWiQe9a1B1c0=
|
||||||
github.com/go-text/typesetting-utils v0.0.0-20241103174707-87a29e9e6066/go.mod h1:DDxDdQEnB70R8owOx3LVpEFvpMK9eeH1o2r0yZhFI9o=
|
github.com/go-text/typesetting-utils v0.0.0-20241103174707-87a29e9e6066/go.mod h1:DDxDdQEnB70R8owOx3LVpEFvpMK9eeH1o2r0yZhFI9o=
|
||||||
|
github.com/goccmack/godsp v0.1.1 h1:NLPDr47wwVdDtQjSca8FSSpcAQKUKbmQq5ligBTZPwc=
|
||||||
|
github.com/goccmack/godsp v0.1.1/go.mod h1:SxJmlwp2eWh5NYP0Oo/ptCd/oqkj1lehn6ApcXtPb4U=
|
||||||
|
github.com/goccmack/goutil v0.4.0 h1:or+SequGBcQp7Rf5q719HlOxtEGueaXEenDbc3pANgk=
|
||||||
|
github.com/goccmack/goutil v0.4.0/go.mod h1:dPBoKv07AeI2DGYE3ECrSLOLpGaBIBGCUCGKHclOPyU=
|
||||||
github.com/godbus/dbus v4.1.0+incompatible h1:WqqLRTsQic3apZUK9qC5sGNfXthmPXzUZ7nQPrNITa4=
|
github.com/godbus/dbus v4.1.0+incompatible h1:WqqLRTsQic3apZUK9qC5sGNfXthmPXzUZ7nQPrNITa4=
|
||||||
github.com/godbus/dbus v4.1.0+incompatible/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
|
github.com/godbus/dbus v4.1.0+incompatible/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
|
||||||
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
|
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
|
||||||
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
|
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
|
||||||
|
github.com/golang/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI=
|
||||||
github.com/hajimehoshi/bitmapfont/v3 v3.2.0 h1:0DISQM/rseKIJhdF29AkhvdzIULqNIIlXAGWit4ez1Q=
|
github.com/hajimehoshi/bitmapfont/v3 v3.2.0 h1:0DISQM/rseKIJhdF29AkhvdzIULqNIIlXAGWit4ez1Q=
|
||||||
github.com/hajimehoshi/bitmapfont/v3 v3.2.0/go.mod h1:8gLqGatKVu0pwcNCJguW3Igg9WQqVXF0zg/RvrGQWyg=
|
github.com/hajimehoshi/bitmapfont/v3 v3.2.0/go.mod h1:8gLqGatKVu0pwcNCJguW3Igg9WQqVXF0zg/RvrGQWyg=
|
||||||
github.com/hajimehoshi/ebiten/v2 v2.8.6 h1:Dkd/sYI0TYyZRCE7GVxV59XC+WCi2BbGAbIBjXeVC1U=
|
github.com/hajimehoshi/ebiten/v2 v2.8.6 h1:Dkd/sYI0TYyZRCE7GVxV59XC+WCi2BbGAbIBjXeVC1U=
|
||||||
github.com/hajimehoshi/ebiten/v2 v2.8.6/go.mod h1:cCQ3np7rdmaJa1ZnvslraVlpxNb3wCjEnAP1LHNyXNA=
|
github.com/hajimehoshi/ebiten/v2 v2.8.6/go.mod h1:cCQ3np7rdmaJa1ZnvslraVlpxNb3wCjEnAP1LHNyXNA=
|
||||||
github.com/jezek/xgb v1.1.1 h1:bE/r8ZZtSv7l9gk6nU0mYx51aXrvnyb44892TwSaqS4=
|
github.com/jezek/xgb v1.1.1 h1:bE/r8ZZtSv7l9gk6nU0mYx51aXrvnyb44892TwSaqS4=
|
||||||
github.com/jezek/xgb v1.1.1/go.mod h1:nrhwO0FX/enq75I7Y7G8iN1ubpSGZEiA3v9e9GyRFlk=
|
github.com/jezek/xgb v1.1.1/go.mod h1:nrhwO0FX/enq75I7Y7G8iN1ubpSGZEiA3v9e9GyRFlk=
|
||||||
|
github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
|
||||||
github.com/kirsle/configdir v0.0.0-20170128060238-e45d2f54772f h1:dKccXx7xA56UNqOcFIbuqFjAWPVtP688j5QMgmo6OHU=
|
github.com/kirsle/configdir v0.0.0-20170128060238-e45d2f54772f h1:dKccXx7xA56UNqOcFIbuqFjAWPVtP688j5QMgmo6OHU=
|
||||||
github.com/kirsle/configdir v0.0.0-20170128060238-e45d2f54772f/go.mod h1:4rEELDSfUAlBSyUjPG0JnaNGjf13JySHFeRdD/3dLP0=
|
github.com/kirsle/configdir v0.0.0-20170128060238-e45d2f54772f/go.mod h1:4rEELDSfUAlBSyUjPG0JnaNGjf13JySHFeRdD/3dLP0=
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
github.com/leberKleber/go-mpris v1.1.0 h1:bHAnmUjVoxAs4uMHH9lfQ8bOm284UWtI7JhLvkiF7O8=
|
github.com/leberKleber/go-mpris v1.1.0 h1:bHAnmUjVoxAs4uMHH9lfQ8bOm284UWtI7JhLvkiF7O8=
|
||||||
github.com/leberKleber/go-mpris v1.1.0/go.mod h1:OwKywFZwFGC0p/8xBUTUXMIFZy0Rq/7C6EayfeASTA0=
|
github.com/leberKleber/go-mpris v1.1.0/go.mod h1:OwKywFZwFGC0p/8xBUTUXMIFZy0Rq/7C6EayfeASTA0=
|
||||||
|
github.com/mjibson/go-dsp v0.0.0-20180508042940-11479a337f12 h1:dd7vnTDfjtwCETZDrRe+GPYNLA1jBtbZeyfyE8eZCyk=
|
||||||
|
github.com/mjibson/go-dsp v0.0.0-20180508042940-11479a337f12/go.mod h1:i/KKcxEWEO8Yyl11DYafRPKOPVYTrhxiTRigjtEEXZU=
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||||
github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ=
|
github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ=
|
||||||
@ -44,8 +54,14 @@ github.com/smallnest/ringbuffer v0.0.0-20241129171057-356c688ba81d h1:Kpy9DIOvTw
|
|||||||
github.com/smallnest/ringbuffer v0.0.0-20241129171057-356c688ba81d/go.mod h1:tAG61zBM1DYRaGIPloumExGvScf08oHuo0kFoOqdbT0=
|
github.com/smallnest/ringbuffer v0.0.0-20241129171057-356c688ba81d/go.mod h1:tAG61zBM1DYRaGIPloumExGvScf08oHuo0kFoOqdbT0=
|
||||||
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
|
github.com/youpy/go-riff v0.0.0-20131220112943-557d78c11efb/go.mod h1:83nxdDV4Z9RzrTut9losK7ve4hUnxUR8ASSz4BsKXwQ=
|
||||||
|
github.com/youpy/go-wav v0.0.0-20160223082350-b63a9887d320/go.mod h1:Zf+Ju+8Ofy5zx/YWWArfcGnl5FAsWumLq/uHeRGgL60=
|
||||||
github.com/ztrue/tracerr v0.4.0 h1:vT5PFxwIGs7rCg9ZgJ/y0NmOpJkPCPFK8x0vVIYzd04=
|
github.com/ztrue/tracerr v0.4.0 h1:vT5PFxwIGs7rCg9ZgJ/y0NmOpJkPCPFK8x0vVIYzd04=
|
||||||
github.com/ztrue/tracerr v0.4.0/go.mod h1:PaFfYlas0DfmXNpo7Eay4MFhZUONqvXM+T2HyGPpngk=
|
github.com/ztrue/tracerr v0.4.0/go.mod h1:PaFfYlas0DfmXNpo7Eay4MFhZUONqvXM+T2HyGPpngk=
|
||||||
|
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
|
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
|
golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
|
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
|
||||||
golang.org/x/image v0.23.0 h1:HseQ7c2OpPKTPVzNjG5fwJsOTCiiwS4QdsYi5XU6H68=
|
golang.org/x/image v0.23.0 h1:HseQ7c2OpPKTPVzNjG5fwJsOTCiiwS4QdsYi5XU6H68=
|
||||||
golang.org/x/image v0.23.0/go.mod h1:wJJBTdLfCCf3tiHa1fNxpZmUI4mmoZvwMCPP0ddoNKY=
|
golang.org/x/image v0.23.0/go.mod h1:wJJBTdLfCCf3tiHa1fNxpZmUI4mmoZvwMCPP0ddoNKY=
|
||||||
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
|
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
|
||||||
@ -54,6 +70,12 @@ golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
|
|||||||
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||||
|
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo=
|
||||||
|
gonum.org/v1/gonum v0.6.2/go.mod h1:9mxDZsDKxgMAuccQkewq682L+0eCu4dCN2yonUJTCLU=
|
||||||
|
gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
|
||||||
|
gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
@ -61,3 +83,4 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
|||||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||||
|
142
vendor/github.com/MicahParks/peakdetect/README.md
generated
vendored
142
vendor/github.com/MicahParks/peakdetect/README.md
generated
vendored
@ -1,142 +0,0 @@
|
|||||||
[![Go Reference](https://pkg.go.dev/badge/github.com/MicahParks/peakdetect.svg)](https://pkg.go.dev/github.com/MicahParks/peakdetect) [![Go Report Card](https://goreportcard.com/badge/github.com/MicahParks/peakdetect)](https://goreportcard.com/report/github.com/MicahParks/peakdetect)
|
|
||||||
# peakdetect
|
|
||||||
Detect peaks in realtime timeseries data using z-scores. This is a Golang implementation for the algorithm described
|
|
||||||
by [this StackOverflow answer](https://stackoverflow.com/a/22640362/14797322).
|
|
||||||
|
|
||||||
Unlike some implementations, a goal is to minimize the memory footprint and allow for the processing of new data points
|
|
||||||
without reprocessing old ones.
|
|
||||||
|
|
||||||
```go
|
|
||||||
import "github.com/MicahParks/peakdetect"
|
|
||||||
```
|
|
||||||
|
|
||||||
# Configuration
|
|
||||||
`Lag` determines how much your data will be smoothed and how adaptive the algorithm is to change in the long-term
|
|
||||||
average of the data. The more stationary your data is, the more lags you should include (this should improve the
|
|
||||||
robustness of the algorithm). If your data contains time-varying trends, you should consider how quickly you want the
|
|
||||||
algorithm to adapt to these trends. I.e., if you put lag at 10, it takes 10 'periods' before the algorithm's threshold
|
|
||||||
is adjusted to any systematic changes in the long-term average. So choose the lag parameter based on the trending
|
|
||||||
behavior of your data and how adaptive you want the algorithm to be.
|
|
||||||
|
|
||||||
`Influence` determines the influence of signals on the algorithm's detection threshold. If put at 0, signals have no
|
|
||||||
influence on the threshold, such that future signals are detected based on a threshold that is calculated with a mean
|
|
||||||
and standard deviation that is not influenced by past signals. If put at 0.5, signals have half the influence of normal
|
|
||||||
data points. Another way to think about this is that if you put the influence at 0, you implicitly assume stationary (
|
|
||||||
i.e. no matter how many signals there are, you always expect the time series to return to the same average over the long
|
|
||||||
term). If this is not the case, you should put the influence parameter somewhere between 0 and 1, depending on the
|
|
||||||
extent to which signals can systematically influence the time-varying trend of the data. E.g., if signals lead to a
|
|
||||||
structural break of the long-term average of the time series, the influence parameter should be put high (close to 1) so
|
|
||||||
the threshold can react to structural breaks quickly
|
|
||||||
|
|
||||||
`Threshold` is the number of standard deviations from the moving mean above which the algorithm will classify a new
|
|
||||||
datapoint as being a signal. For example, if a new datapoint is 4.0 standard deviations above the moving mean and the
|
|
||||||
threshold parameter is set as 3.5, the algorithm will identify the datapoint as a signal. This parameter should be set
|
|
||||||
based on how many signals you expect. For example, if your data is normally distributed, a threshold (or: z-score) of
|
|
||||||
3.5 corresponds to a signaling probability of 0.00047 (from this table), which implies that you expect a signal once
|
|
||||||
every 2128 datapoints (1/0.00047). The threshold therefore directly influences how sensitive the algorithm is and
|
|
||||||
thereby also determines how often the algorithm signals. Examine your own data and choose a sensible threshold that
|
|
||||||
makes the algorithm signal when you want it to (some trial-and-error might be needed here to get to a good threshold for
|
|
||||||
your purpose)
|
|
||||||
|
|
||||||
# Usage
|
|
||||||
```go
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
|
|
||||||
"github.com/MicahParks/peakdetect"
|
|
||||||
)
|
|
||||||
|
|
||||||
// This example is the equivalent of the R example from the algorithm's author.
|
|
||||||
// https://stackoverflow.com/a/54507329/14797322
|
|
||||||
func main() {
|
|
||||||
data := []float64{1, 1, 1.1, 1, 0.9, 1, 1, 1.1, 1, 0.9, 1, 1.1, 1, 1, 0.9, 1, 1, 1.1, 1, 1, 1, 1, 1.1, 0.9, 1, 1.1, 1, 1, 0.9, 1, 1.1, 1, 1, 1.1, 1, 0.8, 0.9, 1, 1.2, 0.9, 1, 1, 1.1, 1.2, 1, 1.5, 1, 3, 2, 5, 3, 2, 1, 1, 1, 0.9, 1, 1, 3, 2.6, 4, 3, 3.2, 2, 1, 1, 0.8, 4, 4, 2, 2.5, 1, 1, 1}
|
|
||||||
|
|
||||||
// Algorithm configuration from example.
|
|
||||||
const (
|
|
||||||
lag = 30
|
|
||||||
threshold = 5
|
|
||||||
influence = 0
|
|
||||||
)
|
|
||||||
|
|
||||||
// Create then initialize the peak detector.
|
|
||||||
detector := peakdetect.NewPeakDetector()
|
|
||||||
err := detector.Initialize(influence, threshold, data[:lag]) // The length of the initial values is the lag.
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Failed to initialize peak detector.\nError: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start processing new data points and determine what signal, if any they produce.
|
|
||||||
//
|
|
||||||
// This method, .Next(), is best for when data are being processed in a stream, but this simply iterates over a
|
|
||||||
// slice.
|
|
||||||
nextDataPoints := data[lag:]
|
|
||||||
for i, newPoint := range nextDataPoints {
|
|
||||||
signal := detector.Next(newPoint)
|
|
||||||
var signalType string
|
|
||||||
switch signal {
|
|
||||||
case peakdetect.SignalNegative:
|
|
||||||
signalType = "negative"
|
|
||||||
case peakdetect.SignalNeutral:
|
|
||||||
signalType = "neutral"
|
|
||||||
case peakdetect.SignalPositive:
|
|
||||||
signalType = "positive"
|
|
||||||
}
|
|
||||||
|
|
||||||
println(fmt.Sprintf("Data point at index %d has the signal: %s", i+lag, signalType))
|
|
||||||
}
|
|
||||||
|
|
||||||
// This method, .NextBatch(), is a helper function for processing many data points at once. It's returned slice
|
|
||||||
// should produce the same signal outputs as the loop above.
|
|
||||||
signals := detector.NextBatch(nextDataPoints)
|
|
||||||
println(fmt.Sprintf("1:1 ratio of batch inputs to signal outputs: %t", len(signals) == len(nextDataPoints)))
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
# Testing
|
|
||||||
```
|
|
||||||
$ go test -cover -race
|
|
||||||
PASS
|
|
||||||
coverage: 100.0% of statements
|
|
||||||
ok github.com/MicahParks/peakdetect 0.019s
|
|
||||||
```
|
|
||||||
|
|
||||||
# Performance
|
|
||||||
To further improve performance, this algorithm uses Welford's algorithm on initialization
|
|
||||||
and an adaptation of [this StackOverflow answer](https://stackoverflow.com/a/14638138/14797322) to calculate the mean
|
|
||||||
and population standard deviation for the lag period (sliding window). This appears to improve performance by more than
|
|
||||||
a factor of 10!
|
|
||||||
|
|
||||||
`v0.0.4`
|
|
||||||
```
|
|
||||||
goos: linux
|
|
||||||
goarch: amd64
|
|
||||||
pkg: github.com/MicahParks/peakdetect
|
|
||||||
cpu: AMD Ryzen 9 7950X 16-Core Processor
|
|
||||||
BenchmarkPeakDetector_NextBatch-32 1000000000 0.0000221 ns/op
|
|
||||||
PASS
|
|
||||||
ok github.com/MicahParks/peakdetect 0.003s
|
|
||||||
```
|
|
||||||
|
|
||||||
`v0.1.0`
|
|
||||||
```
|
|
||||||
goos: linux
|
|
||||||
goarch: amd64
|
|
||||||
pkg: github.com/MicahParks/peakdetect
|
|
||||||
cpu: AMD Ryzen 9 7950X 16-Core Processor
|
|
||||||
BenchmarkPeakDetector_NextBatch-32 1000000000 0.0000011 ns/op
|
|
||||||
PASS
|
|
||||||
ok github.com/MicahParks/peakdetect 0.003s
|
|
||||||
```
|
|
||||||
|
|
||||||
# References
|
|
||||||
Brakel, J.P.G. van (2014). "Robust peak detection algorithm using z-scores". Stack Overflow. Available
|
|
||||||
at: https://stackoverflow.com/questions/22583391/peak-signal-detection-in-realtime-timeseries-data/22640362#22640362
|
|
||||||
(version: 2020-11-08).
|
|
||||||
|
|
||||||
* [StackOverflow: Peak detection in realtime timeseries data](https://stackoverflow.com/a/22640362/14797322).
|
|
||||||
* [StackOverflow: sliding window for online algorithm to calculate mean and standard devation](https://stackoverflow.com/a/14638138/14797322).
|
|
||||||
* [Welford's algorithm related blog post](https://www.johndcook.com/blog/standard_deviation/).
|
|
||||||
* Yeah, I used [Wikipedia](https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance) too.
|
|
186
vendor/github.com/MicahParks/peakdetect/peakdetect.go
generated
vendored
186
vendor/github.com/MicahParks/peakdetect/peakdetect.go
generated
vendored
@ -1,186 +0,0 @@
|
|||||||
package peakdetect
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"math"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// SignalNegative indicates that a particular value is a negative peak.
|
|
||||||
SignalNegative Signal = -1
|
|
||||||
// SignalNeutral indicates that a particular value is not a peak.
|
|
||||||
SignalNeutral Signal = 0
|
|
||||||
// SignalPositive indicates that a particular value is a positive peak.
|
|
||||||
SignalPositive Signal = 1
|
|
||||||
)
|
|
||||||
|
|
||||||
// Signal is a set of enums that indicates what type of peak, if any a particular value is.
|
|
||||||
type Signal int8
|
|
||||||
|
|
||||||
// ErrInvalidInitialValues indicates that the initial values provided are not valid to initialize a PeakDetector.
|
|
||||||
var ErrInvalidInitialValues = errors.New("the initial values provided are invalid")
|
|
||||||
|
|
||||||
type peakDetector struct {
|
|
||||||
index uint
|
|
||||||
influence float64
|
|
||||||
lag uint
|
|
||||||
movingMeanStdDev *movingMeanStdDev
|
|
||||||
prevMean float64
|
|
||||||
prevStdDev float64
|
|
||||||
prevValue float64
|
|
||||||
threshold float64
|
|
||||||
}
|
|
||||||
|
|
||||||
// PeakDetector detects peaks in realtime timeseries data using z-scores.
|
|
||||||
//
|
|
||||||
// This is a Golang interface for the algorithm described by this StackOverflow answer:
|
|
||||||
// https://stackoverflow.com/a/22640362/14797322
|
|
||||||
//
|
|
||||||
// Brakel, J.P.G. van (2014). "Robust peak detection algorithm using z-scores". Stack Overflow. Available
|
|
||||||
// at: https://stackoverflow.com/questions/22583391/peak-signal-detection-in-realtime-timeseries-data/22640362#22640362
|
|
||||||
// (version: 2020-11-08).
|
|
||||||
type PeakDetector interface {
|
|
||||||
// Initialize initializes the PeakDetector with its configuration and initialValues. The initialValues are the first
|
|
||||||
// values to be processed by the PeakDetector. The length of these values are used to configure the PeakDetector's
|
|
||||||
// lag (see description below). The PeakDetector will never return any signals for the initialValues.
|
|
||||||
//
|
|
||||||
// influence determines the influence of signals on the algorithm's detection threshold. If put at 0, signals have
|
|
||||||
// no influence on the threshold, such that future signals are detected based on a threshold that is calculated with
|
|
||||||
// a mean and standard deviation that is not influenced by past signals. If put at 0.5, signals have half the
|
|
||||||
// influence of normal data points. Another way to think about this is that if you put the influence at 0, you
|
|
||||||
// implicitly assume stationary (i.e. no matter how many signals there are, you always expect the time series to
|
|
||||||
// return to the same average over the long term). If this is not the case, you should put the influence parameter
|
|
||||||
// somewhere between 0 and 1, depending on the extent to which signals can systematically influence the time-varying
|
|
||||||
// trend of the data. E.g., if signals lead to a structural break of the long-term average of the time series, the
|
|
||||||
// influence parameter should be put high (close to 1) so the threshold can react to structural breaks quickly.
|
|
||||||
//
|
|
||||||
// threshold is the number of standard deviations from the moving mean above which the algorithm will classify a new
|
|
||||||
// datapoint as being a signal. For example, if a new datapoint is 4.0 standard deviations above the moving mean and
|
|
||||||
// the threshold parameter is set as 3.5, the algorithm will identify the datapoint as a signal. This parameter
|
|
||||||
// should be set based on how many signals you expect. For example, if your data is normally distributed, a
|
|
||||||
// threshold (or: z-score) of 3.5 corresponds to a signaling probability of 0.00047 (from this table), which implies
|
|
||||||
// that you expect a signal once every 2128 datapoints (1/0.00047). The threshold therefore directly influences how
|
|
||||||
// sensitive the algorithm is and thereby also determines how often the algorithm signals. Examine your own data and
|
|
||||||
// choose a sensible threshold that makes the algorithm signal when you want it to (some trial-and-error might be
|
|
||||||
// needed here to get to a good threshold for your purpose).
|
|
||||||
//
|
|
||||||
// lag determines how much your data will be smoothed and how adaptive the algorithm is to change in the long-term
|
|
||||||
// average of the data. The more stationary your data is, the more lags you should include (this should improve the
|
|
||||||
// robustness of the algorithm). If your data contains time-varying trends, you should consider how quickly you want
|
|
||||||
// the algorithm to adapt to these trends. I.e., if you put lag at 10, it takes 10 'periods' before the algorithm's
|
|
||||||
// threshold is adjusted to any systematic changes in the long-term average. So choose the lag parameter based on
|
|
||||||
// the trending behavior of your data and how adaptive you want the algorithm to be.
|
|
||||||
Initialize(influence, threshold float64, initialValues []float64) error
|
|
||||||
// Next processes the next value and determines its signal.
|
|
||||||
Next(value float64) Signal
|
|
||||||
// NextBatch processes the next values and determines their signals. Their signals will be returned in a slice equal
|
|
||||||
// to the length of the input.
|
|
||||||
NextBatch(values []float64) []Signal
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewPeakDetector creates a new PeakDetector. It must be initialized before use.
|
|
||||||
func NewPeakDetector() PeakDetector {
|
|
||||||
return &peakDetector{
|
|
||||||
movingMeanStdDev: &movingMeanStdDev{},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *peakDetector) Initialize(influence, threshold float64, initialValues []float64) error {
|
|
||||||
p.lag = uint(len(initialValues))
|
|
||||||
if p.lag == 0 {
|
|
||||||
return fmt.Errorf("the length of the initial values is zero, the length is used as the lag for the algorithm: %w", ErrInvalidInitialValues)
|
|
||||||
}
|
|
||||||
p.influence = influence
|
|
||||||
p.threshold = threshold
|
|
||||||
|
|
||||||
p.prevMean, p.prevStdDev = p.movingMeanStdDev.initialize(initialValues)
|
|
||||||
p.prevValue = initialValues[p.lag-1]
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *peakDetector) Next(value float64) (signal Signal) {
|
|
||||||
p.index++
|
|
||||||
if p.index == p.lag {
|
|
||||||
p.index = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
if math.Abs(value-p.prevMean) > p.threshold*p.prevStdDev {
|
|
||||||
if value > p.prevMean {
|
|
||||||
signal = SignalPositive
|
|
||||||
} else {
|
|
||||||
signal = SignalNegative
|
|
||||||
}
|
|
||||||
value = p.influence*value + (1-p.influence)*p.prevValue
|
|
||||||
} else {
|
|
||||||
signal = SignalNeutral
|
|
||||||
}
|
|
||||||
|
|
||||||
p.prevMean, p.prevStdDev = p.movingMeanStdDev.next(value)
|
|
||||||
p.prevValue = value
|
|
||||||
|
|
||||||
return signal
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *peakDetector) NextBatch(values []float64) []Signal {
|
|
||||||
signals := make([]Signal, len(values))
|
|
||||||
for i, v := range values {
|
|
||||||
signals[i] = p.Next(v)
|
|
||||||
}
|
|
||||||
return signals
|
|
||||||
}
|
|
||||||
|
|
||||||
// meanStdDev determines the mean and population standard deviation for the given population.
|
|
||||||
type movingMeanStdDev struct {
|
|
||||||
cache []float64
|
|
||||||
cacheLen float64
|
|
||||||
cacheLenU uint
|
|
||||||
index uint
|
|
||||||
prevMean float64
|
|
||||||
prevVariance float64
|
|
||||||
}
|
|
||||||
|
|
||||||
// initialize creates the needed assets for the movingMeanStdDev. It also computes the resulting mean and population
|
|
||||||
// standard deviation using Welford's method.
|
|
||||||
//
|
|
||||||
// https://www.johndcook.com/blog/standard_deviation/
|
|
||||||
func (m *movingMeanStdDev) initialize(initialValues []float64) (mean, stdDev float64) {
|
|
||||||
m.cacheLenU = uint(len(initialValues))
|
|
||||||
m.cacheLen = float64(m.cacheLenU)
|
|
||||||
m.cache = make([]float64, m.cacheLenU)
|
|
||||||
copy(m.cache, initialValues)
|
|
||||||
|
|
||||||
mean = initialValues[0]
|
|
||||||
prevMean := mean
|
|
||||||
var sumOfSquares float64
|
|
||||||
for i := uint(2); i <= m.cacheLenU; i++ {
|
|
||||||
value := initialValues[i-1]
|
|
||||||
mean = prevMean + (value-prevMean)/float64(i)
|
|
||||||
sumOfSquares = sumOfSquares + (value-prevMean)*(value-mean)
|
|
||||||
prevMean = mean
|
|
||||||
}
|
|
||||||
|
|
||||||
m.prevMean = mean
|
|
||||||
m.prevVariance = sumOfSquares / m.cacheLen
|
|
||||||
return mean, math.Sqrt(m.prevVariance)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Next computes the next mean and population standard deviation. It uses a sliding window and is based on Welford's
|
|
||||||
// method.
|
|
||||||
//
|
|
||||||
// https://stackoverflow.com/a/14638138/14797322
|
|
||||||
func (m *movingMeanStdDev) next(value float64) (mean, stdDev float64) {
|
|
||||||
outOfWindow := m.cache[m.index]
|
|
||||||
m.cache[m.index] = value
|
|
||||||
m.index++
|
|
||||||
if m.index == m.cacheLenU {
|
|
||||||
m.index = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
newMean := m.prevMean + (value-outOfWindow)/m.cacheLen
|
|
||||||
m.prevVariance = m.prevVariance + (value-newMean+outOfWindow-m.prevMean)*(value-outOfWindow)/(m.cacheLen)
|
|
||||||
m.prevMean = newMean
|
|
||||||
|
|
||||||
return m.prevMean, math.Sqrt(m.prevVariance)
|
|
||||||
}
|
|
18
vendor/github.com/goccmack/godsp/Readme.md
generated
vendored
Normal file
18
vendor/github.com/goccmack/godsp/Readme.md
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# Package godsp
|
||||||
|
|
||||||
|
Package godsp is a Go package developed to support some basic signal processing functions using the discrete wavelet transform (DWT).
|
||||||
|
|
||||||
|
## Packages
|
||||||
|
|
||||||
|
- **go-dsp**: General functions on vectors or sets of vectors.
|
||||||
|
- **go-dsp/dbscan**: Implementation of DBSCAN (https://en.wikipedia.org/wiki/DBSCAN) to cluster histogram bins.
|
||||||
|
- **go-dsp/dwt**: Lifting implementation of the discrete wavelet transform using the Daubechies 4 wavelet. See:
|
||||||
|
|
||||||
|
Ripples in Mathematics. The Discrete Wavelet Transform.
|
||||||
|
A. Jensen and A. la Cour-Harbo
|
||||||
|
Springer 2001
|
||||||
|
Section 3.4
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
$ go get github.com/mjibson/go-dsp/fft
|
526
vendor/github.com/goccmack/godsp/dsp.go
generated
vendored
Normal file
526
vendor/github.com/goccmack/godsp/dsp.go
generated
vendored
Normal file
@ -0,0 +1,526 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2019 Marius Ackerman
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
Package dsp has a set of digital signal processing functions that are primarily
|
||||||
|
designed to support the discrete wavelet transform
|
||||||
|
("https://github.com/goccmack/dsp/dwt")
|
||||||
|
*/
|
||||||
|
package godsp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"math"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
myioutil "github.com/goccmack/goutil/ioutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Abs returns |x|
|
||||||
|
func Abs(x []float64) []float64 {
|
||||||
|
x1 := make([]float64, len(x))
|
||||||
|
for i, f := range x {
|
||||||
|
x1[i] = math.Abs(f)
|
||||||
|
}
|
||||||
|
return x1
|
||||||
|
}
|
||||||
|
|
||||||
|
// AbsInt returns |x|
|
||||||
|
func AbsInt(x []int) []int {
|
||||||
|
x1 := make([]int, len(x))
|
||||||
|
for i, e := range x {
|
||||||
|
if e < 0 {
|
||||||
|
x1[i] = -e
|
||||||
|
} else {
|
||||||
|
x1[i] = e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return x1
|
||||||
|
}
|
||||||
|
|
||||||
|
// AbsAll returns Abs(x) for every x in X
|
||||||
|
func AbsAll(X [][]float64) [][]float64 {
|
||||||
|
x1 := make([][]float64, len(X))
|
||||||
|
for i, x := range X {
|
||||||
|
x1[i] = Abs(x)
|
||||||
|
}
|
||||||
|
return x1
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Average returns Sum(x)/len(x).
|
||||||
|
*/
|
||||||
|
func Average(x []float64) float64 {
|
||||||
|
return Sum(x) / float64(len(x))
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
DivS returns x/s where x is a vector and s a scalar.
|
||||||
|
*/
|
||||||
|
func DivS(x []float64, s float64) []float64 {
|
||||||
|
y := make([]float64, len(x))
|
||||||
|
for i := range x {
|
||||||
|
y[i] = x[i] / s
|
||||||
|
}
|
||||||
|
return y
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
DownSampleAll returns DownSample(x, len(x)/min(len(xs))) for all x in xs
|
||||||
|
*/
|
||||||
|
func DownSampleAll(xs [][]float64) [][]float64 {
|
||||||
|
N := len(xs[0])
|
||||||
|
for _, x := range xs {
|
||||||
|
if len(x) < N {
|
||||||
|
N = len(x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ys := make([][]float64, len(xs))
|
||||||
|
for i, x := range xs {
|
||||||
|
ys[i] = DownSample(x, len(x)/N)
|
||||||
|
}
|
||||||
|
return ys
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
DownSample returns x downsampled by n
|
||||||
|
Function panics if len(x) is not an integer multiple of n.
|
||||||
|
*/
|
||||||
|
func DownSample(x []float64, n int) []float64 {
|
||||||
|
if len(x)%n != 0 {
|
||||||
|
panic(fmt.Sprintf("len(x) (%d) is not an integer multiple of n (%d)", len(x), n))
|
||||||
|
}
|
||||||
|
|
||||||
|
x1 := make([]float64, len(x)/n)
|
||||||
|
for i, j := 0, 0; j < len(x1); i, j = i+n, j+1 {
|
||||||
|
x1[j] = x[i]
|
||||||
|
}
|
||||||
|
return x1
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindMax returns the value and index of the first element of x equal to the maximum value in x.
|
||||||
|
func FindMax(x []float64) (value float64, index int) {
|
||||||
|
value, index = x[0], 0
|
||||||
|
for i := 1; i < len(x)-1; i++ {
|
||||||
|
if x[i] > value {
|
||||||
|
value, index = x[i], i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindMax* returns the value and index of the first element of x equal to the maximum value in x.
|
||||||
|
func FindMaxI(x []int) (value int, index int) {
|
||||||
|
value, index = x[0], 0
|
||||||
|
for i := 1; i < len(x)-1; i++ {
|
||||||
|
if x[i] > value {
|
||||||
|
value, index = x[i], i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindMin returns the value and index of the first element of x equal to the minimum value in x.
|
||||||
|
func FindMin(x []float64) (value float64, index int) {
|
||||||
|
value, index = x[0], 0
|
||||||
|
for i := 1; i < len(x)-1; i++ {
|
||||||
|
if x[i] < value {
|
||||||
|
value, index = x[i], i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Float32ToFloat64 returns a copy of x with type []float64
|
||||||
|
*/
|
||||||
|
func Float32ToFloat64(x []float32) []float64 {
|
||||||
|
y := make([]float64, len(x))
|
||||||
|
for i, f := range x {
|
||||||
|
y[i] = float64(f)
|
||||||
|
}
|
||||||
|
return y
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsPowerOf2(x int) bool {
|
||||||
|
return (x != 0) && ((x & (x - 1)) == 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
LoadFloats reads a text file containing one float per line.
|
||||||
|
*/
|
||||||
|
func LoadFloats(fname string) []float64 {
|
||||||
|
data, err := ioutil.ReadFile(fname)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
rdr := bufio.NewReader(bytes.NewBuffer(data))
|
||||||
|
x := make([]float64, 0, 1024)
|
||||||
|
for s, err := rdr.ReadString('\n'); err == nil; s, err = rdr.ReadString('\n') {
|
||||||
|
f, err := strconv.ParseFloat(strings.TrimSuffix(s, "\n"), 64)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
x = append(x, f)
|
||||||
|
}
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log2 returns the integer log base 2 of n.
|
||||||
|
// E.g.: log2(12) ~ 3.6. Log2 returns 3
|
||||||
|
func Log2(n int) int {
|
||||||
|
return int(math.Log2(float64(n)))
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
LowpassFilterAll returns LowpassFilter(x) for all x in xs.
|
||||||
|
*/
|
||||||
|
func LowpassFilterAll(xs [][]float64, alpha float64) [][]float64 {
|
||||||
|
ys := make([][]float64, len(xs))
|
||||||
|
for i, x := range xs {
|
||||||
|
ys[i] = LowpassFilter(x, alpha)
|
||||||
|
}
|
||||||
|
return ys
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
LowpassFilter returns x filtered by alpha
|
||||||
|
*/
|
||||||
|
func LowpassFilter(x []float64, alpha float64) []float64 {
|
||||||
|
y := make([]float64, len(x))
|
||||||
|
y[0] = alpha * x[0]
|
||||||
|
for i := 1; i < len(x); i++ {
|
||||||
|
y[i] = y[i-1] + alpha*(x[i]-y[i-1])
|
||||||
|
}
|
||||||
|
return y
|
||||||
|
}
|
||||||
|
|
||||||
|
// Max returns the maximum value of the elements of x
|
||||||
|
func Max(x []float64) float64 {
|
||||||
|
max := x[0]
|
||||||
|
for _, f := range x {
|
||||||
|
if f > max {
|
||||||
|
max = f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return max
|
||||||
|
}
|
||||||
|
|
||||||
|
// MaxInt returns the maximum value of the elements of x
|
||||||
|
func MaxInt(x []int) int {
|
||||||
|
max := x[0]
|
||||||
|
for _, f := range x {
|
||||||
|
if f > max {
|
||||||
|
max = f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return max
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
MovAvg returns the moving average for each x[i], given by sum(x[i-w:i+w])/(2w)
|
||||||
|
*/
|
||||||
|
func MovAvg(x []float64, w int) []float64 {
|
||||||
|
y := make([]float64, len(x))
|
||||||
|
for i := w; i < len(x)-w; i++ {
|
||||||
|
y[i] = Sum(x[i-w:i+w]) / float64(2*w)
|
||||||
|
}
|
||||||
|
return y
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Multiplex returns on vector with the element of vs interleaved
|
||||||
|
*/
|
||||||
|
func Multiplex(channels [][]float64) []float64 {
|
||||||
|
numChans := len(channels)
|
||||||
|
chanLen := len(channels[0])
|
||||||
|
buf := make([]float64, numChans*chanLen)
|
||||||
|
for i := 0; i < chanLen; i++ {
|
||||||
|
k := i * numChans
|
||||||
|
for j := 0; j < numChans; j++ {
|
||||||
|
buf[k+j] = channels[j][i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normalise returns x/max(x)
|
||||||
|
func Normalise(x []float64) []float64 {
|
||||||
|
x1 := make([]float64, len(x))
|
||||||
|
sum := Max(x)
|
||||||
|
for i, f := range x {
|
||||||
|
x1[i] = f / sum
|
||||||
|
}
|
||||||
|
return x1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normalise returns x/max(x) for all x in xs
|
||||||
|
func NormaliseAll(xs [][]float64) [][]float64 {
|
||||||
|
x1 := make([][]float64, len(xs))
|
||||||
|
for i, x := range xs {
|
||||||
|
x1[i] = Normalise(x)
|
||||||
|
}
|
||||||
|
return x1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pow2 returns 2^x.
|
||||||
|
// The function panics if x < 0
|
||||||
|
func Pow2(x int) int {
|
||||||
|
if x < 0 {
|
||||||
|
panic(fmt.Sprintf("X = %d", x))
|
||||||
|
}
|
||||||
|
pw := 1
|
||||||
|
for i := 1; i <= x; i++ {
|
||||||
|
pw *= 2
|
||||||
|
}
|
||||||
|
return pw
|
||||||
|
}
|
||||||
|
|
||||||
|
// Range returns an interger range 0:1:n-1
|
||||||
|
func Range(n int) []int {
|
||||||
|
rng := make([]int, n)
|
||||||
|
for i := range rng {
|
||||||
|
rng[i] = i
|
||||||
|
}
|
||||||
|
return rng
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
RemoveAvgAllZ removes the average of all vectors x in xs. The minimum value
|
||||||
|
of any x[i] is 0.
|
||||||
|
*/
|
||||||
|
func RemoveAvgAllZ(xs [][]float64) [][]float64 {
|
||||||
|
xs1 := make([][]float64, len(xs))
|
||||||
|
for i, x := range xs {
|
||||||
|
xs1[i] = RemoveAvg(x)
|
||||||
|
}
|
||||||
|
return xs1
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveAvgZ returns x[i] = x[i]-sum(x)/len(x) or 0 if x[i]-sum(x)/len(x) < 0
|
||||||
|
func RemoveAvg(x []float64) []float64 {
|
||||||
|
x1 := make([]float64, len(x))
|
||||||
|
avg := Sum(x) / float64(len(x))
|
||||||
|
for i, f := range x {
|
||||||
|
x1[i] = f - avg
|
||||||
|
if x1[i] < 0 {
|
||||||
|
x1[i] = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return x1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Smooth smoothts x: x[i] = sum(x[i-wdw:i+wdw])/(2*wdw)
|
||||||
|
func Smooth(x []float64, wdw int) {
|
||||||
|
for i := 0; i < wdw; i++ {
|
||||||
|
x[i] = 0
|
||||||
|
}
|
||||||
|
for i := wdw; i < len(x)-wdw; i++ {
|
||||||
|
x[i] = Sum(x[i-wdw:i+wdw]) / float64((2 * wdw))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Sub returns x - y. The function panics if len(x) != len(y).
|
||||||
|
*/
|
||||||
|
func Sub(x, y []float64) []float64 {
|
||||||
|
if len(x) != len(y) {
|
||||||
|
panic("len(x) != len(y)")
|
||||||
|
}
|
||||||
|
x1 := make([]float64, len(x))
|
||||||
|
for i := range x {
|
||||||
|
x1[i] = x[i] - y[i]
|
||||||
|
}
|
||||||
|
return x1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sum returns the sum of the elements of the vector x
|
||||||
|
func Sum(x []float64) float64 {
|
||||||
|
sum := 0.0
|
||||||
|
for _, f := range x {
|
||||||
|
sum += f
|
||||||
|
}
|
||||||
|
return sum
|
||||||
|
}
|
||||||
|
|
||||||
|
// SumVectors returns the sum of the vectors in X.
|
||||||
|
// The function panics if all vectors don't have the same length
|
||||||
|
func SumVectors(X [][]float64) []float64 {
|
||||||
|
N := len(X[0])
|
||||||
|
for i, x := range X {
|
||||||
|
if len(x) != N {
|
||||||
|
panic(fmt.Sprintf("N=%d but len(X[%d]=%d", N, i, len(x)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sum := make([]float64, N)
|
||||||
|
for i := 0; i < N; i++ {
|
||||||
|
for j := range X {
|
||||||
|
sum[i] += X[j][i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sum
|
||||||
|
}
|
||||||
|
|
||||||
|
func ToFloat(x []int) []float64 {
|
||||||
|
y := make([]float64, len(x))
|
||||||
|
for i, e := range x {
|
||||||
|
y[i] = float64(e) / float64(math.MaxInt64)
|
||||||
|
}
|
||||||
|
return y
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
ToInt returns y * math.MaxInt64.
|
||||||
|
The range of x is [-1.0,1.0].
|
||||||
|
The function panics if bitsPerSample is not one of 8,16,32.
|
||||||
|
*/
|
||||||
|
func ToInt(x []float64, bitsPerSample int) []int {
|
||||||
|
y := make([]int, len(x))
|
||||||
|
if bitsPerSample != 8 && bitsPerSample != 16 && bitsPerSample != 32 {
|
||||||
|
panic(fmt.Sprintf("Invalid bitsPerSample %d", bitsPerSample))
|
||||||
|
}
|
||||||
|
max := float64(int(1)<<bitsPerSample - 1)
|
||||||
|
for i, f := range x {
|
||||||
|
y[i] = int(f * max)
|
||||||
|
}
|
||||||
|
return y
|
||||||
|
}
|
||||||
|
|
||||||
|
func ToIntS(x float64, bitsPerSample int) int {
|
||||||
|
max := float64(int(1)<<bitsPerSample - 1)
|
||||||
|
return int(x * max)
|
||||||
|
}
|
||||||
|
|
||||||
|
func findLocalMax(x []float64, from, wdw, step int) (maxI, slopeEnd int) {
|
||||||
|
i, slp := from+wdw, 0
|
||||||
|
for slp >= 0 && i < len(x)-wdw {
|
||||||
|
slp = slope(x[i : i+wdw])
|
||||||
|
i += step
|
||||||
|
}
|
||||||
|
_, maxI = FindMax(x[from:i])
|
||||||
|
maxI += from
|
||||||
|
slopeEnd = i
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func findLocalMin(x []float64, from, wdw, step int) (minI, slopeEnd int) {
|
||||||
|
i, slp := from+wdw, 0
|
||||||
|
for slp <= 0 && i < len(x)-wdw {
|
||||||
|
slp = slope(x[i : i+wdw])
|
||||||
|
i += step
|
||||||
|
}
|
||||||
|
_, minI = FindMin(x[from:i])
|
||||||
|
minI += from
|
||||||
|
slopeEnd = i
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func findNon0Slope(x []float64, from, wdw int) (slp, end int) {
|
||||||
|
for i := from; i < len(x)-wdw; i++ {
|
||||||
|
slp := slope(x[i : i+wdw])
|
||||||
|
if slp != 0 {
|
||||||
|
return slp, i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0, len(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// slope returns +1, 0, -1
|
||||||
|
func slope(x []float64) int {
|
||||||
|
end := len(x) - 1
|
||||||
|
if x[0] < x[end] {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
if x[0] == x[end] {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func ivecContain(x []int, v int) bool {
|
||||||
|
for _, v1 := range x {
|
||||||
|
if v1 == v {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteAllDataFile writes each xs[i] in xs to a test file `fname_i.txt`
|
||||||
|
func WriteAllDataFile(xs [][]float64, fname string) {
|
||||||
|
for i, xs := range xs {
|
||||||
|
WriteDataFile(xs, fmt.Sprintf("%s_%d", fname, i))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteDataFile writes x to a text file `fname.txt`
|
||||||
|
func WriteDataFile(x []float64, fname string) {
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
for _, f := range x {
|
||||||
|
fmt.Fprintf(buf, "%f\n", f)
|
||||||
|
}
|
||||||
|
if err := myioutil.WriteFile(fname+".txt", buf.Bytes()); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteIntDataFile writes x to a text file `fname.txt`
|
||||||
|
func WriteIntDataFile(x []int, fname string) {
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
for _, f := range x {
|
||||||
|
fmt.Fprintf(buf, "%d\n", f)
|
||||||
|
}
|
||||||
|
if err := myioutil.WriteFile(fname+".txt", buf.Bytes()); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
WriteIntMatrixDataFile writes an integer matrix to a text file `fname.csv`
|
||||||
|
*/
|
||||||
|
func WriteIntMatrixDataFile(x [][]int, fname string) {
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
for _, row := range x {
|
||||||
|
for i, col := range row {
|
||||||
|
if i > 0 {
|
||||||
|
fmt.Fprint(buf, ",")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(buf, "%d", col)
|
||||||
|
}
|
||||||
|
fmt.Fprintln(buf)
|
||||||
|
}
|
||||||
|
if err := myioutil.WriteFile(fname+".csv", buf.Bytes()); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Xcorr returns the cross correlation of x with y for maxDelay.
|
||||||
|
*/
|
||||||
|
func Xcorr(x, y []float64, maxDelay int) (corr []float64) {
|
||||||
|
N := len(x)
|
||||||
|
corr = make([]float64, maxDelay)
|
||||||
|
for k := 0; k < maxDelay; k++ {
|
||||||
|
for n := 0; n < N-k; n++ {
|
||||||
|
corr[k] += x[n] * y[n+k]
|
||||||
|
}
|
||||||
|
corr[k] /= float64(N)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
135
vendor/github.com/goccmack/godsp/peaks/peaks.go
generated
vendored
Normal file
135
vendor/github.com/goccmack/godsp/peaks/peaks.go
generated
vendored
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
// Copyright 2019 Marius Ackerman
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
/*
|
||||||
|
Package peaks finds the maxima in a vector. It works by lowering a horizontal line
|
||||||
|
across the signal, revealing peaks as it proceeds. Peaks that are closer to
|
||||||
|
each other than a minimum separation distance are merged to the left (lower index).
|
||||||
|
*/
|
||||||
|
package peaks
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"sort"
|
||||||
|
|
||||||
|
"github.com/goccmack/godsp"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
empty = -1
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
Get returns a slice containing the indices of the peaks in x.
|
||||||
|
sep is the minimum distance between 2 peaks. Peaks closer to each other than
|
||||||
|
sep are merged to the lower index.
|
||||||
|
*/
|
||||||
|
func Get(x []float64, sep int) []int {
|
||||||
|
pks := []int{}
|
||||||
|
for i := range x {
|
||||||
|
if isMax(i, i-sep, i+sep, x) {
|
||||||
|
pks = append(pks, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pks
|
||||||
|
}
|
||||||
|
|
||||||
|
func getMaxIndex(x []float64) int {
|
||||||
|
i, max := 0, math.Inf(-1)
|
||||||
|
for j, y := range x {
|
||||||
|
if y > max {
|
||||||
|
i, max = j, y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if max > 0 {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
func isMax(i, min, max int, x []float64) bool {
|
||||||
|
if min < 0 {
|
||||||
|
min = 0
|
||||||
|
}
|
||||||
|
if max > len(x) {
|
||||||
|
max = len(x)
|
||||||
|
}
|
||||||
|
for j := min; j < i; j++ {
|
||||||
|
if x[j] >= x[i] {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for j := i + 1; j < max; j++ {
|
||||||
|
if x[j] > x[i] {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func getWindow(i, sep int, x []float64) (min, max int) {
|
||||||
|
min, max = i-sep, i+sep
|
||||||
|
if min < 0 {
|
||||||
|
min = 0
|
||||||
|
}
|
||||||
|
if max > len(x) {
|
||||||
|
max = len(x)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// func Get(x []float64, sep int) []int {
|
||||||
|
// si := getSortedIndices(x)
|
||||||
|
// pks := getEmptyPeaks(len(x))
|
||||||
|
// for _, xi := range si {
|
||||||
|
// if pks[xi] == empty {
|
||||||
|
// markNeighbours(xi, sep, pks)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// uniquePeaks := make([]int, 0, len(x)/(2*sep))
|
||||||
|
// for i, xi := range pks {
|
||||||
|
// if i == xi {
|
||||||
|
// uniquePeaks = append(uniquePeaks, xi)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// return uniquePeaks
|
||||||
|
// }
|
||||||
|
|
||||||
|
func getEmptyPeaks(n int) []int {
|
||||||
|
epks := make([]int, n)
|
||||||
|
for i := range epks {
|
||||||
|
epks[i] = empty
|
||||||
|
}
|
||||||
|
return epks
|
||||||
|
}
|
||||||
|
|
||||||
|
func getSortedIndices(x []float64) []int {
|
||||||
|
idx := godsp.Range(len(x))
|
||||||
|
sort.SliceStable(idx, func(i, j int) bool { return x[i] > x[j] })
|
||||||
|
return idx
|
||||||
|
}
|
||||||
|
|
||||||
|
func markNeighbours(xi, sep int, pks []int) {
|
||||||
|
min := xi - sep
|
||||||
|
if min < 0 {
|
||||||
|
min = 0
|
||||||
|
}
|
||||||
|
max := xi + sep
|
||||||
|
if max > len(pks) {
|
||||||
|
max = len(pks)
|
||||||
|
}
|
||||||
|
for i := min; i < max; i++ {
|
||||||
|
pks[i] = xi
|
||||||
|
}
|
||||||
|
}
|
56
vendor/github.com/goccmack/godsp/wavread.go
generated
vendored
Normal file
56
vendor/github.com/goccmack/godsp/wavread.go
generated
vendored
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
// Copyright 2019 Marius Ackerman
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package godsp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io/ioutil"
|
||||||
|
|
||||||
|
"github.com/mjibson/go-dsp/wav"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
ReadWavFile returns the demultiplexed channels of a wav file, and the sample rate in Hz.
|
||||||
|
*/
|
||||||
|
func ReadWavFile(wavName string) (channels [][]float64, sampleRate, bitsPerSample int) {
|
||||||
|
buf, err := ioutil.ReadFile(wavName)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
rdr, err := wav.New(bytes.NewBuffer(buf))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
numSamples, numChannels := rdr.Samples, int(rdr.NumChannels)
|
||||||
|
sampleRate = int(rdr.SampleRate)
|
||||||
|
bitsPerSample = int(rdr.Header.BitsPerSample)
|
||||||
|
channels = make([][]float64, numChannels)
|
||||||
|
chanLen := numSamples / numChannels
|
||||||
|
for i := range channels {
|
||||||
|
channels[i] = make([]float64, chanLen)
|
||||||
|
}
|
||||||
|
samples, err := rdr.ReadFloats(rdr.Samples)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
for i, j := 0, 0; i < len(samples); {
|
||||||
|
for _, ch := range channels {
|
||||||
|
ch[j] = float64(samples[i])
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
j++
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
201
vendor/github.com/goccmack/goutil/LICENSE
generated
vendored
Normal file
201
vendor/github.com/goccmack/goutil/LICENSE
generated
vendored
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
55
vendor/github.com/goccmack/goutil/ioutil/ioutil.go
generated
vendored
Normal file
55
vendor/github.com/goccmack/goutil/ioutil/ioutil.go
generated
vendored
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
// Copyright 2020 Marius Ackerman
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
/*
|
||||||
|
Package ioutil contains functions for writing directories and files.
|
||||||
|
*/
|
||||||
|
package ioutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FilePermission given to all created files and directories
|
||||||
|
const FilePermission = 0731
|
||||||
|
|
||||||
|
// Exist returns true if path exists, otherwise false.
|
||||||
|
func Exist(path string) bool {
|
||||||
|
_, err := os.Stat(path)
|
||||||
|
return err == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MkdirAll makes all the directories in path.
|
||||||
|
func MkdirAll(path string) error {
|
||||||
|
if path == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return os.MkdirAll(path, FilePermission)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteFile creates all the non-existend directories in path before writing
|
||||||
|
// data to path.
|
||||||
|
func WriteFile(path string, data []byte) error {
|
||||||
|
dir, _ := filepath.Split(path)
|
||||||
|
if err := MkdirAll(dir); err != nil {
|
||||||
|
return fmt.Errorf("Error creating directory %s: %s", dir, err)
|
||||||
|
}
|
||||||
|
if err := ioutil.WriteFile(path, data, FilePermission); err != nil {
|
||||||
|
return fmt.Errorf("Error writing file %s: %s\n", path, err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
13
vendor/github.com/mjibson/go-dsp/LICENSE
generated
vendored
Normal file
13
vendor/github.com/mjibson/go-dsp/LICENSE
generated
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
Copyright (c) 2011 Matt Jibson <matt.jibson@gmail.com>
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
BIN
vendor/github.com/mjibson/go-dsp/wav/float.wav
generated
vendored
Normal file
BIN
vendor/github.com/mjibson/go-dsp/wav/float.wav
generated
vendored
Normal file
Binary file not shown.
BIN
vendor/github.com/mjibson/go-dsp/wav/small.wav
generated
vendored
Normal file
BIN
vendor/github.com/mjibson/go-dsp/wav/small.wav
generated
vendored
Normal file
Binary file not shown.
161
vendor/github.com/mjibson/go-dsp/wav/wav.go
generated
vendored
Normal file
161
vendor/github.com/mjibson/go-dsp/wav/wav.go
generated
vendored
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2012 Matt Jibson <matt.jibson@gmail.com>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Package wav provides support for the WAV file format.
|
||||||
|
//
|
||||||
|
// Supported formats are PCM 8- and 16-bit, and IEEE float. Extended chunks
|
||||||
|
// (JUNK, bext, and others added by tools like ProTools) are ignored.
|
||||||
|
package wav
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"math"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
wavFormatPCM = 1
|
||||||
|
wavFormatIEEEFloat = 3
|
||||||
|
)
|
||||||
|
|
||||||
|
// Header contains Wav fmt chunk data.
|
||||||
|
type Header struct {
|
||||||
|
AudioFormat uint16
|
||||||
|
NumChannels uint16
|
||||||
|
SampleRate uint32
|
||||||
|
ByteRate uint32
|
||||||
|
BlockAlign uint16
|
||||||
|
BitsPerSample uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wav reads wav files.
|
||||||
|
type Wav struct {
|
||||||
|
Header
|
||||||
|
// Samples is the total number of available samples.
|
||||||
|
Samples int
|
||||||
|
// Duration is the estimated duration based on reported samples.
|
||||||
|
Duration time.Duration
|
||||||
|
|
||||||
|
r io.Reader
|
||||||
|
}
|
||||||
|
|
||||||
|
// New reads the WAV header from r.
|
||||||
|
func New(r io.Reader) (*Wav, error) {
|
||||||
|
var w Wav
|
||||||
|
header := make([]byte, 16)
|
||||||
|
if _, err := io.ReadFull(r, header[:12]); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if string(header[0:4]) != "RIFF" {
|
||||||
|
return nil, fmt.Errorf("wav: missing RIFF")
|
||||||
|
}
|
||||||
|
if string(header[8:12]) != "WAVE" {
|
||||||
|
return nil, fmt.Errorf("wav: missing WAVE")
|
||||||
|
}
|
||||||
|
hasFmt := false
|
||||||
|
for {
|
||||||
|
if _, err := io.ReadFull(r, header[:8]); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
sz := binary.LittleEndian.Uint32(header[4:])
|
||||||
|
switch typ := string(header[:4]); typ {
|
||||||
|
case "fmt ":
|
||||||
|
if sz < 16 {
|
||||||
|
return nil, fmt.Errorf("wav: bad fmt size")
|
||||||
|
}
|
||||||
|
f := make([]byte, sz)
|
||||||
|
if _, err := io.ReadFull(r, f); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := binary.Read(bytes.NewBuffer(f), binary.LittleEndian, &w.Header); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
switch w.AudioFormat {
|
||||||
|
case wavFormatPCM:
|
||||||
|
case wavFormatIEEEFloat:
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("wav: unknown audio format: %02x", w.AudioFormat)
|
||||||
|
}
|
||||||
|
hasFmt = true
|
||||||
|
case "data":
|
||||||
|
if !hasFmt {
|
||||||
|
return nil, fmt.Errorf("wav: unexpected fmt chunk")
|
||||||
|
}
|
||||||
|
w.Samples = int(sz) / int(w.BitsPerSample) * 8
|
||||||
|
w.Duration = time.Duration(w.Samples) * time.Second / time.Duration(w.SampleRate) / time.Duration(w.NumChannels)
|
||||||
|
w.r = io.LimitReader(r, int64(sz))
|
||||||
|
return &w, nil
|
||||||
|
default:
|
||||||
|
io.CopyN(ioutil.Discard, r, int64(sz))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadSamples returns a [n]T, where T is uint8, int16, or float32, based on the
|
||||||
|
// wav data. n is the number of samples to return.
|
||||||
|
func (w *Wav) ReadSamples(n int) (interface{}, error) {
|
||||||
|
var data interface{}
|
||||||
|
switch w.AudioFormat {
|
||||||
|
case wavFormatPCM:
|
||||||
|
switch w.BitsPerSample {
|
||||||
|
case 8:
|
||||||
|
data = make([]uint8, n)
|
||||||
|
case 16:
|
||||||
|
data = make([]int16, n)
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("wav: unknown bits per sample: %v", w.BitsPerSample)
|
||||||
|
}
|
||||||
|
case wavFormatIEEEFloat:
|
||||||
|
data = make([]float32, n)
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("wav: unknown audio format")
|
||||||
|
}
|
||||||
|
if err := binary.Read(w.r, binary.LittleEndian, data); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadFloats is like ReadSamples, but it converts any underlying data to a
|
||||||
|
// float32.
|
||||||
|
func (w *Wav) ReadFloats(n int) ([]float32, error) {
|
||||||
|
d, err := w.ReadSamples(n)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var f []float32
|
||||||
|
switch d := d.(type) {
|
||||||
|
case []uint8:
|
||||||
|
f = make([]float32, len(d))
|
||||||
|
for i, v := range d {
|
||||||
|
f[i] = float32(v) / math.MaxUint8
|
||||||
|
}
|
||||||
|
case []int16:
|
||||||
|
f = make([]float32, len(d))
|
||||||
|
for i, v := range d {
|
||||||
|
f[i] = (float32(v) - math.MinInt16) / (math.MaxInt16 - math.MinInt16)
|
||||||
|
}
|
||||||
|
case []float32:
|
||||||
|
f = d
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("wav: unknown type: %T", d)
|
||||||
|
}
|
||||||
|
return f, nil
|
||||||
|
}
|
13
vendor/modules.txt
vendored
13
vendor/modules.txt
vendored
@ -1,6 +1,3 @@
|
|||||||
# github.com/MicahParks/peakdetect v0.1.2
|
|
||||||
## explicit; go 1.13
|
|
||||||
github.com/MicahParks/peakdetect
|
|
||||||
# github.com/chewxy/math32 v1.11.1
|
# github.com/chewxy/math32 v1.11.1
|
||||||
## explicit; go 1.13
|
## explicit; go 1.13
|
||||||
github.com/chewxy/math32
|
github.com/chewxy/math32
|
||||||
@ -46,6 +43,13 @@ github.com/go-text/typesetting/language
|
|||||||
github.com/go-text/typesetting/segmenter
|
github.com/go-text/typesetting/segmenter
|
||||||
github.com/go-text/typesetting/shaping
|
github.com/go-text/typesetting/shaping
|
||||||
github.com/go-text/typesetting/unicodedata
|
github.com/go-text/typesetting/unicodedata
|
||||||
|
# github.com/goccmack/godsp v0.1.1
|
||||||
|
## explicit; go 1.13
|
||||||
|
github.com/goccmack/godsp
|
||||||
|
github.com/goccmack/godsp/peaks
|
||||||
|
# github.com/goccmack/goutil v0.4.0
|
||||||
|
## explicit; go 1.13
|
||||||
|
github.com/goccmack/goutil/ioutil
|
||||||
# github.com/godbus/dbus v4.1.0+incompatible
|
# github.com/godbus/dbus v4.1.0+incompatible
|
||||||
## explicit
|
## explicit
|
||||||
github.com/godbus/dbus
|
github.com/godbus/dbus
|
||||||
@ -107,6 +111,9 @@ github.com/kirsle/configdir
|
|||||||
# github.com/leberKleber/go-mpris v1.1.0
|
# github.com/leberKleber/go-mpris v1.1.0
|
||||||
## explicit; go 1.19
|
## explicit; go 1.19
|
||||||
github.com/leberKleber/go-mpris
|
github.com/leberKleber/go-mpris
|
||||||
|
# github.com/mjibson/go-dsp v0.0.0-20180508042940-11479a337f12
|
||||||
|
## explicit
|
||||||
|
github.com/mjibson/go-dsp/wav
|
||||||
# github.com/smallnest/ringbuffer v0.0.0-20241129171057-356c688ba81d
|
# github.com/smallnest/ringbuffer v0.0.0-20241129171057-356c688ba81d
|
||||||
## explicit; go 1.19
|
## explicit; go 1.19
|
||||||
github.com/smallnest/ringbuffer
|
github.com/smallnest/ringbuffer
|
||||||
|
Loading…
Reference in New Issue
Block a user