Compare commits
4 Commits
mischief-m
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
790a118273 | ||
|
|
8df2212aad | ||
|
|
1ca794ea1a | ||
|
|
30b0e8debb |
21
internal/web/static/audio/casino/LICENSE.txt
Normal file
21
internal/web/static/audio/casino/LICENSE.txt
Normal file
@@ -0,0 +1,21 @@
|
||||
|
||||
|
||||
Casino Audio
|
||||
|
||||
by Kenney Vleugels (Kenney.nl)
|
||||
|
||||
------------------------------
|
||||
|
||||
License (Creative Commons Zero, CC0)
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
|
||||
You may use these assets in personal and commercial projects.
|
||||
Credit (Kenney or www.kenney.nl) would be nice but is not mandatory.
|
||||
|
||||
------------------------------
|
||||
|
||||
Donate: http://support.kenney.nl
|
||||
Request: http://request.kenney.nl
|
||||
|
||||
Follow on Twitter for updates:
|
||||
@KenneyNL
|
||||
BIN
internal/web/static/audio/casino/cardFan1.ogg
Normal file
BIN
internal/web/static/audio/casino/cardFan1.ogg
Normal file
Binary file not shown.
BIN
internal/web/static/audio/casino/cardFan2.ogg
Normal file
BIN
internal/web/static/audio/casino/cardFan2.ogg
Normal file
Binary file not shown.
BIN
internal/web/static/audio/casino/cardPlace1.ogg
Normal file
BIN
internal/web/static/audio/casino/cardPlace1.ogg
Normal file
Binary file not shown.
BIN
internal/web/static/audio/casino/cardPlace2.ogg
Normal file
BIN
internal/web/static/audio/casino/cardPlace2.ogg
Normal file
Binary file not shown.
BIN
internal/web/static/audio/casino/cardPlace3.ogg
Normal file
BIN
internal/web/static/audio/casino/cardPlace3.ogg
Normal file
Binary file not shown.
BIN
internal/web/static/audio/casino/cardPlace4.ogg
Normal file
BIN
internal/web/static/audio/casino/cardPlace4.ogg
Normal file
Binary file not shown.
BIN
internal/web/static/audio/casino/cardShuffle.ogg
Normal file
BIN
internal/web/static/audio/casino/cardShuffle.ogg
Normal file
Binary file not shown.
BIN
internal/web/static/audio/casino/cardSlide1.ogg
Normal file
BIN
internal/web/static/audio/casino/cardSlide1.ogg
Normal file
Binary file not shown.
BIN
internal/web/static/audio/casino/cardSlide2.ogg
Normal file
BIN
internal/web/static/audio/casino/cardSlide2.ogg
Normal file
Binary file not shown.
BIN
internal/web/static/audio/casino/cardSlide3.ogg
Normal file
BIN
internal/web/static/audio/casino/cardSlide3.ogg
Normal file
Binary file not shown.
BIN
internal/web/static/audio/casino/cardSlide4.ogg
Normal file
BIN
internal/web/static/audio/casino/cardSlide4.ogg
Normal file
Binary file not shown.
BIN
internal/web/static/audio/casino/cardSlide5.ogg
Normal file
BIN
internal/web/static/audio/casino/cardSlide5.ogg
Normal file
Binary file not shown.
BIN
internal/web/static/audio/casino/cardSlide6.ogg
Normal file
BIN
internal/web/static/audio/casino/cardSlide6.ogg
Normal file
Binary file not shown.
BIN
internal/web/static/audio/casino/chipLay1.ogg
Normal file
BIN
internal/web/static/audio/casino/chipLay1.ogg
Normal file
Binary file not shown.
BIN
internal/web/static/audio/casino/chipLay2.ogg
Normal file
BIN
internal/web/static/audio/casino/chipLay2.ogg
Normal file
Binary file not shown.
BIN
internal/web/static/audio/casino/chipsHandle2.ogg
Normal file
BIN
internal/web/static/audio/casino/chipsHandle2.ogg
Normal file
Binary file not shown.
BIN
internal/web/static/audio/casino/chipsHandle4.ogg
Normal file
BIN
internal/web/static/audio/casino/chipsHandle4.ogg
Normal file
Binary file not shown.
BIN
internal/web/static/audio/casino/chipsHandle6.ogg
Normal file
BIN
internal/web/static/audio/casino/chipsHandle6.ogg
Normal file
Binary file not shown.
@@ -1,12 +1,18 @@
|
||||
// The noise the room makes.
|
||||
//
|
||||
// There are no audio files here and there is nothing to download. Every sound in
|
||||
// this casino is *made* — an oscillator, a burst of filtered noise, an envelope —
|
||||
// the same bargain the weather engine takes with its clouds. A card is a short
|
||||
// slap of noise through a bandpass; a chip is two detuned sines with a click on
|
||||
// the front; a win is four notes going up. It costs about six kilobytes and no
|
||||
// round trips, and it means a sound can be pitched, stretched and detuned per
|
||||
// call instead of being the same wav 300 times.
|
||||
// The room speaks in two voices. The cards and chips are *recorded* — real clay
|
||||
// and real card stock, CC0 foley from Kenney's casino pack, sitting as small ogg
|
||||
// files under /static/audio/casino. Synthesis never quite caught the grain of a
|
||||
// chip landing on a chip, so for those we stopped trying. Everything melodic —
|
||||
// the wins, the losses, the little ticks — is still *made*: an oscillator, a
|
||||
// burst of filtered noise, an envelope, the same bargain the weather engine takes
|
||||
// with its clouds. A win is four notes going up; a chip is a recording of a chip.
|
||||
//
|
||||
// A name is a recording if it appears in SAMPLES and synthesised if it appears in
|
||||
// SOUNDS. The recordings guard against the one weakness of a sample — the same wav
|
||||
// 300 times is a machine gun — by keeping several takes per sound and rotating
|
||||
// through them, and by nudging every play a few percent off pitch. The synthesised
|
||||
// half was always immune to that; it varies itself.
|
||||
//
|
||||
// Two rules hold the whole file up.
|
||||
//
|
||||
@@ -21,6 +27,10 @@
|
||||
// default-off switch for the entire file, checked before anything else happens.
|
||||
//
|
||||
// Exposed as window.PeteSFX. Nothing in here knows what blackjack is.
|
||||
//
|
||||
// If a recording hasn't finished decoding on the frame it's first needed, that one
|
||||
// call falls through to the synthesised version — so the table is never silent
|
||||
// while the ogg loads, and by the second card everything is real.
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
@@ -108,6 +118,95 @@
|
||||
src.stop(t0 + (o.attack || 0.003) + (o.decay || 0.09) + 0.02);
|
||||
}
|
||||
|
||||
// ---- the recordings --------------------------------------------------------
|
||||
//
|
||||
// The foley. Each name maps to a handful of takes; `v` (the index of the card or
|
||||
// chip in a run) picks which one and how far off pitch it lands, so a dealt hand
|
||||
// is four different cards rather than one card four times. `gain` is per-sound
|
||||
// and multiplies the master, exactly like the synthesised sounds' peak does.
|
||||
|
||||
var SAMPLE_BASE = "/static/audio/casino/";
|
||||
var SAMPLES = {
|
||||
// A card thrown down onto the table. `vary` is the largest fraction a take is
|
||||
// ever pitched by, up or down — 0.20 means a play can land anywhere from a fifth
|
||||
// slower (deeper) to a fifth faster (brighter).
|
||||
card: { files: ["cardPlace1", "cardPlace2", "cardPlace3", "cardPlace4"], gain: 0.6, vary: 0.20 },
|
||||
// A card slid into place — softer, longer than a throw.
|
||||
deal: { files: ["cardSlide1", "cardSlide2", "cardSlide3", "cardSlide4", "cardSlide5", "cardSlide6"], gain: 0.5, vary: 0.20 },
|
||||
// A card flicked over.
|
||||
flip: { files: ["cardFan1", "cardFan2"], gain: 0.5, vary: 0.18 },
|
||||
// The riffle — one take, so the pitch wobble is all that keeps two shuffles apart.
|
||||
shuffle: { files: ["cardShuffle"], gain: 0.6, vary: 0.10 },
|
||||
// A clay chip set down on a stack.
|
||||
chip: { files: ["chipLay1", "chipLay2"], gain: 0.6, vary: 0.22 },
|
||||
// Chips gathered and slid away.
|
||||
sweep: { files: ["chipsHandle2", "chipsHandle4", "chipsHandle6"], gain: 0.5, vary: 0.14 },
|
||||
};
|
||||
|
||||
// Decoded buffers by filename. A value of null means "claimed, in flight or
|
||||
// decoding"; false means "we tried and it failed, never bother again"; an
|
||||
// AudioBuffer means ready.
|
||||
var buffers = {};
|
||||
var warmed = false;
|
||||
|
||||
// A rotating cursor per sound, so that even a run of identical calls walks
|
||||
// through the takes rather than replaying the first one. Callers pass `v` to
|
||||
// separate simultaneous sounds (the cards of one deal), but many pass the same
|
||||
// constant every time — the cursor is what keeps a stack of chips from being the
|
||||
// same click over and over.
|
||||
var turn = {};
|
||||
|
||||
// decode fetches a file and turns it into a buffer, exactly once per filename.
|
||||
// decodeAudioData exists in a modern promise flavour and an old callback one;
|
||||
// this handles both so the foley works on older Safari too.
|
||||
function decode(file) {
|
||||
if (file in buffers) return; // ready, in flight, or known-bad
|
||||
buffers[file] = null;
|
||||
fetch(SAMPLE_BASE + file + ".ogg")
|
||||
.then(function (r) { return r.ok ? r.arrayBuffer() : Promise.reject(); })
|
||||
.then(function (ab) {
|
||||
return new Promise(function (res, rej) {
|
||||
var p = ctx.decodeAudioData(ab, res, rej);
|
||||
if (p && p.then) p.then(res, rej);
|
||||
});
|
||||
})
|
||||
.then(function (buf) { buffers[file] = buf; })
|
||||
.catch(function () { buffers[file] = false; });
|
||||
}
|
||||
|
||||
// warm pulls every take down once the context is awake, so that after the first
|
||||
// click the whole set is decoded and ready and nothing has to fall back.
|
||||
function warm() {
|
||||
if (warmed || !ctx) return;
|
||||
warmed = true;
|
||||
for (var name in SAMPLES) SAMPLES[name].files.forEach(decode);
|
||||
}
|
||||
|
||||
// sample plays one recorded take. It returns false — rather than making a noise —
|
||||
// when the name isn't a recording or its buffer isn't decoded yet, which is the
|
||||
// caller's signal to reach for the synthesised version instead.
|
||||
function sample(name, t0, v) {
|
||||
var cfg = SAMPLES[name];
|
||||
if (!cfg) return false;
|
||||
// Advance the cursor, then offset it by v. The cursor guarantees consecutive
|
||||
// calls move to the next take; v spreads apart sounds fired together (one
|
||||
// deal's cards) so they don't all land on the same take at once.
|
||||
var k = (turn[name] = (turn[name] || 0) + 1) + Math.abs(Math.round(v));
|
||||
var buf = buffers[cfg.files[k % cfg.files.length]];
|
||||
if (!buf) return false; // not decoded yet, or failed: let synth cover it
|
||||
var src = ctx.createBufferSource();
|
||||
src.buffer = buf;
|
||||
// Pitch, walking with the same cursor across seven steps between -vary and
|
||||
// +vary, so even a single-take sound like the shuffle isn't identical twice in
|
||||
// a row. Seven steps rather than a couple means the runs don't read as a loop.
|
||||
if (cfg.vary) src.playbackRate.value = 1 + (((k % 7) - 3) / 3) * cfg.vary;
|
||||
var g = ctx.createGain();
|
||||
g.gain.value = cfg.gain == null ? 0.6 : cfg.gain;
|
||||
src.connect(g).connect(master);
|
||||
src.start(t0);
|
||||
return true;
|
||||
}
|
||||
|
||||
// ---- the sounds ------------------------------------------------------------
|
||||
//
|
||||
// Each one takes the time it starts at, and a `v` — a small per-call variation,
|
||||
@@ -217,6 +316,7 @@
|
||||
// gesture — after which play() can schedule freely.
|
||||
function wake() {
|
||||
if (ctx && ctx.state === "suspended") ctx.resume().catch(function () {});
|
||||
warm();
|
||||
}
|
||||
["pointerdown", "keydown", "touchstart"].forEach(function (ev) {
|
||||
window.addEventListener(ev, wake, { passive: true });
|
||||
@@ -231,13 +331,18 @@
|
||||
// lands in 400ms can say so rather than sleeping.
|
||||
function play(name, opts) {
|
||||
if (muted) return;
|
||||
var s = SOUNDS[name];
|
||||
if (!s) return;
|
||||
if (!SOUNDS[name] && !SAMPLES[name]) return; // known to neither voice
|
||||
if (!boot()) return;
|
||||
wake();
|
||||
if (ctx.state !== "running") return; // not yet touched: no sound, and no error
|
||||
var t0 = ctx.currentTime + ((opts && opts.delay) || 0);
|
||||
var v = (opts && opts.v) || 0;
|
||||
try {
|
||||
s(ctx.currentTime + ((opts && opts.delay) || 0), ((opts && opts.v) || 0));
|
||||
// A recording if we have one decoded; the synthesised take otherwise — both
|
||||
// for names that are only ever synthesised, and for the first call of a
|
||||
// recorded name while its ogg is still loading.
|
||||
if (sample(name, t0, v)) return;
|
||||
if (SOUNDS[name]) SOUNDS[name](t0, v);
|
||||
} catch (e) {
|
||||
/* a sound is never worth throwing over */
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user