diff --git a/README.md b/README.md
index 12769e2..c535072 100644
--- a/README.md
+++ b/README.md
@@ -17,7 +17,7 @@ Collection of audio utilities for decoding/encoding/processing files and streams
|:----------:|:----------------------------------------------------------------------------------------:|:-------:|:---------------:|:-------------:|:-------:|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **FLAC** | [FLAC](https://xiph.org/flac/format.html), [Ogg](https://xiph.org/flac/ogg_mapping.html) | ✅ | ✅ | `int32` | ✅ | Adjustable encoding compression level and block size.
Decoding/encoding by [libFLAC](https://github.com/xiph/flac) via [goflac](https://git.gammaspectra.live/S.O.N.G/goflac).
If [goflac](https://git.gammaspectra.live/S.O.N.G/goflac) codec is disabled, [flacgo](https://git.gammaspectra.live/S.O.N.G/flacgo) decoder/encoder will be used. |
| **TTA** | [TTA](https://www.tausoft.org/en/true_audio_codec_format/) | ✅ | ✅ | `int32` | ✅ | Decoding/encoding via [S.O.N.G/go-tta](https://git.gammaspectra.live/S.O.N.G/go-tta). |
-| **MP3** | [MP3](http://mpgedit.org/mpgedit/mpeg_format/MP3Format.html) | ✅ | ❌ | `int16` | ✅ | Adjustable encoding bitrate and mode.
Decoding via [minimp3](https://github.com/kvark128/minimp3), encoding by [LAME](https://lame.sourceforge.io/) via [go-lame](https://github.com/viert/go-lame). |
+| **MP3** | [MP3](http://mpgedit.org/mpgedit/mpeg_format/MP3Format.html) | ✅ | ❌ | `int16` | ✅ | Adjustable encoding bitrate and mode.
Decoding via [S.O.N.G/minimp3](https://git.gammaspectra.live/S.O.N.G/minimp3), encoding by [LAME](https://lame.sourceforge.io/) via [go-lame](https://github.com/viert/go-lame). |
| **Opus** | [Ogg](https://www.xiph.org/ogg/doc/framing.html) | ✅ | ❌ | `float32` | ✅ | Adjustable encoding bitrate.
Decoding/encoding by [libopus](https://github.com/xiph/opus) via [go-pus](https://git.gammaspectra.live/S.O.N.G/go-pus).
Linked Ogg streams of different channel count are not supported. |
| **Vorbis** | [Ogg](https://www.xiph.org/ogg/doc/framing.html) | ✅ | ❌ | `float32` | ✅ | Decoding/encoding by [libvorbis](https://github.com/xiph/vorbis) via [go-vorbis](https://git.gammaspectra.live/S.O.N.G/go-vorbis).
If [go-vorbis](https://git.gammaspectra.live/S.O.N.G/go-vorbis) is disabled, [jfreymuth/vorbis](https://github.com/jfreymuth/vorbis) via [jfreymuth/oggvorbis](https://github.com/jfreymuth/oggvorbis) decoder will be used.
Linked Ogg streams of different channel count are not supported. |
| **AAC** | [ADTS](https://wiki.multimedia.cx/index.php/ADTS), ADIF*, MP4** | ✅ | ❌ | `int16` | ✅ | Adjustable encoding bitrate and mode (LC, HEv1, HEv2).
Decoding/encoding by [FDK-AAC](https://github.com/mstorsjo/fdk-aac) via [go-fdkaac](https://git.gammaspectra.live/S.O.N.G/go-fdkaac).
If [go-fdkaac](https://git.gammaspectra.live/S.O.N.G/go-fdkaac) codec is disabled, [VisualOn AAC encoder](https://github.com/gen2brain/aac-go) will be used for limited encoding support.
*ADIF only supported on encoding.
**MP4 only supported on encoding, and fragmented MP4 currently. |
@@ -248,10 +248,6 @@ Subdependencies that are not cgo-based are denoted in cursive.
| [S.O.N.G/minimp3](https://git.gammaspectra.live/S.O.N.G/minimp3) | Go | [MIT](https://git.gammaspectra.live/S.O.N.G/minimp3/src/branch/master/LICENSE.txt) | |
| [lieff/minimp3](https://github.com/lieff/minimp3) | C | [CC0 1.0](https://github.com/lieff/minimp3/blob/master/LICENSE) | Subdependency and included as part of _S.O.N.G/minimp3_. |
| [S.O.N.G/flacgo](https://git.gammaspectra.live/S.O.N.G/flacgo) | Go | [The Unlicense](https://git.gammaspectra.live/S.O.N.G/flacgoblob/master/LICENSE) | |
-| [oov/audio](https://github.com/oov/audio) | Go | [MIT](https://github.com/oov/audio/blob/master/LICENSE) | Used only for its audio resampler. |
-| _[go-audio/audio](https://github.com/go-audio/audio)_ | Go | [Apache 2.0](https://github.com/go-audio/audio/blob/master/LICENSE) | Subdependency of _S.O.N.G/flacgo_. Only used on tests there. |
-| _[go-audio/wav](https://github.com/go-audio/wav)_ | Go | [Apache 2.0](https://github.com/go-audio/wav/blob/master/LICENSE) | Subdependency of _S.O.N.G/flacgo_. Only used on tests there. |
-| _[go-audio/riff](https://github.com/go-audio/riff)_ | Go | [Apache 2.0](https://github.com/go-audio/riff/blob/master/LICENSE) | Subdependency of _go-audio/wav_. Only used on tests there. |
| _[icza/bitio](https://github.com/icza/bitio)_ | Go | [Apache 2.0](https://github.com/icza/bitio/blob/master/LICENSE-APACHE) or [LGPL v2.1](https://github.com/icza/bitio/blob/master/LICENSE-LGPL-v2.1) | Subdependency of _S.O.N.G/flacgo_. |
| [sssgun/mp3](https://github.com/sssgun/mp3) | Go | [MIT](https://github.com/sssgun/mp3/blob/master/LICENSE) | |
| [viert/go-lame](https://github.com/viert/go-lame) | Go | [MIT](https://github.com/viert/go-lame/blob/master/LICENSE) | |
diff --git a/audio/filter/internal/resampler/LICENSE b/audio/filter/internal/resampler/LICENSE
new file mode 100644
index 0000000..b47d78f
--- /dev/null
+++ b/audio/filter/internal/resampler/LICENSE
@@ -0,0 +1,20 @@
+The MIT License (MIT)
+
+Copyright (c) 2013 Masanobu YOSHIOKA
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
diff --git a/audio/filter/internal/resampler/README.md b/audio/filter/internal/resampler/README.md
new file mode 100644
index 0000000..3ecacb4
--- /dev/null
+++ b/audio/filter/internal/resampler/README.md
@@ -0,0 +1,3 @@
+# resampler_nocgo
+
+Taken from [github.com/oov/audio](https://github.com/oov/audio/tree/8e73d0e7f9fdaa878160a5134a2c6993dbee286d/resampler) implementation
\ No newline at end of file
diff --git a/audio/filter/internal/resampler/quality.go b/audio/filter/internal/resampler/quality.go
new file mode 100644
index 0000000..157c55e
--- /dev/null
+++ b/audio/filter/internal/resampler/quality.go
@@ -0,0 +1,189 @@
+// Copyright (C) 2007-2008 Jean-Marc Valin
+// Copyright (C) 2008 Thorvald Natvig
+// Copyright (C) 2013 Oov
+//
+// Arbitrary resampling code
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// 3. The name of the author may not be used to endorse or promote products
+// derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+package resampler
+
+type kaiserTable struct {
+ table []float64
+ oversample int
+}
+
+type quality struct {
+ baseLength int
+ oversample int
+ downsampleBandwidth float64
+ upsampleBandwidth float64
+ table *kaiserTable
+}
+
+var (
+ kaiser12 = kaiserTable{
+ table: []float64{
+ 0.99859849, 1.00000000, 0.99859849, 0.99440475, 0.98745105, 0.97779076,
+ 0.96549770, 0.95066529, 0.93340547, 0.91384741, 0.89213598, 0.86843014,
+ 0.84290116, 0.81573067, 0.78710866, 0.75723148, 0.72629970, 0.69451601,
+ 0.66208321, 0.62920216, 0.59606986, 0.56287762, 0.52980938, 0.49704014,
+ 0.46473455, 0.43304576, 0.40211431, 0.37206735, 0.34301800, 0.31506490,
+ 0.28829195, 0.26276832, 0.23854851, 0.21567274, 0.19416736, 0.17404546,
+ 0.15530766, 0.13794294, 0.12192957, 0.10723616, 0.09382272, 0.08164178,
+ 0.07063950, 0.06075685, 0.05193064, 0.04409466, 0.03718069, 0.03111947,
+ 0.02584161, 0.02127838, 0.01736250, 0.01402878, 0.01121463, 0.00886058,
+ 0.00691064, 0.00531256, 0.00401805, 0.00298291, 0.00216702, 0.00153438,
+ 0.00105297, 0.00069463, 0.00043489, 0.00025272, 0.00013031, 0.0000527734,
+ 0.00001000, 0.00000000},
+ oversample: 64,
+ }
+
+ kaiser10 = kaiserTable{
+ table: []float64{
+ 0.99537781, 1.00000000, 0.99537781, 0.98162644, 0.95908712, 0.92831446,
+ 0.89005583, 0.84522401, 0.79486424, 0.74011713, 0.68217934, 0.62226347,
+ 0.56155915, 0.50119680, 0.44221549, 0.38553619, 0.33194107, 0.28205962,
+ 0.23636152, 0.19515633, 0.15859932, 0.12670280, 0.09935205, 0.07632451,
+ 0.05731132, 0.04193980, 0.02979584, 0.02044510, 0.01345224, 0.00839739,
+ 0.00488951, 0.00257636, 0.00115101, 0.00035515, 0.00000000, 0.00000000},
+ oversample: 32,
+ }
+
+ kaiser8 = kaiserTable{
+ table: []float64{
+ 0.99635258, 1.00000000, 0.99635258, 0.98548012, 0.96759014, 0.94302200,
+ 0.91223751, 0.87580811, 0.83439927, 0.78875245, 0.73966538, 0.68797126,
+ 0.63451750, 0.58014482, 0.52566725, 0.47185369, 0.41941150, 0.36897272,
+ 0.32108304, 0.27619388, 0.23465776, 0.19672670, 0.16255380, 0.13219758,
+ 0.10562887, 0.08273982, 0.06335451, 0.04724088, 0.03412321, 0.02369490,
+ 0.01563093, 0.00959968, 0.00527363, 0.00233883, 0.00050000, 0.00000000},
+ oversample: 32,
+ }
+
+ kaiser6 = kaiserTable{
+ table: []float64{
+ 0.99733006, 1.00000000, 0.99733006, 0.98935595, 0.97618418, 0.95799003,
+ 0.93501423, 0.90755855, 0.87598009, 0.84068475, 0.80211977, 0.76076565,
+ 0.71712752, 0.67172623, 0.62508937, 0.57774224, 0.53019925, 0.48295561,
+ 0.43647969, 0.39120616, 0.34752997, 0.30580127, 0.26632152, 0.22934058,
+ 0.19505503, 0.16360756, 0.13508755, 0.10953262, 0.08693120, 0.06722600,
+ 0.05031820, 0.03607231, 0.02432151, 0.01487334, 0.00752000, 0.00000000},
+ oversample: 32,
+ }
+
+ qualityMap = []quality{
+ // Q0
+ quality{
+ baseLength: 8,
+ oversample: 4,
+ downsampleBandwidth: 0.830,
+ upsampleBandwidth: 0.860,
+ table: &kaiser6,
+ },
+ // Q1
+ quality{
+ baseLength: 16,
+ oversample: 4,
+ downsampleBandwidth: 0.850,
+ upsampleBandwidth: 0.880,
+ table: &kaiser6,
+ },
+ // Q2
+ quality{
+ baseLength: 32,
+ oversample: 4,
+ downsampleBandwidth: 0.882,
+ upsampleBandwidth: 0.910,
+ table: &kaiser6,
+ },
+ // Q3
+ quality{
+ baseLength: 48,
+ oversample: 8,
+ downsampleBandwidth: 0.895,
+ upsampleBandwidth: 0.917,
+ table: &kaiser8,
+ },
+ // Q4
+ quality{
+ baseLength: 64,
+ oversample: 8,
+ downsampleBandwidth: 0.921,
+ upsampleBandwidth: 0.940,
+ table: &kaiser8,
+ },
+ // Q5
+ quality{
+ baseLength: 80,
+ oversample: 16,
+ downsampleBandwidth: 0.922,
+ upsampleBandwidth: 0.940,
+ table: &kaiser10,
+ },
+ // Q6
+ quality{
+ baseLength: 96,
+ oversample: 16,
+ downsampleBandwidth: 0.940,
+ upsampleBandwidth: 0.945,
+ table: &kaiser10,
+ },
+ // Q7
+ quality{
+ baseLength: 128,
+ oversample: 16,
+ downsampleBandwidth: 0.950,
+ upsampleBandwidth: 0.950,
+ table: &kaiser10,
+ },
+ // Q8
+ quality{
+ baseLength: 160,
+ oversample: 16,
+ downsampleBandwidth: 0.960,
+ upsampleBandwidth: 0.960,
+ table: &kaiser10,
+ },
+ // Q9
+ quality{
+ baseLength: 192,
+ oversample: 32,
+ downsampleBandwidth: 0.968,
+ upsampleBandwidth: 0.968,
+ table: &kaiser12,
+ },
+ // Q10
+ quality{
+ baseLength: 256,
+ oversample: 32,
+ downsampleBandwidth: 0.975,
+ upsampleBandwidth: 0.975,
+ table: &kaiser12,
+ },
+ }
+)
diff --git a/audio/filter/internal/resampler/resampler.go b/audio/filter/internal/resampler/resampler.go
new file mode 100644
index 0000000..37c2b75
--- /dev/null
+++ b/audio/filter/internal/resampler/resampler.go
@@ -0,0 +1,544 @@
+// Copyright (C) 2007-2008 Jean-Marc Valin
+// Copyright (C) 2008 Thorvald Natvig
+// Copyright (C) 2013 Oov
+//
+// Arbitrary resampling code
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// 3. The name of the author may not be used to endorse or promote products
+// derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+// Package resampler implements audio resampler.
+//
+// This is a port of the Opus-tools( http://git.xiph.org/?p=opus-tools.git ) audio resampler to the pure Go.
+package resampler
+
+import (
+ "math"
+)
+
+const bufferSize = 160
+
+type channelState struct {
+ lastSample int
+ sampFracNum int
+ magicSamples int
+ mem []float64
+}
+
+type Resampler struct {
+ numRate int
+ denRate int
+
+ quality *quality
+ filtLen int
+ intAdvance int
+ fracAdvance int
+ cutoff float64
+ oversample int
+
+ initialised bool
+ started bool
+ skipZeros bool
+
+ channels []channelState
+ sincTable []float64
+ resampler func(channelIndex int, in []float64, out []float64) int
+}
+
+func Resample64(in []float64, inSampleRate int, out []float64, outSampleRate int, quality int) (read int, written int) {
+ return NewWithSkipZeros(1, inSampleRate, outSampleRate, quality).ProcessFloat64(0, in, out)
+}
+
+func Resample32(in []float32, inSampleRate int, out []float32, outSampleRate int, quality int) (read int, written int) {
+ return NewWithSkipZeros(1, inSampleRate, outSampleRate, quality).ProcessFloat32(0, in, out)
+}
+
+func New(channels int, inSampleRate, outSampleRate int, quality int) *Resampler {
+ if channels < 1 {
+ panic("you must have at least one channel")
+ }
+ r := &Resampler{
+ cutoff: 1.0,
+ channels: make([]channelState, channels),
+ }
+
+ r.setQuality(quality)
+ r.setSampleRate(inSampleRate, outSampleRate)
+ r.updateFilter()
+ r.initialised = true
+ return r
+}
+
+func NewWithSkipZeros(channels int, inSampleRate, outSampleRate int, quality int) *Resampler {
+ r := New(channels, inSampleRate, outSampleRate, quality)
+ r.skipZeros = true
+ return r
+}
+
+// cannot change quality on running in now implementation.
+func (r *Resampler) setQuality(q int) {
+ if q < 0 || q > 10 {
+ panic("invalid quality value")
+ }
+
+ if r.quality == &qualityMap[q] {
+ return
+ }
+ r.quality = &qualityMap[q]
+ if r.initialised {
+ r.updateFilter()
+ }
+}
+
+func imin(a, b int) int {
+ if a < b {
+ return a
+ } else {
+ return b
+ }
+}
+
+// cannot change sample on running in now implementation.
+func (r *Resampler) setSampleRate(in int, out int) {
+ ratioNum := in
+ ratioDen := out
+
+ // FIXME: This is terribly inefficient, but who cares (at least for now)?
+ for fact := 2; fact <= imin(ratioNum, ratioDen); fact++ {
+ for (ratioNum%fact == 0) && (ratioDen%fact == 0) {
+ ratioNum /= fact
+ ratioDen /= fact
+ }
+ }
+
+ if r.numRate == ratioNum && r.denRate == ratioDen {
+ return
+ }
+
+ if r.denRate > 0 {
+ for i := range r.channels {
+ ch := &r.channels[i]
+ ch.sampFracNum = ch.sampFracNum * ratioDen / r.denRate
+ // Safety net
+ if ch.sampFracNum >= ratioDen {
+ ch.sampFracNum = ratioDen - 1
+ }
+ }
+ }
+
+ r.numRate = ratioNum
+ r.denRate = ratioDen
+
+ if r.initialised {
+ r.updateFilter()
+ }
+}
+
+func (r *Resampler) ProcessFloat64(channelIndex int, in []float64, out []float64) (read int, written int) {
+ if r.skipZeros {
+ for i := range r.channels {
+ r.channels[i].lastSample = r.InputLatency()
+ }
+ r.skipZeros = false
+ }
+
+ ch := &r.channels[channelIndex]
+ x := ch.mem
+ filtOffs := r.filtLen - 1
+ iLen, oLen, xLen := len(in), len(out), len(x)-filtOffs
+ read, written = iLen, oLen
+
+ if ch.magicSamples != 0 {
+ oLen -= r.magic(channelIndex, out)
+ }
+
+ if ch.magicSamples == 0 {
+ for iLen != 0 && oLen != 0 {
+ ichunk, ochunk := imin(xLen, iLen), 0
+ if in != nil {
+ copy(x[filtOffs:], in[:ichunk])
+ } else {
+ for j := filtOffs; j < ichunk+filtOffs; j++ {
+ x[j] = 0
+ }
+ }
+ ichunk, ochunk = r.processNative(channelIndex, ichunk, out)
+ iLen -= ichunk
+ oLen -= ochunk
+ out = out[ochunk:]
+ if in != nil {
+ in = in[ichunk:]
+ }
+ }
+ }
+ read -= iLen
+ written -= oLen
+ return
+}
+
+func (r *Resampler) ProcessFloat32(channelIndex int, in []float32, out []float32) (read int, written int) {
+ const stackSize = 1024
+ var stack [stackSize]float64
+
+ if r.skipZeros {
+ for i := range r.channels {
+ r.channels[i].lastSample = r.InputLatency()
+ }
+ r.skipZeros = false
+ }
+
+ ch := &r.channels[channelIndex]
+ x := ch.mem
+ filtOffs := r.filtLen - 1
+ iLen, oLen := len(in), len(out)
+ xLen, yLen := len(x)-filtOffs, stackSize
+ read, written = iLen, oLen
+
+ if ch.magicSamples != 0 {
+ m := r.magic(channelIndex, stack[:imin(yLen, oLen)])
+ oLen -= m
+ for i, s := range stack[:m] {
+ out[i] = float32(s)
+ }
+ out = out[m:]
+ }
+
+ if ch.magicSamples == 0 {
+ for iLen != 0 && oLen != 0 {
+ ichunk, ochunk := imin(xLen, iLen), imin(yLen, oLen)
+ if in != nil {
+ for i, s := range in[:ichunk] {
+ x[filtOffs+i] = float64(s)
+ }
+ } else {
+ for j := filtOffs; j < ichunk+filtOffs; j++ {
+ x[j] = 0
+ }
+ }
+ ichunk, ochunk = r.processNative(channelIndex, ichunk, stack[:ochunk])
+ iLen -= ichunk
+ oLen -= ochunk
+ for i, s := range stack[:ochunk] {
+ out[i] = float32(s)
+ }
+ out = out[ochunk:]
+ if in != nil {
+ in = in[ichunk:]
+ }
+ }
+ }
+ read -= iLen
+ written -= oLen
+ return
+}
+
+func (r *Resampler) processNative(channelIndex int, inLen int, out []float64) (inLenRet int, outLenRet int) {
+ ch := &r.channels[channelIndex]
+ r.started = true
+
+ outLenRet = r.resampler(channelIndex, ch.mem[:inLen], out)
+ if ch.lastSample < inLen {
+ inLenRet = ch.lastSample
+ } else {
+ inLenRet = inLen
+ }
+ ch.lastSample -= inLenRet
+ copy(ch.mem, ch.mem[inLenRet:inLenRet+r.filtLen-1])
+ return
+}
+
+func (r *Resampler) magic(channelIndex int, out []float64) (outWritten int) {
+ ch := &r.channels[channelIndex]
+ n := r.filtLen - 1
+
+ inLen, outLen := r.processNative(channelIndex, ch.magicSamples, out)
+
+ ch.magicSamples -= inLen
+
+ // If we couldn't process all "magic" input samples, save the rest for next time
+ if ch.magicSamples != 0 {
+ copy(ch.mem[n:n+ch.magicSamples], ch.mem[n+inLen:])
+ }
+ return outLen
+}
+
+func computeFunc(x float64, windowFunc *kaiserTable) float64 {
+ y := x * float64(windowFunc.oversample)
+ ind := int(math.Floor(y))
+ frac := y - float64(ind)
+ fracx2 := frac * frac
+ fracx3 := fracx2 * frac
+ fracx2mul0_5 := 0.5 * fracx2
+ fracx3mul0_16 := 0.1666666667 * fracx3
+ // Compute interpolation coefficients. I'm not sure whether this corresponds to cubic interpolation but I know it's MMSE-optimal on a sinc
+ i3 := -0.1666666667*frac + fracx3mul0_16
+ i2 := frac + fracx2mul0_5 - 0.5*fracx3
+ // interp[2] = 1.f - 0.5f*frac - frac*frac + 0.5f*frac*frac*frac;
+ i0 := -0.3333333333*frac + fracx2mul0_5 - fracx3mul0_16
+ // Just to make sure we don't have rounding problems
+ i1 := 1.0 - i3 - i2 - i0
+ return i0*windowFunc.table[ind] + i1*windowFunc.table[ind+1] + i2*windowFunc.table[ind+2] + i3*windowFunc.table[ind+3]
+}
+
+// The slow way of computing a sinc for the table. Should improve that some day
+func sinc(cutoff float64, x float64, n float64, windowFunc *kaiserTable) float64 {
+ xabs := math.Abs(x)
+ if xabs < 1e-6 {
+ return cutoff
+ } else if xabs > 0.5*n {
+ return 0
+ }
+ // FIXME: Can it really be any slower than this?
+ xx := x * cutoff * math.Pi
+ return cutoff * math.Sin(xx) / xx * computeFunc(2.0*xabs/n, windowFunc)
+}
+
+func (r *Resampler) resamplerBasicDirect(channelIndex int, in []float64, out []float64) int {
+ ch := &r.channels[channelIndex]
+ n := r.filtLen
+ outSample := 0
+ lastSample := ch.lastSample
+ sampFracNum := ch.sampFracNum
+ sincTable := r.sincTable
+ intAdvance := r.intAdvance
+ fracAdvance := r.fracAdvance
+ denRate := r.denRate
+
+ for lastSample < len(in) && outSample < len(out) {
+ sinct := sincTable[sampFracNum*n : sampFracNum*n+n]
+ var sum float64
+ for j, s := range in[lastSample : lastSample+n] {
+ sum += sinct[j] * s
+ }
+
+ out[outSample] = sum
+ outSample++
+ lastSample += intAdvance
+ sampFracNum += fracAdvance
+ if sampFracNum >= denRate {
+ sampFracNum -= denRate
+ lastSample++
+ }
+ }
+ ch.lastSample = lastSample
+ ch.sampFracNum = sampFracNum
+ return outSample
+}
+
+func cubicCoef(frac float64) (float64, float64, float64, float64) {
+ fracx2 := frac * frac
+ fracx3 := fracx2 * frac
+ fracx2mul0_5 := 0.5 * fracx2
+ fracx3mul0_16 := 0.1666666667 * fracx3
+ // Compute interpolation coefficients. I'm not sure whether this corresponds to cubic interpolation but I know it's MMSE-optimal on a sinc
+ i0 := -0.1666666667*frac + fracx3mul0_16
+ i1 := frac + fracx2mul0_5 - 0.5*fracx3
+ // interp[2] = 1.f - 0.5f*frac - frac*frac + 0.5f*frac*frac*frac;
+ i3 := -0.3333333333*frac + fracx2mul0_5 - fracx3mul0_16
+ // Just to make sure we don't have rounding problems
+ i2 := 1.0 - i0 - i1 - i3
+ return i0, i1, i2, i3
+}
+
+func (r *Resampler) resamplerBasicInterpolate(channelIndex int, in []float64, out []float64) int {
+ ch := &r.channels[channelIndex]
+ n := r.filtLen
+ outSample := 0
+ lastSample := ch.lastSample
+ sampFracNum := ch.sampFracNum
+ intAdvance := r.intAdvance
+ fracAdvance := r.fracAdvance
+ denRate := r.denRate
+
+ for lastSample < len(in) && outSample < len(out) {
+ offset := sampFracNum * r.oversample / r.denRate
+ frac := float64((sampFracNum*r.oversample)%r.denRate) / float64(r.denRate)
+ var accum0, accum1, accum2, accum3 float64
+ for j, s := range in[lastSample : lastSample+n] {
+ t := 4 + (j+1)*r.oversample - offset
+ accum0 += s * r.sincTable[t-2]
+ accum1 += s * r.sincTable[t-1]
+ accum2 += s * r.sincTable[t]
+ accum3 += s * r.sincTable[t+1]
+ }
+ i0, i1, i2, i3 := cubicCoef(frac)
+ out[outSample] = i0*accum0 + i1*accum1 + i2*accum2 + i3*accum3
+ outSample++
+ lastSample += intAdvance
+ sampFracNum += fracAdvance
+ if sampFracNum >= denRate {
+ sampFracNum -= denRate
+ lastSample++
+ }
+ }
+ ch.lastSample = lastSample
+ ch.sampFracNum = sampFracNum
+ return outSample
+}
+
+func (r *Resampler) updateFilter() {
+ oldLength := r.filtLen
+ r.oversample = r.quality.oversample
+ r.filtLen = r.quality.baseLength
+
+ if r.numRate > r.denRate {
+ // down-sampling
+ r.cutoff = r.quality.downsampleBandwidth * float64(r.denRate) / float64(r.numRate)
+ // FIXME: divide the numerator and denominator by a certain amount if they're too large
+ r.filtLen = r.filtLen * r.numRate / r.denRate
+ // Round up to make sure we have a multiple of 8
+ r.filtLen = ((r.filtLen - 1) & (^int(0x7))) + 8
+ if r.denRate<<1 < r.numRate {
+ r.oversample >>= 1
+ }
+ if r.denRate<<2 < r.numRate {
+ r.oversample >>= 1
+ }
+ if r.denRate<<3 < r.numRate {
+ r.oversample >>= 1
+ }
+ if r.denRate<<4 < r.numRate {
+ r.oversample >>= 1
+ }
+ if r.oversample < 1 {
+ r.oversample = 1
+ }
+ } else {
+ // up-sampling
+ r.cutoff = r.quality.upsampleBandwidth
+ }
+
+ // Choose the resampling type that requires the least amount of memory
+ if r.denRate <= 16*(r.oversample+8) {
+ if r.sincTable == nil || len(r.sincTable) < r.filtLen*r.denRate {
+ r.sincTable = make([]float64, r.filtLen*r.denRate)
+ }
+ for i := 0; i < r.denRate; i++ {
+ for j := 0; j < r.filtLen; j++ {
+ r.sincTable[i*r.filtLen+j] = sinc(
+ r.cutoff,
+ float64(j-(r.filtLen>>1)+1)-float64(i)/float64(r.denRate),
+ float64(r.filtLen),
+ r.quality.table,
+ )
+ }
+ }
+ r.resampler = r.resamplerBasicDirect
+ } else {
+ if r.sincTable == nil || len(r.sincTable) < r.filtLen*r.oversample+8 {
+ r.sincTable = make([]float64, r.filtLen*r.oversample+8)
+ }
+ for i := -4; i < r.oversample*r.filtLen+4; i++ {
+ r.sincTable[i+4] = sinc(
+ r.cutoff,
+ float64(i)/float64(r.oversample)-float64(r.filtLen>>1),
+ float64(r.filtLen),
+ r.quality.table,
+ )
+ }
+ r.resampler = r.resamplerBasicInterpolate
+ }
+
+ r.intAdvance = r.numRate / r.denRate
+ r.fracAdvance = r.numRate % r.denRate
+
+ // Here's the place where we update the filter memory to take into account
+ // the change in filter length. It's probably the messiest part of the code
+ // due to handling of lots of corner cases.
+ switch {
+ case r.channels[0].mem == nil || !r.started:
+ size := r.filtLen - 1 + bufferSize
+ for i := range r.channels {
+ r.channels[i].mem = make([]float64, size)
+ }
+ case r.filtLen > oldLength:
+ panic("not implemented")
+ // Increase the filter length
+ // oldAllocSize := len(r.mem) / len(r.channels)
+ // if r.filtLen-1+r.bufferSize > oldAllocSize {
+ // m := make([]float64, (r.filtLen-1+r.bufferSize)*len(r.channels))
+ // copy(m, r.mem)
+ // r.mem = m
+ // }
+ for i := len(r.channels) - 1; i >= 0; i-- {
+ // spx_int32_t j;
+ // spx_uint32_t olen = old_length;
+ // /*if (st->magic_samples[i])*/
+ // {
+ // /* Try and remove the magic samples as if nothing had happened */
+ //
+ // /* FIXME: This is wrong but for now we need it to avoid going over the array bounds */
+ // olen = old_length + 2*st->magic_samples[i];
+ // for (j=old_length-2+st->magic_samples[i];j>=0;j--)
+ // st->mem[i*st->mem_alloc_size+j+st->magic_samples[i]] = st->mem[i*old_alloc_size+j];
+ // for (j=0;jmagic_samples[i];j++)
+ // st->mem[i*st->mem_alloc_size+j] = 0;
+ // st->magic_samples[i] = 0;
+ // }
+ // if (st->filt_len > olen)
+ // {
+ // /* If the new filter length is still bigger than the "augmented" length */
+ // /* Copy data going backward */
+ // for (j=0;jmem[i*st->mem_alloc_size+(st->filt_len-2-j)] = st->mem[i*st->mem_alloc_size+(olen-2-j)];
+ // /* Then put zeros for lack of anything better */
+ // for (;jfilt_len-1;j++)
+ // st->mem[i*st->mem_alloc_size+(st->filt_len-2-j)] = 0;
+ // /* Adjust last_sample */
+ // st->last_sample[i] += (st->filt_len - olen)/2;
+ // } else {
+ // /* Put back some of the magic! */
+ // st->magic_samples[i] = (olen - st->filt_len)/2;
+ // for (j=0;jfilt_len-1+st->magic_samples[i];j++)
+ // st->mem[i*st->mem_alloc_size+j] = st->mem[i*st->mem_alloc_size+j+st->magic_samples[i]];
+ // }
+ }
+ case r.filtLen < oldLength:
+ panic("not implemented")
+ // spx_uint32_t i;
+ // /* Reduce filter length, this a bit tricky. We need to store some of the memory as "magic"
+ // samples so they can be used directly as input the next time(s) */
+ // for (i=0;inb_channels;i++)
+ // {
+ // spx_uint32_t j;
+ // spx_uint32_t old_magic = st->magic_samples[i];
+ // st->magic_samples[i] = (old_length - st->filt_len)/2;
+ // /* We must copy some of the memory that's no longer used */
+ // /* Copy data going backward */
+ // for (j=0;jfilt_len-1+st->magic_samples[i]+old_magic;j++)
+ // st->mem[i*st->mem_alloc_size+j] = st->mem[i*st->mem_alloc_size+j+st->magic_samples[i]];
+ // st->magic_samples[i] += old_magic;
+ // }
+ }
+}
+
+func (r *Resampler) InputLatency() int {
+ return r.filtLen >> 1
+}
+
+func (r *Resampler) OutputLatency() int {
+ return ((r.filtLen>>1)*r.denRate + (r.numRate >> 1)) / r.numRate
+}
diff --git a/audio/filter/internal/resampler/resampler_test.go b/audio/filter/internal/resampler/resampler_test.go
new file mode 100644
index 0000000..39e5290
--- /dev/null
+++ b/audio/filter/internal/resampler/resampler_test.go
@@ -0,0 +1,182 @@
+package resampler
+
+import (
+ "fmt"
+ "math"
+ "testing"
+)
+
+var (
+ wave = []float64{
+ //-1 to 1 (16 samples)
+ -1, -0.75, -0.5, -0.25, 0, 0.25, 0.5, 0.75, 1, 1, 1, 1, 1, 1, 1, 1,
+ //1 to -1 (16 samples)
+ 1, 0.75, 0.5, 0.25, 0, -0.25, -0.5, -0.75, -1, -1, -1, -1, -1, -1, -1, -1,
+ }
+)
+
+func resample64(t *testing.T, inSamplerate, outSamplerate, q int) []float64 {
+ r := New(1, inSamplerate, outSamplerate, q)
+ t.Log("samplerate:", "in:", inSamplerate, "out:", outSamplerate)
+ t.Log("latency:", "in:", r.InputLatency(), "out:", r.OutputLatency())
+
+ in := make([]float64, inSamplerate/100)
+ out := make([]float64, outSamplerate/100)
+
+ copy(in, wave)
+ read, written := r.ProcessFloat64(0, in, out)
+ t.Log("read:", read)
+ t.Log("written:", written)
+
+ s := ""
+ out = out[r.OutputLatency():][:(32*outSamplerate)/inSamplerate]
+ for _, v := range out {
+ s += fmt.Sprintf("%0.3f ", v)
+ }
+ t.Log("wave:", s)
+ return out
+}
+
+func resample32(t *testing.T, inSamplerate, outSamplerate, q int) []float32 {
+ r := New(1, inSamplerate, outSamplerate, q)
+ t.Log("samplerate:", "in:", inSamplerate, "out:", outSamplerate)
+ t.Log("latency:", "in:", r.InputLatency(), "out:", r.OutputLatency())
+
+ in := make([]float32, inSamplerate/100)
+ out := make([]float32, outSamplerate/100)
+ for i, s := range wave {
+ in[i] = float32(s)
+ }
+
+ read, written := r.ProcessFloat32(0, in, out)
+ t.Log("read:", read)
+ t.Log("written:", written)
+
+ s := ""
+ out = out[r.OutputLatency():][:(32*outSamplerate)/inSamplerate]
+ for _, v := range out {
+ s += fmt.Sprintf("%0.3f ", v)
+ }
+ t.Log("wave:", s)
+ return out
+}
+
+func TestSame64(t *testing.T) {
+ for q := 0; q < 11; q++ {
+ out := resample64(t, 48000, 48000, q)
+ if math.Abs(out[8]-1.0) > 0.1 {
+ t.Fail()
+ }
+ if math.Abs(out[24] - -1.0) > 0.1 {
+ t.Fail()
+ }
+ }
+}
+
+func TestSame32(t *testing.T) {
+ for q := 0; q < 11; q++ {
+ out := resample32(t, 48000, 48000, q)
+ if math.Abs(float64(out[8])-1.0) > 0.1 {
+ t.Fail()
+ }
+ if math.Abs(float64(out[24]) - -1.0) > 0.1 {
+ t.Fail()
+ }
+ }
+}
+
+func TestDirectDownSampling64(t *testing.T) {
+ for q := 0; q < 11; q++ {
+ out := resample64(t, 48000, 24000, q)
+ if math.Abs(out[4]-1.0) > 0.1 {
+ t.Fail()
+ }
+ if math.Abs(out[12] - -1.0) > 0.1 {
+ t.Fail()
+ }
+ }
+}
+
+func TestDirectDownSampling32(t *testing.T) {
+ for q := 0; q < 11; q++ {
+ out := resample32(t, 48000, 24000, q)
+ if math.Abs(float64(out[4])-1.0) > 0.1 {
+ t.Fail()
+ }
+ if math.Abs(float64(out[12]) - -1.0) > 0.1 {
+ t.Fail()
+ }
+ }
+}
+
+func TestDirectUpSampling64(t *testing.T) {
+ for q := 0; q < 11; q++ {
+ out := resample64(t, 24000, 48000, q)
+ if math.Abs(out[16]-1.0) > 0.1 {
+ t.Fail()
+ }
+ if math.Abs(out[48] - -1.0) > 0.1 {
+ t.Fail()
+ }
+ }
+}
+
+func TestDirectUpSampling32(t *testing.T) {
+ for q := 0; q < 11; q++ {
+ out := resample32(t, 24000, 48000, q)
+ if math.Abs(float64(out[16])-1.0) > 0.1 {
+ t.Fail()
+ }
+ if math.Abs(float64(out[48]) - -1.0) > 0.1 {
+ t.Fail()
+ }
+ }
+}
+
+func TestInterpolateDownSampling64(t *testing.T) {
+ for q := 0; q < 11; q++ {
+ out := resample64(t, 48000, 23999, q)
+ if math.Abs(out[4]-1.0) > 0.1 {
+ t.Fail()
+ }
+ if math.Abs(out[12] - -1.0) > 0.1 {
+ t.Fail()
+ }
+ }
+}
+
+func TestInterpolateDownSampling32(t *testing.T) {
+ for q := 0; q < 11; q++ {
+ out := resample32(t, 48000, 23999, q)
+ if math.Abs(float64(out[4])-1.0) > 0.1 {
+ t.Fail()
+ }
+ if math.Abs(float64(out[12]) - -1.0) > 0.1 {
+ t.Fail()
+ }
+ }
+}
+
+func TestInterpolateUpSampling64(t *testing.T) {
+ for q := 0; q < 11; q++ {
+ out := resample64(t, 23999, 48000, q)
+ if math.Abs(out[16]-1.0) > 0.1 {
+ t.Fail()
+ }
+ if math.Abs(out[48] - -1.0) > 0.1 {
+ t.Fail()
+ }
+ }
+}
+
+func TestInterpolateUpSampling32(t *testing.T) {
+ for q := 0; q < 11; q++ {
+ out := resample32(t, 23999, 48000, q)
+ if math.Abs(float64(out[16])-1.0) > 0.1 {
+ t.Fail()
+ }
+ if math.Abs(float64(out[48]) - -1.0) > 0.1 {
+ t.Fail()
+ }
+ }
+}
diff --git a/audio/filter/resample_filter_nocgo.go b/audio/filter/resample_filter_nocgo.go
index 4f1cea1..6d199e5 100644
--- a/audio/filter/resample_filter_nocgo.go
+++ b/audio/filter/resample_filter_nocgo.go
@@ -4,7 +4,7 @@ package filter
import (
"git.gammaspectra.live/S.O.N.G/Kirika/audio"
- "github.com/oov/audio/resampler"
+ "git.gammaspectra.live/S.O.N.G/Kirika/audio/filter/internal/resampler"
"log"
)
diff --git a/go.mod b/go.mod
index 9d2a128..dd44f7a 100644
--- a/go.mod
+++ b/go.mod
@@ -3,7 +3,7 @@ module git.gammaspectra.live/S.O.N.G/Kirika
go 1.19
require (
- git.gammaspectra.live/S.O.N.G/flacgo v0.0.0-20220726151057-28f458bc5391
+ git.gammaspectra.live/S.O.N.G/flacgo v0.0.0-20221130070259-2ac188033aab
git.gammaspectra.live/S.O.N.G/go-alac v0.0.0-20220421115623-d0b3bfe57e0f
git.gammaspectra.live/S.O.N.G/go-ebur128 v0.0.0-20220720163421-db0c1911921d
git.gammaspectra.live/S.O.N.G/go-fdkaac v0.0.0-20220910135048-823922bd661a
@@ -19,17 +19,16 @@ require (
github.com/icza/bitio v1.1.0
github.com/jfreymuth/oggvorbis v1.0.4
github.com/minio/sha256-simd v1.0.0
- github.com/oov/audio v0.0.0-20171004131523-88a2be6dbe38
github.com/sssgun/mp3 v0.0.0-20170810093403-85f2ec632081
github.com/viert/go-lame v0.0.0-20201108052322-bb552596b11d
- golang.org/x/exp v0.0.0-20221106115401-f9659909a136
+ golang.org/x/exp v0.0.0-20221126150942-6ab00d035af9
)
require (
github.com/jfreymuth/vorbis v1.0.2 // indirect
github.com/klauspost/cpuid v1.3.1 // indirect
- github.com/klauspost/cpuid/v2 v2.1.2 // indirect
+ github.com/klauspost/cpuid/v2 v2.2.1 // indirect
github.com/mewkiz/pkg v0.0.0-20220820102221-bbbca16e2a6c // indirect
github.com/youpy/go-wav v0.3.2 // indirect
- golang.org/x/sys v0.1.0 // indirect
+ golang.org/x/sys v0.2.0 // indirect
)
diff --git a/go.sum b/go.sum
index 88878db..119f40a 100644
--- a/go.sum
+++ b/go.sum
@@ -1,5 +1,5 @@
-git.gammaspectra.live/S.O.N.G/flacgo v0.0.0-20220726151057-28f458bc5391 h1:us3yKKsnMe0FZVHRSFZCw113ddiNrZgKf5M5PNr3SQ4=
-git.gammaspectra.live/S.O.N.G/flacgo v0.0.0-20220726151057-28f458bc5391/go.mod h1:ZVHB/7Vrs9xxK1j98+SJ5TRYBc7Q9dIUaNJHEmysZcI=
+git.gammaspectra.live/S.O.N.G/flacgo v0.0.0-20221130070259-2ac188033aab h1:1jKDhlDQK6hzR6KulALSYZTPgiCthL8UqufYXCRSuRo=
+git.gammaspectra.live/S.O.N.G/flacgo v0.0.0-20221130070259-2ac188033aab/go.mod h1:sYjxqsHurCJUzQQiSUNZbjW8st2fiD3kLxO08gBqmW8=
git.gammaspectra.live/S.O.N.G/go-alac v0.0.0-20220421115623-d0b3bfe57e0f h1:CxN7zlk5FdAieyRKQSbwBGBsvQ2cDF8JVCODZpzcRkA=
git.gammaspectra.live/S.O.N.G/go-alac v0.0.0-20220421115623-d0b3bfe57e0f/go.mod h1:f1+h7KOnuM9zcEQp7ri4UaVvgX4m1NFFIXgReIyjGMA=
git.gammaspectra.live/S.O.N.G/go-ebur128 v0.0.0-20220720163421-db0c1911921d h1:3M0GZgm2H1tBkDxCwvQdRvnYhF0/velekO9uhVZsbP0=
@@ -43,14 +43,12 @@ github.com/jszwec/csvutil v1.5.1/go.mod h1:Rpu7Uu9giO9subDyMCIQfHVDuLrcaC36UA4Yc
github.com/klauspost/cpuid v1.3.1 h1:5JNjFYYQrZeKRJ0734q51WCEEn2huer72Dc7K+R/b6s=
github.com/klauspost/cpuid v1.3.1/go.mod h1:bYW4mA6ZgKPob1/Dlai2LviZJO7KGI3uoWLd42rAQw4=
github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
-github.com/klauspost/cpuid/v2 v2.1.2 h1:XhdX4fqAJUA0yj+kUwMavO0hHrSPAecYdYf1ZmxHvak=
-github.com/klauspost/cpuid/v2 v2.1.2/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
+github.com/klauspost/cpuid/v2 v2.2.1 h1:U33DW0aiEj633gHYw3LoDNfkDiYnE5Q8M/TKJn2f2jI=
+github.com/klauspost/cpuid/v2 v2.2.1/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
github.com/mewkiz/pkg v0.0.0-20220820102221-bbbca16e2a6c h1:6AzCfQNCql3Of8ee1JY6dufssFnBWJYuCVrGcES84AA=
github.com/mewkiz/pkg v0.0.0-20220820102221-bbbca16e2a6c/go.mod h1:J/rDzvIiwiVpv72OEP8aJFxLXjGpUdviIIeqJPLIctA=
github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g=
github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM=
-github.com/oov/audio v0.0.0-20171004131523-88a2be6dbe38 h1:4Upfs5rLQdx7KwBct3bmPYAhWsDDJdx660gYb7Lv9TQ=
-github.com/oov/audio v0.0.0-20171004131523-88a2be6dbe38/go.mod h1:Xj06yMta9R1RSKiHmxL0Bo2TB8wiKVnMgA0KVopHHkk=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
@@ -66,14 +64,14 @@ github.com/youpy/go-wav v0.3.2 h1:NLM8L/7yZ0Bntadw/0h95OyUsen+DQIVf9gay+SUsMU=
github.com/youpy/go-wav v0.3.2/go.mod h1:0FCieAXAeSdcxFfwLpRuEo0PFmAoc+8NU34h7TUvk50=
github.com/zaf/g711 v0.0.0-20190814101024-76a4a538f52b h1:QqixIpc5WFIqTLxB3Hq8qs0qImAgBdq0p6rq2Qdl634=
github.com/zaf/g711 v0.0.0-20190814101024-76a4a538f52b/go.mod h1:T2h1zV50R/q0CVYnsQOQ6L7P4a2ZxH47ixWcMXFGyx8=
-golang.org/x/exp v0.0.0-20221106115401-f9659909a136 h1:Fq7F/w7MAa1KJ5bt2aJ62ihqp9HDcRuyILskkpIAurw=
-golang.org/x/exp v0.0.0-20221106115401-f9659909a136/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
+golang.org/x/exp v0.0.0-20221126150942-6ab00d035af9 h1:yZNXmy+j/JpX19vZkVktWqAo7Gny4PBWYYK3zskGpx4=
+golang.org/x/exp v0.0.0-20221126150942-6ab00d035af9/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
golang.org/x/image v0.0.0-20190220214146-31aff87c08e9/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220712014510-0a85c31ab51e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
-golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A=
+golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=