initialize method

  1. @override
Future<int> initialize({
  1. required int outputSampleRate,
})
override

Implementation

@override
Future<int> initialize({required int outputSampleRate}) async {
  if (_isInitialized) {
    return _inputSampleRate!;
  }

  _outputSampleRate = outputSampleRate;

  try {
    _audioContext = AudioContext();
    _inputSampleRate = _audioContext!.sampleRate.toInt();

    // Use native AudioContext mixing with GainNodes for volume control.
    // Echo canceller monitors destination output, so all audio mixed here
    // is included in the echo cancellation reference signal.
    _voiceGainNode = _audioContext!.createGain();
    _voiceGainNode!.gain.value = 1.0;
    _voiceGainNode!.connect(_audioContext!.destination);

    _backgroundGainNode = _audioContext!.createGain();
    _backgroundGainNode!.gain.value = 0.3;
    _backgroundGainNode!.connect(_audioContext!.destination);

    _soundGainNode = _audioContext!.createGain();
    _soundGainNode!.gain.value = 0.5;
    _soundGainNode!.connect(_audioContext!.destination);

    final mediaDevices = window.navigator.mediaDevices;
    final audioConstraints =
        {
              'echoCancellation': true,
              'noiseSuppression': true,
              'autoGainControl': true,
            }.jsify()
            as JSObject;

    final constraints = MediaStreamConstraints(audio: audioConstraints);

    _mediaStream = await mediaDevices.getUserMedia(constraints).toDart;

    final audioTracks = _mediaStream!.getAudioTracks().toDart;
    if (audioTracks.isEmpty) {
      throw Exception('No audio track available');
    }

    final track = audioTracks.first;
    final settings = track.getSettings();
    final settingsObj = settings as JSObject;

    final echoCancellation = settingsObj.getProperty('echoCancellation'.toJS);
    final hasEchoCancellation =
        echoCancellation != null && (echoCancellation as JSBoolean).toDart;

    final sinkId = settingsObj.getProperty('sinkId'.toJS);
    final hasSinkId =
        sinkId != null && (sinkId as JSString).toDart.isNotEmpty;
    final hasHeadphones = hasSinkId;

    if (!hasEchoCancellation && !hasHeadphones) {
      await _cleanup();
      throw Exception(
        'Echo cancellation not available and no headphones connected',
      );
    }

    await _setupMediaRecorder();

    _isInitialized = true;

    return _inputSampleRate!;
  } catch (e) {
    await _cleanup();
    if (e.toString().contains('NotAllowedError') ||
        e.toString().contains('Permission')) {
      throw Exception('PERMISSION_DENIED');
    }
    rethrow;
  }
}