/* * EchoClip, a web tool to record and play back audio clips. * Copyright 2024 Pacien TRAN-GIRARD * SPDX-License-Identifier: EUPL-1.2 */ const errorContainer = document.querySelector("#error"); const recordingIndicator = document.querySelector("#recording-indicator"); const recordBtn = document.querySelector("#record"); const autoplayCheckbox = document.querySelector("#autoplay"); const clearBtn = document.querySelector("#clear"); const clips = document.querySelector("#clips"); function wrapElement(wrapper, child) { const wrapperElement = document.createElement(wrapper); wrapperElement.append(child); return wrapperElement; } // TODO: fancy player element with spectrogram and spectrum analyser? function audioElementForBlob(blob) { const audioElement = document.createElement("audio"); audioElement.src = window.URL.createObjectURL(blob); audioElement.setAttribute("controls", ""); return audioElement; } function onGetDeviceSuccess(stream) { const mediaRecorder = new MediaRecorder(stream); let recordingChunks = []; mediaRecorder.addEventListener("dataavailable", event => { recordingChunks.push(event.data); }); mediaRecorder.addEventListener("start", _event => { recordingIndicator.classList.add("active"); }); mediaRecorder.addEventListener("stop", _event => { recordingIndicator.classList.remove("active"); const blob = new Blob(recordingChunks, { type: mediaRecorder.mimeType }); recordingChunks = []; const audioElement = audioElementForBlob(blob); // TODO: record blob and list to local persistent storage // TODO: "clear all" button to clear all clips // TODO: buttons to clear individual clips // TODO: keyboard shortcut to play clips for the ten last indexes clips.prepend(wrapElement("li", audioElement)); if (autoplayCheckbox.checked) { // TODO: stop playing any other clip audioElement.play(); } }); recordBtn.addEventListener("mousedown", _event => { mediaRecorder.start(); }); recordBtn.addEventListener("mouseup", _event => { mediaRecorder.stop(); }); document.addEventListener("keydown", event => { if (event.key == " ") { event.preventDefault(); // prevent scroll if (mediaRecorder.state == "inactive") mediaRecorder.start(); } }); document.addEventListener("keyup", event => { if (event.key == " ") { event.preventDefault(); // prevent scroll mediaRecorder.stop(); } }); } function onGetDeviceError(error) { console.log(error); errorContainer.innerHTML = error; } navigator .mediaDevices .getUserMedia({ audio: true }) .then(onGetDeviceSuccess, onGetDeviceError);