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.
|
// The noise the room makes.
|
||||||
//
|
//
|
||||||
// There are no audio files here and there is nothing to download. Every sound in
|
// The room speaks in two voices. The cards and chips are *recorded* — real clay
|
||||||
// this casino is *made* — an oscillator, a burst of filtered noise, an envelope —
|
// and real card stock, CC0 foley from Kenney's casino pack, sitting as small ogg
|
||||||
// the same bargain the weather engine takes with its clouds. A card is a short
|
// files under /static/audio/casino. Synthesis never quite caught the grain of a
|
||||||
// slap of noise through a bandpass; a chip is two detuned sines with a click on
|
// chip landing on a chip, so for those we stopped trying. Everything melodic —
|
||||||
// the front; a win is four notes going up. It costs about six kilobytes and no
|
// the wins, the losses, the little ticks — is still *made*: an oscillator, a
|
||||||
// round trips, and it means a sound can be pitched, stretched and detuned per
|
// burst of filtered noise, an envelope, the same bargain the weather engine takes
|
||||||
// call instead of being the same wav 300 times.
|
// 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.
|
// Two rules hold the whole file up.
|
||||||
//
|
//
|
||||||
@@ -21,6 +27,10 @@
|
|||||||
// default-off switch for the entire file, checked before anything else happens.
|
// default-off switch for the entire file, checked before anything else happens.
|
||||||
//
|
//
|
||||||
// Exposed as window.PeteSFX. Nothing in here knows what blackjack is.
|
// 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 () {
|
(function () {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
@@ -108,6 +118,95 @@
|
|||||||
src.stop(t0 + (o.attack || 0.003) + (o.decay || 0.09) + 0.02);
|
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 ------------------------------------------------------------
|
// ---- the sounds ------------------------------------------------------------
|
||||||
//
|
//
|
||||||
// Each one takes the time it starts at, and a `v` — a small per-call variation,
|
// 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.
|
// gesture — after which play() can schedule freely.
|
||||||
function wake() {
|
function wake() {
|
||||||
if (ctx && ctx.state === "suspended") ctx.resume().catch(function () {});
|
if (ctx && ctx.state === "suspended") ctx.resume().catch(function () {});
|
||||||
|
warm();
|
||||||
}
|
}
|
||||||
["pointerdown", "keydown", "touchstart"].forEach(function (ev) {
|
["pointerdown", "keydown", "touchstart"].forEach(function (ev) {
|
||||||
window.addEventListener(ev, wake, { passive: true });
|
window.addEventListener(ev, wake, { passive: true });
|
||||||
@@ -231,13 +331,18 @@
|
|||||||
// lands in 400ms can say so rather than sleeping.
|
// lands in 400ms can say so rather than sleeping.
|
||||||
function play(name, opts) {
|
function play(name, opts) {
|
||||||
if (muted) return;
|
if (muted) return;
|
||||||
var s = SOUNDS[name];
|
if (!SOUNDS[name] && !SAMPLES[name]) return; // known to neither voice
|
||||||
if (!s) return;
|
|
||||||
if (!boot()) return;
|
if (!boot()) return;
|
||||||
wake();
|
wake();
|
||||||
if (ctx.state !== "running") return; // not yet touched: no sound, and no error
|
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 {
|
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) {
|
} catch (e) {
|
||||||
/* a sound is never worth throwing over */
|
/* a sound is never worth throwing over */
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user