Звук воспроизводится в Chrome, но не в Safari
У меня есть угловое приложение, где я установил click
обработчик кнопки для загрузки аудио файла и его воспроизведения. Я использую этот код для этого:
onPreviewPressed(media: Media): void {
const url = ".....";
this.httpClient.get(url, {responseType: 'blob'}).subscribe(x => {
const fileReader = new FileReader();
fileReader.onloadend = () => {
const context = new ((<any>window).AudioContext || (<any>window).webkitAudioContext)();
const source = context.createBufferSource();
context.decodeAudioData(fileReader.result, buffer => {
source.buffer = buffer;
source.connect(context.destination);
source.start(0);
}, y => {
console.info("Error: " + y);
});
};
fileReader.readAsArrayBuffer(x);
});
}
Если я перехожу на страницу в Chrome и нажимаю кнопку, звук начинается прямо вверх. Если я делаю это в Safari, то ничего не происходит. Я знаю, что Safari заблокировал все, но это в ответ на нажатие кнопки, это не автоматическое воспроизведение.
Аудио отправляется обратно с сервера через скрипт PHP, и он отправляет заголовки следующим образом, если это имеет значение:
header("Content-Type: audio/mpeg");
header('Content-Transfer-Encoding: binary');
header('Content-Length: ' . filesize($_GET['file']));
header('Cache-Control: no-cache');
1 ответ
Нет, это не "в ответ на нажатие кнопки".
В ответ на это событие щелчка вы запускаете асинхронную задачу. К тому времени как ты позвонишь source.start(0)
Ваше событие давно умерло (или, по крайней мере, больше не является "жестом доверенного пользователя"). Поэтому они действительно заблокируют этот вызов.
Чтобы обойти это, вы можете просто пометить свой контекст как разрешенный с молчанием. Затем, когда данные будут доступны, вы сможете запустить их без ограничений:
function markContextAsAllowed(context) {
const gain = context.createGain();
gain.gain.value = 0; // silence
const osc = context.createOscillator();
osc.connect(gain);
gain.connect(context.destination);
osc.onended = e => gain.disconnect();
osc.start(0);
osc.stop(0.01);
}
onPreviewPressed(media: Media): void {
const url = ".....";
// declare in the event handler
const context = new(window.AudioContext || window.webkitAudioContext)();
const source = context.createBufferSource();
// allow context synchronously
markContextAsAllowed(context);
this.httpClient.get(url, {
responseType: 'blob'
}).subscribe(x => {
const fileReader = new FileReader();
fileReader.onloadend = () => {
context.decodeAudioData(fileReader.result, buffer => {
source.buffer = buffer;
source.connect(context.destination);
source.start(0);
}, y => {
console.info("Error: " + y);
});
};
fileReader.readAsArrayBuffer(x);
});
}
Как скрипка, так как Safari не любит чрезмерно защищенный StackSnippets®
Кроме того, мои угловые знания очень ограничены, но если httpClient.get
поддерживает {responseType: 'arraybuffer'}
вариант, вы могли бы избавиться от этого FileReader и избежать заполнения дважды памяти одними и теми же данными.
Наконец, если вы собираетесь воспроизводить этот звук более одного раза, рассмотрите возможность его предварительной выборки и предварительного декодирования, тогда вы сможете избежать всего асинхронного беспорядка.