// organ_dsp.el — the AFFERENT DSP organ, own-core, ported from peripheral/src/periph.swift. // // The mirror of speech.el: where speech.el RENDERS meaning out through a voice // (efferent), this module HEARS — it takes a raw 16-bit PCM RIFF/WAVE capture // and metabolizes it into GEOMETRY: a compact 8-number audio descriptor, a // voice-signature (F0 + formants F1-F5 by LPC), and an LPC analysis-resynthesis // that speaks the heard voice back from its own signature. // // Everything here is float physics (math_sin/math_cos/math_sqrt), not the // fixed-point integer path speech.el uses for synthesis — the analysis side // needs the dynamic range that autocorrelation and Levinson-Durbin demand. // // Binary I/O note: an El String truncates at the first NUL, so a WAV can never // be read through fs_read(). We read it through fs_read_b64_chunk(), which // hands back plain-ASCII base64 of a byte window, and decode that base64 HERE, // in El, into a [Int] of byte values. Chunks are a multiple of 3 bytes so each // base64 window decodes cleanly with no padding except the final one. // // Every function is prefixed dsp_ so nothing here can collide with speech.el // (write_wav / wav_le16 / wav_le32 / sp_* / voice_* all live there and are NOT // redefined). imitate returns [Int] samples — hand them to speech.el's write_wav. // --------------------------------------------------------------------------- // Base64 -> bytes (own-core; the only way binary reaches El intact) // --------------------------------------------------------------------------- // Standard RFC 4648 alphabet A-Za-z0-9+/ -> 0..63. Padding '=' and any other // character -> -1 (a sentinel; there is no exception handling in El). fn dsp_b64_val(c: Int) -> Int { if c >= 65 { if c <= 90 { return c - 65 } } if c >= 97 { if c <= 122 { return c - 71 } } if c >= 48 { if c <= 57 { return c + 4 } } if c == 43 { return 62 } if c == 47 { return 63 } return 0 - 1 } // Read a whole file as a list of byte values 0..255. Empty list on failure. fn dsp_read_bytes(path: String) -> [Int] { let bytes: [Int] = native_list_empty() let size: Int = fs_size(path) if size <= 0 { return bytes } let chunk: Int = 60000 // multiple of 3 -> no interior padding let off: Int = 0 while off < size { let s: String = fs_read_b64_chunk(path, off, chunk) let sl: Int = str_len(s) if sl < 4 { return bytes } let i: Int = 0 while i + 3 < sl { let c0: Int = dsp_b64_val(str_char_code(s, i)) let c1: Int = dsp_b64_val(str_char_code(s, i + 1)) let c2: Int = dsp_b64_val(str_char_code(s, i + 2)) let c3: Int = dsp_b64_val(str_char_code(s, i + 3)) if c0 < 0 { return bytes } if c1 < 0 { return bytes } let b0: Int = c0 * 4 + c1 / 16 bytes = native_list_append(bytes, b0) if c2 >= 0 { let lo1: Int = c1 - (c1 / 16) * 16 let b1: Int = lo1 * 16 + c2 / 4 bytes = native_list_append(bytes, b1) if c3 >= 0 { let lo2: Int = c2 - (c2 / 4) * 4 let b2: Int = lo2 * 64 + c3 bytes = native_list_append(bytes, b2) } } i = i + 4 } off = off + chunk } return bytes } fn dsp_rd16(b: [Int], o: Int) -> Int { let b0: Int = native_list_get(b, o) let b1: Int = native_list_get(b, o + 1) return b0 + b1 * 256 } fn dsp_rd32(b: [Int], o: Int) -> Int { let b0: Int = native_list_get(b, o) let b1: Int = native_list_get(b, o + 1) let b2: Int = native_list_get(b, o + 2) let b3: Int = native_list_get(b, o + 3) return b0 + b1 * 256 + b2 * 65536 + b3 * 16777216 } // Four bytes at o compared against a 4-char ASCII chunk id. fn dsp_chunk_is(b: [Int], o: Int, id: String) -> Bool { let k: Int = 0 while k < 4 { let got: Int = native_list_get(b, o + k) let want: Int = str_char_code(id, k) if got != want { return false } k = k + 1 } return true } // --------------------------------------------------------------------------- // readWavSamples — 16-bit PCM RIFF/WAVE -> normalized samples. // Walks chunks to find 'fmt ' and 'data', so JUNK/FLLR padding (which // AVAudioRecorder emits) is stepped over rather than mistaken for audio. // Channel 0 only if stereo. // // The returned list is PACKED: [ sr, ch, n, s0, s1, ... s(n-1) ] with the three // header numbers carried as Floats (El has no tuples). Use dsp_wav_sr / // dsp_wav_ch / dsp_wav_n / dsp_wav_pcm to open it. Empty list on failure. // --------------------------------------------------------------------------- fn dsp_read_wav(path: String) -> [Float] { let out: [Float] = native_list_empty() let d: [Int] = dsp_read_bytes(path) let dn: Int = native_list_len(d) if dn <= 44 { return out } let ch: Int = 0 let sr: Int = 0 let bits: Int = 0 let o: Int = 12 while o + 8 <= dn { let sz: Int = dsp_rd32(d, o + 4) if dsp_chunk_is(d, o, "fmt ") { if o + 24 <= dn { ch = dsp_rd16(d, o + 10) sr = dsp_rd32(d, o + 12) bits = dsp_rd16(d, o + 22) } } if dsp_chunk_is(d, o, "data") { if bits != 16 { return out } if ch <= 0 { return out } // Faithful to periph.swift, including its `d.count - 1` bound and // the `while i + 1 < end` test — the last sample of a file whose // data chunk runs to EOF is dropped there, so it is dropped here. let start: Int = o + 8 let end: Int = start + sz if end > dn - 1 { end = dn - 1 } let samples: [Float] = native_list_empty() let step: Int = 2 * ch let i: Int = start let count: Int = 0 while i + 1 < end { let v: Int = dsp_rd16(d, i) if v >= 32768 { v = v - 65536 } let f: Float = int_to_float(v) / 32768.0 samples = native_list_append(samples, f) count = count + 1 i = i + step } let srf: Float = int_to_float(sr) let chf: Float = int_to_float(ch) let nf: Float = int_to_float(count) out = native_list_append(out, srf) out = native_list_append(out, chf) out = native_list_append(out, nf) let j: Int = 0 while j < count { let sv: Float = native_list_get(samples, j) out = native_list_append(out, sv) j = j + 1 } return out } let adv: Int = 8 + sz + (sz - (sz / 2) * 2) if adv <= 0 { return out } o = o + adv } return out } fn dsp_wav_sr(w: [Float]) -> Int { if native_list_len(w) < 3 { return 0 } let v: Float = native_list_get(w, 0) return float_to_int(v) } fn dsp_wav_ch(w: [Float]) -> Int { if native_list_len(w) < 3 { return 0 } let v: Float = native_list_get(w, 1) return float_to_int(v) } fn dsp_wav_n(w: [Float]) -> Int { if native_list_len(w) < 3 { return 0 } let v: Float = native_list_get(w, 2) return float_to_int(v) } // The bare sample list, unpacked from the header-prefixed form. fn dsp_wav_pcm(w: [Float]) -> [Float] { let out: [Float] = native_list_empty() let n: Int = dsp_wav_n(w) let i: Int = 0 while i < n { let v: Float = native_list_get(w, i + 3) out = native_list_append(out, v) i = i + 1 } return out } // --------------------------------------------------------------------------- // small float helpers (El has no unary minus on Float in every position, and // no min/max builtin — so they are written out) // --------------------------------------------------------------------------- fn dsp_fabs(v: Float) -> Float { if v < 0.0 { return 0.0 - v } return v } // --------------------------------------------------------------------------- // computeAudio — the compact audio descriptor. // Returns the 8-number vector [seconds, sr, ch, rms, peak, zcr, centroid, f0]. // Empty list if the WAV cannot be read (no exceptions in El — sentinels only). // --------------------------------------------------------------------------- fn dsp_compute_audio(path: String) -> [Float] { let vec: [Float] = native_list_empty() let w: [Float] = dsp_read_wav(path) let n: Int = dsp_wav_n(w) if n <= 0 { return vec } let sr: Int = dsp_wav_sr(w) let ch: Int = dsp_wav_ch(w) let s: [Float] = dsp_wav_pcm(w) let nf: Float = int_to_float(n) let srf: Float = int_to_float(sr) let seconds: Float = nf / srf // energy / peak / zero crossings let sumsq: Float = 0.0 let peak: Float = 0.0 let zc: Float = 0.0 let i: Int = 0 while i < n { let v: Float = native_list_get(s, i) sumsq = sumsq + v * v let av: Float = dsp_fabs(v) if av > peak { peak = av } if i > 0 { let pv: Float = native_list_get(s, i - 1) let a: Bool = pv < 0.0 let b: Bool = v < 0.0 if a != b { zc = zc + 1.0 } } i = i + 1 } let rms: Float = math_sqrt(sumsq / nf) let zcr: Float = zc / nf * srf // ~2*dominant freq for tonal // Spectral centroid via a coarse 64-bin DFT on a centered 2048 window. let ww: Int = 2048 if n < ww { ww = n } let off: Int = (n - ww) / 2 if off < 0 { off = 0 } let two: Float = 2.0 let pi: Float = math_pi() let num: Float = 0.0 let den: Float = 0.0 let bins: Int = 64 let binsf: Float = 128.0 // Double(2*bins) let k: Int = 1 while k < bins { let kf: Float = int_to_float(k) let f: Float = kf * srf / binsf let re: Float = 0.0 let im: Float = 0.0 let j: Int = 0 while j < ww { let jf: Float = int_to_float(j) let ang: Float = (0.0 - two) * pi * kf * jf / binsf let xv: Float = native_list_get(s, off + j) let cv: Float = math_cos(ang) let sv: Float = math_sin(ang) re = re + xv * cv im = im + xv * sv j = j + 1 } let mag: Float = math_sqrt(re * re + im * im) num = num + f * mag den = den + mag k = k + 1 } let centroid: Float = 0.0 if den > 0.0 { centroid = num / den } // F0 by autocorrelation over the plausible speech range 70-400 Hz. let lag_min: Int = sr / 400 let lag_max: Int = sr / 70 if lag_max > n - 1 { lag_max = n - 1 } let bound: Int = off + ww if bound > n { bound = n } let best_lag: Int = 0 let best_corr: Float = 0.0 if lag_max > lag_min { let lag: Int = lag_min while lag <= lag_max { let c: Float = 0.0 let p: Int = 0 while p + lag < bound { let a1: Float = native_list_get(s, off + p) let a2: Float = native_list_get(s, off + p + lag) c = c + a1 * a2 p = p + 1 } if c > best_corr { best_corr = c best_lag = lag } lag = lag + 1 } } let f0: Float = 0.0 if best_lag > 0 { f0 = srf / int_to_float(best_lag) } vec = native_list_append(vec, seconds) vec = native_list_append(vec, srf) let chf: Float = int_to_float(ch) vec = native_list_append(vec, chf) vec = native_list_append(vec, rms) vec = native_list_append(vec, peak) vec = native_list_append(vec, zcr) vec = native_list_append(vec, centroid) vec = native_list_append(vec, f0) return vec } // --------------------------------------------------------------------------- // LPC core: hamming, autocorr, Levinson-Durbin, formant peak-pick, pitch. // --------------------------------------------------------------------------- fn dsp_hamming(x: [Float]) -> [Float] { let n: Int = native_list_len(x) if n < 2 { return x } let out: [Float] = native_list_empty() let pi: Float = math_pi() let dn: Float = int_to_float(n - 1) let i: Int = 0 while i < n { let v: Float = native_list_get(x, i) let ang: Float = 2.0 * pi * int_to_float(i) / dn let cv: Float = math_cos(ang) let wv: Float = 0.54 - 0.46 * cv out = native_list_append(out, v * wv) i = i + 1 } return out } // r[lag] = sum_i x[i]*x[i-lag], lag = 0..p. Returns p+1 numbers. fn dsp_autocorr(x: [Float], p: Int) -> [Float] { let n: Int = native_list_len(x) let r: [Float] = native_list_empty() let lag: Int = 0 while lag <= p { let acc: Float = 0.0 let i: Int = lag while i < n { let a: Float = native_list_get(x, i) let b: Float = native_list_get(x, i - lag) acc = acc + a * b i = i + 1 } r = native_list_append(r, acc) lag = lag + 1 } return r } // Levinson-Durbin -> LPC coeffs a[0..p] with A(z) = 1 + sum a[k] z^-k. // Returns p+2 numbers: a[0..p] followed by the residual energy at index p+1. fn dsp_levinson(r: [Float], p: Int) -> [Float] { let a: [Float] = native_list_empty() a = native_list_append(a, 1.0) let z: Int = 1 while z <= p { a = native_list_append(a, 0.0) z = z + 1 } let err: Float = native_list_get(r, 0) if err <= 0.0 { a = native_list_append(a, 0.0) return a } let i: Int = 1 let stopped: Bool = false while i <= p { if stopped { i = p + 1 } else { let acc: Float = native_list_get(r, i) if i > 1 { let j: Int = 1 while j < i { let aj: Float = native_list_get(a, j) let rij: Float = native_list_get(r, i - j) acc = acc + aj * rij j = j + 1 } } let k: Float = (0.0 - acc) / err // na = a, then na[i] = k, then na[j] = a[j] + k*a[i-j] for 1<=j [Float] { let peaks: [Float] = native_list_empty() let p: Int = native_list_len(a) - 1 if p < 1 { return peaks } let steps: Int = 512 let stepsf: Float = 512.0 let srf: Float = int_to_float(sr) let pi: Float = math_pi() let mag: [Float] = native_list_empty() let s: Int = 0 while s < steps { let wq: Float = pi * int_to_float(s) / stepsf // 0..pi -> 0..sr/2 let re: Float = 0.0 let im: Float = 0.0 let k: Int = 0 while k <= p { let ak: Float = native_list_get(a, k) let ang: Float = wq * int_to_float(k) let cv: Float = math_cos(ang) let sv: Float = math_sin(ang) re = re + ak * cv im = im - ak * sv k = k + 1 } let d: Float = math_sqrt(re * re + im * im) if d < 0.000000001 { d = 0.000000001 } mag = native_list_append(mag, 1.0 / d) s = s + 1 } let found: Int = 0 let t: Int = 1 while t < steps - 1 { if found < 5 { let m0: Float = native_list_get(mag, t - 1) let m1: Float = native_list_get(mag, t) let m2: Float = native_list_get(mag, t + 1) let rise: Bool = m1 > m0 let fall: Bool = m1 >= m2 if rise { if fall { let f: Float = int_to_float(t) * srf / 2.0 / stepsf if f > 150.0 { if f < 5200.0 { let thr: Float = m1 / 1.4142 let lo: Int = t let scan_lo: Bool = true while scan_lo { if lo > 0 { let mv: Float = native_list_get(mag, lo) if mv > thr { lo = lo - 1 } else { scan_lo = false } } else { scan_lo = false } } let hi: Int = t let scan_hi: Bool = true while scan_hi { if hi < steps - 1 { let mv2: Float = native_list_get(mag, hi) if mv2 > thr { hi = hi + 1 } else { scan_hi = false } } else { scan_hi = false } } let bw: Float = int_to_float(hi - lo) * srf / 2.0 / stepsf peaks = native_list_append(peaks, f) peaks = native_list_append(peaks, bw) found = found + 1 } } } } } t = t + 1 } return peaks } // Autocorrelation pitch over 70-400 Hz with the 0.30 voicing threshold. // Returns 0.0 for an unvoiced (or silent) frame. fn dsp_pitch_of(frame: [Float], sr: Int) -> Float { let n: Int = native_list_len(frame) let lag_min: Int = sr / 400 let lag_max: Int = sr / 70 if lag_max > n - 1 { lag_max = n - 1 } if lag_max <= lag_min { return 0.0 } let r0: Float = 0.0 let i: Int = 0 while i < n { let v: Float = native_list_get(frame, i) r0 = r0 + v * v i = i + 1 } if r0 < 0.00001 { return 0.0 } let best_lag: Int = 0 let best: Float = 0.0 let lag: Int = lag_min while lag <= lag_max { let c: Float = 0.0 let j: Int = lag while j < n { let a: Float = native_list_get(frame, j) let b: Float = native_list_get(frame, j - lag) c = c + a * b j = j + 1 } if c > best { best = c best_lag = lag } lag = lag + 1 } if best_lag > 0 { let ratio: Float = best / r0 if ratio > 0.30 { let srf: Float = int_to_float(sr) return srf / int_to_float(best_lag) } } return 0.0 } // A copy of x[pos..pos+n) — the El stand-in for Swift's Array(x[a.. [Float] { let out: [Float] = native_list_empty() let total: Int = native_list_len(x) let i: Int = 0 while i < n { if pos + i < total { let v: Float = native_list_get(x, pos + i) out = native_list_append(out, v) } i = i + 1 } return out } // median == sorted()[count/2]. There is no list-set in El, so instead of // sorting we find the value whose rank bracket contains index count/2 — // numerically identical to the Swift expression, without a mutable buffer. fn dsp_median(v: [Float]) -> Float { let n: Int = native_list_len(v) if n == 0 { return 0.0 } let target: Int = n / 2 let i: Int = 0 while i < n { let x: Float = native_list_get(v, i) let less: Int = 0 let eq: Int = 0 let j: Int = 0 while j < n { let y: Float = native_list_get(v, j) if y < x { less = less + 1 } if y == x { eq = eq + 1 } j = j + 1 } if less <= target { if target < less + eq { return x } } i = i + 1 } return 0.0 } fn dsp_min_of(v: [Float]) -> Float { let n: Int = native_list_len(v) if n == 0 { return 0.0 } let m: Float = native_list_get(v, 0) let i: Int = 1 while i < n { let x: Float = native_list_get(v, i) if x < m { m = x } i = i + 1 } return m } fn dsp_max_of(v: [Float]) -> Float { let n: Int = native_list_len(v) if n == 0 { return 0.0 } let m: Float = native_list_get(v, 0) let i: Int = 1 while i < n { let x: Float = native_list_get(v, i) if x > m { m = x } i = i + 1 } return m } // --------------------------------------------------------------------------- // voiceprint — the voice-signature: median F0 over voiced frames + the median // of each formant F1..F5 (and its bandwidth), plus the F0 min/max range. // FRAME = 400 (25 ms @ 16k), HOP = 160 (10 ms), LPC order 16. // // Returns PACKED: [ f0med, f0lo, f0hi, nf, f1, b1, f2, b2, ... ] where nf is // how many formants were recovered (0..5). Empty list if unreadable. // --------------------------------------------------------------------------- fn dsp_voiceprint(path: String) -> [Float] { let out: [Float] = native_list_empty() let w: [Float] = dsp_read_wav(path) let n: Int = dsp_wav_n(w) let frame_len: Int = 400 let hop: Int = 160 let order: Int = 16 if n <= frame_len { return out } let sr: Int = dsp_wav_sr(w) let x: [Float] = dsp_wav_pcm(w) let f0s: [Float] = native_list_empty() // five formant banks + five bandwidth banks, flat lists each let f1s: [Float] = native_list_empty() let f2s: [Float] = native_list_empty() let f3s: [Float] = native_list_empty() let f4s: [Float] = native_list_empty() let f5s: [Float] = native_list_empty() let b1s: [Float] = native_list_empty() let b2s: [Float] = native_list_empty() let b3s: [Float] = native_list_empty() let b4s: [Float] = native_list_empty() let b5s: [Float] = native_list_empty() let pos: Int = 0 while pos + frame_len <= n { let raw: [Float] = dsp_slice(x, pos, frame_len) let f0: Float = dsp_pitch_of(raw, sr) if f0 > 0.0 { f0s = native_list_append(f0s, f0) let win: [Float] = dsp_hamming(raw) let r: [Float] = dsp_autocorr(win, order) let r0: Float = native_list_get(r, 0) if r0 > 0.000001 { let al: [Float] = dsp_levinson(r, order) // strip the trailing residual energy: coeffs are a[0..order] let a: [Float] = native_list_empty() let ci: Int = 0 while ci <= order { let av: Float = native_list_get(al, ci) a = native_list_append(a, av) ci = ci + 1 } let fs: [Float] = dsp_formants(a, sr) let nfs: Int = native_list_len(fs) / 2 if nfs > 0 { let fv: Float = native_list_get(fs, 0) let bv: Float = native_list_get(fs, 1) f1s = native_list_append(f1s, fv) b1s = native_list_append(b1s, bv) } if nfs > 1 { let fv2: Float = native_list_get(fs, 2) let bv2: Float = native_list_get(fs, 3) f2s = native_list_append(f2s, fv2) b2s = native_list_append(b2s, bv2) } if nfs > 2 { let fv3: Float = native_list_get(fs, 4) let bv3: Float = native_list_get(fs, 5) f3s = native_list_append(f3s, fv3) b3s = native_list_append(b3s, bv3) } if nfs > 3 { let fv4: Float = native_list_get(fs, 6) let bv4: Float = native_list_get(fs, 7) f4s = native_list_append(f4s, fv4) b4s = native_list_append(b4s, bv4) } if nfs > 4 { let fv5: Float = native_list_get(fs, 8) let bv5: Float = native_list_get(fs, 9) f5s = native_list_append(f5s, fv5) b5s = native_list_append(b5s, bv5) } } } pos = pos + hop } let f0med: Float = dsp_median(f0s) let f0lo: Float = dsp_min_of(f0s) let f0hi: Float = dsp_max_of(f0s) out = native_list_append(out, f0med) out = native_list_append(out, f0lo) out = native_list_append(out, f0hi) let nf: Int = 0 if native_list_len(f1s) > 0 { nf = nf + 1 } if native_list_len(f2s) > 0 { nf = nf + 1 } if native_list_len(f3s) > 0 { nf = nf + 1 } if native_list_len(f4s) > 0 { nf = nf + 1 } if native_list_len(f5s) > 0 { nf = nf + 1 } let nff: Float = int_to_float(nf) out = native_list_append(out, nff) if native_list_len(f1s) > 0 { let m: Float = dsp_median(f1s) let b: Float = dsp_median(b1s) out = native_list_append(out, m) out = native_list_append(out, b) } if native_list_len(f2s) > 0 { let m2: Float = dsp_median(f2s) let bb2: Float = dsp_median(b2s) out = native_list_append(out, m2) out = native_list_append(out, bb2) } if native_list_len(f3s) > 0 { let m3: Float = dsp_median(f3s) let bb3: Float = dsp_median(b3s) out = native_list_append(out, m3) out = native_list_append(out, bb3) } if native_list_len(f4s) > 0 { let m4: Float = dsp_median(f4s) let bb4: Float = dsp_median(b4s) out = native_list_append(out, m4) out = native_list_append(out, bb4) } if native_list_len(f5s) > 0 { let m5: Float = dsp_median(f5s) let bb5: Float = dsp_median(b5s) out = native_list_append(out, m5) out = native_list_append(out, bb5) } return out } // --------------------------------------------------------------------------- // imitate — LPC analysis-resynthesis. Per frame: autocorr + Levinson, gain = // sqrt(residual energy), excitation = an energy-normalized glottal impulse // train at F0 for voiced frames or white noise for unvoiced, run through the // all-pole filter using the past-output state. The whole output is normalized // to peak 0.9 and returned as int16 samples — hand them to speech.el's // write_wav(samples, sr, path). // --------------------------------------------------------------------------- fn dsp_imitate(path: String) -> [Int] { let res: [Int] = native_list_empty() let w: [Float] = dsp_read_wav(path) let n: Int = dsp_wav_n(w) let frame_len: Int = 400 let hop: Int = 160 let order: Int = 16 if n <= frame_len { return res } let sr: Int = dsp_wav_sr(w) let srf: Float = int_to_float(sr) let x: [Float] = dsp_wav_pcm(w) let out: [Float] = native_list_empty() // past outputs, order deep let state: [Float] = native_list_empty() let si: Int = 0 while si < order { state = native_list_append(state, 0.0) si = si + 1 } let phase: Float = 0.0 let last_f0: Float = 0.0 let nstate: Int = 22695 // LCG for the unvoiced source let written: Int = 0 let pos: Int = 0 while pos + frame_len <= n { let raw: [Float] = dsp_slice(x, pos, frame_len) let win: [Float] = dsp_hamming(raw) let r: [Float] = dsp_autocorr(win, order) let f0: Float = dsp_pitch_of(raw, sr) let r0: Float = native_list_get(r, 0) if r0 < 0.0000001 { // frame skipped in periph.swift -> those output samples stay zero let z: Int = 0 while z < hop { if written < n { out = native_list_append(out, 0.0) written = written + 1 } z = z + 1 } } else { let al: [Float] = dsp_levinson(r, order) let errv: Float = native_list_get(al, order + 1) let ge: Float = errv if ge < 0.0 { ge = 0.0 } let gain: Float = math_sqrt(ge) let use_f0: Float = f0 if f0 <= 0.0 { use_f0 = 0.0 if last_f0 > 0.0 { use_f0 = last_f0 } } last_f0 = f0 let i: Int = 0 while i < hop { if written < n { let e: Float = 0.0 if use_f0 > 0.0 { phase = phase + use_f0 / srf if phase >= 1.0 { phase = phase - 1.0 e = math_sqrt(srf / use_f0) } } else { nstate = nstate * 1103515245 + 12345 nstate = nstate - (nstate / 2147483648) * 2147483648 if nstate < 0 { nstate = 0 - nstate } let u: Float = int_to_float(nstate) / 2147483648.0 e = u * 2.0 - 1.0 } let y: Float = gain * e let k: Int = 1 while k <= order { let ak: Float = native_list_get(al, k) let sk: Float = native_list_get(state, k - 1) y = y - ak * sk k = k + 1 } let ns: [Float] = native_list_empty() ns = native_list_append(ns, y) let m: Int = 0 while m < order - 1 { let sv: Float = native_list_get(state, m) ns = native_list_append(ns, sv) m = m + 1 } state = ns out = native_list_append(out, y) written = written + 1 } i = i + 1 } } pos = pos + hop } // the tail past the last full frame stays silent, exactly as in periph.swift while written < n { out = native_list_append(out, 0.0) written = written + 1 } // normalize to peak 0.9, then to int16 let peak: Float = 0.0 let q: Int = 0 while q < n { let v: Float = native_list_get(out, q) let av: Float = dsp_fabs(v) if av > peak { peak = av } q = q + 1 } let scale: Float = 1.0 if peak > 0.000000001 { scale = 0.9 / peak } let t: Int = 0 while t < n { let v2: Float = native_list_get(out, t) let sv2: Float = v2 * scale * 32767.0 if sv2 > 32767.0 { sv2 = 32767.0 } if sv2 < 0.0 - 32767.0 { sv2 = 0.0 - 32767.0 } let iv: Int = float_to_int(sv2) res = native_list_append(res, iv) t = t + 1 } return res }