diff --git a/docs/LANES/LANE_E_NOTES.md b/docs/LANES/LANE_E_NOTES.md index 0d029c4..cf7a1db 100644 --- a/docs/LANES/LANE_E_NOTES.md +++ b/docs/LANES/LANE_E_NOTES.md @@ -1,5 +1,71 @@ # LANE E — cross-lane notes +## Round 13 — two genres, the gigKey rename (debt #1), lighter instruments + +**v3.0-beta THE DISTRICT.** All local work landed + verified; the one gated step (depot publish) +waits on John. Verification: core gates GREEN (scaffold · consistency 0-warn · citygen selfcheck +3273/3273 incl. v3 gig-layer + assets-on-disk), manifest validator `--depot` GREEN, and the real +app boots clean flags-off / `?gigs=1` (A's 3-venue district, 14 gigs/posters — all three gig beds +resolve by canonical key, no stale key, 0 console errors) / `?noassets=1&gigs=1` (0 audio + 0 GLB +fetches, silent-and-fine). + +### 1. gigKey rename — DONE (debt #1). **→ Lane B + Lane F: switch your readers.** +The manifest music key IS the gigKey, **always `gig-`** — one key, zero mapping. Renamed +`pubrock-live` → **`gig-pubrock`**; added `gig-grunge`, `gig-covers`. Files are now +`audio/music-gig-{pubrock,grunge,covers}.{ogg,m4a}`; the old `music-pubrock-live.*` are **deleted**. +Manifest regen'd + validates 0/0; no stale `pubrock-live` anywhere. +- **→ Lane F (interior_mode.js):** DELETE the `GIG_BED` bridge (the `{'gig-pubrock':'pubrock-live'}` + map + `gigBedKey()`); read `ra.gigKey` (`gig-`) straight against `AUDIO.music[...]`. The + bridge currently maps to the now-gone `pubrock-live` → **fails soft to silence** (I verified: no + crash, just a silent band) until you delete it. This is your R13 task #4. +- **→ Lane B (audio.js ~L274/276):** you hardcode `'pubrock-live'` for the street spill — read each + venue's `gigKey` bed instead (`AUDIO.music[venue.gigKey]`, i.e. `'gig-'+genreKey`). Same + fail-soft-silent until you switch. Your R13 task #2 (per-genre spill) covers this. +- **CITY_SPEC §v3 contract line (coordinate wording w/ A):** *"The audio manifest key for a gig + genre IS the gigKey, always `gig-` (`gig-pubrock`, `gig-grunge`, `gig-covers`). No + mapping table: C emits `room.audio.gigKey`, E names the bed, B/F read the same key."* + +### 2. Two new gig beds — grunge (band_room) + covers (RSL). +Procedural, seeded, loopable, OGG+M4A, loudnorm, $0 on-device (same discipline as R11/12). All three +are audibly + spectrally distinct (spectral centroids pubrock 2336 / grunge 2271 / covers 1115 Hz; +contact sheet `docs/shots/laneE/gig_beds_spectrograms.png`): +- **`gig-grunge`** (0.6 gain, 17s): down-tuned sludge, quiet-loud dynamic (clean-ish verse → full-fuzz + chorus — visible in the spectrogram), loose heavy kit. Sits at −14.6 LUFS (genre-dense; a 1.02 + *inter-sample* peak, not real clipping — plays at gain 0.6 so absolute output ≈0.6). +- **`gig-covers`** (0.5 gain, 19s): mellow RSL covers band — new drawbar-`organ()` voice, walking + bass, soft kit, sparse organ answer. Warm, dark highs (that low continuous band + horizontal + harmonic striations in the spectrogram = the organ). New synth helpers: `organ()`, `grungechord()`. +- **John may re-flavour genres (names only) — I synthesize to order.** Note it's now a coordinated + A+E change: the genreKey lives in A's `registry.js` (drives `gigKeyFor` → the bed name), so a + re-flavour = A renames the genreKey + E renames/re-synths the bed. Defaults grunge/covers are live. + +### 3. Instrument decimation — DONE. 5 GLBs → 14k tris each (≤15k target). **→ Lane D.** +Collapse-decimated (Blender, `pipeline/decimate_props.py`) — **not** voxel-remesh, so thin features +survive: mic pole + hanging cable, drum cymbals/stands, guitar necks/headstocks all read (thumbs +re-rendered + eyeballed). Dims/orientation/base-origin/WebP preserved; GLB-law clean (no Draco, tex +≤1024). Fleet **236k → 70k tris (−70%)** across the district's multiple stages. + +| instrument | tris (norm→14k) | footprint (m) | height | +|---|---|---|---| +| electric_guitar | 43.5k → 14k | 0.38×0.08 | 1.00 | +| bass_guitar | 36.0k → 14k | 0.34×0.13 | 1.15 | +| mic_stand | 34.1k → 14k | 0.57×0.57 | 1.70 | +| drum_kit | 26.2k → 14k | 1.50×1.24 | 1.30 | +| guitar_amp | 96.6k → 14k | 0.52×0.29 | 0.61 | + +- **→ Lane D:** wire against the SAME filenames (`procity_fit_{electric_guitar,bass_guitar,mic_stand, + drum_kit,guitar_amp}_01.glb`) — footprints/heights in the manifest are **unchanged**, so your + `instrumentFor(role)` fittings are unaffected; they're just lighter now. (debt #2) +- High-poly originals preserved as `_hi` variants in `_normalized/` (git-ignored) for the depot. + +### 4. Depot publish — GATED on John (the one outward-facing step). Gate 3 flag. +Ground truth: the 5 base instrument **names are LIVE** on the depot, but serving the **stale R12 +high-poly bytes** (43–96k). My 14k versions + the 5 `_hi` originals are **local-only** until a +re-publish. `validate_manifest --depot` is GREEN (names resolve) but can't see the stale bytes — so +**the decimation win only reaches production after a push.** On John's OK: `publish.py` overwrites the +5 base names with the 14k GLBs + uploads the 5 `_hi` originals (idempotent, reversible). Until then I +carry the flag; ?localdepot already serves the 14k so QA/local dev see the light versions. + ## Round 9 — VRAM audit verdict + one rec for C **VERDICT: fine to ship v2.0.** Texture VRAM ~240 MB default boot / ~420 MB worst-case (all flags + diff --git a/docs/shots/laneE/gig_beds_spectrograms.png b/docs/shots/laneE/gig_beds_spectrograms.png new file mode 100644 index 0000000..bac886a Binary files /dev/null and b/docs/shots/laneE/gig_beds_spectrograms.png differ diff --git a/pipeline/_gig_results.json b/pipeline/_gig_results.json index 34c3744..08bb9cc 100644 --- a/pipeline/_gig_results.json +++ b/pipeline/_gig_results.json @@ -6,16 +6,17 @@ "out": "pipeline/_normalized/procity_fit_electric_guitar_01.glb", "thumb": "web/assets/thumbs/procity_fit_electric_guitar_01.png", "tri_before": 181716, - "tri_after": 43484, + "tri_after": 14000, "size_m": [ 0.38, 1.0, - 0.077 + 0.078 ], "footprint": [ 0.38, 0.08 - ] + ], + "hi_variant": "procity_fit_electric_guitar_01_hi.glb" }, { "src": "/Users/m3ultra/Documents/procity/pipeline/.genprops/bass-guitar.glb", @@ -23,16 +24,17 @@ "out": "pipeline/_normalized/procity_fit_bass_guitar_01.glb", "thumb": "web/assets/thumbs/procity_fit_bass_guitar_01.png", "tri_before": 192238, - "tri_after": 36048, + "tri_after": 14000, "size_m": [ 0.337, 1.15, - 0.124 + 0.125 ], "footprint": [ 0.34, - 0.12 - ] + 0.13 + ], + "hi_variant": "procity_fit_bass_guitar_01_hi.glb" }, { "src": "/Users/m3ultra/Documents/procity/pipeline/.genprops/drum-kit.glb", @@ -40,16 +42,17 @@ "out": "pipeline/_normalized/procity_fit_drum_kit_01.glb", "thumb": "web/assets/thumbs/procity_fit_drum_kit_01.png", "tri_before": 197990, - "tri_after": 29890, + "tri_after": 14000, "size_m": [ - 1.498, + 1.497, 1.3, 1.242 ], "footprint": [ 1.5, 1.24 - ] + ], + "hi_variant": "procity_fit_drum_kit_01_hi.glb" }, { "src": "/Users/m3ultra/Documents/procity/pipeline/.genprops/guitar-amp.glb", @@ -57,16 +60,17 @@ "out": "pipeline/_normalized/procity_fit_guitar_amp_01.glb", "thumb": "web/assets/thumbs/procity_fit_guitar_amp_01.png", "tri_before": 180813, - "tri_after": 96592, + "tri_after": 14000, "size_m": [ - 0.511, - 0.6, - 0.285 + 0.516, + 0.607, + 0.294 ], "footprint": [ - 0.51, + 0.52, 0.29 - ] + ], + "hi_variant": "procity_fit_guitar_amp_01_hi.glb" }, { "src": "/Users/m3ultra/Documents/procity/pipeline/.genprops/mic-stand.glb", @@ -74,16 +78,17 @@ "out": "pipeline/_normalized/procity_fit_mic_stand_01.glb", "thumb": "web/assets/thumbs/procity_fit_mic_stand_01.png", "tri_before": 162256, - "tri_after": 34124, + "tri_after": 14000, "size_m": [ 0.571, - 1.7, + 1.702, 0.573 ], "footprint": [ 0.57, 0.57 - ] + ], + "hi_variant": "procity_fit_mic_stand_01_hi.glb" } ], "errors": [] diff --git a/pipeline/build_manifest.py b/pipeline/build_manifest.py index 32a0850..654f99d 100644 --- a/pipeline/build_manifest.py +++ b/pipeline/build_manifest.py @@ -33,7 +33,11 @@ AUDIO_MUSIC = { # interior music key -> (gain, shop types, note) "milkbar": (0.50, ["milkbar", "general"], "milk bar radio"), "video-synth": (0.50, ["video"], "video store synth hum"), "arcade": (0.50, ["arcade", "dept"], "arcade cabinet"), - "pubrock-live":(0.60, ["pub"], "gig live bed — F plays via room.audio gigKey when a gig is on"), + # ── gig beds: the manifest key IS the gigKey, ALWAYS `gig-` (R13 debt #1; one key, + # zero mapping). C emits room.audio.gigKey='gig-'+genreKey, F/B read this key directly. ── + "gig-pubrock": (0.60, ["pub"], "gig live bed — pub-rock (pub); F plays via room.audio gigKey"), + "gig-grunge": (0.60, ["band_room"], "gig live bed — grunge (band_room)"), + "gig-covers": (0.50, ["rsl"], "gig live bed — covers (RSL); mellower, organ-led"), } AUDIO_INTERIOR_AMB = {"crowd-walla": (0.4, "gig crowd layer (interior; F plays when gig on)")} # R12 AUDIO_SFX = {"doorbell": 0.7, "door-open": 0.6, "till": 0.7, "riffle": 0.6, # one-shots @@ -147,8 +151,9 @@ def build_audio(): footsteps[surf] = vs if footsteps: sfx["footstep"] = footsteps - if "pubrock-live" in music: # R12: flag gig-only assets for F - music["pubrock-live"]["gig"] = True + for k in music: # flag gig-only beds for F (any 'gig-*' key) + if k.startswith("gig-"): + music[k]["gig"] = True if "applause" in sfx: sfx["applause"]["gig"] = True return {"ambience": ambience, "sfx": sfx, "music": music} diff --git a/pipeline/decimate_props.py b/pipeline/decimate_props.py new file mode 100644 index 0000000..12eb2fc --- /dev/null +++ b/pipeline/decimate_props.py @@ -0,0 +1,152 @@ +"""PROCITY prop decimation — collapse-decimate a normalized GLB to a tri budget while KEEPING its +original materials + UVs (no voxel-remesh, no re-bake). The detail-preserving path for props whose +thin features (mic pole, cymbal stands, guitar necks) a voxel-remesh would fatten. + +Runs headless on this box's Blender: + BL=/Applications/Blender.app/Contents/MacOS/Blender + "$BL" --background --python decimate_props.py -- BATCH.json OUT_DIR [THUMB_DIR] + +BATCH.json: [ {"src":"web/assets/models/procity_fit_guitar_amp_01.glb", + "id":"guitar_amp", "category":"fit", "tris":14000} , ... ] + +vs bake_lowpoly.py: that one throws topology away (voxel remesh → bake) to beat the multi-shell floor +on boxy hero props; this one collapses edges in place to keep silhouettes on thin/among-shell props. +Pick per asset by eyeballing the thumbs — anything that floors above budget or reads wrong here goes +to bake_lowpoly.py instead. Same house GLB law out: metres, +Y up, base-origin, WebP, no Draco. +""" +import bpy, sys, os, json, math +from mathutils import Vector + +ARGV = sys.argv[sys.argv.index("--") + 1:] +BATCH, OUT_DIR = ARGV[0], (ARGV[1] if len(ARGV) > 1 else "/tmp/procity_decim") +THUMB_DIR = ARGV[2] if len(ARGV) > 2 else os.path.join(OUT_DIR, "thumbs") +os.makedirs(OUT_DIR, exist_ok=True); os.makedirs(THUMB_DIR, exist_ok=True) + + +def wipe(): + for o in list(bpy.data.objects): + bpy.data.objects.remove(o, do_unlink=True) + for coll in (bpy.data.meshes, bpy.data.materials, bpy.data.images): + for d in list(coll): + if d.users == 0: + coll.remove(d) + + +def import_join(src): + bpy.ops.import_scene.gltf(filepath=src) + meshes = [o for o in bpy.data.objects if o.type == 'MESH'] + bpy.ops.object.select_all(action='DESELECT') + for m in meshes: + m.select_set(True) + bpy.context.view_layer.objects.active = meshes[0] + if len(meshes) > 1: + bpy.ops.object.join() + obj = bpy.context.view_layer.objects.active + bpy.ops.object.transform_apply(location=True, rotation=True, scale=True) + return obj + + +def bounds(obj): + bb = [obj.matrix_world @ Vector(c) for c in obj.bound_box] + mn = Vector((min(v.x for v in bb), min(v.y for v in bb), min(v.z for v in bb))) + mx = Vector((max(v.x for v in bb), max(v.y for v in bb), max(v.z for v in bb))) + return mn, mx + + +def tri_count(obj): + """Honest triangle count (n-gons split): sum of (loop_total - 2) per polygon.""" + return sum(len(p.vertices) - 2 for p in obj.data.polygons) + + +def decimate(obj, target): + # triangulate first so the collapse ratio targets triangles honestly, then collapse-decimate. + bpy.ops.object.select_all(action='DESELECT') + obj.select_set(True); bpy.context.view_layer.objects.active = obj + bpy.ops.object.mode_set(mode='EDIT') + bpy.ops.mesh.select_all(action='SELECT') + bpy.ops.mesh.quads_convert_to_tris() + bpy.ops.object.mode_set(mode='OBJECT') + n = len(obj.data.polygons) + if n > target: + dm = obj.modifiers.new("dec", 'DECIMATE') + dm.decimate_type = 'COLLAPSE' + dm.ratio = max(0.01, target / n) + dm.use_collapse_triangulate = True + bpy.ops.object.modifier_apply(modifier=dm.name) + return n + + +def base_origin(obj): + mn, mx = bounds(obj) + obj.location -= Vector(((mn.x + mx.x) / 2, (mn.y + mx.y) / 2, mn.z)) + bpy.ops.object.transform_apply(location=True) + + +def render_thumb(obj, png_name): + scn = bpy.context.scene + mn, mx = bounds(obj); ctr = (mn + mx) / 2 + size = max((mx - mn).x, (mx - mn).y, (mx - mn).z) or 1.0 + for o in list(bpy.data.objects): + if o.type in ('CAMERA', 'LIGHT'): + bpy.data.objects.remove(o, do_unlink=True) + w = bpy.data.worlds.get("tw") or bpy.data.worlds.new("tw") + scn.world = w; w.use_nodes = True + w.node_tree.nodes["Background"].inputs[1].default_value = 1.05 + bpy.ops.object.light_add(type='AREA', location=(ctr.x + size, ctr.y - size, mx.z + size)) + bpy.context.object.data.energy = 800 * size; bpy.context.object.data.size = 6 + bpy.ops.object.camera_add(); cam = bpy.context.object; scn.camera = cam + r = size * 1.9 + cam.location = Vector((ctr.x + r * 0.8, ctr.y - r, ctr.z + size * 0.6)) + cam.rotation_euler = (ctr - cam.location).to_track_quat('-Z', 'Y').to_euler() + scn.render.engine = 'BLENDER_WORKBENCH' + scn.display.shading.light = 'STUDIO'; scn.display.shading.color_type = 'TEXTURE' + scn.display.shading.show_cavity = True + scn.view_settings.view_transform = 'Standard' + scn.render.film_transparent = True + scn.render.resolution_x = scn.render.resolution_y = 256 + p = os.path.join(THUMB_DIR, png_name); scn.render.filepath = p + bpy.ops.render.render(write_still=True) + return p + + +def decimate_one(spec): + wipe() + src = spec["src"] if os.path.isabs(spec["src"]) else os.path.join(os.getcwd(), spec["src"]) + obj = import_join(src) + tri_before = tri_count(obj) + decimate(obj, spec.get("tris", 14000)) + base_origin(obj) + tri_after = tri_count(obj) + name = f"procity_{spec['category']}_{spec['id']}_01.glb" + out = os.path.join(OUT_DIR, name) + bpy.ops.object.select_all(action='DESELECT') + obj.select_set(True); bpy.context.view_layer.objects.active = obj + bpy.ops.export_scene.gltf(filepath=out, export_format='GLB', export_yup=True, + use_selection=True, export_image_format='WEBP', export_image_quality=88) + thumb = render_thumb(obj, name.replace(".glb", ".png")) + mn, mx = bounds(obj) + # Blender is Z-up: height is Z, the ground footprint is X×Y (matches bake_lowpoly.py + _gig_results). + return {"file": name, "tri_before": tri_before, "tri_after": tri_after, + "size_m": [round(mx.x - mn.x, 3), round(mx.z - mn.z, 3), round(mx.y - mn.y, 3)], + "footprint": [round(mx.x - mn.x, 2), round(mx.y - mn.y, 2)], "thumb": thumb, "out": out} + + +def main(): + specs = json.load(open(BATCH)) + results, errors = [], [] + for spec in specs: + try: + r = decimate_one(spec) + results.append(r) + print(f"OK {r['file']:40} {r['tri_before']:>6}→{r['tri_after']:>5} tris " + f"footprint {r['footprint']} h {r['size_m'][1]}") + except Exception as e: + import traceback; traceback.print_exc() + errors.append({"id": spec.get("id"), "error": str(e)}) + print(f"ERR {spec.get('id')}: {e}") + json.dump({"results": results, "errors": errors}, + open(os.path.join(OUT_DIR, "_decim_results.json"), "w"), indent=2) + print(f"\nDECIM_DONE ok={len(results)} err={len(errors)} → {OUT_DIR}") + + +main() diff --git a/pipeline/gen_audio.py b/pipeline/gen_audio.py index 0bae1df..8fa82d1 100644 --- a/pipeline/gen_audio.py +++ b/pipeline/gen_audio.py @@ -248,6 +248,16 @@ def pad(midi, dur, detune=0.008): return x * adsr(dur, 0.4, 0.2, 0.8, 0.5) +def organ(midi, dur): + # drawbar-organ-ish: stacked harmonic sines (16'/8'/5⅓'/4'/2') + a gentle Leslie tremolo and a + # soft percussive attack. Warm and mellow — the RSL covers-band voice. + f = midi_hz(midi); t = tarr(dur) + drawbars = [(0.5, 1), (1.0, 2), (0.55, 3), (0.4, 4), (0.22, 6)] + x = sum(a * np.sin(2 * np.pi * f * mult * t) for a, mult in drawbars) / 2.6 + trem = 1 + 0.06 * np.sin(2 * np.pi * 5.2 * t) # gentle Leslie tremolo + return x * trem * adsr(dur, 0.012, 0.15, 0.82, 0.12) + + def kick(dur=0.28): n = int(dur * SR) f = np.linspace(120, 45, n) @@ -602,6 +612,16 @@ def powerchord(root, dur, drive=3.5): return x * adsr(dur, 0.003, 0.05, 0.7, 0.04) +def grungechord(root, dur, drive=5.0): + # sludgier, down-tuned cousin of powerchord: root+5th+octave, a square sub-octave for weight, + # heavier fuzz and a darker filter. `drive` swings the quiet-loud grunge dynamic. + x = (osc(midi_hz(root), dur, "saw") + osc(midi_hz(root+7), dur, "saw") + + osc(midi_hz(root+12), dur, "saw") + 0.6 * osc(midi_hz(root-12), dur, "square")) + x = softclip(x * 0.5, drive) # thick fuzz + x = biquad_lp(x, 2400, 0.9) # darker than pub-rock's 3200 + return x * adsr(dur, 0.004, 0.06, 0.75, 0.06) + + def music_pubrock(name): # the gig live bed — 90s Aussie pub-rock: driving power chords, punchy bass, rock kit. Louder # + rawer than the shop beds. Original I-bVII-IV-I riff in E. @@ -617,6 +637,51 @@ def music_pubrock(name): return loop_music(body, 0.06) +def music_grunge(name): + # band-room gig bed — 90s grunge: down-tuned sludge riff with a quiet-loud dynamic (clean-ish + # verse → full-fuzz chorus) and a loose, heavy kit. Original i-bVII-bVI riff, low + dark. Slower + # and murkier than the pub-rock bed so the two venues never sound the same. + r = rng(name); bpm = 112 + prog = [35, 35, 42, 40] # 4 bars (B B E D-ish), played verse then chorus + verse = render_line([(root, 0.5) for root in prog for _ in range(8)], # palm-mute 8ths, cleanish + bpm, lambda m, d: grungechord(m, d, 2.4) * 0.32, gap=0.03) + chorus = render_line([(root, 0.5) for root in prog for _ in range(8)], # full sludge + bpm, lambda m, d: grungechord(m, d, 5.5) * 0.55, gap=0.0) + guitar = np.concatenate([verse, chorus]) + bass = render_line([(root - 12, 0.5) for root in prog for _ in range(8)] * 2, + bpm, lambda m, d: synth_note(m, d, "saw", 0.004, 0.06, 0.7, 0.04, cutoff=520)) + vdr = render_drums({"k": [0, 8], "s": [4, 12], "h": [0, 4, 8, 12]}, bpm, 4, r) # sparse verse + cdr = render_drums({"k": [0, 3, 6, 8, 11, 14], "s": [4, 12], + "h": [0, 2, 4, 6, 8, 10, 12, 14], "o": [7, 15]}, bpm, 4, r) # driving chorus + drums = np.concatenate([vdr, cdr]) + body = mix(pan(guitar, -0.1)*0.72, pan(bass, 0.08)*0.62, pan(drums, 0)*0.62) # headroom: dense chorus + return loop_music(body, 0.10) + + +def music_covers(name): + # RSL gig bed — a covers band at the returned-servicemen's club: mellow, organ-led easy-listening. + # Warm I-vi-IV-V turnaround (original voicing), walking bass, soft kit, a sparse organ answer. + # Cleaner and warmer than the pub/band-room beds — the crowd-cap stress room reads relaxed. + r = rng(name); bpm = 100; bars = 8 + spb = 60 / bpm + prog = [48, 45, 53, 55] # C Am F G7 (roots), one per bar, ×2 = 8 bars + chords = {48: [0, 4, 7], 45: [0, 3, 7], 53: [0, 4, 7], 55: [0, 4, 7, 10]} # G7 on the turnaround + org_bars = [] + for root in prog * 2: # sustained organ triad per bar + ch = mix(*[organ(root + 12 + iv, 4 * spb) for iv in chords[root]]) * 0.5 + cell = int(4 * spb * SR) + org_bars.append(np.concatenate([ch, np.zeros(max(cell - len(ch), 0))])[:cell]) + organ_part = np.concatenate(org_bars) + walk = sum([[(root - 12, 1.0), (root - 5, 1.0), (root, 1.0), (root - 3, 1.0)] for root in prog * 2], []) + bass = render_line(walk, bpm, lambda m, d: synth_note(m, d, "tri", 0.006, 0.12, 0.6, 0.06, cutoff=800)) + drums = render_drums({"k": [0, 8], "s": [4, 12], "h": [0, 2, 4, 6, 8, 10, 12, 14]}, bpm, bars, r) * 0.5 + lead_notes = sum([[(root + 12, 1.5), (None, 0.5), (root + 19, 1.0), (None, 1.0)] for root in prog * 2], []) + lead = render_line(lead_notes, bpm, # gentle organ-ish answer + lambda m, d: synth_note(m, d, "tri", 0.02, 0.12, 0.5, 0.2, cutoff=1600, vib=0.8)) + body = mix(pan(organ_part, -0.1)*0.7, pan(bass, 0.05)*0.8, pan(drums, 0)*0.5, pan(lead, 0.25)*0.3) + return loop_music(body, 0.18) # warmer reverb than the rock beds + + # ───────────────────────────────────────────────────────────────────────────── # the pack: name -> (category, generator, {loop, gain, note}) # ───────────────────────────────────────────────────────────────────────────── @@ -648,8 +713,11 @@ PACK = { "music-milkbar": ("music", music_milkbar, dict(loop=True, gain=0.5, note="milk bar radio")), "music-video-synth": ("music", music_video_synth, dict(loop=True, gain=0.5, note="video store")), "music-arcade": ("music", music_arcade, dict(loop=True, gain=0.5, note="arcade")), - # ── round-12 gig layer (?gigs=1 consumers: F plays these when a gig is on) ──────────── - "music-pubrock-live": ("music", music_pubrock, dict(loop=True, gain=0.6, note="gig live bed — pub-rock; F plays via room.audio gigKey")), + # ── gig layer (?gigs=1 consumers: F plays these when a gig is on) — the manifest key IS the + # gigKey, ALWAYS `gig-` (R13 debt #1: one key, zero mapping). One bed per venue kind. ── + "music-gig-pubrock": ("music", music_pubrock, dict(loop=True, gain=0.6, note="gig live bed — pub-rock (pub); F plays via room.audio gigKey")), + "music-gig-grunge": ("music", music_grunge, dict(loop=True, gain=0.6, note="gig live bed — grunge (band_room)")), + "music-gig-covers": ("music", music_covers, dict(loop=True, gain=0.5, note="gig live bed — covers (RSL); mellower, organ-led")), "ambience-crowd-walla":("ambience", amb_crowd_walla, dict(loop=True, gain=0.4, note="gig crowd layer (interior)")), "sfx-applause": ("sfx", sfx_applause, dict(loop=False, gain=0.7, note="between-sets cheer")), } diff --git a/web/assets/audio/music-gig-covers.m4a b/web/assets/audio/music-gig-covers.m4a new file mode 100644 index 0000000..83ea71f Binary files /dev/null and b/web/assets/audio/music-gig-covers.m4a differ diff --git a/web/assets/audio/music-gig-covers.ogg b/web/assets/audio/music-gig-covers.ogg new file mode 100644 index 0000000..51b873a Binary files /dev/null and b/web/assets/audio/music-gig-covers.ogg differ diff --git a/web/assets/audio/music-gig-grunge.m4a b/web/assets/audio/music-gig-grunge.m4a new file mode 100644 index 0000000..d674478 Binary files /dev/null and b/web/assets/audio/music-gig-grunge.m4a differ diff --git a/web/assets/audio/music-gig-grunge.ogg b/web/assets/audio/music-gig-grunge.ogg new file mode 100644 index 0000000..b0f1325 Binary files /dev/null and b/web/assets/audio/music-gig-grunge.ogg differ diff --git a/web/assets/audio/music-gig-pubrock.m4a b/web/assets/audio/music-gig-pubrock.m4a new file mode 100644 index 0000000..20083da Binary files /dev/null and b/web/assets/audio/music-gig-pubrock.m4a differ diff --git a/web/assets/audio/music-gig-pubrock.ogg b/web/assets/audio/music-gig-pubrock.ogg new file mode 100644 index 0000000..c75ea07 Binary files /dev/null and b/web/assets/audio/music-gig-pubrock.ogg differ diff --git a/web/assets/audio/music-pubrock-live.m4a b/web/assets/audio/music-pubrock-live.m4a deleted file mode 100644 index 8a5188d..0000000 Binary files a/web/assets/audio/music-pubrock-live.m4a and /dev/null differ diff --git a/web/assets/audio/music-pubrock-live.ogg b/web/assets/audio/music-pubrock-live.ogg deleted file mode 100644 index 95bba24..0000000 Binary files a/web/assets/audio/music-pubrock-live.ogg and /dev/null differ diff --git a/web/assets/manifest.json b/web/assets/manifest.json index 65851ad..cde0c26 100644 --- a/web/assets/manifest.json +++ b/web/assets/manifest.json @@ -776,7 +776,7 @@ "file": "procity_fit_bass_guitar_01.glb", "footprint": [ 0.34, - 0.12 + 0.13 ], "height": 1.15, "thumb": "thumbs/procity_fit_bass_guitar_01.png" @@ -793,10 +793,10 @@ "guitar_amp": { "file": "procity_fit_guitar_amp_01.glb", "footprint": [ - 0.51, + 0.52, 0.29 ], - "height": 0.6, + "height": 0.607, "thumb": "thumbs/procity_fit_guitar_amp_01.png" }, "mic_stand": { @@ -805,7 +805,7 @@ 0.57, 0.57 ], - "height": 1.7, + "height": 1.702, "thumb": "thumbs/procity_fit_mic_stand_01.png" }, "cash_register": { @@ -1115,15 +1115,37 @@ ], "note": "arcade cabinet" }, - "pubrock-live": { - "file": "audio/music-pubrock-live.ogg", - "fallback": "audio/music-pubrock-live.m4a", + "gig-pubrock": { + "file": "audio/music-gig-pubrock.ogg", + "fallback": "audio/music-gig-pubrock.m4a", "loop": true, "gain": 0.6, "types": [ "pub" ], - "note": "gig live bed \u2014 F plays via room.audio gigKey when a gig is on", + "note": "gig live bed \u2014 pub-rock (pub); F plays via room.audio gigKey", + "gig": true + }, + "gig-grunge": { + "file": "audio/music-gig-grunge.ogg", + "fallback": "audio/music-gig-grunge.m4a", + "loop": true, + "gain": 0.6, + "types": [ + "band_room" + ], + "note": "gig live bed \u2014 grunge (band_room)", + "gig": true + }, + "gig-covers": { + "file": "audio/music-gig-covers.ogg", + "fallback": "audio/music-gig-covers.m4a", + "loop": true, + "gain": 0.5, + "types": [ + "rsl" + ], + "note": "gig live bed \u2014 covers (RSL); mellower, organ-led", "gig": true } } diff --git a/web/assets/thumbs/procity_fit_bass_guitar_01.png b/web/assets/thumbs/procity_fit_bass_guitar_01.png index cfe2450..89b5ba6 100644 Binary files a/web/assets/thumbs/procity_fit_bass_guitar_01.png and b/web/assets/thumbs/procity_fit_bass_guitar_01.png differ diff --git a/web/assets/thumbs/procity_fit_drum_kit_01.png b/web/assets/thumbs/procity_fit_drum_kit_01.png index 5b639e0..414d795 100644 Binary files a/web/assets/thumbs/procity_fit_drum_kit_01.png and b/web/assets/thumbs/procity_fit_drum_kit_01.png differ diff --git a/web/assets/thumbs/procity_fit_electric_guitar_01.png b/web/assets/thumbs/procity_fit_electric_guitar_01.png index a73c735..e056fc4 100644 Binary files a/web/assets/thumbs/procity_fit_electric_guitar_01.png and b/web/assets/thumbs/procity_fit_electric_guitar_01.png differ diff --git a/web/assets/thumbs/procity_fit_guitar_amp_01.png b/web/assets/thumbs/procity_fit_guitar_amp_01.png index cad7afb..7c33482 100644 Binary files a/web/assets/thumbs/procity_fit_guitar_amp_01.png and b/web/assets/thumbs/procity_fit_guitar_amp_01.png differ diff --git a/web/assets/thumbs/procity_fit_mic_stand_01.png b/web/assets/thumbs/procity_fit_mic_stand_01.png index 79fcf46..4634076 100644 Binary files a/web/assets/thumbs/procity_fit_mic_stand_01.png and b/web/assets/thumbs/procity_fit_mic_stand_01.png differ