I am making a timer app in React. Whenever the timer counts back to 0 it should play a wav sound file. It works fine on IE, Chrome, Edge, Android, but not on iOS.
I have found an explanation why this is happening, but not how to work around it.
In Safari on iOS (for all devices, including iPad), where the user may be on a cellular network and be charged per data unit, preload and autoplay are disabled. No data is loaded until the user initiates it. This means the JavaScript play() and load() methods are also inactive until the user initiates playback, unless the play() or load() method is triggered by user action. In other words, a user-initiated Play button works, but an onLoad="play()" event does not. (Source: Apple Developer)
<audio id="beep" src="http://www.pacdv.com/sounds/interface_sound_effects/sound106.wav"></audio>
startTimer = () => {
document.getElementById('beep').play();
}
The start timer function runs when the timer reaches 0, not on a click event. Expected result would be on iOS to play the audio when counts back to 0.
Related
I am working on a website which gives users the option to have the pages read aloud to them.
I am using SoundJS (https://www.createjs.com/soundjs) to load in an OGG, MP3, and WAV version of a narration file, and then play it as soon as it finishes loading. Here is the code:
var narration = {
path: '../assets/sounds/',
manifest: [
{
id: 'narration',
src: {
mp3: 'narration.mp3',
ogg: 'narration.ogg',
wav: 'narration.wav'
}
}
]
};
createjs.Sound.alternateExtensions = ['mp3', 'wav'];
createjs.Sound.registerSounds(narration);
createjs.Sound.addEventListener('fileload', function(){
createjs.Sound.play('narration');
});
On the iPhone X and iPad, in Safari and Chrome, the audio file starts playing, but then cuts out before it finishes. The audio files on the site range from 7 to over 30 seconds, and the audio always cuts out a few seconds before the end. For instance, in Chrome, a 10 second clip stops playing after 8 seconds, and a 31 second clip stops playing after 29. In Safari, the same 10 second clip stops after 7 seconds, and the same 31 second clip stops after 22.
On all non-iOS devices we've tested, in many different browsers, this issue has never occurred.
What is the reason behind this, and what can I do to ensure the audio plays all the way through?
As long as there is no solution found for this issue, we're considering it a bug with iOS mobile devices, and not something we can control.
As a workaround, we've implemented a button on each page that the user must click before the audio plays. The button runs createjs.Sound.play('narration'), rather than it being triggered through the fileload event listener. For some reason, registering a click on each page before attempting to play audio allows it to play all the way through, reliably.
Have you tried using the HTML5 audio element? Simply add it as an element that appears over the Canvas. I added an audio element, "", and it seems to work well in my iPad
Solution below
I am working on a running app which gives voiceover instruction at the start of each exercise. It works as intended when the device is active but doesn't work when the phone is locked. [Current audio continues but no future audio plays.]
Question
How can I start playing an audio voiceover while the users iPhone is locked.
Currently I track the workout using Timer.scheduledTimer for 2 timers I display [current activity and the overall workout]. When the timer hits a pre-defined time towards the end of one activity, the voiceover audio plays to introduce the next activity. The timer firing is what starts the audio and I believe this is the issue. I can pause the workout, skip sections and all works fine - until the device is locked.
If an audio voiceover is playing when I press the home button or lock the iPhone it continues to play as expected. The issue is then that the timer doesn't fire so the next audio voiceover [in say 90 seconds] is never played.
Some of the answers and comments I've read have said that this functionality just isn't possible in iOS. The Couch to 5k app https://itunes.apple.com/gb/app/one-you-couch-to-5k/id1082307672?mt=8 uses this functionality so I know it's possible to achieve, I just don't know how they're doing it. [It will be a dedicated member that downloads it to see what I mean :)]
From searching SO I've read a lot of post saying that Timer or NSTimer can't run when the app is in the background or the phone is locked. Any posts that say it can work are old and based on iOS4/5
I've read about suggestions of using silence and essentially having 1 long audio file. While this would pose some new challenges for skipping the sections in the workout I've also read comments that say this behaviour would not pass Apple's testing of the app for the AppStore. The 3rd downside being that my audio file size would increase.
An option I've seen is local notifications, however I'm yet to see an example of one used to play audio voiceover when the app is in the background.
How can I achieve the same functionality as the Couch to 5k app?
Thanks
Ok, I worked up a solution to play audio prompts to the user throughout the course of a 30 minute workout, even if the iPhone is locked or the app is in the background. The app has been submitted and approved for the AppStore.
To get the app to perform as required I use 2 AVAudioPlayers; one plays the voiceover, the other plays a 15 minute track of silence [as it’s a separate track and just silent it’s a low quality .mp3 and only added 900kb to the app size]. 15 minutes well exceeds any interval between voiceovers so it works perfect.
When the user starts an exercise the first audio prompt is played. From that point, until the user stops the exercise there will be audio playing in the background. Because background audio is playing, the Timer still operates.
When a voiceover finishes, the AVAudioPlayerDelegate method audioPlayerDidFinishPlaying is called. Within this method I reset the audio properties [I’ll explain why shortly], instantiate my silent AVAudioPlayer, and start playing the 15 minute track of silence. Playing another track using this delegate method means that there is no real break in background audio so the app is kept alive. With the app ‘alive’ the timer continues to countdown. When it’s time for another voiceover the timer calls a method which resets the audio category, instantiates my voiceover AVAudioPlayer and plays a voiceover. This process is repeated until the final voiceover. When that completes no further silence is played and the app can safely be backgrounded.
The audio voiceover needs to duck the users music [reduces the music volume so that the audio can be clearly heard]. The silent audio shouldn’t do that, music should continue to play at full volume. As both AVAudioPlayers use the AVAudioSession.sharedInstance I found that I had to deactivate the sharedInstance, reset the properties, with or without .duckOthers, and then reactivate the sharedInstance before instantiating my AVAudioSession. That then gave me the desired result. Constant background audio that only ducked the users music when a voiceover played.
do{
try AVAudioSession.sharedInstance().setActive(false)
try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default, options: [.interruptSpokenAudioAndMixWithOthers, .duckOthers])
try AVAudioSession.sharedInstance().setActive(true)
} catch{
print(error.localizedDescription)
}
I know that some said Apple do not allow silent audio, however, in this circumstance the silent audio only plays for the duration of the exercise routine. As soon as it ends the app can terminate, thereby not keeping the app alive any longer than necessary.
My understanding of VoiceOver leads me to the way that only a foreground application can control what it reads out.
You need focusing with VoiceOver to make it speak and you focus nothing when the device is locked or your app is in the background mode.
I suggest to take a look at the speech synthesis or the AVAudioSession class to enable background audio: both might be good workarounds to take over in the background mode: just try and let us know. ;o)
This is my scenario: I have a video player playing some video/audio and suddenly the alarm is called since I set it earlier.
Before iOS 10, the alarm's UI and audio would block the main UI thread so the alarm event can be captured by observe the UIWindowDidResignKeyNotification.
But on iOS 10, it seems that alarm UI behaves like Notification messages and will not block the main UI.
But its alarm sound would take over audio thread, so the result is that the video picture is playing while the video sound is not, instead the alarm sound is playing.
With iOS 10 what I have done is I observe AVAudioSessionInterruptionNotification, which would be posted when alarm is running, and once I get it I pause my video playing.
But this results another issue, which is that my users have to click the play button manually again after they dismiss the alarm, this is not so good.
Plus AVAudioSessionInterruptionNotification would be posted when any other type of system audio is triggered, such as iMessage, Push Notification which are messages with very short audio, and that causing pauses would really gets users into trouble.
So I would like to know if there is a way to listen the events that users dismiss the alarm UI up/down.
Or if anyone could come up with some other solution to solve this kind of problem.
UPDATE:
This alarm sound takes over the audio thread issue would only happen when video is decoded by hardware.
If it's default system decoding for video then it'll be fine.
And if incoming call is triggered and call sound also would take over the audio thread which is fine, since incoming call would block the main UI so I can also manage it by observing UIWindowDidResignKeyNotification.
Ok, my fault that I didn't read through apple doc, everything is here.
You can use the userInfo in AVAudioSessionInterruptionNotification.
It gives the enums that when it has began, when it ended and even when it's the best time to resume.
I have an app that uses AVPlayer (or AVQueuePLayer) to play local files that were recorded by the App. All works great. But I also want this to work on iPhone when a call is in progress (the videos are event recordings). What I found is that during a phone call, the video feed to avplayerLayer goes blank, AVPlayer rate change to 0 (STOP), and all attempts to change rate to non-zero (PLAY) are ignored (rate stays at 0). There does not appear to be any documentation on this, and the only way to detect this condition in the player, is that player is STOPPED and will not start PLAYBACK. Of course, I also check for audio interruptions, and call center calls in progress.
Obviously, in this case the interruption is caused by a call, so there is always a inactive/resume or a intactive/background/foreground/resume transition. As well as audio route notification, audio interruption. So indirectly I know the condition is probably occurring.
So questions are:
(1) Is there any direct method (specific to AVPlayer,AVPlayerLayer) to be notified that AVPlayer is in this non-playing mode. I now use "avplayer.rate failed to change rate from 0 to non-zero", but this seems hacky (and too much "crossing the streams"!) I want to Notify user that video temporarily can not be played or previewed, so they do not think the App is broken. And also inform them or automatically continue Playback when iPhone call ends. (Without a looping process that keeps trying to start playback every 500ms!)
(2) Can AVPlayer play anything while a iPhone call (Green Bar) is in progress? or is this just the way apple designed the AVPlayer SDK? (If so there is no documentation on this) Obviously, other apps can play video during an iPhone call, but I suspect they are using a lower level SDK and not AVPlayer.
i have done a game using construct 2, this game is called in an iframe in a JqueryMobile page.
my problem is that sometime when i click 'back button' some sound still played ( sounds are not a background sound but FX and are in queu so i cant stop theme with construct 2 pause function ).
my solution is to set sound off on iphone, but according this post, i have to use private api and that mean a rejected app.
have you a solution for this ?
thank you.
Construct 2 automatically pauses audio when the app is 'suspended' (in an invisible tab in a browser, or moved to the background as a mobile app). However I guess an iframe in a PhoneGap app doesn't fire the right events.
You can call them yourself, though: call these functions in the iframe to pause and resume the entire game (which includes audio):
cr_setSuspended(true); // pause game and mute audio
cr_setSuspended(false); // resume game and unmute audio