"""Edit CC0 field recordings into the frigate's restrained industrial soundscape. Run fetch_freighter_sources.py first. Requires numpy, no audio service/API. Output: 48 kHz, stereo, PCM16 WAV. Source credits live beside the assets. """ from pathlib import Path import json import shutil import struct import wave import numpy as np ROOT = Path(__file__).resolve().parents[2] SOURCE = ROOT / "tmp/freighter_audio_sources" OUT = ROOT / "assets/sounds/freighter" RATE = 48000 MANIFEST = {} def read(relative): raw = (SOURCE / relative).read_bytes() cursor = 12 fmt = data = None while cursor + 8 <= len(raw): name, size = struct.unpack_from("<4sI", raw, cursor) chunk = raw[cursor+8:cursor+8+size] if name == b"fmt ": fmt = chunk if name == b"data": data = chunk cursor += 8 + size + size % 2 kind, channels, rate, _, _, bits = struct.unpack_from(" max(rms.max()*.045,.00001))[0] if not len(active): raise ValueError("Silent recording") return x[max(0,int(active[0])*block-960):min(len(x),(int(active[-1])+1)*block+4800)] def room(x, amount=.14): # Short non-musical reflections, staggered between left and right walls. output = np.pad(x,((0,int(RATE*.65)),(0,0))) wet = eq(x,80,3300) for index, delay in enumerate([.037,.061,.089,.127,.173,.229,.307,.413,.521]): offset = int(delay*RATE) output[offset:offset+len(x)] += np.roll(wet,index%2,axis=1)*amount*.76**index return output def mix(*entries): length = max(int(offset*RATE)+len(x) for x,offset,gain in entries) output = np.zeros((length,2)) for x,offset,gain in entries: start = int(offset*RATE) output[start:start+len(x)] += x*gain return output def loop(x, seconds=1.2): n = min(int(seconds*RATE),len(x)//4) ramp = np.linspace(0,1,n)[:,None] return np.concatenate([x[n:-n],x[-n:]*(1-ramp)+x[:n]*ramp]) def save(name, x, sources, processing, looping=False): x = peak(x,-3.5 if looping else -2) assert np.isfinite(x).all() and np.max(np.abs(x)) < 1 with wave.open(str(OUT/(name+".wav")),"wb") as stream: stream.setparams((2,2,RATE,0,"NONE","not compressed")) stream.writeframes((x*32767).astype("=0)&(local<.24),np.sin(np.pi*np.clip(local/.24,0,1))**2,0) tone += env*(np.sin(2*np.pi*freq*t)+.11*np.sin(2*np.pi*freq*2*t)) save("service_chime",fade(room(np.repeat(tone[:,None],2,axis=1),.13)),[],"Original two-tone 540/680 Hz service annunciator, smooth envelope, short reflections") warning = mix((np.repeat(tone[:,None],2,axis=1),0,.8),(np.repeat(tone[:,None],2,axis=1),1.15,.7)) save("maintenance_alarm",fade(room(warning,.19)),[],"Original restrained maintenance warning, two paired tones with room reflections") for i in [0,1]: source = "scifi/Audio/computerNoise_%03d.ogg"%i shutil.copyfile(SOURCE/source,OUT/("console_%d.ogg"%(i+1))) MANIFEST["console_%d"%(i+1)] = {"sources":[source],"processing":"Unmodified CC0 Kenney electronic effect, mixed quietly in-engine"} (OUT/"manifest.json").write_text(json.dumps(MANIFEST,indent=2,ensure_ascii=False),encoding="utf-8") preview = np.zeros((int(34*RATE),2)) for name,db in [("reactor_loop",-14),("ventilation_loop",-21)]: bed = read(OUT/(name+".wav")) preview += bed[np.arange(len(preview))%len(bed)]*10**(db/20) for name,offset,db,pan in [("docking_clamp",1,-13,.65),("airlock_open",3,-13.5,-.48),("tool_1",8,-13,-.48),("metal_1",13,-15,-.48),("tool_3",17,-15,-.48),("pressure_release",23,-23,.6),("maintenance_alarm",26,-22,-.7),("airlock_close",30,-17,.2)]: clip = read(OUT/(name+".wav"))*10**(db/20) clip *= np.array([min(1,1-pan),min(1,1+pan)]) start = int(offset*RATE) preview[start:start+len(clip)] += clip[:len(preview)-start] preview = fade(peak(preview,-3),1.0,1.4) path = ROOT/"artifacts/freighter_ambience_demo.wav" with wave.open(str(path),"wb") as stream: stream.setparams((2,2,RATE,0,"NONE","not compressed")) stream.writeframes((preview*32767).astype("