简体   繁体   中英

Javascript audio not working properly with React and Socket.io on iOS

I'm working on creating a simple web app that uses React and Socket.io. The principle is that there are two devices connected, which send each other nothing but 'go to this view' -type messages, which then react renders just fine.

The issue I have is with playing audio clips with javascript when the views change. When I click on the button on the iPad that changes the view, the audio starts to play just fine, but when the view change is initiated by another device over websocket connection, the audio won't play.

All this works just fine on desktop both Chrome and Safari. This doesn't work on iPad running iOS 9.3.5 nor iPhone running iOs 11.0.1 (tested both safari and chrome)

Here's the code for playing audio file:

// Importing audio files
import responsePromptSound from 'A/responsePrompt.aac';
import endsound from 'A/endsound.aac';

function playSound(view) {
  // Stop playing a repeating sound if there is one
  if (window.soundPlaying) {
    clearInterval(window.soundPlaying);
  }

  // The audio clips to be used in each view
  const views = {
    start: null,
    waitingForResponse: null,
    responsePrompt: responsePromptSound,
    end: endsound,
  };

  // Current audio clip
  const soundClip = views[view];

  if (!soundClip) {
    return;
  }

  const audio = new Audio(soundClip);

  function playMe() {
    audio.play();
  }

  // Repeating notification sound
  if (view === 'responsePrompt') {
    window.soundPlaying = setInterval(() => {
      playMe();
    }, 7 * 1000);
  }

  // Play always at least once
  playMe();
}

export default playSound;

Here's the code that renders a button that also calls the playSound :

import React from 'react';
import PropTypes from 'prop-types';

import playSound from 'C/Audio.js';

function Button(props) {
  // text content for each view
  const views = {
    start: ['start1', 'start2'],
    waitingForResponse: null,
    responsePrompt: ['prompt1', 'prompt2'],
    end: ['end1', 'end2'],
  };

  // Current button text
  const btnText = views[props.view];

  let btn;

  // Different views for button
  if (!btnText) {
    btn = null;
  } else if (props.view === 'end') {
    btn = (
      <div className="end-box">
        <div className="end-box-inner">

          <div className="button-txt-one">{btnText[0]}</div>
          <div className="line" />
          <div className="button-txt-two">{btnText[1]}</div>

        </div>
      </div>
    );
  } else {
    btn = (
      <button onClick={props.changeView}>
        <div className="button-inner">
          <span>

            <div className="button-txt-one">{btnText[0]}</div>
            <div className="line" />
            <div className="button-txt-two">{btnText[1]}</div>

          </span>
        </div>
      </button>
    );
  }

  playSound(props.view);

  return (
    btn
  );
}

Button.propTypes = {
  view: PropTypes.string.isRequired,
  changeView: PropTypes.func.isRequired,
};

export default Button;

The socket connection only triggers setState() on the parent element with the correct view .

EDIT: Forgot to add that I also tried using MutationObserver on the main element with everything turned on but still got nothing.

Ps. I know there's like a billion things not done all that well here, bear with me.

I found this article which describes the limitations of HTML5 audio on iOS. Related to this issue, on iOS the playing of the audio must always be initiated by user action, and the only solution is to design the app in such a way that the user always initiates the playback.

I'm gonna look into some possible workarounds and will edit my answer asap to include my findings, even though at least according to the article there's no workaround. It's from 2012 though, so there might be some changes.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM