Reduce dependencies, vendor resampler part of github.com/oov/audio
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
f56867ca73
commit
6fde234fe6
|
@ -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.<br/>Decoding/encoding by [libFLAC](https://github.com/xiph/flac) via [goflac](https://git.gammaspectra.live/S.O.N.G/goflac).<br/>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.<br/>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.<br/>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.<br/>Decoding/encoding by [libopus](https://github.com/xiph/opus) via [go-pus](https://git.gammaspectra.live/S.O.N.G/go-pus).<br/>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).<br/>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.<br/>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).<br/>Decoding/encoding by [FDK-AAC](https://github.com/mstorsjo/fdk-aac) via [go-fdkaac](https://git.gammaspectra.live/S.O.N.G/go-fdkaac).<br/>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.<br/>*ADIF only supported on encoding.<br/>**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) | |
|
||||
|
|
20
audio/filter/internal/resampler/LICENSE
Normal file
20
audio/filter/internal/resampler/LICENSE
Normal file
|
@ -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.
|
3
audio/filter/internal/resampler/README.md
Normal file
3
audio/filter/internal/resampler/README.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
# resampler_nocgo
|
||||
|
||||
Taken from [github.com/oov/audio](https://github.com/oov/audio/tree/8e73d0e7f9fdaa878160a5134a2c6993dbee286d/resampler) implementation
|
189
audio/filter/internal/resampler/quality.go
Normal file
189
audio/filter/internal/resampler/quality.go
Normal file
|
@ -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,
|
||||
},
|
||||
}
|
||||
)
|
544
audio/filter/internal/resampler/resampler.go
Normal file
544
audio/filter/internal/resampler/resampler.go
Normal file
|
@ -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;j<st->magic_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;j<olen-1;j++)
|
||||
// st->mem[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 (;j<st->filt_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;j<st->filt_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;i<st->nb_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;j<st->filt_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
|
||||
}
|
182
audio/filter/internal/resampler/resampler_test.go
Normal file
182
audio/filter/internal/resampler/resampler_test.go
Normal file
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
)
|
||||
|
||||
|
|
9
go.mod
9
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
|
||||
)
|
||||
|
|
18
go.sum
18
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=
|
||||
|
|
Loading…
Reference in a new issue