From ff40ee8f63c5695742e53772ee6fa2abdbe5e643 Mon Sep 17 00:00:00 2001 From: Louis Dalibard Date: Thu, 16 Jan 2025 22:56:38 +0100 Subject: [PATCH] might be more efficient --- go.mod | 4 +- go.sum | 27 +- .../MicahParks/peakdetect/README.md | 142 ----- .../MicahParks/peakdetect/peakdetect.go | 186 ------- .../peakdetect => goccmack/godsp}/LICENSE | 0 vendor/github.com/goccmack/godsp/Readme.md | 18 + vendor/github.com/goccmack/godsp/dsp.go | 526 ++++++++++++++++++ .../github.com/goccmack/godsp/peaks/peaks.go | 135 +++++ vendor/github.com/goccmack/godsp/wavread.go | 56 ++ vendor/github.com/goccmack/goutil/LICENSE | 201 +++++++ .../goccmack/goutil/ioutil/ioutil.go | 55 ++ vendor/github.com/mjibson/go-dsp/LICENSE | 13 + .../github.com/mjibson/go-dsp/wav/float.wav | Bin 0 -> 1889324 bytes .../github.com/mjibson/go-dsp/wav/small.wav | Bin 0 -> 83834 bytes vendor/github.com/mjibson/go-dsp/wav/wav.go | 161 ++++++ vendor/modules.txt | 13 +- 16 files changed, 1203 insertions(+), 334 deletions(-) delete mode 100644 vendor/github.com/MicahParks/peakdetect/README.md delete mode 100644 vendor/github.com/MicahParks/peakdetect/peakdetect.go rename vendor/github.com/{MicahParks/peakdetect => goccmack/godsp}/LICENSE (100%) create mode 100644 vendor/github.com/goccmack/godsp/Readme.md create mode 100644 vendor/github.com/goccmack/godsp/dsp.go create mode 100644 vendor/github.com/goccmack/godsp/peaks/peaks.go create mode 100644 vendor/github.com/goccmack/godsp/wavread.go create mode 100644 vendor/github.com/goccmack/goutil/LICENSE create mode 100644 vendor/github.com/goccmack/goutil/ioutil/ioutil.go create mode 100644 vendor/github.com/mjibson/go-dsp/LICENSE create mode 100644 vendor/github.com/mjibson/go-dsp/wav/float.wav create mode 100644 vendor/github.com/mjibson/go-dsp/wav/small.wav create mode 100644 vendor/github.com/mjibson/go-dsp/wav/wav.go diff --git a/go.mod b/go.mod index bc49759..37ebc41 100644 --- a/go.mod +++ b/go.mod @@ -3,10 +3,10 @@ module xyosc go 1.23.2 require ( - github.com/MicahParks/peakdetect v0.1.2 github.com/chewxy/math32 v1.11.1 github.com/fsnotify/fsnotify v1.8.0 github.com/gen2brain/malgo v0.11.23 + github.com/goccmack/godsp v0.1.1 github.com/godbus/dbus v4.1.0+incompatible github.com/hajimehoshi/ebiten/v2 v2.8.6 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/purego v0.8.2 // 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/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/sync v0.10.0 // indirect golang.org/x/sys v0.29.0 // indirect diff --git a/go.sum b/go.sum index 207cfc9..3297466 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,4 @@ -github.com/MicahParks/peakdetect v0.1.2 h1:DYQXgBzfl/kkuTKErM/4/2iSkk63okzTka6haTQFK5Y= -github.com/MicahParks/peakdetect v0.1.2/go.mod h1:78d4YnCFxrVbu1Calxc3LIOqN/xtcr7a8lmSwPRylts= +github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= github.com/chewxy/math32 v1.11.1 h1:b7PGHlp8KjylDoU8RrcEsRuGZhJuz8haxnKfuMMRqy8= github.com/chewxy/math32 v1.11.1/go.mod h1:dOB2rcuFrCn6UHrze36WSLVPKtzPMRAQvBvUwkSsLqs= 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/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I= 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/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= 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/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/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/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/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= 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/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/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/go.mod h1:cCQ3np7rdmaJa1ZnvslraVlpxNb3wCjEnAP1LHNyXNA= github.com/jezek/xgb v1.1.1 h1:bE/r8ZZtSv7l9gk6nU0mYx51aXrvnyb44892TwSaqS4= 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/go.mod h1:4rEELDSfUAlBSyUjPG0JnaNGjf13JySHFeRdD/3dLP0= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 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/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/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= 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/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/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/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/go.mod h1:wJJBTdLfCCf3tiHa1fNxpZmUI4mmoZvwMCPP0ddoNKY= 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/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= 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 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= 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.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/vendor/github.com/MicahParks/peakdetect/README.md b/vendor/github.com/MicahParks/peakdetect/README.md deleted file mode 100644 index 11a225a..0000000 --- a/vendor/github.com/MicahParks/peakdetect/README.md +++ /dev/null @@ -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. diff --git a/vendor/github.com/MicahParks/peakdetect/peakdetect.go b/vendor/github.com/MicahParks/peakdetect/peakdetect.go deleted file mode 100644 index 55c1b7d..0000000 --- a/vendor/github.com/MicahParks/peakdetect/peakdetect.go +++ /dev/null @@ -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) -} diff --git a/vendor/github.com/MicahParks/peakdetect/LICENSE b/vendor/github.com/goccmack/godsp/LICENSE similarity index 100% rename from vendor/github.com/MicahParks/peakdetect/LICENSE rename to vendor/github.com/goccmack/godsp/LICENSE diff --git a/vendor/github.com/goccmack/godsp/Readme.md b/vendor/github.com/goccmack/godsp/Readme.md new file mode 100644 index 0000000..d4ba5ef --- /dev/null +++ b/vendor/github.com/goccmack/godsp/Readme.md @@ -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 diff --git a/vendor/github.com/goccmack/godsp/dsp.go b/vendor/github.com/goccmack/godsp/dsp.go new file mode 100644 index 0000000..1e0f59a --- /dev/null +++ b/vendor/github.com/goccmack/godsp/dsp.go @@ -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)<= 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 +} diff --git a/vendor/github.com/goccmack/godsp/peaks/peaks.go b/vendor/github.com/goccmack/godsp/peaks/peaks.go new file mode 100644 index 0000000..7f404c8 --- /dev/null +++ b/vendor/github.com/goccmack/godsp/peaks/peaks.go @@ -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 + } +} diff --git a/vendor/github.com/goccmack/godsp/wavread.go b/vendor/github.com/goccmack/godsp/wavread.go new file mode 100644 index 0000000..64b31d2 --- /dev/null +++ b/vendor/github.com/goccmack/godsp/wavread.go @@ -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 +} diff --git a/vendor/github.com/goccmack/goutil/LICENSE b/vendor/github.com/goccmack/goutil/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/vendor/github.com/goccmack/goutil/LICENSE @@ -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. diff --git a/vendor/github.com/goccmack/goutil/ioutil/ioutil.go b/vendor/github.com/goccmack/goutil/ioutil/ioutil.go new file mode 100644 index 0000000..50994de --- /dev/null +++ b/vendor/github.com/goccmack/goutil/ioutil/ioutil.go @@ -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 +} diff --git a/vendor/github.com/mjibson/go-dsp/LICENSE b/vendor/github.com/mjibson/go-dsp/LICENSE new file mode 100644 index 0000000..d412027 --- /dev/null +++ b/vendor/github.com/mjibson/go-dsp/LICENSE @@ -0,0 +1,13 @@ +Copyright (c) 2011 Matt Jibson + +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. diff --git a/vendor/github.com/mjibson/go-dsp/wav/float.wav b/vendor/github.com/mjibson/go-dsp/wav/float.wav new file mode 100644 index 0000000000000000000000000000000000000000..4d61315d7bda4d98707bd4f9df0e2b7764703d3e GIT binary patch literal 1889324 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 I(m(w_0EfLY4*&oF literal 0 HcmV?d00001 diff --git a/vendor/github.com/mjibson/go-dsp/wav/small.wav b/vendor/github.com/mjibson/go-dsp/wav/small.wav new file mode 100644 index 0000000000000000000000000000000000000000..e01fb14ca24f6570d3917a5c872140e6780d9ca9 GIT binary patch literal 83834 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 aF`V4V^%O*AH>z2H4t4JU{r}vhKFeP=!mX + * + * 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 +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 752c49f..4d12fca 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -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 ## explicit; go 1.13 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/shaping 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 ## explicit github.com/godbus/dbus @@ -107,6 +111,9 @@ github.com/kirsle/configdir # github.com/leberKleber/go-mpris v1.1.0 ## explicit; go 1.19 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 ## explicit; go 1.19 github.com/smallnest/ringbuffer