///|
let pghi_buckets : Int = 256
///|
priv struct Pghi {
frames : Int
bins : Int
win : Int
hop : Int
gamma : Double
tol_hi : Double
tol_lo : Double
arena : FixedArray[Double]
off_mag : Int
off_phase : Int
}
///|
let pghi_jobs : FixedArray[Pghi?] = FixedArray::make(max_jobs, None)
///|
fn pghi_at(h : Int) -> Pghi? {
let j = job_seg(h)
if j < 0 {
None
} else {
pghi_jobs.unsafe_get(j)
}
}
///|
priv struct Pg {
frames : Int
bins : Int
n : Int
win : Double
hop : Double
top : Double
tol_hi : Double
tol_lo : Double
c_f : Double
c_t : Double
off_mag : Int
off_phase : Int
arena : FixedArray[Double]
fgrad : FixedArray[Float]
tgrad : FixedArray[Float]
done : FixedArray[Bool]
queued : FixedArray[Bool]
order : FixedArray[Int]
h_idx : FixedArray[Int]
h_key : FixedArray[Double]
mut h_size : Int
}
///|
#export_name("dsp_pghi_open")
pub fn pghi_open(
frames : Int,
bins : Int,
win : Int,
hop : Int,
gamma : Double,
tol_hi : Double,
tol_lo : Double,
) -> Int {
if frames < 1 || bins < 2 || win < 2 || hop < 1 {
return 0
}
let fb = frames * bins
let h = job_open(2 * fb, 0)
if h == 0 {
return 0
}
let job : Pghi = {
frames,
bins,
win,
hop,
gamma,
tol_hi,
tol_lo,
arena: job_d(h),
off_mag: 0,
off_phase: fb,
}
pghi_jobs.unsafe_set(h - 1, Some(job))
h
}
///|
#export_name("dsp_pghi_off")
pub fn pghi_off(h : Int, which : Int) -> Int {
match pghi_at(h) {
None => -1
Some(job) =>
if which == 0 {
job.off_mag
} else if which == 1 {
job.off_phase
} else {
-1
}
}
}
///|
#export_name("dsp_pghi_close")
pub fn pghi_close(h : Int) -> Unit {
let j = job_seg(h)
if j < 0 {
return
}
pghi_jobs.unsafe_set(j, None)
job_close(h)
}
///|
fn pghi_wrap(a : Double) -> Double {
let two_pi = 2.0 * @math.PI
let mut v = (a + @math.PI) % two_pi
if v < 0.0 {
v = v + two_pi
}
v - @math.PI
}
///|
fn pg_push(pg : Pg, value : Int, key : Double) -> Unit {
let c0 = pg.h_size
pg.h_size = c0 + 1
let mut c = c0
while c > 0 {
let p = (c - 1) >> 1
if pg.h_key.unsafe_get(p) >= key {
break
}
pg.h_idx.unsafe_set(c, pg.h_idx.unsafe_get(p))
pg.h_key.unsafe_set(c, pg.h_key.unsafe_get(p))
c = p
}
pg.h_idx.unsafe_set(c, value)
pg.h_key.unsafe_set(c, key)
}
///|
fn pg_pop(pg : Pg) -> Int {
let top = pg.h_idx.unsafe_get(0)
let last = pg.h_size - 1
pg.h_size = last
if last > 0 {
let value = pg.h_idx.unsafe_get(last)
let key = pg.h_key.unsafe_get(last)
let mut c = 0
while true {
let ch = 2 * c + 1
if ch >= last {
break
}
let mut best = ch
if ch + 1 < last && pg.h_key.unsafe_get(ch + 1) > pg.h_key.unsafe_get(ch) {
best = ch + 1
}
if pg.h_key.unsafe_get(best) <= key {
break
}
pg.h_idx.unsafe_set(c, pg.h_idx.unsafe_get(best))
pg.h_key.unsafe_set(c, pg.h_key.unsafe_get(best))
c = best
}
pg.h_idx.unsafe_set(c, value)
pg.h_key.unsafe_set(c, key)
}
top
}
///|
fn pg_mag(pg : Pg, j : Int) -> Double {
pg.arena.unsafe_get(pg.off_mag + j)
}
///|
fn pg_offer(pg : Pg, j : Int, limit : Double) -> Unit {
if pg.done.unsafe_get(j) || pg.queued.unsafe_get(j) {
return
}
let m = pg_mag(pg, j)
if m <= limit {
return
}
pg.queued.unsafe_set(j, true)
pg_push(pg, j, m)
}
///|
fn pg_drain(pg : Pg, limit : Double) -> Unit {
let bins = pg.bins
let frames = pg.frames
while pg.h_size > 0 {
let i = pg_pop(pg)
if pg.done.unsafe_get(i) {
continue
}
let f = i / bins
let b = i - f * bins
let mut best = -1
let mut best_mag = -1.0
if b > 0 && pg.done.unsafe_get(i - 1) {
let m = pg_mag(pg, i - 1)
if m > best_mag {
best = i - 1
best_mag = m
}
}
if b < bins - 1 && pg.done.unsafe_get(i + 1) {
let m = pg_mag(pg, i + 1)
if m > best_mag {
best = i + 1
best_mag = m
}
}
if f > 0 && pg.done.unsafe_get(i - bins) {
let m = pg_mag(pg, i - bins)
if m > best_mag {
best = i - bins
best_mag = m
}
}
if f < frames - 1 && pg.done.unsafe_get(i + bins) {
let m = pg_mag(pg, i + bins)
if m > best_mag {
best = i + bins
best_mag = m
}
}
let value = if best < 0 {
0.0
} else if best == i - 1 {
pghi_wrap(
pg.arena.unsafe_get(pg.off_phase + best) +
0.5 *
(
pg.fgrad.unsafe_get(best).to_double() +
pg.fgrad.unsafe_get(i).to_double()
),
)
} else if best == i + 1 {
pghi_wrap(
pg.arena.unsafe_get(pg.off_phase + best) -
0.5 *
(
pg.fgrad.unsafe_get(best).to_double() +
pg.fgrad.unsafe_get(i).to_double()
),
)
} else if best == i - bins {
pghi_wrap(
pg.arena.unsafe_get(pg.off_phase + best) +
0.5 *
(
pg.tgrad.unsafe_get(best).to_double() +
pg.tgrad.unsafe_get(i).to_double()
),
)
} else {
pghi_wrap(
pg.arena.unsafe_get(pg.off_phase + best) -
0.5 *
(
pg.tgrad.unsafe_get(best).to_double() +
pg.tgrad.unsafe_get(i).to_double()
),
)
}
pg.arena.unsafe_set(pg.off_phase + i, value)
pg.done.unsafe_set(i, true)
if b > 0 {
pg_offer(pg, i - 1, limit)
}
if b < bins - 1 {
pg_offer(pg, i + 1, limit)
}
if f > 0 {
pg_offer(pg, i - bins, limit)
}
if f < frames - 1 {
pg_offer(pg, i + bins, limit)
}
}
}
///|
fn pg_setup(pg : Pg) -> Unit {
let n = pg.n
let bins = pg.bins
let frames = pg.frames
let two_pi = 2.0 * @math.PI
let floor = pg.top * 1.0e-12
let slog = FixedArray::make(n, 0.0)
for i = 0; i < n; i = i + 1 {
let v = pg.arena.unsafe_get(pg.off_mag + i)
slog.unsafe_set(i, @math.ln(v.max(floor)))
}
for f = 0; f < frames; f = f + 1 {
let base = f * bins
let up = (f - 1).max(0) * bins
let dn = (f + 1).min(frames - 1) * bins
let dt = if f > 0 && f < frames - 1 { 2.0 } else { 1.0 }
for b = 0; b < bins; b = b + 1 {
let g = -pg.c_f * (slog.unsafe_get(dn + b) - slog.unsafe_get(up + b)) / dt -
@math.PI
pg.fgrad.unsafe_set(base + b, Float::from_double(g))
}
}
for f = 0; f < frames; f = f + 1 {
let base = f * bins
for b = 0; b < bins; b = b + 1 {
let lo = (b - 1).max(0)
let hi = (b + 1).min(bins - 1)
let db = if b > 0 && b < bins - 1 { 2.0 } else { 1.0 }
let g = pg.c_t *
(slog.unsafe_get(base + hi) - slog.unsafe_get(base + lo)) /
db +
two_pi * pg.hop * b.to_double() / pg.win
pg.tgrad.unsafe_set(base + b, Float::from_double(g))
}
}
let counts = FixedArray::make(pghi_buckets, 0)
let keys = FixedArray::make(n, b'\x00')
for i = 0; i < n; i = i + 1 {
let raw = (pg.arena.unsafe_get(pg.off_mag + i) /
pg.top *
pghi_buckets.to_double()).to_int()
let k = raw.min(pghi_buckets - 1)
keys.unsafe_set(i, k.to_byte())
counts.unsafe_set(k, counts.unsafe_get(k) + 1)
}
let start = FixedArray::make(pghi_buckets + 1, 0)
let mut b = pghi_buckets - 1
while b >= 0 {
start.unsafe_set(b, start.unsafe_get(b + 1) + counts.unsafe_get(b))
b = b - 1
}
let at = FixedArray::make(pghi_buckets, 0)
for b = 0; b < pghi_buckets; b = b + 1 {
at.unsafe_set(b, start.unsafe_get(b + 1))
}
for i = 0; i < n; i = i + 1 {
let k = keys.unsafe_get(i).to_int()
let p = at.unsafe_get(k)
pg.order.unsafe_set(p, i)
at.unsafe_set(k, p + 1)
}
}
///|
fn pg_propagate(pg : Pg) -> Unit {
for limit in [pg.top * pg.tol_hi, pg.top * pg.tol_lo] {
for k = 0; k < pg.n; k = k + 1 {
let i = pg.order.unsafe_get(k)
if pg.done.unsafe_get(i) {
continue
}
let m = pg_mag(pg, i)
if m <= limit {
continue
}
pg.queued.unsafe_set(i, true)
pg_push(pg, i, m)
pg_drain(pg, limit)
}
}
}
///|
#export_name("dsp_pghi_run")
pub fn pghi_run(h : Int) -> Int {
match pghi_at(h) {
None => 0
Some(job) => {
let n = job.frames * job.bins
let phase = job.off_phase
for i = 0; i < n; i = i + 1 {
job.arena.unsafe_set(phase + i, 0.0)
}
let mut top = 0.0
for i = 0; i < n; i = i + 1 {
top = top.max(job.arena.unsafe_get(job.off_mag + i))
}
if top <= 0.0 || job.frames < 2 || job.bins < 2 {
return 1
}
let zero_f : Float = 0.0
let pg : Pg = {
frames: job.frames,
bins: job.bins,
n,
win: job.win.to_double(),
hop: job.hop.to_double(),
top,
tol_hi: job.tol_hi,
tol_lo: job.tol_lo,
c_f: job.gamma *
(job.win * job.win).to_double() /
(job.hop * job.win).to_double(),
c_t: (job.hop * job.win).to_double() /
(job.gamma * (job.win * job.win).to_double()),
off_mag: job.off_mag,
off_phase: phase,
arena: job.arena,
fgrad: FixedArray::make(n, zero_f),
tgrad: FixedArray::make(n, zero_f),
done: FixedArray::make(n, false),
queued: FixedArray::make(n, false),
order: FixedArray::make(n, 0),
h_idx: FixedArray::make(n, 0),
h_key: FixedArray::make(n, 0.0),
h_size: 0,
}
pg_setup(pg)
pg_propagate(pg)
let two_pi = 2.0 * @math.PI
let mut seed : UInt = 0x9e3779b9
for i = 0; i < n; i = i + 1 {
if pg.done.unsafe_get(i) {
continue
}
seed = seed ^ (seed << 13)
seed = seed ^ (seed >> 17)
seed = seed ^ (seed << 5)
job.arena.unsafe_set(
phase + i,
seed.to_double() / 4294967295.0 * two_pi - @math.PI,
)
}
1
}
}
}