Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Audio Recording Issue on iOS Browsers Using Flutter Sound #1

Open
HatimPresswala opened this issue Oct 21, 2024 · 1 comment
Open

Comments

@HatimPresswala
Copy link

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

  • The audio should be recorded correctly and converted into a byte list without issues.
  • The audio recording should sound clear and recognizable when played back.

Actual Behavior

  • The audio recording does not recognize the voice properly on iOS browsers.
  • When attempting to fetch the audio data from the Blob URL, the conversion to a byte list does not work as expected, leading to
    failures when sending to an API.

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 have tested this implementation on various devices and browsers, and the audio recording works flawlessly on Android devices,
    desktops, and other browsers, but fails specifically on iOS browsers.
  • Ensure that the microphone permissions are granted in the browser settings.
  • The issue persists even with a simplified version of the code that only handles recording and conversion to bytes.

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
Copy link
Contributor

Larpoux commented Oct 21, 2024

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants