ebt-vue
Version:
Vue/Vuetify component library for EBT-Site
204 lines (192 loc) • 6.52 kB
HTML
<html>
<script>
var AudioContext = window.AudioContext
|| window.webkitAudioContext;
const BR = '<br>';
var audioContext = new AudioContext();
function unlockAudioContext(audioContext, elt=document.body) {
let emsg = document.getElementById("emsg");
console.log(`unlockAudioContext state:${audioContext.state}`);
if (audioContext.state !== 'suspended') {
emsg.innerHTML += `unlockAudioContext state:${audioContext.state} OK`+BR;
return;
}
emsg.innerHTML += `unlockAudioContext state:${audioContext.state} => resume()`+BR;
const events = ['touchstart','touchend', 'mousedown','keydown'];
events.forEach(e => elt.addEventListener(e, unlock, false));
function unlock() { audioContext.resume().then(clean); }
function clean() { events.forEach(e => elt.removeEventListener(e, unlock)); }
}
function eMsg(msg) {
let emsg = document.getElementById("emsg");
if (msg) {
emsg.innerHTML += msg + BR;
} else {
emsg.innerHTML = new Date() + BR;
}
}
function onClickClear() {
let emsg = document.getElementById("emsg");
eMsg();
}
async function playOne(audioContext, url, n) {
console.log("TEST: playOne()");
try {
let length = 0;
let numberOfChannels = 2;
let sampleRate = 48000;
let headers = new Headers();
headers.append('Accept', 'audio/mp3');
let res = await fetch(url, { headers });
if (!res.ok) {
throw new Error(`TEST: playOne() ${e.message}`);
}
let urlBuf = await res.arrayBuffer();
let emsg = document.getElementById("emsg");
emsg.innerHTML += n+`audioContext.state:${audioContext.state}`+BR;
emsg.innerHTML += n+"createBufferSource<br>";
let audioSource = audioContext.createBufferSource();
let urlAudio = await new Promise((resolve, reject)=>{
emsg.innerHTML += n+"decodeAudioData<br>";
let res = audioContext.decodeAudioData(urlBuf, resolve, reject);
});
emsg.innerHTML += n+`decodeAudioData => ${urlAudio}<br>`;
numberOfChannels = Math.min(numberOfChannels, urlAudio.numberOfChannels);
length += urlAudio.length;
sampleRate = Math.max(sampleRate, urlAudio.sampleRate);
console.log(`${n}playOne()`, {sampleRate, length, numberOfChannels});
msg = [
`audioContext.createBuffer`,
JSON.stringify({numberOfChannels, length, sampleRate}),
].join(' ');
emsg.innerHTML += n+msg+'<br>';
let audioBuffer = audioContext.createBuffer(numberOfChannels, length, sampleRate);
emsg.innerHTML += n+'audioBuffer created<br>';
for (let channelNumber = 0; channelNumber < numberOfChannels; channelNumber++) {
let offset = 0;
msg = [
`new Float32Array`,
typeof Float32Array,
typeof window.Float32Array,
Float32Array == null ? "null" : "OK",
].join(' ');
emsg.innerHTML += n+msg+'<br>';
let channelData = new Float32Array(length);
channelData.set(urlAudio.getChannelData(channelNumber), offset);
audioBuffer.getChannelData(channelNumber).set(channelData);
offset += urlAudio.length;
}
audioSource.buffer = audioBuffer;
emsg.innerHTML += n+`connect ${audioContext.destination}`+BR;
audioSource.connect(audioContext.destination);
return new Promise((resolve, reject) => { try {
audioSource.onerror = evt => {
console.log(`TEST: ERROR`, evt);
emsg.innerHTML += n+'onerror ${evt}<br>';
reject();
};
audioSource.onended = evt => {
console.log(`TEST: onended OK`);
emsg.innerHTML += n+'onended OK<br>';
resolve();
};
emsg.innerHTML += n+'start()<br>';
let res = audioSource.start();
console.log(`TEST: started`, res);
} catch(e) {
alert(`TEST: audioSource caught exception ${e.message}`);
}});
emsg.innerHTML += 'playOne => OK<br>';
} catch(e) {
console.error(`TEST: failed ${e.message}`, e);
alert(e.message);
reject(e);
}
}
async function onTouchStart(event={}) {
let { state } = audioContext;
let { type } = event;
if (state === 'suspended') {
eMsg(`onTouchStart ${state} ${JSON.stringify({type})} => resume()`);
//audioContext.resume();
} else {
eMsg(`onTouchStart ${state} ${JSON.stringify({type})}`);
}
}
function onTouchEnd(event) {
let { state } = audioContext;
let { type } = event;
if (state === 'suspended') {
audioContext.resume();
eMsg(`onTouchEnd ${state} => ${audioContext.state}`);
} else {
eMsg(`onTouchEnd ${state} ${JSON.stringify({type})}`);
}
}
async function onClickPlay() {
console.log("TEST: onClickPlay()");
let url = "https://voice.suttacentral.net/scv/audio/dn34/en/sujato/Amy/ec017f3bba333bd764e6dbf9c50da229";
let url2 = "https://voice.suttacentral.net/scv/audio/mn1/en/sujato/Amy/b627aa0151c721837225614412434a77";
let emsg = document.getElementById("emsg");
audioContext.onstatechange = evt => {
console.log(`TEST: audioContext.onstatechange`, evt);
eMsg(`onstatechange ${audioContext.state}`);
};
console.log(`AudioContext`, audioContext);
await this.playOne(audioContext, url, 1);
await this.playOne(audioContext, url2, 2);
await this.playOne(audioContext, url, 3);
eMsg(`END audioContext.state:${audioContext.state}`);
}
async function onClickBell() {
}
</script>
<style>
.buttons {
font-size: larger;
width:20em;
display:flex;
justify-content: space-between;
background: red;
padding: 1em;
}
audio {
border: 1pt solid red;
}
</style>
<body>
<h1>TEST AUDIO</h1>
<div style="display: flex; flex-flow: column; align-items: center;">
<div>
<a href="https://voice.suttacentral.net/scv/audio/dn34/en/sujato/Amy/ec017f3bba333bd764e6dbf9c50da229" target="_blank">
https://voice.suttacentral.net/scv/audio/dn34/en/sujato/Amy/ec017f3bba333bd764e6dbf9c50da229
</a>
<br>
<a href="/ebt-vue/suttas?search=mn1">mn1</a>
</div>
<audio controls>
<source type="audio/mp3"
src="https://voice.suttacentral.net/scv/audio/dn34/en/sujato/Amy/ec017f3bba333bd764e6dbf9c50da229"></source>
</audio>
<div id="buttons" class="buttons">
<button
ontouchstart="onTouchStart(event)"
ontouchend="onTouchEnd(event)"
>
Unlock
</button>
<button onclick="onClickClear()">
Clear
</button>
<button onclick="onClickPlay()"
>
Play
</button>
<button onclick="onClickBell()">
Bell
</button>
</div>
<div id="emsg">EMSG<br></div>
</div>
</body>
</html>