// audio-surface.el - Native own-core additive-synthesis audio surface. // // The AUDIO efferent seam, native, no Python and no library. This renders real // PCM .wav bytes from instrument SIGNATURES read from engram-sourced .sig data // files (elp/faculty/sig/*.sig) - the partial amplitudes are NEVER literals in // this source; they are parsed from the learned signature at run time. That is // the whole proof: render-from-learned-signatures. // // EL has no float arithmetic operator (codegen emits raw int64 ops for + - * / // on the shared 64-bit slot) and no float-arithmetic natives - so ALL synthesis // math here is own-core INTEGER fixed-point. Angles use a quarter-wave sine // table (scale 10000) from a fixed-point Taylor series; amplitudes are parsed to // micro (scale 1e6) straight from the .sig text; frequencies are milliHz ints. // // Pipeline mirrors the two-stage projector (midi.py): plan_note(frame) reads a // frame's meaning-geometry slot-map and derives (pitch, duration, amplitude); // realize_audio SUPERPOSES the signature's partials (the compose op) and // serialises RIFF/WAVE. Same frame -> midi OR audio. // -- integer decimal + string helpers ----------------------------------------- fn str_to_int_el(s: String) -> Int { let n: Int = str_len(s) let i: Int = 0 let v: Int = 0 let neg: Bool = false while i < n { let c: Int = str_char_code(s, i) if c == 45 { let neg: Bool = true } if c >= 48 { if c < 58 { let v: Int = v * 10 + (c - 48) } } let i: Int = i + 1 } if neg { return 0 - v } return v } fn parse_micro(s: String) -> Int { let dot: Int = str_index_of(s, ".") if dot < 0 { return str_to_int_el(s) * 1000000 } let n: Int = str_len(s) let ipart: String = str_slice(s, 0, dot) let fpart: String = str_slice(s, dot + 1, n) let iv: Int = str_to_int_el(ipart) let fv: Int = 0 let scale: Int = 100000 let fn2: Int = str_len(fpart) let i: Int = 0 while i < 6 { let d: Int = 0 if i < fn2 { let d: Int = str_char_code(fpart, i) - 48 } let fv: Int = fv + d * scale let scale: Int = scale / 10 let i: Int = i + 1 } return iv * 1000000 + fv } // -- signature (engram data file) loader --------------------------------------- fn sig_load(path: String) -> [String] { let text: String = fs_read(path) return str_split(text, "\n") } fn sig_field(lines: [String], key: String) -> String { let pref: String = key + ": " let n: Int = native_list_len(lines) let plen: Int = str_len(pref) let i: Int = 0 while i < n { let ln: String = native_list_get(lines, i) if str_starts_with(ln, pref) { return str_slice(ln, plen, str_len(ln)) } let i: Int = i + 1 } return "" } fn parse_micros(csv: String) -> [Int] { let parts: [String] = str_split(csv, ",") let n: Int = native_list_len(parts) let out: [Int] = native_list_empty() let i: Int = 0 while i < n { let out: [Int] = native_list_append(out, parse_micro(native_list_get(parts, i))) let i: Int = i + 1 } return out } // -- fixed-point sine (own-core, quarter-wave Taylor table, scale 10000) -------- fn sin_table() -> [Int] { let HP: Int = 1570796 let t: [Int] = native_list_empty() let q: Int = 0 while q < 257 { let x: Int = q * HP / 256 let x2: Int = x * x / 1000000 let x3: Int = x2 * x / 1000000 let x5: Int = x3 * x2 / 1000000 let x7: Int = x5 * x2 / 1000000 let x9: Int = x7 * x2 / 1000000 let s: Int = x - x3 / 6 + x5 / 120 - x7 / 5040 + x9 / 362880 let t: [Int] = native_list_append(t, s / 100) let q: Int = q + 1 } return t } fn sin_lookup(t: [Int], phase: Int) -> Int { let p: Int = phase % 1024 if p < 0 { let p: Int = p + 1024 } let quad: Int = p / 256 let r: Int = p % 256 if quad == 0 { return native_list_get(t, r) } if quad == 1 { return native_list_get(t, 256 - r) } if quad == 2 { return 0 - native_list_get(t, r) } return 0 - native_list_get(t, 256 - r) } fn isqrt_int(n: Int) -> Int { if n <= 0 { return 0 } let x: Int = n let y: Int = (x + 1) / 2 while y < x { let x: Int = y let y: Int = (x + n / x) / 2 } return x } // freq_of_midi: equal-tempered frequency in milliHz. 440000 mHz at midi 69. fn freq_of_midi(m: Int) -> Int { let f: Int = 440000 if m > 69 { let k: Int = m - 69 let i: Int = 0 while i < k { let f: Int = f * 1059463 / 1000000 let i: Int = i + 1 } return f } if m < 69 { let k: Int = 69 - m let i: Int = 0 while i < k { let f: Int = f * 1000000 / 1059463 let i: Int = i + 1 } return f } return f } // -- envelope (ADSR), scale 1000 ----------------------------------------------- fn adsr_env(i: Int, total: Int, atk_n: Int, dec_n: Int, sus_pm: Int, rel_n: Int) -> Int { if i < atk_n { if atk_n == 0 { return 1000 } return 1000 * i / atk_n } if i < atk_n + dec_n { if dec_n == 0 { return sus_pm } return 1000 - (1000 - sus_pm) * (i - atk_n) / dec_n } let rel_start: Int = total - rel_n if i < rel_start { return sus_pm } if rel_n == 0 { return 0 } let left: Int = total - i return sus_pm * left / rel_n } // -- note synthesis: SUPERPOSE the learned partials -> [Int] samples ----------- fn note_samples(freq_mHz: Int, dur_ms: Int, rate: Int, partials: [Int], sumP: Int, b_micro: Int, vib_rate: Int, vib_cents: Int, atk_ms: Int, dec_ms: Int, sus_pm: Int, rel_ms: Int, amp_pm: Int, table: [Int]) -> [Int] { let total: Int = dur_ms * rate / 1000 let atk_n: Int = atk_ms * rate / 1000 let dec_n: Int = dec_ms * rate / 1000 let rel_n: Int = rel_ms * rate / 1000 let np: Int = native_list_len(partials) let half_mhz: Int = rate * 1000 / 2 let out: [Int] = native_list_empty() let i: Int = 0 while i < total { let acc: Int = 0 let k: Int = 0 while k < np { let harm: Int = k + 1 let amp_k: Int = native_list_get(partials, k) let factor: Int = 1000000 if b_micro > 0 { let val: Int = 1000000 + b_micro * harm * harm let factor: Int = isqrt_int(val * 1000000) } let fn_mhz: Int = freq_mHz * harm let fn_mhz: Int = fn_mhz * factor / 1000000 if vib_cents > 0 { if vib_rate > 0 { let vphase: Int = i * vib_rate * 1024 / rate let vs: Int = sin_lookup(table, vphase) let vibf: Int = 1000000 + (vib_cents * vs * 833) / 10000 let fn_mhz: Int = fn_mhz * vibf / 1000000 } } if fn_mhz <= half_mhz { let phase: Int = i * fn_mhz * 1024 / (rate * 1000) let sv: Int = sin_lookup(table, phase) let acc: Int = acc + sv * amp_k / 1000000 } let k: Int = k + 1 } let env: Int = adsr_env(i, total, atk_n, dec_n, sus_pm, rel_n) let s16: Int = acc * 2800000 / sumP let s16: Int = s16 * env / 1000 let s16: Int = s16 * amp_pm / 1000 if s16 > 32767 { let s16: Int = 32767 } if s16 < 0 - 32767 { let s16: Int = 0 - 32767 } let out: [Int] = native_list_append(out, s16) let i: Int = i + 1 } return out } fn synth_from_sig(lines: [String], freq_mHz: Int, dur_ms: Int, amp_pm: Int, rate: Int, table: [Int]) -> [Int] { let partials: [Int] = parse_micros(sig_field(lines, "partials")) let np: Int = native_list_len(partials) let sumP: Int = 0 let j: Int = 0 while j < np { let pj: Int = native_list_get(partials, j) let sumP: Int = sumP + pj let j: Int = j + 1 } if sumP <= 0 { let sumP: Int = 1000000 } let adsr: [String] = str_split(sig_field(lines, "adsr"), ",") let atk_ms: Int = parse_micro(native_list_get(adsr, 0)) / 1000 let dec_ms: Int = parse_micro(native_list_get(adsr, 1)) / 1000 let sus_pm: Int = parse_micro(native_list_get(adsr, 2)) / 1000 let rel_ms: Int = parse_micro(native_list_get(adsr, 3)) / 1000 let b_micro: Int = parse_micro(sig_field(lines, "inharmonicity_B")) let vib_rate: Int = str_to_int_el(sig_field(lines, "vibrato_rate_hz")) let vib_cents: Int = str_to_int_el(sig_field(lines, "vibrato_depth_cents")) return note_samples(freq_mHz, dur_ms, rate, partials, sumP, b_micro, vib_rate, vib_cents, atk_ms, dec_ms, sus_pm, rel_ms, amp_pm, table) } // -- byte-buffer helpers (own-core, no library) -------------------------------- fn put_tag(buf: String, pos: Int, s: String) -> String { let n: Int = str_len(s) let i: Int = 0 while i < n { let buf: String = __str_set_char(buf, pos + i, str_char_code(s, i)) let i: Int = i + 1 } return buf } fn put_u32le(buf: String, pos: Int, v: Int) -> String { let buf: String = __str_set_char(buf, pos, v % 256) let buf: String = __str_set_char(buf, pos + 1, (v / 256) % 256) let buf: String = __str_set_char(buf, pos + 2, (v / 65536) % 256) let buf: String = __str_set_char(buf, pos + 3, (v / 16777216) % 256) return buf } fn put_u16le(buf: String, pos: Int, v: Int) -> String { let buf: String = __str_set_char(buf, pos, v % 256) let buf: String = __str_set_char(buf, pos + 1, (v / 256) % 256) return buf } // -- WAV serializer: own-core RIFF/WAVE, PCM mono 16-bit ----------------------- fn wav_write(path: String, samples: [Int], n: Int, rate: Int) -> Int { let data_len: Int = n * 2 let total: Int = 44 + data_len let buf: String = __str_alloc(total) let buf: String = put_tag(buf, 0, "RIFF") let buf: String = put_u32le(buf, 4, 36 + data_len) let buf: String = put_tag(buf, 8, "WAVE") let buf: String = put_tag(buf, 12, "fmt ") let buf: String = put_u32le(buf, 16, 16) let buf: String = put_u16le(buf, 20, 1) let buf: String = put_u16le(buf, 22, 1) let buf: String = put_u32le(buf, 24, rate) let buf: String = put_u32le(buf, 28, rate * 2) let buf: String = put_u16le(buf, 32, 2) let buf: String = put_u16le(buf, 34, 16) let buf: String = put_tag(buf, 36, "data") let buf: String = put_u32le(buf, 40, data_len) let i: Int = 0 while i < n { let v: Int = native_list_get(samples, i) if v < 0 { let v: Int = v + 65536 } let buf: String = __str_set_char(buf, 44 + i * 2, v % 256) let buf: String = __str_set_char(buf, 44 + i * 2 + 1, (v / 256) % 256) let i: Int = i + 1 } let ok: Int = fs_write_bytes(path, buf, total) return ok } // -- plan: frame slot-map -> note atom (pitch, duration, amplitude) ------------ fn audio_frame(relation: String, polarity: String, confidence: String, importance: String, salience: String, subj_id: String) -> [String] { let f: [String] = native_list_empty() let f: [String] = native_list_append(f, "relation") let f: [String] = native_list_append(f, relation) let f: [String] = native_list_append(f, "polarity") let f: [String] = native_list_append(f, polarity) let f: [String] = native_list_append(f, "confidence") let f: [String] = native_list_append(f, confidence) let f: [String] = native_list_append(f, "importance") let f: [String] = native_list_append(f, importance) let f: [String] = native_list_append(f, "salience") let f: [String] = native_list_append(f, salience) let f: [String] = native_list_append(f, "subj_id") let f: [String] = native_list_append(f, subj_id) return f } fn degree_offset(deg: Int) -> Int { if deg == 0 { return 0 } if deg == 1 { return 2 } if deg == 2 { return 4 } if deg == 3 { return 5 } if deg == 4 { return 7 } if deg == 5 { return 9 } return 11 } // returns [midi, dur_ms, amp_pm] fn plan_note(frame: [String]) -> [Int] { let relation: String = surface_get(frame, "relation") let polarity: String = surface_get(frame, "polarity") let confidence: String = surface_get(frame, "confidence") let importance: String = surface_get(frame, "importance") let salience: String = surface_get(frame, "salience") let rn: Int = str_len(relation) let csum: Int = 0 let i: Int = 0 while i < rn { let cc: Int = str_char_code(relation, i) let csum: Int = csum + cc let i: Int = i + 1 } let deg: Int = csum % 7 let third: Int = 4 if str_eq(polarity, "neg") { let third: Int = 3 } let sal_oct: Int = str_to_int_el(salience) let doff: Int = degree_offset(deg) let midi: Int = 60 + sal_oct * 12 + doff + third let conf_micro: Int = parse_micro(confidence) let dur_ms: Int = 200 + conf_micro / 1000 let imp_micro: Int = parse_micro(importance) let amp_pm: Int = 400 + imp_micro / 2000 let out: [Int] = native_list_empty() let out: [Int] = native_list_append(out, midi) let out: [Int] = native_list_append(out, dur_ms) let out: [Int] = native_list_append(out, amp_pm) return out } fn realize_audio(frames: [[String]], sig_lines: [String], path: String, rate: Int, table: [Int]) -> Int { let nf: Int = native_list_len(frames) let all: [Int] = native_list_empty() let count: Int = 0 let fi: Int = 0 while fi < nf { let frame: [String] = native_list_get(frames, fi) let plan: [Int] = plan_note(frame) let midi: Int = native_list_get(plan, 0) let dur_ms: Int = native_list_get(plan, 1) let amp_pm: Int = native_list_get(plan, 2) let freq: Int = freq_of_midi(midi) let note: [Int] = synth_from_sig(sig_lines, freq, dur_ms, amp_pm, rate, table) let nn: Int = native_list_len(note) let j: Int = 0 while j < nn { let all: [Int] = native_list_append(all, native_list_get(note, j)) let j: Int = j + 1 } let count: Int = count + nn let fi: Int = fi + 1 } let ok: Int = wav_write(path, all, count, rate) return count }