Sounds: Japanese-house sound set in the app, layout demo removed
Eleven sounds for events that carry news (ready, unready, everyone ready, join, leave, kick, new video, others' chat, idle warning, connection lost/back); none for what the video already says. New voices: koto (Karplus-Strong, bendable), furin chime, rin bowl, bamboo, hyoshigi, shoji slide, in the Yo scale on D. Played by SynthSfx at the Settings volume with per-sound switches and previews. The debug layout mock is gone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
d8fd464e33
commit
fce392e425
19 files changed
+546
-6306
No files matched your search
@@ -1,130 +1,98 @@
|
||||
import 'sfx.dart';
|
||||
import 'synth.dart';
|
||||
|
||||
// The app's sound set, all in D major pentatonic so any two that overlap
|
||||
// still agree. Rising = someone arrives / gets ready, falling = leaves /
|
||||
// unready; bells for people, soft sines for the player, plucks for ticks.
|
||||
// Everything is quiet (-16 dBFS peaks or lower) and short.
|
||||
// The sound of the house: koto for readiness, the wind chime for people
|
||||
// arriving and leaving, the shoji door for a new video, bamboo for chat,
|
||||
// clappers for a kick, the singing bowl for the idle warning, a string
|
||||
// going slack and re-tuned for the connection. All in the Yo scale on D
|
||||
// (D E G A B), so overlapping sounds agree; quiet and short.
|
||||
|
||||
// Pitches, in semitones from A4.
|
||||
const _d4 = -7, _e4 = -5, _a4 = 0;
|
||||
const _d5 = 5, _e5 = 7, _fs5 = 9, _a5 = 12, _b5 = 14;
|
||||
const _d6 = 17, _e6 = 19, _fs6 = 21, _a6 = 24;
|
||||
const _d4 = -7, _a4 = 0, _b4 = 2;
|
||||
const _d5 = 5, _e5 = 7, _g5 = 10, _a5 = 12, _b5 = 14;
|
||||
const _d6 = 17, _e6 = 19, _g6 = 22, _a6 = 24, _b6 = 26;
|
||||
|
||||
double _p(int s) => semi(s);
|
||||
|
||||
/// The ready chime's second note climbs with how many are ready (1st, 2nd…).
|
||||
const _readyLadder = [_fs5, _a5, _b5, _d6, _e6, _fs6];
|
||||
/// The ready pluck's answer climbs with each person ready.
|
||||
const _readyLadder = [_g5, _a5, _b5, _d6, _e6, _g6];
|
||||
|
||||
SoundSpec readySound(int readyIndex) {
|
||||
final top = _readyLadder[(readyIndex - 1).clamp(0, _readyLadder.length - 1)];
|
||||
return SoundSpec([
|
||||
Note(0, _p(_d5), 0.35, gain: 0.8),
|
||||
Note(0.075, _p(top), 0.55),
|
||||
]);
|
||||
Note(0, _p(_d5), 0.9, gain: 0.75, bright: 0.55),
|
||||
Note(0.085, _p(top), 1.1, bright: 0.65),
|
||||
], peakDb: -17);
|
||||
}
|
||||
|
||||
SoundSpec soundFor(SfxKind kind, {int readyIndex = 1}) => switch (kind) {
|
||||
SfxKind.ready => readySound(readyIndex),
|
||||
// The same strings, muted and stepping down.
|
||||
SfxKind.unready => SoundSpec([
|
||||
Note(0, _p(_a5), 0.3, gain: 0.8),
|
||||
Note(0.08, _p(_e5), 0.45, gain: 0.7),
|
||||
], cutoff: 4200, peakDb: -19),
|
||||
Note(0, _p(_a5), 0.35, gain: 0.8, bright: 0.2),
|
||||
Note(0.09, _p(_g5), 0.45, bright: 0.15),
|
||||
], cutoff: 3800, peakDb: -20, room: 0.1),
|
||||
// A strum up the open strings, a chime on top.
|
||||
SfxKind.forceReady => SoundSpec([
|
||||
Note(0, _p(_d5), 0.35, gain: 0.7),
|
||||
Note(0.06, _p(_fs5), 0.35, gain: 0.8),
|
||||
Note(0.12, _p(_a5), 0.6),
|
||||
]),
|
||||
SfxKind.forceUnready => SoundSpec([
|
||||
Note(0, _p(_a5), 0.3, gain: 0.8),
|
||||
Note(0.06, _p(_fs5), 0.3, gain: 0.7),
|
||||
Note(0.12, _p(_d5), 0.5, gain: 0.7),
|
||||
], cutoff: 4200, peakDb: -19),
|
||||
SfxKind.forcePlay => SoundSpec([
|
||||
Note(0, _p(_d5), 0.7, gain: 0.6, voice: Voice.soft),
|
||||
Note(0, _p(_a5), 0.7, gain: 0.5, voice: Voice.soft),
|
||||
Note(0.12, _p(_d6), 0.8),
|
||||
]),
|
||||
SfxKind.forceHold => SoundSpec([
|
||||
Note(0, _p(_a4), 0.35, voice: Voice.soft),
|
||||
Note(0.1, _p(_d4), 0.55, voice: Voice.soft),
|
||||
], cutoff: 3000, peakDb: -18),
|
||||
SfxKind.kick => SoundSpec([
|
||||
Note(0, _p(_e4), 0.4, voice: Voice.soft, to: _p(_d4 - 2)),
|
||||
Note(0.14, _p(_d4 - 5), 0.6, voice: Voice.soft, gain: 0.8),
|
||||
], cutoff: 1800, peakDb: -19),
|
||||
for (final (i, s) in [_d5, _e5, _g5, _a5, _b5, _d6].indexed)
|
||||
Note(i * 0.032, _p(s), 1.2, gain: 0.55 + i * 0.05, bright: 0.55),
|
||||
Note(0.22, _p(_d6) * 2, 1.4, voice: Voice.chime, gain: 0.18),
|
||||
], peakDb: -17),
|
||||
// Wind through the furin: three uneven tinkles.
|
||||
SfxKind.join => SoundSpec([
|
||||
Note(0, _p(_d5), 0.3, gain: 0.7),
|
||||
Note(0.07, _p(_e5), 0.3, gain: 0.75),
|
||||
Note(0.14, _p(_a5), 0.6),
|
||||
]),
|
||||
Note(0, _p(_a6), 1.3, voice: Voice.chime, gain: 0.8),
|
||||
Note(0.11, _p(_d6) * 2, 1.2, voice: Voice.chime, gain: 0.55),
|
||||
Note(0.26, _p(_e6) * 2, 1.4, voice: Voice.chime, gain: 0.45),
|
||||
], cutoff: 7000, peakDb: -19, room: 0.18),
|
||||
// The wind settling: two softer, lower tinkles.
|
||||
SfxKind.leave => SoundSpec([
|
||||
Note(0, _p(_a5), 0.28, gain: 0.8),
|
||||
Note(0.08, _p(_e5), 0.28, gain: 0.7),
|
||||
Note(0.16, _p(_d5), 0.5, gain: 0.6),
|
||||
], cutoff: 3800, peakDb: -20),
|
||||
SfxKind.stale => SoundSpec([
|
||||
Note(0, _p(_fs5), 0.6, voice: Voice.soft, wobble: 0.25),
|
||||
], cutoff: 3500, peakDb: -21),
|
||||
Note(0, _p(_b6), 1.0, voice: Voice.chime, gain: 0.6),
|
||||
Note(0.17, _p(_e6), 1.3, voice: Voice.chime, gain: 0.5),
|
||||
], cutoff: 5000, peakDb: -22, room: 0.18),
|
||||
// Hyoshigi: clack… clack, then a low muted string.
|
||||
SfxKind.kick => SoundSpec([
|
||||
Note(0, 1900, 0.09, voice: Voice.clack),
|
||||
Note(0.16, 1750, 0.11, voice: Voice.clack, gain: 0.9),
|
||||
Note(0.3, _p(_d4), 0.7, bright: 0.15, gain: 0.7),
|
||||
], cutoff: 5000, peakDb: -19, room: 0.12),
|
||||
// The door slides open on something new: air, then a fifth.
|
||||
SfxKind.swap => SoundSpec([
|
||||
Note(0, _p(_d5), 0.6, voice: Voice.soft, gain: 0.6),
|
||||
Note(0.05, _p(_a5), 0.3, gain: 0.5),
|
||||
Note(0.1, _p(_d6), 0.3, gain: 0.5),
|
||||
Note(0.15, _p(_fs6), 0.5, gain: 0.45),
|
||||
], cutoff: 6000, peakDb: -18),
|
||||
SfxKind.seek => const SoundSpec([
|
||||
Note(0, 987.77, 0.06, voice: Voice.pluck),
|
||||
], cutoff: 3500, peakDb: -26, room: 0),
|
||||
SfxKind.pause => SoundSpec([
|
||||
Note(0, _p(_a4), 0.28, voice: Voice.soft, to: _p(_e4)),
|
||||
], cutoff: 3000, peakDb: -21, room: 0.08),
|
||||
SfxKind.resume => SoundSpec([
|
||||
Note(0, _p(_a4), 0.28, voice: Voice.soft, to: _p(_d5)),
|
||||
], cutoff: 3500, peakDb: -21, room: 0.08),
|
||||
Note(0, 500, 0.42, voice: Voice.slide, to: 1700, gain: 0.5),
|
||||
Note(0.33, _p(_d5), 1.1, bright: 0.6, gain: 0.8),
|
||||
Note(0.36, _p(_a5), 1.1, bright: 0.6, gain: 0.7),
|
||||
], peakDb: -18),
|
||||
// Bamboo: a hollow tok-tok, the second higher.
|
||||
SfxKind.chat => SoundSpec([
|
||||
Note(0, _p(_b5), 0.18, gain: 0.7),
|
||||
Note(0.055, _p(_e6), 0.3),
|
||||
Note(0, _p(_g5), 0.13, voice: Voice.bamboo, gain: 0.85),
|
||||
Note(0.075, _p(_d6), 0.16, voice: Voice.bamboo),
|
||||
], cutoff: 5500, peakDb: -19, room: 0.1),
|
||||
// The singing bowl: one soft strike that hums.
|
||||
SfxKind.warn => SoundSpec([
|
||||
Note(0, _p(_fs5), 0.25, voice: Voice.soft),
|
||||
Note(0.22, _p(_fs5), 0.25, voice: Voice.soft, gain: 0.85),
|
||||
Note(0.44, _p(_d5), 0.45, voice: Voice.soft, gain: 0.8),
|
||||
], cutoff: 3800, peakDb: -18),
|
||||
Note(0, _p(_d5), 2.6, voice: Voice.rin),
|
||||
], cutoff: 4500, peakDb: -20, room: 0.1),
|
||||
// A string going slack: plucked, its pitch sagging away.
|
||||
SfxKind.lost => SoundSpec([
|
||||
Note(0, _p(_a5), 1.1, to: _p(_d5), bright: 0.4),
|
||||
Note(0.05, _p(_d5), 1.0, to: _p(_a4), bright: 0.3, gain: 0.6),
|
||||
], cutoff: 3500, peakDb: -19),
|
||||
// Re-tuned: a quick bend up that lands, and a tinkle.
|
||||
SfxKind.back => SoundSpec([
|
||||
Note(0, _p(_b4), 0.9, to: _p(_d5), bright: 0.5),
|
||||
Note(0.2, _p(_a5), 1.0, bright: 0.6, gain: 0.7),
|
||||
Note(0.3, _p(_d6) * 2, 1.0, voice: Voice.chime, gain: 0.2),
|
||||
], peakDb: -18),
|
||||
};
|
||||
|
||||
/// Sounds the app has no event for yet (proposals, shown in the demo).
|
||||
enum ExtraSound { start, finished, lost, back, image, mention }
|
||||
|
||||
SoundSpec extraSound(ExtraSound s) => switch (s) {
|
||||
ExtraSound.start => SoundSpec([
|
||||
Note(0, _p(_d5), 0.9, voice: Voice.soft, gain: 0.5),
|
||||
Note(0.04, _p(_fs5), 0.9, voice: Voice.soft, gain: 0.45),
|
||||
Note(0.08, _p(_a5), 0.9, voice: Voice.soft, gain: 0.45),
|
||||
Note(0.12, _p(_d6), 0.9, gain: 0.6),
|
||||
], cutoff: 5000, peakDb: -18, room: 0.2),
|
||||
ExtraSound.finished => SoundSpec([
|
||||
Note(0, _p(_a5), 0.5, gain: 0.8),
|
||||
Note(0.16, _p(_fs5), 0.5, gain: 0.75),
|
||||
Note(0.32, _p(_d5), 1.1, gain: 0.8),
|
||||
Note(0.32, _p(_a4), 1.1, voice: Voice.soft, gain: 0.4),
|
||||
], cutoff: 4500, peakDb: -18, room: 0.22),
|
||||
ExtraSound.lost => SoundSpec([
|
||||
Note(0, _p(_e4), 0.35, voice: Voice.soft),
|
||||
Note(0.18, _p(_d4), 0.6, voice: Voice.soft),
|
||||
], cutoff: 2500, peakDb: -18),
|
||||
ExtraSound.back => SoundSpec([
|
||||
Note(0, _p(_d4 + 12), 0.3, voice: Voice.soft),
|
||||
Note(0.12, _p(_a4), 0.5, voice: Voice.soft),
|
||||
Note(0.12, _p(_d5), 0.5, gain: 0.5),
|
||||
], cutoff: 4000, peakDb: -18),
|
||||
ExtraSound.image => SoundSpec([
|
||||
Note(0, _p(_e6), 0.12, voice: Voice.pluck, gain: 0.6),
|
||||
Note(0.05, _p(_b5), 0.18, gain: 0.7),
|
||||
Note(0.1, _p(_a6), 0.35, gain: 0.6),
|
||||
], cutoff: 6000, peakDb: -20, room: 0.1),
|
||||
ExtraSound.mention => SoundSpec([
|
||||
Note(0, _p(_d6), 0.25),
|
||||
Note(0.09, _p(_a5), 0.25, gain: 0.8),
|
||||
Note(0.18, _p(_d6), 0.45),
|
||||
], cutoff: 6000, peakDb: -17),
|
||||
};
|
||||
/// Shown in Settings → Notifications & sounds: name and when it plays.
|
||||
const soundInfo = {
|
||||
SfxKind.ready: ('Ready', 'Someone presses Space; climbs with each ready person'),
|
||||
SfxKind.unready: ('Not ready', 'Someone takes their ready back'),
|
||||
SfxKind.forceReady: ('Everyone ready', 'Someone marks everyone ready'),
|
||||
SfxKind.join: ('Joined', 'Someone joins the server'),
|
||||
SfxKind.leave: ('Left', 'Someone leaves'),
|
||||
SfxKind.kick: ('Removed', 'Someone is kicked'),
|
||||
SfxKind.swap: ('New video', 'A different video or episode is put up'),
|
||||
SfxKind.chat: ('Message', 'A chat message from someone else'),
|
||||
SfxKind.warn: ('Idle warning', 'Auto-unready soon unless you press Space'),
|
||||
SfxKind.lost: ('Connection lost', 'The server stopped answering; reconnecting'),
|
||||
SfxKind.back: ('Reconnected', 'Back in the server after a drop'),
|
||||
};
|
||||
Reference in new issue
Block a user