mirror of
https://github.com/sky-systems/sky_phone.git
synced 2026-09-04 09:13:24 +00:00
FIX - start 2048 audio reliably
This commit is contained in:
@@ -9,10 +9,10 @@ type Tone = {
|
||||
}
|
||||
|
||||
const sounds: Record<NumberMergeSound, Tone[]> = {
|
||||
move: [{ duration: 0.05, frequency: 260, offset: 0, type: 'sine', volume: 0.045 }],
|
||||
move: [{ duration: 0.065, frequency: 290, offset: 0, type: 'sine', volume: 0.085 }],
|
||||
merge: [
|
||||
{ duration: 0.09, frequency: 390, offset: 0, type: 'sine', volume: 0.065 },
|
||||
{ duration: 0.12, frequency: 520, offset: 0.055, type: 'sine', volume: 0.075 },
|
||||
{ duration: 0.1, frequency: 390, offset: 0, type: 'sine', volume: 0.1 },
|
||||
{ duration: 0.14, frequency: 540, offset: 0.055, type: 'sine', volume: 0.12 },
|
||||
],
|
||||
win: [
|
||||
{ duration: 0.16, frequency: 523.25, offset: 0, type: 'sine', volume: 0.08 },
|
||||
@@ -35,24 +35,43 @@ export function playNumberMergeSound(
|
||||
): void {
|
||||
if (!enabled) return
|
||||
|
||||
audioContext ??= new AudioContext()
|
||||
if (audioContext.state === 'suspended') void audioContext.resume()
|
||||
|
||||
const now = audioContext.currentTime
|
||||
for (const tone of sounds[sound]) {
|
||||
const oscillator = audioContext.createOscillator()
|
||||
const gain = audioContext.createGain()
|
||||
const start = now + tone.offset
|
||||
const end = start + tone.duration
|
||||
|
||||
oscillator.type = tone.type
|
||||
oscillator.frequency.setValueAtTime(tone.frequency, start)
|
||||
gain.gain.setValueAtTime(0.0001, start)
|
||||
gain.gain.exponentialRampToValueAtTime(tone.volume, start + 0.012)
|
||||
gain.gain.exponentialRampToValueAtTime(0.0001, end)
|
||||
oscillator.connect(gain)
|
||||
gain.connect(audioContext.destination)
|
||||
oscillator.start(start)
|
||||
oscillator.stop(end)
|
||||
const AudioContextConstructor =
|
||||
window.AudioContext ??
|
||||
(window as typeof window & { webkitAudioContext?: typeof AudioContext })
|
||||
.webkitAudioContext
|
||||
if (!AudioContextConstructor) {
|
||||
console.error('[2048 audio] Web Audio is unavailable')
|
||||
return
|
||||
}
|
||||
|
||||
audioContext ??= new AudioContextConstructor()
|
||||
const context = audioContext
|
||||
|
||||
const scheduleSound = (): void => {
|
||||
const now = context.currentTime + 0.02
|
||||
for (const tone of sounds[sound]) {
|
||||
const oscillator = context.createOscillator()
|
||||
const gain = context.createGain()
|
||||
const start = now + tone.offset
|
||||
const end = start + tone.duration
|
||||
|
||||
oscillator.type = tone.type
|
||||
oscillator.frequency.setValueAtTime(tone.frequency, start)
|
||||
gain.gain.setValueAtTime(0.0001, start)
|
||||
gain.gain.linearRampToValueAtTime(tone.volume, start + 0.012)
|
||||
gain.gain.exponentialRampToValueAtTime(0.0001, end)
|
||||
oscillator.connect(gain).connect(context.destination)
|
||||
oscillator.start(start)
|
||||
oscillator.stop(end + 0.01)
|
||||
}
|
||||
}
|
||||
|
||||
if (context.state === 'running') {
|
||||
scheduleSound()
|
||||
return
|
||||
}
|
||||
|
||||
void context.resume().then(scheduleSound).catch((error: unknown) => {
|
||||
console.error('[2048 audio] Failed to start sound', error)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -197,6 +197,8 @@ const defaultLocales: LocaleTree = {
|
||||
gameOverTitle: 'No more moves',
|
||||
highest: 'Highest Tile',
|
||||
howToBody: 'Swipe to combine matching tiles. Each pair becomes one larger tile.',
|
||||
howToExample: '2 + 2 = 4 · 4 + 4 = 8 · … · 1024 + 1024 = 2048',
|
||||
howToGoal: 'Goal: create the 2048 tile before the board has no moves left.',
|
||||
howToTitle: 'How to play',
|
||||
keepPlaying: 'Keep Playing',
|
||||
mainMenu: 'Main Menu',
|
||||
|
||||
@@ -213,6 +213,8 @@ onBeforeUnmount(() => window.removeEventListener('keydown', handleKeydown))
|
||||
<div class="number-merge-how-to">
|
||||
<strong>{{ phone.t('Apps.numberMerge.howToTitle') }}</strong>
|
||||
<p>{{ phone.t('Apps.numberMerge.howToBody') }}</p>
|
||||
<div>{{ phone.t('Apps.numberMerge.howToExample') }}</div>
|
||||
<small>{{ phone.t('Apps.numberMerge.howToGoal') }}</small>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -497,6 +499,14 @@ onBeforeUnmount(() => window.removeEventListener('keydown', handleKeydown))
|
||||
|
||||
.number-merge-how-to strong { font-size: 10px; text-transform: uppercase; }
|
||||
.number-merge-how-to p { margin: 3px 0 0; font-size: 9px; line-height: 1.35; }
|
||||
.number-merge-how-to div {
|
||||
margin: 6px 0 3px;
|
||||
color: #a7472d;
|
||||
font-size: 11px;
|
||||
font-weight: 900;
|
||||
letter-spacing: -0.2px;
|
||||
}
|
||||
.number-merge-how-to small { display: block; color: #8b6559; font-size: 8px; line-height: 1.25; }
|
||||
|
||||
.number-merge-game { padding-top: 5px; }
|
||||
|
||||
|
||||
@@ -89,7 +89,9 @@ Locales["en"] = {
|
||||
},
|
||||
eyebrow = "Number puzzle", gameOverBody = "No empty spaces or matching neighbours remain.",
|
||||
gameOverTitle = "No more moves", highest = "Highest Tile",
|
||||
howToBody = "Swipe to combine matching tiles. Each pair becomes one larger tile.", howToTitle = "How to play",
|
||||
howToBody = "Swipe to combine matching tiles. Each pair becomes one larger tile.",
|
||||
howToExample = "2 + 2 = 4 · 4 + 4 = 8 · … · 1024 + 1024 = 2048",
|
||||
howToGoal = "Goal: create the 2048 tile before the board has no moves left.", howToTitle = "How to play",
|
||||
keepPlaying = "Keep Playing", mainMenu = "Main Menu",
|
||||
menuBody = "Slide matching numbers together and work your way up to 2048.", menuTitle = "Build the 2048 tile",
|
||||
mute = "Mute game sounds", newGame = "New Game", score = "Score",
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Sky Phone</title>
|
||||
<script type="module" crossorigin src="./assets/sky-index-DUWYfXgl.js"></script>
|
||||
<script type="module" crossorigin src="./assets/sky-index-CgcZvXN7.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="./assets/sky-index-yBie1g9x.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
Reference in New Issue
Block a user