139 lines
4.3 KiB
JavaScript
139 lines
4.3 KiB
JavaScript
let mediaRecorder;
|
|
let audioChunks = [];
|
|
|
|
let audioContext;
|
|
let dataArray;
|
|
let animationId;
|
|
let audioOutSource;
|
|
|
|
console.log("Asking for Audio permissions...");
|
|
window.addEventListener("load", async (event) => {
|
|
await navigator.mediaDevices.getUserMedia({ audio: true });
|
|
});
|
|
|
|
document.getElementById('recordbuttonimage').oncontextmenu = function(event) {
|
|
event.preventDefault();
|
|
event.stopPropagation(); // not necessary in my case, could leave in case stopImmediateProp isn't available?
|
|
event.stopImmediatePropagation();
|
|
return false;
|
|
};
|
|
|
|
function startButtonPulse(analyser) {
|
|
const button = document.querySelector('#recordbutton');
|
|
|
|
function updateScale(analyser) {
|
|
if (!analyser) return;
|
|
|
|
analyser.getByteTimeDomainData(dataArray);
|
|
|
|
let sum = 0;
|
|
for (let i = 0; i < dataArray.length; i++) {
|
|
const value = dataArray[i] - 128;
|
|
sum += value * value;
|
|
}
|
|
const volume = Math.sqrt(sum / dataArray.length);
|
|
|
|
const scale = 1 + Math.min(volume / 25, 0.4); // Scales from 1.0 to 1.2
|
|
|
|
button.style.transform = `scale(${scale})`;
|
|
|
|
animationId = requestAnimationFrame(function() {
|
|
updateScale(analyser);
|
|
});
|
|
}
|
|
|
|
updateScale(analyser);
|
|
}
|
|
|
|
function stopButtonPulse() {
|
|
cancelAnimationFrame(animationId);
|
|
const button = document.querySelector('#recordbutton');
|
|
button.style.transform = 'scale(1)';
|
|
}
|
|
|
|
const startPlayback = async () => {
|
|
const audio_out = document.querySelector('#audioout');
|
|
try {
|
|
console.log("Started playback!");
|
|
|
|
// Audio analysis
|
|
if (!audioOutSource) {
|
|
audioContext = new AudioContext();
|
|
audioOutSource = audioContext.createMediaElementSource(audio_out);
|
|
analyser = audioContext.createAnalyser();
|
|
analyser.fftSize = 2048;
|
|
dataArray = new Uint8Array(analyser.frequencyBinCount);
|
|
audioOutSource.connect(analyser);
|
|
analyser.connect(audioContext.destination);
|
|
}
|
|
|
|
startButtonPulse(analyser);
|
|
|
|
await audio_out.play();
|
|
|
|
} catch (err) {
|
|
console.error('Something happend oh no!', err);
|
|
alert('Some error happened during playback! :(');
|
|
}
|
|
}
|
|
|
|
const stopPlayback = async () => {
|
|
const audio_out = document.querySelector('#audioout');
|
|
if (audio_out) {
|
|
stopButtonPulse();
|
|
}
|
|
}
|
|
|
|
const startRecording = async () => {
|
|
try {
|
|
navigator.permissions.query({ name: "microphone" }).then(async (result) => {
|
|
if (result.state === "granted") {
|
|
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
|
mediaRecorder = new MediaRecorder(stream);
|
|
audioChunks = [];
|
|
console.log("Started recording!");
|
|
|
|
// Audio analysis
|
|
audioContext = new AudioContext();
|
|
analyser = audioContext.createAnalyser();
|
|
const source = audioContext.createMediaStreamSource(stream);
|
|
analyser.fftSize = 2048;
|
|
dataArray = new Uint8Array(analyser.frequencyBinCount);
|
|
source.connect(analyser);
|
|
|
|
startButtonPulse(analyser);
|
|
|
|
mediaRecorder.ondataavailable = async event => {
|
|
if (event.data.size > 0) {
|
|
console.log("audio available!");
|
|
audioChunks.push(event.data);
|
|
url = "/api/v1/upload";
|
|
if (mediaRecorder.state === 'inactive') {
|
|
url = "/api/v1/send";
|
|
}
|
|
fetch(url, {
|
|
method: 'POST',
|
|
body: event.data,
|
|
});
|
|
}
|
|
};
|
|
|
|
mediaRecorder.onstop = async () => {
|
|
console.log("Stopped recording!");
|
|
stopButtonPulse();
|
|
};
|
|
|
|
mediaRecorder.start(1000);
|
|
}
|
|
});
|
|
} catch (err) {
|
|
console.error('Microphone access denied or error:', err);
|
|
alert('Please allow microphone access to record audio.');
|
|
}
|
|
};
|
|
|
|
const stopRecording = () => {
|
|
if (mediaRecorder && mediaRecorder.state !== 'inactive') {
|
|
mediaRecorder.stop();
|
|
}
|
|
}; |