- Инфраструктура: RTTI-дампы (rtti_dsp/full.json), декомпиляции DSP-классов (decomp_*.txt), Ghidra-скрипты (Dump*.java, ImportRtti*.java, Diag.java), depthcurve/curve_fits LUT. - Поведенческая модель sim_v5.py + sim.py (RMSE ~0.3dB), утилиты (measure, tt_sweep, patchparam, sweep, harness_*, verify_sim). - summary.md + roadmap.md (контракт из manual M1-M12, топология пайплайна из OCR, фазы A-C). - pipeline_ocr.txt: OCR диаграммы Appendix A (mid/side ручка, trim/mix/bypass порядок).
41 lines
1.3 KiB
Python
41 lines
1.3 KiB
Python
#!/usr/bin/env python3
|
|
import wave, array, math, sys
|
|
|
|
SR = 44100
|
|
|
|
def synth(path, freqs, dur=6.0, amp=1.0, gain_dbs=None, sr=SR):
|
|
# amp = target peak amplitude (1.0 = full scale)
|
|
n = int(sr*dur)
|
|
out = [0.0]*n
|
|
if gain_dbs is None:
|
|
gain_dbs = [-20*math.log10(len(freqs))*0 + 0]*len(freqs) # equal
|
|
for k, f in enumerate(freqs):
|
|
g = 1.0/len(freqs) if len(gain_dbs) != len(freqs) else 10**(gain_dbs[k]/20)
|
|
for i in range(n):
|
|
t = i/sr
|
|
out[i] += g*math.sin(2*math.pi*f*t)
|
|
peak = max(abs(v) for v in out) or 1.0
|
|
sc = amp/peak
|
|
a = array.array('h')
|
|
for i, v in enumerate(out):
|
|
t = i/sr
|
|
fade = min(1.0, t/0.03, (dur-t)/0.05)
|
|
a.append(max(-32767, min(32767, int(v*sc*fade*32767))))
|
|
w = wave.open(path, 'wb')
|
|
w.setnchannels(1); w.setsampwidth(2); w.setframerate(sr)
|
|
w.writeframes(a.tobytes())
|
|
w.close()
|
|
|
|
if __name__ == '__main__':
|
|
# usage: synth.py <freq> [<amp level dBFS or amplitude>] [<path>]
|
|
import os
|
|
freq = float(sys.argv[1])
|
|
amp = 1.0
|
|
path = '/home/m/soothe-bt/tone.wav'
|
|
for a in sys.argv[2:]:
|
|
try:
|
|
amp = float(a)
|
|
except ValueError:
|
|
path = a
|
|
synth(path, [freq], amp=amp)
|
|
print("wrote", path, freq, "Hz amp", amp) |