Press n or j to go to the next uncovered block, b, p or k for the previous block.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 | 1x 1x 1x 1x 1x 1x 1x 14x 14x 14x 14x 29x 9x 9x 29x 14x 4x 2x 2x 4x 3x 3x 1x 3x 3x 3x 3x 3x 1x 1x 1x 1x 1x 2x 2x 2x 1x 23x 23x 23x 23x 23x 1x 3x 3x 1x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x | /** * Audio utility functions for Korean martial arts game */ import type { AudioFormat } from "./types"; export const AUDIO_FORMATS: readonly AudioFormat[] = [ "audio/webm", "audio/mp3", "audio/wav", "audio/ogg", ] as const; /** * Select the best available audio format based on browser support */ export function selectAudioFormat( availableFormats: readonly AudioFormat[], preferredFormats: readonly AudioFormat[] = AUDIO_FORMATS ): AudioFormat | null { // Check each preferred format against available formats for (const preferred of preferredFormats) { if (availableFormats.includes(preferred) && canPlayType(preferred)) { return preferred; } } // If no preferred format is available, return the first available that can be played for (const format of availableFormats) { if (canPlayType(format)) { return format; } } return null; } /** * Get preferred audio URLs based on format selection * Returns an array of URLs to try in order of preference */ export function getPreferredFormat( availableFormats: readonly AudioFormat[], basePath: string ): string[] { const selectedFormat = selectAudioFormat(availableFormats); if (!selectedFormat) { // Fallback to first format if none can be determined const fallbackFormat = availableFormats[0]; if (fallbackFormat) { const extension = fallbackFormat.replace("audio/", ""); return [`${basePath}.${extension}`]; } return []; } const extension = selectedFormat.replace("audio/", ""); return [`${basePath}.${extension}`]; } /** * Check if the browser can play a specific audio type */ export function canPlayType(format: AudioFormat): boolean { if (typeof Audio === "undefined") { // In test environment, mock basic support return ["audio/mp3", "audio/wav"].includes(format); } const audio = new Audio(); const support = audio.canPlayType(format); return support === "probably" || support === "maybe"; } /** * Clamp volume to valid range (0-1) */ export function clampVolume(volume: number): number { return Math.max(0, Math.min(1, volume)); } /** * Get metadata for the best available format */ export function getBestFormatMetadata( availableFormats: readonly AudioFormat[] ) { const selectedFormat = selectAudioFormat(availableFormats); return { format: selectedFormat, supported: selectedFormat ? canPlayType(selectedFormat) : false, quality: getFormatQuality(selectedFormat), }; } function getFormatQuality( format: AudioFormat | null ): "high" | "medium" | "low" { if (!format) return "low"; switch (format) { case "audio/wav": return "high"; case "audio/webm": case "audio/ogg": return "medium"; case "audio/mp3": default: return "medium"; } } export class AudioUtils { /** * Check if browser can play audio type */ static canPlayType(mimeType: string): boolean { const audio = new Audio(); const canPlay = audio.canPlayType(mimeType); return canPlay === "probably" || canPlay === "maybe"; } /** * Select best audio format from available options */ static selectAudioFormat( available: readonly AudioFormat[], preferred: readonly AudioFormat[] = [ "audio/mp3", "audio/wav", "audio/ogg", "audio/webm", ] ): AudioFormat | null { // Convert to mutable arrays for processing const availableFormats: AudioFormat[] = [...available]; const preferredFormats: AudioFormat[] = [...preferred]; // Find first preferred format that is both available and supported for (const format of preferredFormats) { if (availableFormats.includes(format) && this.canPlayType(format)) { return format; } } // Fallback: find any available format that is supported for (const format of availableFormats) { if (this.canPlayType(format)) { return format; } } return null; } /** * Get best format metadata */ static getBestFormatMetadata(formats: readonly AudioFormat[]): { format: AudioFormat | null; supported: boolean; confidence: string; } { const selectedFormat = this.selectAudioFormat(formats); if (!selectedFormat) { return { format: null, supported: false, confidence: "", }; } const audio = new Audio(); const confidence = audio.canPlayType(selectedFormat); return { format: selectedFormat, supported: this.canPlayType(selectedFormat), confidence, }; } } /** * Detect supported audio formats in the current browser */ export function detectSupportedFormats(): AudioFormat[] { const audio = new Audio(); const formats: AudioFormat[] = []; if (audio.canPlayType("audio/mp3")) { formats.push("audio/mp3"); } if (audio.canPlayType("audio/wav")) { formats.push("audio/wav"); } if (audio.canPlayType("audio/ogg")) { formats.push("audio/ogg"); } if (audio.canPlayType("audio/webm")) { formats.push("audio/webm"); } return formats; } /** * Create and configure an audio element */ export function createAudioElement( url: string, volume: number = 1.0 ): HTMLAudioElement { const audio = new Audio(url); audio.volume = volume; audio.preload = "auto"; return audio; } /** * Validate audio URL format */ export function validateAudioUrl(url: string): boolean { return typeof url === "string" && url.length > 0; } /** * Normalize volume to range [0, 1] */ export function normalizeVolume(volume: number): number { return Math.max(0, Math.min(1, volume)); } /** * Format audio duration from seconds to mm:ss */ export function formatDuration(seconds: number): string { const minutes = Math.floor(seconds / 60); const remainingSeconds = Math.floor(seconds % 60); return `${minutes}:${remainingSeconds.toString().padStart(2, "0")}`; } /** * Check if the Audio API is supported */ export function isAudioSupported(): boolean { return typeof Audio !== "undefined"; } /** * Get the optimal audio format from supported formats */ export function getOptimalFormat( supportedFormats: AudioFormat[] ): AudioFormat | null { const preferredOrder: AudioFormat[] = [ "audio/webm", "audio/mp3", "audio/wav", "audio/ogg", ]; for (const format of preferredOrder) { if (supportedFormats.includes(format)) { return format; } } return supportedFormats.length > 0 ? supportedFormats[0] : null; } |