startRecording method
Start audio recording and get a stream of recorded audio data
Uses Web Audio API with ScriptProcessorNode to capture raw PCM audio data. Returns 16-bit PCM audio samples (mono, typically 44100 or 48000 Hz).
Implementation
@override
Stream<List<int>> startRecording() async* {
try {
// Request microphone access
final stream = await window.navigator.mediaDevices.getUserMedia(
MediaStreamConstraints(audio: true.toJS),
).toDart;
_mediaStream = stream;
// Create audio context if needed
_audioContext ??= _createAudioContext();
final context = _audioContext!;
// Create media stream source
_sourceNode = context.createMediaStreamSource(stream);
// Create script processor for capturing raw audio
// Buffer size: 4096 samples
// Input channels: 1 (mono)
// Output channels: 1 (mono)
const bufferSize = 4096;
_scriptProcessor = context.createScriptProcessor(
bufferSize,
1,
1,
);
// Connect nodes: source -> processor -> destination
_sourceNode!.connect(_scriptProcessor!);
_scriptProcessor!.connect(context.destination);
// Create audioprocess handler
void audioProcessHandler(JSAny event) {
final audioProcessingEvent = event as AudioProcessingEvent;
final inputBuffer = audioProcessingEvent.inputBuffer;
final channelData = inputBuffer.getChannelData(0);
// Convert Float32Array to 16-bit PCM
final pcmData = <int>[];
final length = channelData.length;
for (var i = 0; i < length; i++) {
// Get float sample (-1.0 to 1.0)
final sample = channelData[i];
// Convert to 16-bit PCM (-32768 to 32767)
final pcmSample = (sample * 32767.0).clamp(-32768.0, 32767.0).toInt();
// Add as little-endian bytes
pcmData.add(pcmSample & 0xFF);
pcmData.add((pcmSample >> 8) & 0xFF);
}
if (!_recordingController.isClosed) {
_recordingController.add(pcmData);
}
}
// Listen to audioprocess event
_scriptProcessor!.onaudioprocess = (audioProcessHandler.toJS);
yield* _recordingController.stream;
} catch (e) {
_cleanupRecording();
throw Exception('Error starting recording: $e');
}
}