| 73 | let arecordProbe: Promise<ArecordProbeResult> | null = null |
| 74 | |
| 75 | function probeArecord(): Promise<ArecordProbeResult> { |
| 76 | arecordProbe ??= new Promise(resolve => { |
| 77 | const child = spawn( |
| 78 | 'arecord', |
| 79 | [ |
| 80 | '-f', |
| 81 | 'S16_LE', |
| 82 | '-r', |
| 83 | String(RECORDING_SAMPLE_RATE), |
| 84 | '-c', |
| 85 | String(RECORDING_CHANNELS), |
| 86 | '-t', |
| 87 | 'raw', |
| 88 | '/dev/null', |
| 89 | ], |
| 90 | { stdio: ['ignore', 'ignore', 'pipe'] }, |
| 91 | ) |
| 92 | let stderr = '' |
| 93 | child.stderr?.on('data', (chunk: Buffer) => { |
| 94 | stderr += chunk.toString() |
| 95 | }) |
| 96 | const timer = setTimeout( |
| 97 | (c: ChildProcess, r: (v: ArecordProbeResult) => void) => { |
| 98 | c.kill('SIGTERM') |
| 99 | r({ ok: true, stderr: '' }) |
| 100 | }, |
| 101 | 150, |
| 102 | child, |
| 103 | resolve, |
| 104 | ) |
| 105 | child.once('close', code => { |
| 106 | clearTimeout(timer) |
| 107 | // SIGTERM close (code=null) after timer fired is already resolved. |
| 108 | // Early close with code=0 is unusual (arecord shouldn't exit on its |
| 109 | // own) but treat as ok. |
| 110 | void resolve({ ok: code === 0, stderr: stderr.trim() }) |
| 111 | }) |
| 112 | child.once('error', () => { |
| 113 | clearTimeout(timer) |
| 114 | void resolve({ ok: false, stderr: 'arecord: command not found' }) |
| 115 | }) |
| 116 | }) |
| 117 | return arecordProbe |
| 118 | } |
| 119 | |
| 120 | export function _resetArecordProbeForTesting(): void { |
| 121 | arecordProbe = null |