Skip to content

Commit 07fed06

Browse files
committed
Initial commit
1 parent 1289577 commit 07fed06

File tree

3 files changed

+89
-0
lines changed

3 files changed

+89
-0
lines changed

README.md

Whitespace-only changes.

package.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"name": "@activetheory/ios-silent-hack",
3+
"version": "1.0.0",
4+
"description": "Simple hack to allow web audio even if physical mute switch is on iPhone/iPads",
5+
"main": "index.js",
6+
"scripts": {
7+
"test": "echo \"Error: no test specified\" && exit 1"
8+
},
9+
"author": "@activetheory",
10+
"license": "MIT",
11+
"repository": {
12+
"type": "git",
13+
"url": "https://github.com/activetheory/ios-silent-hack"
14+
}
15+
}

src/index.js

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
const EVENTS = [
2+
'auxclick',
3+
'click',
4+
'contextmenu',
5+
'dblclick',
6+
'keydown',
7+
'keyup',
8+
'mousedown',
9+
'mouseup',
10+
'touchend'
11+
]
12+
13+
export default class {
14+
constructor() {
15+
this.trying = false;
16+
this.state = 'blocked';
17+
this.audioFile = this.createAudioData();
18+
19+
EVENTS.forEach(evtName => {
20+
window.addEventListener(evtName, tryUnblock, { capture: true, passive: true });
21+
})
22+
}
23+
24+
tryUnblock() {
25+
if (this.state === 'allowed' || this.trying) return;
26+
createHTMLAudio();
27+
}
28+
29+
createAudioData() {
30+
const rate = 48000;
31+
const arrayBuffer = new ArrayBuffer(10);
32+
const dataView = new DataView(arrayBuffer);
33+
34+
dataView.setUint32(0, rate, true);
35+
dataView.setUint32(4, rate, true);
36+
dataView.setUint16(8, 1, true);
37+
38+
const missingCharacters = window.btoa(String.fromCharCode(...new Uint8Array(arrayBuffer))).slice(0, 13);
39+
40+
return `data:audio/wav;base64,UklGRisAAABXQVZFZm10IBAAAAABAAEA${missingCharacters}AgAZGF0YQcAAACAgICAgICAAAA=`;
41+
}
42+
43+
createHTMLAudio() {
44+
this.trying = true;
45+
46+
let audio = document.createElement('audio');
47+
48+
audio.setAttribute('x-webkit-airplay', 'deny');
49+
audio.preload = 'auto';
50+
audio.loop = true;
51+
audio.src = this.audioFile;
52+
audio.load();
53+
54+
audio.play().then(() => {
55+
this.state = 'allowed';
56+
}, () => {
57+
this.state = 'blocked';
58+
audio.src = "about:blank";
59+
audio.load();
60+
audio = null;
61+
this.trying = false;
62+
});
63+
}
64+
65+
destroy() {
66+
EVENTS.forEach(evtName => {
67+
window.removeEventListener(evtName, tryUnblock, { capture: true, passive: true });
68+
});
69+
}
70+
71+
get allowed() {
72+
return this.state === 'allowed';
73+
}
74+
}

0 commit comments

Comments
 (0)