I am creating a Flutter plugin. Currently, the code:

  1. Synthesises an String into an audio file
  2. Gets the path to the file
  3. Plays the audio file using audioplayers

When I run the application in Android, it perfectly works. Nevertheless, when I run it on iOS, it does not (assuming relates permissions/restrictions)

await flutterTts
        .synthesizeToFile(
            "This is my first audio synthesizer in Flutter", audioFileName)
        .then((success) => {
              if (success == 1)
                {
                  print('Successfully synthesized!!'),

                  // Gets the path to the generated file depending on the platform
                  pathFile =
                      Platform.isAndroid ? pathToFileAndroid : pathToFileIos,
                  pathFile
                      .then((pathToAudioFile) => {
                            print('Path to file: ' + pathToAudioFile),
                            // Speakers/earpiece
                            // int result = await player.earpieceOrSpeakersToggle();
                            // Sets the path to the file
                            audioPlayer.setUrl(pathToAudioFile),
                            audioPlayer.setVolume(1.0),
                            // Gets the duration of the audio file to be reproduced
                            // Plays the audio file
                            playLocal(audioPlayer, pathToAudioFile),
                          }) // pathFile
                      .catchError((e) {
                    print('Error: Unable to get the path to the audio file');
                  }),
                }, // success
            }) //synthesizeToFile
        .catchError((e) {
      print('Error during the synthesisation: $e');
    });
  }

/// Gets the path to the file to be accessed (iOS)
  static Future<String> get pathToFileIos async {
    Directory appDocDir = await getApplicationDocumentsDirectory();
    String appDocPath = appDocDir.path;
    print('appDocPath: $appDocPath');
    return '$appDocPath/$audioFileName';
  }

I implemented audioPlayer.onDurationChanged.listen to confirm that the path to the file is the correct one (it actually gets the duration of the file). Nevertheless, the file is not played.

I am using a simulator to test the application, and this is the path where the file is stored

/localPaty/Devices/deviceID/data/Containers/Data/Application/appID/Documents/temp_audio_cue.caf

I read different questions regarding this topic, but I did not find a solution for my case

I have tried:

Editing Transport security as the documentation recommends

   <key>NSAppTransportSecurity</key>
    <dict>
        <key>NSAllowsArbitraryLoads</key>
        <true/>
    </dict> 

Using AudioCache (question)

Anyone has any other suggestion? Do I need to move the file to my local assets and use AudioCache to reproduce the file?

Update

I have also tried to add the flag isLocal.

  playLocal() async {
    int result = await audioPlayer.play(localPath, isLocal: true);
  }

According to the documentation is is needed cause

The isLocal flag is required only because iOS and macOS make a difference about it

It returns 1, so I guess that it means success, but the sound is still not triggered. Could be the simulator failing?

"iOS => call startHeadlessService, playerId ID"
"iOS => call setUrl, playerId ID"
"iOS => call setVolume, playerId ID"
"iOS => call play, playerId ID"
2021-02-03 11:59:33.149925+0100 Runner[61496:850044] flutter: Played successfully?: 1

I have also tested to call playLocal() with a wrong path. It keeps returning 1 but after it it displays the following error:

FigFileForkOpenMainByCFURL signalled err=2 (errno) (open failed)

Nevertheless, the error is not triggered with calling the method with the correct localPath so I assume it should be playing it correctly.

Note

If instead of calling playLocal(), I call flutter_tts.speak('test'), it works. Therefore, the error should probably be related to audioplayers.

The versions that I am using are flutter_tts ^2.1.0 and audioplayers: ^0.17.3


Solution 1: bellotas

In the end, I realised that the problem was related to AVAudioSession and its session management. Both libraries, flutter_tts and audioplayers make use of it and therefore they could overlay it other.

I had to change the method to the following:

 /// Synthesises the current audio cue into an audio file
  static Future<void> synthesiseStringToAudioFile() async {
    int synthesized = await flutterTts.synthesizeToFile(
        "This is my first audio synthesizer in Flutter", audioFileName);
    if (synthesized == 1) {
      String pathFile =
          await (Platform.isAndroid ? pathToFileAndroid : pathToFileIos);
      print('Path to file: ' + pathFile);
      playAudioFile(pathFile);
    } else {
      print('Error during the synthesisation');
    }
  }

to start triggering some audio in iOS. Still, there are some problems within the notifications, but I consider that the answer has been answered.


Solution 2: Kab Agouda

When you play sounds with audio players and use flutter_tts to say words, it makes flutter_tts dysfunctional on iOS

Thus, only one of the package works, but as soon as both are used at the same time, tts stops working after a while. Sometimes it lasts longer, but sometimes it stops immediately.

In reality ,this is due to a conflict on the iOS side.

Assuming it works on Android, that means there shouldn't be a problem in your code.

Another alternative would have to be found .


Solution 3: Muhammad Arslan

Today I've fixed this way. I was using audio from the URL and I was using this line

  var res = await audioPlayer.play(url, isLocal: true);

I've to use this below line into my code

  var res = await audioPlayer.play(url, isLocal: false);

isLocal stands for your file is stored locally or not. if the file is stored remotely which means in the form of URL then set to isLocal:false.

hope it solves your problem.


Solution 4: TechSolutions

Use play() method instead of playBytes() while working with IOS. int result=0; if(Platform.isIOS){ result = await player.play("Audio url",isLocal: false); }


Solution 5: M adnan

final AudioContext audioContext = AudioContext(
iOS: AudioContextIOS(
defaultToSpeaker: true,
category: AVAudioSessionCategory.ambient,
options: [
AVAudioSessionOptions.defaultToSpeaker,
AVAudioSessionOptions.mixWithOthers,
],
),
android: AudioContextAndroid(
isSpeakerphoneOn: true,
stayAwake: true,
contentType: AndroidContentType.sonification,
usageType: AndroidUsageType.assistanceSonification,
audioFocus: AndroidAudioFocus.none,
),
);
AudioPlayer.global.setGlobalAudioContext(audioContext);

Add this in you main.dart file before this runApp(App());