#include "game/sound.h" #include "core/audio.h" #include "world/camp.h" #include "world/weather.h" #include #define HEAR_REF 4.0f /* m: full loudness this near ... */ #define HEAR_MOST 70.0f /* ... nothing from further than this */ #define STEP_GAIN 0.45f /* steps are soft */ #define BIRD_EVERY 1.6f /* s: a bird now and then, by day */ static unsigned g_rng = 0xC0FFEEu; static float dice(void) { g_rng = g_rng * 1664525u + 1013904223u; return (float)(g_rng >> 8) / 16777216.0f; } typedef struct { Vec3 eye, right; } Ear; /* How loud from here (0..1) and from which side (-1 left .. 1 right); muffled: the high end goes further off. */ static float hear(const Ear *ear, Vec3 at, float *pan, float *dull) { Vec3 to = vec3_sub(at, ear->eye); float d = vec3_length(to); *pan = d > 0.3f ? 0.85f * vec3_dot(vec3_scale(to, 1.0f / d), ear->right) : 0.0f; if (dull) *dull = 1.0f / (1.0f + d / 25.0f); if (d > HEAR_MOST) return 0.0f; return HEAR_REF / (HEAR_REF + d) * (1.0f - d / HEAR_MOST); } static void play(Wave wave, float f0, float f1, float low, float high, float attack, float decay, float length, float gain, float pan, float delay) { Voice v = { wave, f0, f1, low, high, attack, decay, length, gain, pan, delay }; Audio_play(&v); } static void step(const Noise *n, float g, float pan, float dull) { g *= STEP_GAIN * (0.85f + 0.3f * dice()); float lo = dull; switch (n->surface) { case STEP_GRAVEL: play(WAVE_CRACKLE, 900.0f, 0.0f, 7000.0f * lo, 900.0f, 0.004f, 0.05f, 0.14f, 0.9f * g, pan, 0.0f); play(WAVE_NOISE, 0.0f, 0.0f, 2500.0f * lo, 300.0f, 0.004f, 0.04f, 0.1f, 0.4f * g, pan, 0.0f); break; case STEP_HARD: play(WAVE_NOISE, 0.0f, 0.0f, 3500.0f * lo, 400.0f, 0.001f, 0.018f, 0.06f, 0.7f * g, pan, 0.0f); play(WAVE_SINE, 160.0f, 110.0f, 0.0f, 0.0f, 0.001f, 0.02f, 0.06f, 0.35f * g, pan, 0.0f); break; case STEP_WOOD: play(WAVE_SINE, 150.0f, 120.0f, 0.0f, 0.0f, 0.002f, 0.05f, 0.15f, 0.6f * g, pan, 0.0f); play(WAVE_NOISE, 0.0f, 0.0f, 1500.0f * lo, 200.0f, 0.002f, 0.025f, 0.08f, 0.35f * g, pan, 0.0f); break; case STEP_EARTH: play(WAVE_NOISE, 0.0f, 0.0f, 900.0f * lo, 90.0f, 0.004f, 0.045f, 0.12f, 0.7f * g, pan, 0.0f); break; case STEP_MUD: /* a squelch */ play(WAVE_NOISE, 0.0f, 0.0f, 700.0f * lo, 60.0f, 0.01f, 0.08f, 0.2f, 0.7f * g, pan, 0.0f); play(WAVE_SINE, 220.0f, 90.0f, 0.0f, 0.0f, 0.01f, 0.07f, 0.16f, 0.35f * g, pan, 0.03f); break; case STEP_WATER: /* a splash */ play(WAVE_NOISE, 0.0f, 0.0f, 6000.0f * lo, 900.0f, 0.005f, 0.09f, 0.25f, 0.6f * g, pan, 0.0f); play(WAVE_CRACKLE, 300.0f, 0.0f, 5000.0f * lo, 1500.0f, 0.005f, 0.1f, 0.25f, 0.4f * g, pan, 0.02f); break; default: /* grass: a soft swish */ play(WAVE_NOISE, 0.0f, 0.0f, 2800.0f * lo, 700.0f, 0.01f, 0.05f, 0.14f, 0.45f * g, pan, 0.0f); break; } } static void noiseOut(const Noise *n, const Ear *ear) { float pan, dull, g = hear(ear, n->at, &pan, &dull) * n->loud; if (g < 0.003f) return; switch (n->kind) { case NOISE_STEP: step(n, g, pan, dull); break; case NOISE_CHOP: /* the edge into the wood: a knock and a crack */ play(WAVE_SINE, 110.0f, 70.0f, 0.0f, 0.0f, 0.001f, 0.06f, 0.2f, 0.8f * g, pan, 0.0f); play(WAVE_NOISE, 0.0f, 0.0f, 3000.0f * dull, 250.0f, 0.001f, 0.035f, 0.12f, 0.7f * g, pan, 0.0f); play(WAVE_CRACKLE, 700.0f, 0.0f, 6000.0f * dull, 1200.0f, 0.001f, 0.04f, 0.1f, 0.4f * g, pan, 0.01f); break; case NOISE_CLINK: { /* glass on glass, twice */ float f = 2400.0f + 600.0f * dice(); play(WAVE_SINE, f, 0.0f, 0.0f, 0.0f, 0.001f, 0.12f, 0.5f, 0.35f * g, pan, 0.0f); play(WAVE_SINE, f * 1.62f, 0.0f, 0.0f, 0.0f, 0.001f, 0.08f, 0.4f, 0.22f * g, pan, 0.0f); play(WAVE_SINE, f * 1.1f, 0.0f, 0.0f, 0.0f, 0.001f, 0.07f, 0.3f, 0.2f * g, pan, 0.07f + 0.05f * dice()); break; } case NOISE_SMASH: /* glass breaking: a crash and the shards tinkling */ play(WAVE_CRACKLE, 2500.0f, 0.0f, 9000.0f * dull, 1800.0f, 0.001f, 0.12f, 0.5f, 0.9f * g, pan, 0.0f); for (int k = 0; k < 6; k++) play(WAVE_SINE, 3000.0f + 4000.0f * dice(), 0.0f, 0.0f, 0.0f, 0.001f, 0.05f, 0.2f, 0.18f * g, pan, 0.03f + 0.25f * dice()); break; case NOISE_THUD: /* somebody down on the ground */ play(WAVE_SINE, 75.0f, 45.0f, 0.0f, 0.0f, 0.002f, 0.12f, 0.4f, 0.9f * g, pan, 0.0f); play(WAVE_NOISE, 0.0f, 0.0f, 500.0f * dull, 40.0f, 0.002f, 0.08f, 0.3f, 0.6f * g, pan, 0.0f); break; case NOISE_CRASH: /* a tree down: branches cracking, the trunk hitting the ground */ play(WAVE_CRACKLE, 400.0f, 0.0f, 5000.0f * dull, 600.0f, 0.02f, 0.5f, 2.0f, 0.9f * g, pan, 0.0f); play(WAVE_NOISE, 0.0f, 0.0f, 260.0f, 25.0f, 0.03f, 0.7f, 2.5f, 1.0f * g, pan, 0.05f); play(WAVE_SINE, 55.0f, 35.0f, 0.0f, 0.0f, 0.01f, 0.4f, 1.5f, 0.8f * g, pan, 0.05f); break; case NOISE_VOICE: { /* words heard, not made out: a murmur, syllable after syllable, rising and falling */ static const float PITCH[3] = { 95.0f, 125.0f, 215.0f }; int voice = n->surface & 3, syllables = n->surface >> 2; float f = PITCH[voice > 2 ? 1 : voice] * (0.92f + 0.16f * dice()), at = 0.0f; for (int k = 0; k < syllables; k++) { float lift = 1.0f + 0.12f * sinf(1.7f * (float)k + 6.0f * dice()) - (k == syllables - 1 ? 0.1f : 0.0f); /* (and down at the end) */ float len = 0.09f + 0.08f * dice(); play(WAVE_SAW, f * lift, f * lift * (0.95f + 0.1f * dice()), (700.0f + 1100.0f * dice()) * dull, 150.0f, 0.02f, 0.05f, len, 0.22f * g, pan, at); at += len + 0.03f + (dice() < 0.15f ? 0.12f : 0.0f); /* (a pause now and then) */ } break; } case NOISE_BARK: /* woof */ for (int k = 0; k < n->surface && k < 4; k++) { float at = 0.3f * (float)k; play(WAVE_SAW, 420.0f + 40.0f * dice(), 250.0f, 1900.0f * dull, 220.0f, 0.004f, 0.06f, 0.16f, 0.7f * g, pan, at); play(WAVE_NOISE, 0.0f, 0.0f, 2500.0f * dull, 400.0f, 0.003f, 0.04f, 0.1f, 0.35f * g, pan, at); } break; case NOISE_GULP: if (n->surface == 1) /* a bite: crunch */ for (int k = 0; k < 3; k++) play(WAVE_CRACKLE, 900.0f, 0.0f, 4500.0f * dull, 500.0f, 0.003f, 0.04f, 0.1f, 0.5f * g, pan, 0.13f * (float)k); else { /* a swallow */ play(WAVE_SINE, 320.0f, 150.0f, 0.0f, 0.0f, 0.01f, 0.05f, 0.12f, 0.35f * g, pan, 0.0f); play(WAVE_NOISE, 0.0f, 0.0f, 900.0f * dull, 100.0f, 0.01f, 0.06f, 0.15f, 0.2f * g, pan, 0.02f); } break; case NOISE_FLUSH: /* the cistern: a rush, then gurgling down */ play(WAVE_NOISE, 0.0f, 0.0f, 2600.0f * dull, 300.0f, 0.25f, 1.0f, 3.0f, 0.6f * g, pan, 0.0f); play(WAVE_NOISE, 0.0f, 0.0f, 500.0f * dull, 60.0f, 0.4f, 1.4f, 3.5f, 0.5f * g, pan, 0.3f); play(WAVE_CRACKLE, 60.0f, 0.0f, 1200.0f * dull, 100.0f, 0.5f, 1.2f, 3.0f, 0.3f * g, pan, 0.6f); break; case NOISE_RUSTLE: /* leaves and twigs */ play(WAVE_NOISE, 0.0f, 0.0f, 5000.0f * dull, 900.0f, 0.03f, 0.25f, 0.7f, 0.6f * g, pan, 0.0f); play(WAVE_CRACKLE, 500.0f, 0.0f, 6000.0f * dull, 1200.0f, 0.02f, 0.2f, 0.6f, 0.5f * g, pan, 0.05f); break; case NOISE_CREAK: { /* old hinges: a wavering squeak, then the leaf against the stop */ float f = 500.0f + 300.0f * dice(); play(WAVE_SAW, f, f * (0.7f + 0.2f * dice()), 2500.0f * dull, 300.0f, 0.08f, 0.25f, 0.7f, 0.12f * g, pan, 0.0f); play(WAVE_SINE, 120.0f, 80.0f, 0.0f, 0.0f, 0.002f, 0.08f, 0.25f, 0.4f * g, pan, 0.8f); break; } case NOISE_SLAM: /* a car door or the boot: a solid thunk */ play(WAVE_SINE, 90.0f, 55.0f, 0.0f, 0.0f, 0.002f, 0.09f, 0.3f, 0.8f * g, pan, 0.0f); play(WAVE_NOISE, 0.0f, 0.0f, 1200.0f * dull, 80.0f, 0.002f, 0.05f, 0.2f, 0.5f * g, pan, 0.0f); play(WAVE_CRACKLE, 300.0f, 0.0f, 4000.0f * dull, 1500.0f, 0.001f, 0.03f, 0.08f, 0.3f * g, pan, 0.0f); /* (the latch) */ break; case NOISE_PANT: { /* hah - hah: a breath out, a quicker one in */ float per = n->surface > 0 ? 10.0f / (float)n->surface : 0.3f; play(WAVE_NOISE, 0.0f, 0.0f, 1700.0f * dull, 450.0f, 0.012f, 0.05f, 0.11f, 0.45f * g, pan, 0.0f); play(WAVE_NOISE, 0.0f, 0.0f, 2400.0f * dull, 800.0f, 0.01f, 0.035f, 0.08f, 0.25f * g, pan, 0.45f * per); break; } case NOISE_SNAP: /* a dry branch underfoot */ play(WAVE_CRACKLE, 1500.0f, 0.0f, 7000.0f * dull, 900.0f, 0.001f, 0.03f, 0.08f, 0.8f * g, pan, 0.0f); play(WAVE_NOISE, 0.0f, 0.0f, 2500.0f * dull, 400.0f, 0.001f, 0.02f, 0.06f, 0.5f * g, pan, 0.0f); break; default: break; } } /* Lightning just now: the thunder rolling, long and low (near: a crack first). */ static void thunder(void) { float near = dice(), pan = 1.6f * dice() - 0.8f; if (near > 0.6f) play(WAVE_CRACKLE, 600.0f, 0.0f, 3500.0f, 300.0f, 0.005f, 0.25f, 0.8f, 0.7f, pan, 0.0f); play(WAVE_NOISE, 0.0f, 0.0f, 140.0f + 120.0f * near, 20.0f, 0.3f, 1.6f + 1.0f * (1.0f - near), 7.0f, 0.9f, pan, 0.05f); play(WAVE_NOISE, 0.0f, 0.0f, 90.0f, 15.0f, 0.6f, 2.2f, 7.0f, 0.7f, -pan, 0.9f + 0.6f * dice()); } /* A bird somewhere near: a few quick rising whistles. */ static void bird(void) { float f = 2200.0f + 2600.0f * dice(), pan = 1.8f * dice() - 0.9f, g = 0.05f + 0.06f * dice(); int notes = 2 + (int)(4.0f * dice()); for (int k = 0; k < notes; k++) play(WAVE_SINE, f, f * (1.15f + 0.4f * dice()), 0.0f, 0.0f, 0.006f, 0.03f, 0.09f, g, pan, 0.11f * (float)k); } void Sound_update(Game *game, float dt, bool paused) { World *w = &game->world; Audio_pause(paused); if (paused) { w->noise_count = 0; return; } Ear ear = { game->camera.position, game->camera.right }; const Object *you = game->player; bool tent = you && you->in_tent; for (int i = 0; i < w->noise_count; i++) noiseOut(&w->noises[i], &ear); w->noise_count = 0; if (w->thunder) thunder(); /* the air: wind and rain (in a tent: dull and drumming on it) */ float wind = fminf(1.0f, hypotf(w->wind.x, w->wind.z) / WIND_MAX); Audio_bed(BED_WIND, (0.06f + 0.55f * wind) * (tent ? 0.5f : 1.0f), 0.0f, wind); bool berlin = you && Game_inBerlin(you); /* (at a meeting: indoors, far off) */ Audio_bed(BED_RAIN, berlin ? 0.0f : 0.6f * smoothstepf(0.02f, 0.6f, w->rain), 0.0f, tent ? 1.0f : 0.0f); /* fires, a chainsaw, the hose: the loudest near you */ float fire_g = 0.0f, fire_pan = 0.0f, fire_k = 0.0f, saw_g = 0.0f, saw_pan = 0.0f, saw_k = 0.0f, hose_g = 0.0f, hose_pan = 0.0f; for (int i = 0; i < w->count; i++) { const Object *o = w->objects[i]; float pan, g; if (o->type == OBJ_FIREPLACE && o->burning) { float size = Camp_flameSize(o); g = hear(&ear, o->position, &pan, NULL) * (0.4f + 0.6f * size); if (g > fire_g) { fire_g = g; fire_pan = pan; fire_k = size; } } else if (o->is_character) { const Work *ws = &o->work_state; if (ws->tool == TOOL_CHAINSAW && Tools_held(o, NULL) == TOOL_CHAINSAW && (Tools_isWorking(o) || ws->saw_in > 0.05f)) { g = hear(&ear, o->position, &pan, NULL); if (g > saw_g) { saw_g = g; saw_pan = pan; saw_k = ws->saw_in; } } else if (o->carving && Tools_held(o, NULL) == TOOL_CHAINSAW) { /* (Kerry carving: in and out of the wood) */ g = hear(&ear, o->position, &pan, NULL); if (g > saw_g) { saw_g = g; saw_pan = pan; saw_k = 0.5f + 0.4f * sinf(o->body.clock * 1.7f); } } if (o->spraying) { g = hear(&ear, o->position, &pan, NULL); if (g > hose_g) { hose_g = g; hose_pan = pan; } } } } Audio_bed(BED_FIRE, 0.9f * fire_g, fire_pan, fire_k); Audio_bed(BED_SAW, 0.8f * saw_g, saw_pan, saw_k); Audio_bed(BED_HOSE, 0.5f * hose_g, hose_pan, 0.0f); /* the car coming in */ const Object *car = game->arrival.car; if (car && game->arrival.stage == ARRIVAL_DRIVING) { float pan, g = you && you->riding ? 0.6f : hear(&ear, car->position, &pan, NULL); Audio_bed(BED_ENGINE, g, you && you->riding ? 0.0f : pan, game->arrival.speed / 5.5f); } else Audio_bed(BED_ENGINE, 0.0f, 0.0f, 0.0f); /* the ambulance: the two-tone horn (driving in and out, a while once it's stopped) */ static float horn_t = 0.0f; static int horn_k = 0; const Ambulance *amb = &game->ambulance; if (amb->car && (amb->stage == AMB_IN || amb->stage == AMB_OUT || (amb->stage == AMB_PARKED && amb->t < 3.0f)) && (horn_t -= dt) <= 0.0f) { float pan, g = hear(&ear, amb->car->position, &pan, NULL); horn_t = 0.62f; horn_k ^= 1; float f = horn_k ? 466.0f : 622.0f; /* (B flat and E flat: tatue - tata) */ play(WAVE_SAW, f, f, 2200.0f, 300.0f, 0.02f, 0.6f, 0.6f, 0.25f * fmaxf(g, 0.05f), pan, 0.0f); } /* the floors playing: each its own music, from its stage (far off only the bass and the kick carry) */ for (int k = 0; k < FEST_FLOORS; k++) { float pan = 0.0f, g = 0.0f, far = 1.0f; if (w->fest.on & (1u << k)) { Vec3 at = w->fest.dance[k]; float d = hypotf(at.x - ear.eye.x, at.z - ear.eye.z); hear(&ear, at, &pan, NULL); g = 0.75f / (1.0f + d / 14.0f) * (tent ? 0.6f : 1.0f) * (Game_inBerlin(you) ? 0.0f : 1.0f); far = clampf(d / 90.0f, 0.0f, 1.0f); } Audio_bed((Bed)(BED_FLOOR + k), g, pan, far); } /* night: crickets; day: birds (not in the rain, less in the wind) */ float hour = w->hour, calm = (1.0f - smoothstepf(0.05f, 0.4f, w->rain)) * (1.0f - 0.7f * wind); bool night = hour > 21.0f || hour < 5.0f; Audio_bed(BED_CRICKETS, night ? 0.12f * calm * (tent ? 0.5f : 1.0f) : 0.0f, 0.0f, 0.0f); static float bird_t = 0.0f; if (!night && hour > 5.5f && hour < 20.5f && (bird_t -= dt) <= 0.0f) { bird_t = BIRD_EVERY * (0.3f + 1.7f * dice()) / fmaxf(0.15f, calm); if (dice() < calm * (hour < 9.0f ? 1.0f : 0.5f)) bird(); /* (mornings: more of them) */ } }