WebKit Audio искажается на iOS 6 (iPhone 5) впервые после включения питания
Я боролся с неуловимой ошибкой звукового искажения, используя webkitAudioContext в HTML5 под iOS 6. Это может произойти в других обстоятельствах, но единственный способ получить 100% -ный повтор - это первый визит на мою страницу после силовой езды Устройство. Кажется, что если вы посещаете любую страницу с поддержкой аудио до того, как ее посещать, проблема не возникнет.
Искажение происходит только с аудио, созданным webkitAudioContext.decodeAudioData(), а затем воспроизводится через webkitAudioContext.createBufferSource(). Воспроизведение аудио в webkitAudioContext.createMediaElementSource() будет искажать не.
Я пропустил какой-то шаг инициализации? Здесь код и HTML полностью, что я представил Apple в качестве отчета об ошибке (но не получил ответа):
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript">
var buffer = null;
var context = null;
var voice = null;
function load_music(file) {
context = new webkitAudioContext();
voice = context.createBufferSource();
var request = new XMLHttpRequest();
request.onload = function() {
context.decodeAudioData(request.response, function(result) {
buffer = result;
document.getElementById("start").value = "Start";
});
};
var base = window.location.pathname;
base = base.substring(0, base.lastIndexOf("/") + 1);
request.open("GET", base + file, true);
request.responseType = "arraybuffer";
request.send(null);
}
function start_music() {
if (!buffer) {
alert("Not ready yet");
return;
}
voice.buffer = buffer;
voice.connect(context.destination);
voice.noteOn(0);
document.getElementById("compare").style.display = "block";
}
</script>
</head>
<body onload="load_music('music.mp3')">
<p>This is a simple demo page to reproduce a <strong>webkitAudio</strong>
problem occurring in Safari on iOS 6.1.4. This is a stripped down demo
of a phenomenon discovered in our HTML5 game under development,
using different assets.</p>
<p><u>Steps to reproduce:</u></p>
<ol>
<li>Power cycle <strong>iPhone 5 with iOS 6.1.4</strong>.</li>
<li>Launch Safari immediately, and visit this page.</li>
<li>Wait for "Loading..." below to change to
"Start".</li>
<li>Tap "Start".</li>
</ol>
<p><u>Issue:</u></p>
<p>Audio will be excessively distorted and play at wrong pitch. If
another audio-enabled web site is visited before this one, or this
site is reloaded, the audio will fix. The distortion only happens on
the first visit after cold boot. <strong>To reproduce the bug, it is
critical to power cycle before testing.</strong></p>
<p>This bug has not been observed on any other iOS version (e.g. does
not occur on iPad Mini or iPod 5 using iOS 6.1.3).</p>
<input id="start" type="button" value="Loading..." onmousedown="start_music()" />
<span id="compare" style="display:none;"><p><a href="music.mp3">Direct link</a> to audio file, for
comparison.</p></span>
</body>
</html>
Примечание. Текст корпуса предполагает, что это происходит только в iOS 6.1.4, но я хочу сказать, что проблема возникает только при силовой цикличности в этой ситуации. Я тоже столкнулся с проблемой на iPad Mini под 6.1.3, но не при силовой цикличности.
Изменить: несколько вещей, которые я пробовал... Отложить создание источника буфера не имеет значения. Использование разных транскодеров для создания файла .mp3, который он играет, не имеет никакого значения. Игра в тишину, когда первый звук не имеет никакого значения, поскольку искажение продолжается для каждого звука decodeAudioData до тех пор, пока страница не перезагрузится. Если источники createMediaElementSource и createBufferSource смешаны на одной странице, будет искажаться только звук createBufferSource (с использованием decodeAudioData). Когда я проверяю request.response.byteLength в случае сбоя и случае без отказа, они одинаковы, предполагая, что XMLHttpRequest не возвращает неверные данные, хотя я думаю, что повреждение данных может повредить заголовок MP3 и отобразить файл не воспроизводится в любом случае.
Существует одна наблюдаемая разница между состоянием отказа и условием отсутствия отказа. Значение context.sampleRate, доступное только для чтения, будет 48000 в состоянии сбоя и 44100 в состоянии без отказа. (Тем не менее, состояние отказа звучит ниже, чем состояние отказа.) Единственное, что происходит со мной, это взлом, в котором я обновляю страницу через JavaScript, если 48000 обнаружен в браузере, который должен сообщать о 44100, но этот серьезный userAgent скрининг и не очень будущее доказательство, что заставляет меня нервничать.
Ответы
Ответ 1
У меня были подобные проблемы, даже на iOS 9.2.
Даже без тэга <video>
воспроизведение искажается при первом воспроизведении звука на странице после холодной загрузки. После перезагрузки он отлично работает.
Первоначальный AudioContext
по умолчанию равен 48 кГц, в котором происходит искажение (даже с нашим звуком с частотой дискретизации 48 кГц). Когда воспроизведение работает правильно, AudioContext
имеет частоту дискретизации 44,1 кГц.
Я нашел обходное решение: после воспроизведения начального звука можно воссоздать AudioContext
. У недавно созданного AudioContext
есть правильная частота дискретизации. Для этого:
// inside the click/touch handler
var playInitSound = function playInitSound() {
var source = context.createBufferSource();
source.buffer = context.createBuffer(1, 1, 48000);
source.connect(context.destination);
if (source.start) {
source.start(0);
} else {
source.noteOn(0);
}
};
playInit();
if (context.sampleRate === 48000) {
context = new AudioContext();
playInit();
}
Ответ 2
Я нашел связанную ошибку с видео HTML5 и думаю, что я обнаружил корень проблемы.
Я заметил, что если вы воспроизводите видео с помощью тега <video>
, он устанавливает значение context.sampleRate в зависимости от того, что кодировалось видео. Кажется, что у iOS Safari есть один глобальный образец, который он использует для всего. Чтобы увидеть это, попробуйте следующее:
// Play a video with audio encoded at 44100 Hz
video.play();
// This will console log 44100
var ctx = new webkitAudioContext();
console.log(ctx.sampleRate);
// Play a video with audio encoded at 48000 Hz
video2.play();
// This will console log 48000
var ctx = new webkitAudioContext();
console.log(ctx.sampleRate);
Эта глобальная частота выборки, по-видимому, сохраняется на всех загрузках страниц и делится между вкладками и экземплярами браузера. Таким образом, воспроизведение видео с YouTube на другой вкладке может сломать все ваши декодированные аудио.
Звук становится искаженным, когда он декодируется с одной частотой дискретизации и воспроизводится на другом.
- Декодирование звука и сохранение буфера
- Сделайте что-то, чтобы изменить частоту дискретизации, например, воспроизведение видео или аудиофайла.
- Буфер воспроизведения (искаженный)
Я не знаю, почему это происходит после холодного старта. Если бы я должен был догадаться, что Safari не инициализирует эту глобальную частоту дискретизации, пока вы не попытаетесь ее использовать.
Проблема все еще существует на iOS 7, поэтому я не думаю, что исправление поступит в ближайшее время. Мы застряли с хаками в среднем времени, например, проверяем изменение частоты дискретизации.
Ответ 3
Пакет npm находится в сети, чтобы исправить это:
https://github.com/Jam3/ios-safe-audio-context
npm install ios-safe-audio-context