Lane E R13 (v3.0-beta): gigKey rename + grunge/covers beds + instrument decimation

- debt #1: the manifest music key IS the gigKey, always gig-<genreKey>. Renamed
  pubrock-live -> gig-pubrock (files + gen_audio + build_manifest); old files deleted.
  B/F switch readers, F deletes the GIG_BED bridge (both fail soft to silence til then).
- two new gig beds: gig-grunge (band_room, quiet-loud dynamic) + gig-covers (RSL, mellow
  organ-led); procedural/seeded/loopable/$0. New organ() + grungechord() synth helpers.
- decimate the 5 instrument GLBs to 14k tris each (was 43-96k; fleet 236k->70k, -70%) via
  collapse (thin features survive); _hi hi-poly originals kept for depot; thumbs re-rendered.
- manifest regen (validates 0/0 incl --depot); app boots clean flags-off / ?gigs=1 (3-venue
  district, all 3 beds resolve) / ?noassets=1&gigs=1 (0 audio+GLB fetches).
- depot re-publish (14k bytes + _hi originals) gated on John's OK.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
m3ultra 2026-07-15 23:03:14 +10:00
parent 6fba79a1cb
commit bf98b41b16
20 changed files with 350 additions and 32 deletions

View File

@ -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-<genreKey>`** — 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-<genre>`) 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-<genreKey>` (`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** (4396k). 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 +

Binary file not shown.

After

Width:  |  Height:  |  Size: 618 KiB

View File

@ -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": []

View File

@ -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-<genreKey>` (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}

152
pipeline/decimate_props.py Normal file
View File

@ -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()

View File

@ -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-<genreKey>` (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")),
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -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
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB