From 5084b13105a9a3d7fea38aee7b52e91c653cbf93 Mon Sep 17 00:00:00 2001 From: ffont Date: Mon, 22 Jan 2024 11:44:37 +0100 Subject: [PATCH] Fix sound skipping frames in safari re-play https://github.com/MTG/freesound/issues/1720 --- .../src/components/player/audio-element.js | 1 + .../src/components/player/player-ui.js | 21 ++++++++++++----- .../src/components/player/utils.js | 3 ++- .../static/bw-frontend/src/utils/browser.js | 20 ++++++++++++++++ package-lock.json | 23 +++++++++++++++++++ package.json | 1 + 6 files changed, 62 insertions(+), 7 deletions(-) create mode 100644 freesound/static/bw-frontend/src/utils/browser.js diff --git a/freesound/static/bw-frontend/src/components/player/audio-element.js b/freesound/static/bw-frontend/src/components/player/audio-element.js index f5e54e2f0..75caded46 100644 --- a/freesound/static/bw-frontend/src/components/player/audio-element.js +++ b/freesound/static/bw-frontend/src/components/player/audio-element.js @@ -151,6 +151,7 @@ export const createAudioElement = parentNode => { audioElement.addEventListener('ended', () => { onPlayerTimeUpdate(audioElement, parentNode); + audioElement.currentTime = 0.0; }) audioElement.addEventListener('pause', () => { diff --git a/freesound/static/bw-frontend/src/components/player/player-ui.js b/freesound/static/bw-frontend/src/components/player/player-ui.js index 375b5f838..7829801ec 100644 --- a/freesound/static/bw-frontend/src/components/player/player-ui.js +++ b/freesound/static/bw-frontend/src/components/player/player-ui.js @@ -5,6 +5,7 @@ import { formatAudioDuration, playAtTime, getAudioElementDurationOrDurationPrope import { createIconElement } from '../../utils/icons' import { createAudioElement, setProgressIndicator, onPlayerTimeUpdate } from './audio-element' import { rulerFrequencyMapping } from './utils' +import { isDesktopMacOSWithSafari, isTouchEnabledDevice } from '../../utils/browser' const removeAllLastPlayedClasses = () => { document.getElementsByClassName('last-played').forEach(element => { @@ -12,10 +13,6 @@ const removeAllLastPlayedClasses = () => { }); } -const isTouchEnabledDevice = () => { - return (('ontouchstart' in window) || (navigator.maxTouchPoints > 0) || (navigator.msMaxTouchPoints > 0)) -} - if (isTouchEnabledDevice()){ document.addEventListener('click', (evt) => { /* In touch devics, make sure we remove the last-played class when user touches @@ -223,7 +220,19 @@ const createPlayButton = (audioElement, playerSize) => { if (simultaneousPlaybackDisallowed() || evt.altKey){ stopAllPlayers(); } - audioElement.play() + if (isDesktopMacOSWithSafari()){ + // In some "oldish" macs with safari there is an issue in which re-playing a sound that has already been played results in + // the first few milliseconds of the sound being skipped. To avoid this, we reload the audio element before playing + // again. This means that the sound is loaded again from the server. This is not ideal but it's the only way to + // get around this issue. The check of isDesktopMacOSWithSafari() is not perfect because it also includes newer macs, but + // it is useful to filter out most of the case in which this issue does not happen. + const previousCurrentTime = audioElement.currentTime; + audioElement.load() + audioElement.currentTime = previousCurrentTime; + audioElement.play() + } else { + audioElement.play() + } } evt.stopPropagation() }) @@ -704,4 +713,4 @@ const createPlayer = parentNode => { } -export {createPlayer, isTouchEnabledDevice}; +export {createPlayer}; diff --git a/freesound/static/bw-frontend/src/components/player/utils.js b/freesound/static/bw-frontend/src/components/player/utils.js index b6ae689a3..b385a056e 100644 --- a/freesound/static/bw-frontend/src/components/player/utils.js +++ b/freesound/static/bw-frontend/src/components/player/utils.js @@ -1,5 +1,5 @@ /* eslint-disable import/prefer-default-export */ -import {createPlayer, isTouchEnabledDevice} from './player-ui' +import {createPlayer} from './player-ui' export const simultaneousPlaybackDisallowed = () => { return document.cookie.indexOf('disallowSimultaneousAudioPlayback=yes') > -1; @@ -109,3 +109,4 @@ var y_min = Math.log(100.0) / Math.LN10; // See utils.audioprocessing.processin var y_max = Math.log(22050.0) / Math.LN10; for (var y = 500;y >= 0; y--) rulerFrequencyMapping.push(Math.pow(10.0, y_min + y / 500.0 * (y_max - y_min))); + diff --git a/freesound/static/bw-frontend/src/utils/browser.js b/freesound/static/bw-frontend/src/utils/browser.js new file mode 100644 index 000000000..fe26fdda2 --- /dev/null +++ b/freesound/static/bw-frontend/src/utils/browser.js @@ -0,0 +1,20 @@ +import { UAParser } from 'ua-parser-js'; + +const { browser, cpu, device } = UAParser(navigator.userAgent); + +const isIPad = () => { + return device.model === 'iPad'; +} + +const isSafari = () => { + return browser.name === 'Safari'; +} + +export const isTouchEnabledDevice = () => { + return (('ontouchstart' in window) || (navigator.maxTouchPoints > 0) || (navigator.msMaxTouchPoints > 0)) +} + +export const isDesktopMacOSWithSafari = () => { + return isSafari() && !isTouchEnabledDevice(); +} + diff --git a/package-lock.json b/package-lock.json index 0f4a1b57e..974d56ef4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,7 @@ "lodash.throttle": "^4.1.1", "normalize.css": "^8.0.1", "regenerator-runtime": "^0.13.3", + "ua-parser-js": "^1.0.37", "whatwg-fetch": "^3.0.0" }, "devDependencies": { @@ -11357,6 +11358,28 @@ "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", "dev": true }, + "node_modules/ua-parser-js": { + "version": "1.0.37", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.37.tgz", + "integrity": "sha512-bhTyI94tZofjo+Dn8SN6Zv8nBDvyXTymAdM3LDI/0IboIUwTu1rEhW7v2TfiVsoYWgkQ4kOVqnI8APUFbIQIFQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/ua-parser-js" + }, + { + "type": "paypal", + "url": "https://paypal.me/faisalman" + }, + { + "type": "github", + "url": "https://github.com/sponsors/faisalman" + } + ], + "engines": { + "node": "*" + } + }, "node_modules/unbox-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", diff --git a/package.json b/package.json index 0599739f7..12de7441c 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,7 @@ "lodash.throttle": "^4.1.1", "normalize.css": "^8.0.1", "regenerator-runtime": "^0.13.3", + "ua-parser-js": "^1.0.37", "whatwg-fetch": "^3.0.0" } }