| 466 | } |
| 467 | |
| 468 | function startArecordRecording( |
| 469 | onData: (chunk: Buffer) => void, |
| 470 | onEnd: () => void, |
| 471 | ): boolean { |
| 472 | // Record raw PCM: 16 kHz, 16-bit signed little-endian, mono, to stdout. |
| 473 | // arecord does not support built-in silence detection, so this backend |
| 474 | // is best suited for push-to-talk (silenceDetection: false). |
| 475 | const args = [ |
| 476 | '-f', |
| 477 | 'S16_LE', // signed 16-bit little-endian |
| 478 | '-r', |
| 479 | String(RECORDING_SAMPLE_RATE), |
| 480 | '-c', |
| 481 | String(RECORDING_CHANNELS), |
| 482 | '-t', |
| 483 | 'raw', // raw PCM, no WAV header |
| 484 | '-q', // quiet — no progress output |
| 485 | '-', // write to stdout |
| 486 | ] |
| 487 | |
| 488 | const child = spawn('arecord', args, { |
| 489 | stdio: ['pipe', 'pipe', 'pipe'], |
| 490 | }) |
| 491 | |
| 492 | activeRecorder = child |
| 493 | |
| 494 | child.stdout?.on('data', (chunk: Buffer) => { |
| 495 | onData(chunk) |
| 496 | }) |
| 497 | |
| 498 | // Consume stderr to prevent backpressure |
| 499 | child.stderr?.on('data', () => {}) |
| 500 | |
| 501 | child.on('close', () => { |
| 502 | activeRecorder = null |
| 503 | onEnd() |
| 504 | }) |
| 505 | |
| 506 | child.on('error', err => { |
| 507 | logError(err) |
| 508 | activeRecorder = null |
| 509 | onEnd() |
| 510 | }) |
| 511 | |
| 512 | return true |
| 513 | } |
| 514 | |
| 515 | export function stopRecording(): void { |
| 516 | if (nativeRecordingActive && audioNapi) { |