Canardoux / tau

A set of projects about W3C : Web Audio API
GNU General Public License v3.0
0 stars 0 forks source link

Audio Recording Issue on iOS Browsers Using Flutter Sound #1

Open HatimPresswala opened 6 days ago

HatimPresswala commented 6 days ago

I am experiencing an issue with audio recording in a Flutter web app using the Flutter Sound package. The audio recording functionality works perfectly on all other devices and browsers, but when I attempt to record audio on iOS browsers (e.g., Safari), the voice is not recognized properly. Additionally, the conversion of the recorded audio to a byte list fails.

Steps to Reproduce :-

  1. Open the Flutter web app on an iOS browser (e.g., Safari).
  2. Click the "Start Recording" button to begin recording audio.
  3. Speak into the microphone during the recording.
  4. Click the "Stop Recording" button to stop the recording.
  5. Observe that the recorded audio is not recognized properly.
  6. Attempt to convert the recorded audio to bytes and send it to an API, which also fails.

Expected Behavior

Actual Behavior

Here is a relevant functions of my implementation for recording audio :-

@override void initState() { _mPlayer!.openPlayer().then((value) { setState(() { _mPlayerIsInited = true; }); });

openTheRecorder().then((value) {
  setState(() {
    _mRecorderIsInited = true;
  });
});

WidgetsBinding.instance.addPostFrameCallback(
  (_) {
    _extractAccountIdFromUrl();
    _showStartChatDialog();
  },
);
super.initState();

}

Future openTheRecorder() async { if (!kIsWeb) { var status = await Permission.microphone.request(); if (status != PermissionStatus.granted) { throw RecordingPermissionException('Microphone permission not granted'); } }

await _mRecorder!.openRecorder();

if (!await _mRecorder!.isEncoderSupported(_codec) && kIsWeb) {
  _codec = Codec.aacMP4;
  _mPath = 'tau_file.mp4';
  if (!await _mRecorder!.isEncoderSupported(_codec) && kIsWeb) {
    _mRecorderIsInited = true;
    return;
  }
}

session = await AudioSession.instance;

await session.configure(AudioSessionConfiguration(
  avAudioSessionCategory: AVAudioSessionCategory.playAndRecord,
  avAudioSessionCategoryOptions: AVAudioSessionCategoryOptions
          .allowBluetooth |
      AVAudioSessionCategoryOptions.defaultToSpeaker |
      AVAudioSessionCategoryOptions.interruptSpokenAudioAndMixWithOthers,
  avAudioSessionMode: AVAudioSessionMode.spokenAudio,
  androidAudioAttributes: const AndroidAudioAttributes(
    contentType: AndroidAudioContentType.speech,
    flags: AndroidAudioFlags.none,
    usage: AndroidAudioUsage.voiceCommunication,
  ),
  androidAudioFocusGainType: AndroidAudioFocusGainType.gain,
  androidWillPauseWhenDucked: true,
));

// React to interruptions (e.g., phone call)
session.interruptionEventStream.listen((event) {
  if (event.begin) {
    if (event.type == AudioInterruptionType.pause) {
      // Pause audio or recording
    }
  } else {
    if (event.type == AudioInterruptionType.pause) {
      // Resume audio or recording
    }
  }
});

// React to unplugged headphones or noisy environment
session.becomingNoisyEventStream.listen((_) {
  // Pause or adjust audio volume
});

}

// ---------------------- Here is the code for recording and playback -------

void record() async { stopAudio(); await session.setActive(true);

await _mRecorder!
    .startRecorder(
  toFile: _mPath,
  codec: _codec,
  sampleRate: 44100, // Set a fixed sample rate
  bitRate: 128000, // Set a fixed bit rate
  audioSource: theSource,
)
    .then((value) {
  setState(() {});
});

}

void stopRecorder() async { // Stop the recorder and get the Blob URL final blobUrl = await _mRecorder!.stopRecorder();

setState(() {
  isListening = false; // Indicate recording has stopped
  isLoading = true;
});

if (blobUrl != null) {
  print("Blob URL: $blobUrl");

  try {
    // Fetch the Blob URL
    final response = await http.get(Uri.parse(blobUrl));

    if (response.statusCode == 200) {
      // Convert the response body to bytes
      Uint8List audioBytes = response.bodyBytes;

      // Debugging: Check the size of the audio file in bytes
      print("Audio file size in bytes: ${audioBytes.length}");

      setState(() {
        audiosend = audioBytes; // Store the audio bytes for later use
      });

      // Send the audio bytes using WebSocket
      await session.setActive(false);
      webSocketService?.sendMessage("", audioBytes);

      // If using Azure or any API, ensure it accepts the format being sent
      // sendAudioToAzure(audioBytes);
    } else {
      print("Failed to fetch audio data: ${response.statusCode}");
    }
  } catch (e) {
    print("Error fetching audio data: $e");
  }
} else {
  print("Recording failed or Blob URL is null.");
}

}

@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text("Audio Recorder")), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ ElevatedButton( onPressed: _isRecording ? null : record, child: Text("Start Recording"), ), ElevatedButton( onPressed: !_isRecording ? null : stopRecorder, child: Text("Stop Recording"), ), if (_audioBytes != null) Text("Audio bytes length: ${_audioBytes!.length}"), ], ), ), ); }

Additional Information

I kindly request assistance in diagnosing and resolving this issue, as it significantly affects the functionality of my Flutter web app on iOS devices. Thank you for your attention to this matter.

Larpoux commented 5 days ago

If I understood correctly, your issue is not on iOS but on safari. Is that‘a right? Did you try with safari on another device (Linux, macOS, windows,…)? I will do some tests tomorrow, recording aac-mp4 on safari.