forked from probablycorey/baudy
Resolve conflicts: accept server split, apply hint reorder to terminal.ts, add JSX pragma to phone.tsx Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
130 lines
4.0 KiB
TypeScript
130 lines
4.0 KiB
TypeScript
import { loopbackTest, playAudio, startMicListener } from './audio'
|
|
import { handleGuess, getSecret } from './game'
|
|
import type { GuessResult } from './game'
|
|
|
|
const RESET = '\x1b[0m'
|
|
const BOLD = '\x1b[1m'
|
|
const DIM = '\x1b[2m'
|
|
const GREEN = '\x1b[32m'
|
|
const YELLOW = '\x1b[33m'
|
|
const BLUE = '\x1b[34m'
|
|
const CYAN = '\x1b[36m'
|
|
const RED = '\x1b[31m'
|
|
|
|
async function prompt(question: string): Promise<string> {
|
|
process.stdout.write(question)
|
|
for await (const line of console) {
|
|
return line.trim()
|
|
}
|
|
return ''
|
|
}
|
|
|
|
function getLocalHostname() {
|
|
const proc = Bun.spawnSync(['scutil', '--get', 'LocalHostName'])
|
|
if (proc.exitCode === 0) return proc.stdout.toString().trim() + '.local'
|
|
return 'localhost'
|
|
}
|
|
|
|
function getDeviceName(type: 'input' | 'output'): string {
|
|
const args = type === 'input' ? ['-c', '-t', 'input'] : ['-c']
|
|
const proc = Bun.spawnSync(['SwitchAudioSource', ...args])
|
|
if (proc.exitCode === 0) return proc.stdout.toString().trim()
|
|
return 'system default'
|
|
}
|
|
|
|
function logGuess(result: GuessResult) {
|
|
const { type, guess, guessCount } = result
|
|
if (type === 'higher') {
|
|
console.log(`${DIM}Guess #${guessCount}:${RESET} ${BOLD}${guess}${RESET} → ${YELLOW}📢 Higher!${RESET}`)
|
|
return
|
|
}
|
|
if (type === 'lower') {
|
|
console.log(`${DIM}Guess #${guessCount}:${RESET} ${BOLD}${guess}${RESET} → ${BLUE}📢 Lower!${RESET}`)
|
|
return
|
|
}
|
|
console.log(`${DIM}Guess #${guessCount}:${RESET} ${BOLD}${guess}${RESET} → ${GREEN}🎉 CORRECT!${RESET}`)
|
|
console.log()
|
|
console.log(`${CYAN}New round! Secret number: ${BOLD}${getSecret()}${RESET}`)
|
|
console.log(`${'─'.repeat(40)}`)
|
|
console.log()
|
|
}
|
|
|
|
async function onMessage(text: string) {
|
|
await Bun.sleep(500)
|
|
|
|
if (text === 'HELLO') {
|
|
console.log(`${GREEN}${BOLD}Player connected via audio!${RESET}`)
|
|
await playAudio('HEY BUDDY')
|
|
return
|
|
}
|
|
|
|
const result = await handleGuess(text)
|
|
if (result) logGuess(result)
|
|
}
|
|
|
|
export async function startup(port: number) {
|
|
const soxCheck = Bun.spawnSync(['which', 'sox'])
|
|
if (soxCheck.exitCode !== 0) {
|
|
console.error(`\n ${BOLD}sox is not installed.${RESET} Run: brew install sox\n`)
|
|
process.exit(1)
|
|
}
|
|
|
|
console.clear()
|
|
console.log()
|
|
console.log(`${BOLD}Corey's Screechy Audio Demo${RESET}`)
|
|
console.log()
|
|
|
|
const speaker = getDeviceName('output')
|
|
const mic = getDeviceName('input')
|
|
console.log(`${BOLD}Speaker:${RESET} ${GREEN}${speaker}${RESET}`)
|
|
console.log(`${BOLD}Microphone:${RESET} ${GREEN}${mic}${RESET}`)
|
|
console.log()
|
|
|
|
console.log(`${YELLOW}${BOLD}🔊 Turn your volume up!${RESET}`)
|
|
console.log(`${DIM}Testing audio: playing a chirp and listening for it...${RESET}`)
|
|
console.log()
|
|
|
|
const loopbackOk = await loopbackTest()
|
|
|
|
if (loopbackOk) {
|
|
console.log(`${GREEN}${BOLD}✓ Audio working!${RESET} ${DIM}Speaker → mic pipeline verified${RESET}`)
|
|
} else {
|
|
console.log(`${RED}${BOLD}✗ Couldn't hear the test chirp.${RESET}`)
|
|
console.log()
|
|
console.log(`${YELLOW}Try:${RESET}`)
|
|
console.log(` • Disconnect headphones — sound needs to travel through the air`)
|
|
console.log(` • Check System Settings > Sound (output: "${speaker}", input: "${mic}")`)
|
|
console.log(` • Turn your volume up`)
|
|
console.log()
|
|
await prompt(` Press Enter to continue anyway... `)
|
|
}
|
|
|
|
console.clear()
|
|
console.log()
|
|
console.log(`${BOLD}Corey's Screechy Audio Demo${RESET}`)
|
|
console.log(`${DIM}Speaker: ${speaker} · Mic: ${mic}${RESET}`)
|
|
console.log()
|
|
|
|
const hostname = getLocalHostname()
|
|
const url = `https://${hostname}:${port}`
|
|
|
|
console.log(`${GREEN}${BOLD}Scan QR code on your phone to play!${RESET}`)
|
|
console.log()
|
|
|
|
try {
|
|
const QRCode = await import('qrcode')
|
|
const qr = await QRCode.toString(url, { type: 'terminal', small: true })
|
|
console.log(qr)
|
|
} catch {
|
|
console.log(`${BOLD}${CYAN}${url}${RESET}`)
|
|
console.log()
|
|
}
|
|
|
|
console.log(`${BOLD}Secret number: ${GREEN}${getSecret()}${RESET}`)
|
|
console.log(`${'─'.repeat(40)}`)
|
|
console.log(`${DIM}🎤 Listening for guesses...${RESET}`)
|
|
console.log()
|
|
|
|
startMicListener(onMessage)
|
|
}
|