#javascript #ios #progressive-web-apps #web-audio-api
Вопрос:
Я надеюсь, что кто-нибудь здесь, в сообществе переполнения стека, сможет найти обходной путь для этого. Это уже долгое время было постоянной занозой в моем боку.
Мое требование довольно простое: уменьшите громкость для воспроизведения звука на странице.
Есть несколько способов сделать это с помощью javascript:
- Обновите свойство «объем» на an
HTMLAudioElement
. Это вообще не работает для устройств iOS, поскольку свойство доступно только для чтения для ОС. - Используйте API веб — аудио
AudioContext.createMediaElementSource()
GainNode
с. В настоящее время это работает, однако вывод звука часто будет искажен-трещины и хлопки испортят качество любого потокового аудио. Это не вызвано использованием другой частоты дискретизации аудиоконтекста. - Используйте 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>