// Copyright 2020 Justine Alexandra Roberts Tunney // // 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. // Additive White Gaussian Noise Generator // // This is based off the SpanDSP implementation, which is based off some // research paper. package dsp import ( "math" ) const ( m1 = 259200 ia1 = 7141 ic1 = 54773 rm1 = 1.0 / m1 m2 = 134456 ia2 = 8121 ic2 = 28411 rm2 = 1.0 / m2 m3 = 243000 ia3 = 4561 ic3 = 51349 dbm0MaxPower = 3.14 + 3.02 ) // Awgn contains the white noise generator state information. type AWGN struct { rms float64 ix1 int64 ix2 int64 ix3 int64 r [98]float64 gset float64 iset int } // Returns a new comfort noise generator. The volume param is in decibels and // -50.0 is a good value. The closer you get to 0.0, the louder the noise will // become. func NewAWGN(volume float64) (s *AWGN) { return NewAWGN_DBM0(7162534, volume) } func NewAWGN_DBM0(idum int64, level float64) (s *AWGN) { return NewAWGN_DBOV(idum, level-dbm0MaxPower) } func NewAWGN_DBOV(idum int64, level float64) (s *AWGN) { s = new(AWGN) if idum < 0.0 { idum = -idum } s.rms = math.Pow(10.0, level/20.0) * 32768.0 s.ix1 = (ic1 + idum) % m1 s.ix1 = (ia1*s.ix1 + ic1) % m1 s.ix2 = s.ix1 % m2 s.ix1 = (ia1*s.ix1 + ic1) % m1 s.ix3 = s.ix1 % m3 s.r[0] = 0.0 for j := 1; j <= 97; j++ { s.ix1 = (ia1*s.ix1 + ic1) % m1 s.ix2 = (ia2*s.ix2 + ic2) % m2 s.r[j] = (float64(s.ix1) + float64(s.ix2)*rm2) * rm1 } s.gset = 0.0 s.iset = 0 return } func (s *AWGN) Get() int16 { var fac, r, v1, v2, amp float64 if s.iset == 0 { for { v1 = 2.0*s.ran1() - 1.0 v2 = 2.0*s.ran1() - 1.0 r = v1*v1 + v2*v2 if r < 1.0 { break } } fac = math.Sqrt(-2.0 * math.Log(r) / r) s.gset = v1 * fac s.iset = 1 amp = v2 * fac * s.rms } else { s.iset = 0 amp = s.gset * s.rms } return fsaturate(amp) } func (s *AWGN) ran1() (res float64) { var j int64 s.ix1 = (ia1*s.ix1 + ic1) % m1 s.ix2 = (ia2*s.ix2 + ic2) % m2 s.ix3 = (ia3*s.ix3 + ic3) % m3 j = 1 + ((97 * s.ix3) / m3) if j > 97 || j < 1 { return -1 } res = s.r[j] s.r[j] = (float64(s.ix1) + float64(s.ix2)*rm2) * rm1 return } // TODO(jart): How does FISTP work in Plan9 assembly? func fsaturate(damp float64) int16 { if damp > float64(math.MaxInt16) { return math.MaxInt16 } if damp < float64(math.MinInt16) { return math.MinInt16 } return int16(damp) // return int16(lrint(damp)) }