Уменьшение громкости веб — звука для устройств iOS 13

#javascript #ios #progressive-web-apps #web-audio-api

Вопрос:

Я надеюсь, что кто-нибудь здесь, в сообществе переполнения стека, сможет найти обходной путь для этого. Это уже долгое время было постоянной занозой в моем боку.

Мое требование довольно простое: уменьшите громкость для воспроизведения звука на странице.

Есть несколько способов сделать это с помощью javascript:

  1. Обновите свойство «объем» на an HTMLAudioElement . Это вообще не работает для устройств iOS, поскольку свойство доступно только для чтения для ОС.
  2. Используйте API веб — аудио AudioContext.createMediaElementSource() GainNode с. В настоящее время это работает, однако вывод звука часто будет искажен-трещины и хлопки испортят качество любого потокового аудио. Это не вызвано использованием другой частоты дискретизации аудиоконтекста.
  3. Используйте API веб-аудио AudioBuffer с файлом, загруженным XMLHttpRequest с помощью arraybuffer типа ответа. Это, похоже, вообще не приводит к увеличению громкости. Он работает с симулятором, но не на физических устройствах.

Все три подхода работают практически в любом браузере на любом устройстве, за исключением современных устройств iOS. Версии iOS до 13 отлично работают для подхода 2 или 3. Я не хочу быть сторонником теории заговора, но может ли Apple намеренно нарушить поддержку PWA, чтобы направить больше доходов в свой магазин приложений?

Комментарии:

1. Можете ли вы опубликовать свою попытку для 3-го подхода?

2. Да вот оно codepen.io/lunatichombre/full/GRWYzVv

3. Оказывается, этот подход работает и сейчас. В какой — то момент это не сработало. Понятия не имею, почему, у меня нет устройства iOS для тестирования, чтобы выяснить, почему.

4. @BruceHamilton вы нашли какое-либо решение для поддержки затухания во время потоковой передачи звука?

Ответ №1:

TL;DR все, что вам нужно, содержится в этом фрагменте (надеюсь)

У меня есть этот фрагмент, вы можете попробовать и сказать мне, работает ли это для вас:

 // input
const FADE_IN_DURATION = 10
const url = '/path/to/audio.mp3'

// setup
const context = /** @type {AudioContext} */(new (window.AudioContext || window.webkitAudioContext)())
const gainNode = context.createGain()
gainNode.connect(context.destination)
gainNode.gain.value = 0

// fetch
const response = await fetch(url)
const audioData = await response.arrayBuffer()
const buffer = await new Promise((resolve, reject) => {
    context.decodeAudioData(audioData, resolve, reject)
})

// connect
const source = context.createBufferSource()
source.buffer = buffer
source.connect(gainNode)

// play
const startTime = context.currentTime
gainNode.gain.linearRampToValueAtTime(1, startTime   FADE_IN_DURATION)
source.start(startTime)
 

Ключевые моменты

  • GainNode возвращенный context.createGain() ответ дает вам доступ к «выигрышу» AudioParam . И AudioParam есть метод, который называется linearRampToValueAtTime , который делает именно то, что вам нужно
  • значение усиления выше 1 создаст некоторое «искажение», потому что «пики громкости» начнут превышать максимально возможное значение. Так что, если вы просто хотите плавного затухания, оставайтесь между 0 и 1 .
  • «Проводка», которую я делаю, — это очень простая AudioBufferSourceNode --> GainNode --> AudioContext

Реальный рабочий сценарий

Приведенный выше фрагмент на самом деле не работает из коробки, потому что вам нужно

  • функция для обработки асинхронного/ожидания,
  • и пользовательский ввод для начала воспроизведения звукового контекста). Итак, вот оно со всеми надлежащими упаковками:
 // input
const FADE_IN_DURATION = 10
const url = 'https://upload.wikimedia.org/wikipedia/commons/d/de/Lorem_ipsum.ogg'

// setup
const context = /** @type {AudioContext} */ (new(window.AudioContext || window.webkitAudioContext)())
const gainNode = context.createGain()
gainNode.connect(context.destination)
gainNode.gain.value = 0

// fetch
;(async function() {
  const response = await fetch(url)
  const audioData = await response.arrayBuffer()
  const buffer = await new Promise((resolve, reject) => {
    context.decodeAudioData(audioData, resolve, reject)
  })

  // user input necessary
  document.getElementById('text').textContent = "Ready. Click anywhere to start audio."
  document.addEventListener('click', () => {
    document.getElementById('text').textContent = "Now playing..."

    // connect
    const source = context.createBufferSource()
    source.buffer = buffer
    source.connect(gainNode)

    // play
    const startTime = context.currentTime
    gainNode.gain.linearRampToValueAtTime(1, startTime   FADE_IN_DURATION)
    source.start(startTime)
  }, {
    once: true
  })
})() 
 <div id="text">Loading file...</div>