Écouter - Rejouer
Score général de la partie :
0
Tour 1 / 8
Début du jeu
Valider le tempo
Enregistrer les accords
Écouter les accords
Imiter les accords
Fin de tour
Augmenter score manuellement
Conversion timestamps
Exporter les tables
Calculer scores
Copier la table
Copier table imitateur
Table horizontale
Afficher table imitateur horizontale
MODÈLE
IMITATEUR
Sélection du joueur courant :
Je suis joueur 1
Je suis joueur 2
Copier la table
Sélection du joueur modèle :
Modèle = joueur 1
Modèle = joueur 2
t = 0
Supprimer table
Créer table
Évaluer la précision
Calcul score tour
Incrémenter score partie
Nettoyer table (fin de tour)
Inverser rôle
Connexion MIDI en attente...
Analyse des accords
Données enregistrées
document.getElementById("calculerScore").addEventListener("click", calculerScore); function calculerScore() { fetch("php/getTable.php?" + Date.now()) // Évite le cache .then(res => res.text()) .then(html => { const tempDiv = document.createElement("div"); tempDiv.innerHTML = html; const lignes = tempDiv.querySelectorAll("table tr"); lignes.forEach((ligne, index) => { if (index === 0) return; // Ignorer l'en-tête const cellules = ligne.querySelectorAll("td"); if (cellules.length < 8) return; const id = parseInt(cellules[0].textContent.trim()); const joueur = cellules[1].textContent.trim().toLowerCase(); const type = cellules[2].textContent.trim().toLowerCase(); const beat = parseFloat(cellules[7].textContent.trim()); const joueurCourant = window.joueur?.toLowerCase?.(); if ( type === "chord" && !isNaN(beat) && joueur === joueurCourant ) { const entierProche = Math.round(beat); const ecart = Math.abs(beat - entierProche); if (ecart < 0.1) { fetch("php/updateScore.php", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ id: id, points: 2 }) }); } } }); }) .catch(err => console.warn("Erreur dans calculerScore():", err)); } function synchroniserTempoDepuisServeur() { fetch("php/getDernierTempo.php?" + Date.now()) // éviter le cache navigateur .then(res => res.text()) .then(t => { const tempo = parseFloat(t.trim()); if (isNaN(tempo) || tempo <= 0) { console.warn("Tempo non disponible ou invalide."); return; } window.tempoBPM = tempo; }) .catch(err => { console.warn("Erreur lors de la lecture du tempo depuis le serveur :", err); }); } // Appel automatique toutes les secondes setInterval(synchroniserTempoDepuisServeur, 1000); window.jeuDemarre = false; window.tempoBPM = 60; document.getElementById("augmenterScore").addEventListener("click", () => { const id = prompt("Entrez l'identifiant (id) de la ligne à récompenser :"); if (!id || isNaN(id)) return alert("Identifiant invalide."); const points = prompt("Combien de points ajouter ?"); if (!points || isNaN(points)) return alert("Nombre de points invalide."); fetch("php/updateScore.php", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ id: parseInt(id), points: parseFloat(points) }) }).then(() => alert(`+${points} point(s) ajoutés à la ligne ${id}`)); }); document.addEventListener("DOMContentLoaded", () => { const bouton = document.getElementById("boutonCopierTable"); if (bouton) { bouton.addEventListener("click", copierTableAffichage); } const boutonImitateur = document.getElementById("boutonCopierTableImitateur"); if (boutonImitateur) { boutonImitateur.addEventListener("click", copierTableAffichageImitateur); } }); function pause(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } let nomServeur = null; let tableExists = false; function afficherMessageJeu() { const div = document.getElementById("messageJeu"); if (!div || !window.joueur || typeof window.modeleEstJoueur1 === "undefined") return; const estModele = (window.modeleEstJoueur1 && window.joueur === "joueur1") || (!window.modeleEstJoueur1 && window.joueur === "joueur2"); div.textContent = estModele ? "Votre rôle : 🎹 Enregistrer une séquence d'accords" : "Votre rôle : 🎧 Imiter la séquence de l'autre joueur"; } function afficherIdentiteJoueur() { const div = document.getElementById("identiteJoueur"); if (div && window.joueur) { div.textContent = `🎮 Vous êtes ${window.joueur}`; } } function traiterReponseCreateTable(result) { if (typeof result.tableExists === 'undefined') return; tableExists = result.tableExists; const msg = document.getElementById("message"); msg.textContent = `🧪 tableExists = ${tableExists}`; if (tableExists) { setJoueur('joueur2'); setModele('joueur1'); } else { setJoueur('joueur1'); setModele('joueur1'); } // ✅ Initialisation audio (t = 0) document.getElementById('btnInitAudio')?.click(); //setTimeout(inverserRole, 200); setTimeout(rafraichirRole, 300); } function demanderNomServeur() { synchroniserTempoDepuisServeur(); const saisie = prompt("Entrez le nom du serveur :"); if (!saisie) return; nomServeur = saisie; // mémoriser pour plus tard fetch('php/createTable.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ nomServeur }) }) .then(response => response.json()) .then(traiterReponseCreateTable) .catch(err => { console.error("❌ Erreur lors de la création ou vérification de la table :", err); }); window.jeuDemarre = true; configBoutonsModeleVsImitateur() //jouerBoucleAudio('sounds/ambiance/ambianceBase.mp3'); //jouerClochettesAleatoires(); } function nettoyerNomPourFichier(chaine) { return chaine .normalize("NFD") .replace(/[\u0300-\u036f]/g, "") // accents .replace(/[^a-zA-Z0-9_-]/g, "_"); // tout le reste } function jouerBoucleAudio(fichier) { const audioContext = new (window.AudioContext || window.webkitAudioContext)(); const gainNode = audioContext.createGain(); // Fixer le volume à 50 % gainNode.gain.value = 0.05; gainNode.connect(audioContext.destination); fetch(fichier) .then(response => response.arrayBuffer()) .then(data => audioContext.decodeAudioData(data)) .then(buffer => { const source = audioContext.createBufferSource(); source.buffer = buffer; source.loop = true; source.connect(gainNode); source.start(0); }) .catch(err => console.error("Erreur lecture boucle audio :", err)); } function jouerClochettesAleatoires() { const audioContext = new (window.AudioContext || window.webkitAudioContext)(); const gainNode = audioContext.createGain(); gainNode.gain.value = 0.3; gainNode.connect(audioContext.destination); const dossier = 'sounds/ambiance/bells/'; const fichiers = [ "bell_1.mp3", "bell_2.mp3", "bell_3.mp3", "bell_4.mp3", "bell_5.mp3", "bell_6.mp3", "bell_7.mp3", "bell_8.mp3", "bell_9.mp3", "bell_10.mp3", "bell_11.mp3", "bell_12.mp3", "bell_13.mp3", "bell_14.mp3", "bell_15.mp3", "bell_16.mp3", "bell_17.mp3", "bell_18.mp3", "bell_19.mp3", "bell_20.mp3", "bell_21.mp3", "bell_22.mp3", "bell_23.mp3", "bell_24.mp3", "bell_25.mp3", "bell_26.mp3", "bell_27.mp3", "bell_28.mp3", "bell_29.mp3", "bell_30.mp3", "bell_31.mp3", "bell_32.mp3", "bell_33.mp3", "bell_34.mp3", "bell_35.mp3", "bell_36.mp3", "bell_37.mp3", "bell_38.mp3", "bell_39.mp3", "bell_40.mp3" ]; function jouerSon() { const nom = fichiers[Math.floor(Math.random() * fichiers.length)]; const chemin = dossier + nom; fetch(chemin) .then(r => r.arrayBuffer()) .then(data => audioContext.decodeAudioData(data)) .then(buffer => { const source = audioContext.createBufferSource(); source.buffer = buffer; source.connect(gainNode); source.start(); }) .catch(err => console.error("Erreur lecture clochette :", err)); // Prochain son entre 5 et 15 secondes const prochainDelai = Math.random() * 10000 + 5000; setTimeout(jouerSon, prochainDelai); } // Lancer la première clochette jouerSon(); } function debutJeu() { setJoueur('joueur1'); const boutons = [ 'btnInitAudio', // t = 0 'btnSupprimer', // Supprimer table 'btnCreer', // Créer table 'btnJoueur1', // Je suis joueur 1 'btnModele1', // Modèle = joueur 1 'btnValiderTempo' // Valider le tempo ]; boutons.forEach((id, index) => { setTimeout(() => { const bouton = document.getElementById(id); if (bouton) { bouton.click(); } else { console.warn(`❌ Bouton ${id} introuvable`); } }, index * 200); // 200 ms entre chaque clic }); } function finDeTour() { jouerSonUnique('sounds/ambiance/finDeTour.mp3'); const btnFin = document.getElementById("btnFinTour"); fetch("php/nettoyerTable.php") .then(res => res.text()) .then(msg => { // Inverser le rôle directement via API const nouveauRole = window.modeleEstJoueur1 ? "joueur2:modèle" : "joueur1:modèle"; return fetch(`php/inverserRole.php?role=${nouveauRole}`); }) .then(res => res.text()) .then(msg => { window.modeleEstJoueur1 = !window.modeleEstJoueur1; afficherMessage(`🎭 Rôle mis à jour: ${window.modeleEstJoueur1 ? "modèle = joueur1" : "modèle = joueur2"}`); afficherMessageJeu(); rafraichirRole(); // Mise à jour de l’interface }) .catch(err => console.error("❌ Erreur en fin de tour :", err)); nettoyerTable(); } function inverserRole() { const nouveauRole = window.modeleEstJoueur1 ? "joueur2:modèle" : "joueur1:modèle"; fetch(`php/inverserRole.php?role=${nouveauRole}`) .then(res => res.text()) .then(msg => { afficherMessage(msg); window.modeleEstJoueur1 = !window.modeleEstJoueur1; afficherMessage(`Nouveau rôle : ${nouveauRole}`); afficherMessageJeu(); }); } function rafraichirRole() { if (!window.joueur || window.verrouRole) return; fetch("php/getDernierRole.php") .then(res => res.text()) .then(role => { const estJoueur1Modele = role.trim() === "joueur1:modèle"; window.modeleEstJoueur1 = estJoueur1Modele; afficherMessage(`↻ Rôle rafraîchi: modèle = ${estJoueur1Modele ? 'joueur1' : 'joueur2'}`); const estModele = (estJoueur1Modele && window.joueur === "joueur1") || (!estJoueur1Modele && window.joueur === "joueur2"); }) .catch(err => console.error("❌ Erreur rafraîchissement rôle :", err)); } function setModele(nom) { window.modeleEstJoueur1 = (nom === "joueur1"); // Envoi au serveur pour éviter que le rafraîchissement l’écrase const nouveauRole = window.modeleEstJoueur1 ? "joueur1:modèle" : "joueur2:modèle"; fetch(`php/inverserRole.php?role=${nouveauRole}`) .then(res => res.text()) .then(msg => { afficherMessage(`Modèle défini : ${nom}`); rafraichirRole(); // ✅ Maintenant qu’on est sûr que le rôle est bien enregistré window.verrouRole = true; // ← bloque les rafraîchissements temporaires setTimeout(() => window.verrouRole = false, 1500); }); // Mise à jour visuelle des boutons const btn1 = document.getElementById("btnModele1"); const btn2 = document.getElementById("btnModele2"); if (btn1 && btn2) { if (window.modeleEstJoueur1) { btn1.classList.add("bouton-modele-actif");//à effacer? btn2.classList.remove("bouton-modele-actif");//à effacer? } else { btn1.classList.remove("bouton-modele-actif");//à effacer? btn2.classList.add("bouton-modele-actif");//à effacer? } } } window.setModele = setModele; setTimeout(() => { if (!window.audioContext) { console.warn("⛔ audioContext non initialisé."); return; } const lignes = document.querySelectorAll("#tableAffichage table tr"); const now = window.audioContext.currentTime; for (let i = 1; i < lignes.length; i++) { const cellules = lignes[i].querySelectorAll("td"); const type = cellules[2]?.textContent.trim(); const note = cellules[3]?.textContent.trim(); const chord = cellules[4]?.textContent.trim(); const timestamp = parseFloat(cellules[6]?.textContent.trim()); if (isNaN(timestamp)) continue; const delay = (timestamp - (now - window.t0)) * 1000; if (type === "système" && note) { setTimeout(() => { const chemin = `sounds/drumSounds/${nettoyerNomPourFichier(note)}.mp3`; jouerMp3(chemin); }, Math.max(0, delay)); } } }, 1000); window.t0 = null; //window.joueur = "joueur1"; afficherIdentiteJoueur(); document.getElementById("status").textContent = "Audio activé, connexion MIDI..."; function afficherMessage(msg) { const el = document.getElementById('message'); el.textContent = msg; el.style.opacity = 1; setTimeout(() => { el.style.opacity = 0.3; }, 4000); } function createTable() { fetch('php/createTable.php') .then(res => res.text()) .then(msg => { afficherMessage(msg); setTimeout(loadTable, 500); // ✅ délai avant affichage }); } /* ------------------------------------------------------------------ * Lecture unique d’un MP3 * Appel : jouerSonUnique('sounds/drumSounds/monFichier.mp3'); * ------------------------------------------------------------------ */ let audioCtxUnique = null; // Contexte partagé let gainUnique = null; // Gain réglé à 1 (volume normal) function jouerSonUnique(chemin) { if (!audioCtxUnique) { // créer le contexte au 1ᵉʳ appel audioCtxUnique = new (window.AudioContext || window.webkitAudioContext)(); gainUnique = audioCtxUnique.createGain(); gainUnique.gain.value = 0.3; // volume à 100 % gainUnique.connect(audioCtxUnique.destination); } fetch(chemin) .then(r => r.arrayBuffer()) .then(buf => audioCtxUnique.decodeAudioData(buf)) .then(buffer => { const src = audioCtxUnique.createBufferSource(); src.buffer = buffer; src.connect(gainUnique); src.start(); // lecture immédiate }) .catch(err => console.error("Erreur lecture MP3 :", err)); } function dropTable() { fetch('php/dropTable.php') .then(res => res.text()) .then(msg => { afficherMessage(msg); loadTable(); }); } function loadTable() { fetch("php/getTable.php") .then(res => res.text()) .then(html => { if (html.startsWith("
")) { console.warn("⚠️ Table probablement non créée (getTable.php)"); return; } document.getElementById('tableAffichage').innerHTML = html; }) .catch(err => { console.warn("⚠️ Erreur fetch table :", err.message); }); } function calculerScoreTour() { fetch("php/inscrireScoreTour.php") .then(res => res.text()) .then(msg => afficherMessage(msg)); } function incrementerScorePartie() { fetch("php/getAllData.php") .then(res => res.json()) .then(data => { const lignes = data.filter(d => d.type === "score tour"); if (lignes.length === 0) { afficherMessage("Aucun score tour trouvé."); return null; // ← renvoie null pour éviter les erreurs dans les .then suivants } const dernier = lignes[lignes.length - 1]; const scoreTour = parseFloat(dernier.timestamp); return fetch("php/insertEvent.php", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ joueur: "système", type: "score partie", note: "", chord: "", position: "", timestamp: scoreTour }) }); }) .then(res => { if (!res) return; // ← insertion non faite (score tour manquant) return res.text(); }) .then(msg => { if (!msg) return; // ← évite tout traitement inutile afficherMessage("Score partie mis à jour."); setTimeout(actualiserScorePartie, 150); }); } function nettoyerTable() { fetch("php/nettoyerTable.php") .then(res => res.text()) .then(msg => { afficherMessage(msg); loadTable(); passerAuTourSuivant(); }); } function actualiserScorePartie() { //if (!window.jeuDemarre || !window.nomTable) return; fetch("php/getAllData.php") .then(res => res.text()) .then(text => { try { const data = JSON.parse(text); const lignes = data.filter(l => l.type === "score partie"); const total = lignes.reduce((s, l) => s + parseFloat(l.timestamp), 0); document.getElementById("valeurScorePartie").textContent = total.toFixed(0); } catch (err) { console.warn("⚠️ Erreur JSON probable dans getAllData.php :", err.message); } }) .catch(err => { console.warn("⚠️ Erreur fetch getAllData :", err.message); }); } // Expositions à la fin window.validerTempo = validerTempo; window.createTable = createTable; window.dropTable = dropTable; window.loadTable = loadTable; window.demarrer = demarrer; window.calculerScoreTour = calculerScoreTour; window.incrementerScorePartie = incrementerScorePartie; window.actualiserScorePartie = actualiserScorePartie; window.nettoyerTable = nettoyerTable; setInterval(loadTable, 500); setInterval(actualiserScorePartie, 500); setInterval(rafraichirRole, 1000); setInterval(() => { const bouton = document.getElementById("boutonCopierTable"); if (bouton) bouton.click(); }, 500); setInterval(() => { const btnImit = document.getElementById("boutonCopierTableImitateur"); if (btnImit) btnImit.click(); // ⬅️ 1re ligne }, 500); window.comparerEtScorer = comparerEtScorer; window.addEventListener("pagehide", function () { if (!window.joueur) return; navigator.sendBeacon("php/dropTable.php"); }); function setJoueur(nom) { window.joueur = nom; afficherIdentiteJoueur(); // ✅ Enregistrement dans la table SQL fetch("php/setJoueurCourant.php", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ joueur: nom }) }); const select = document.getElementById("joueurSelect"); if (select) select.value = nom; afficherMessage(`Vous êtes maintenant ${nom}`); } window.setJoueur = setJoueur; setInterval(afficherMessageJeu, 1000); function detecterIdentite() { fetch("php/getDernierJoueurCourant.php") .then(res => res.text()) .then(joueurDefini => { if (joueurDefini === "joueur1") { window.joueur = "joueur2"; afficherMessage("🧍 Vous êtes automatiquement joueur2 (détecté)"); afficherIdentiteJoueur(); } else if (joueurDefini === "joueur2") { window.joueur = "joueur1"; afficherMessage("🧍 Vous êtes automatiquement joueur1 (détecté)"); afficherIdentiteJoueur(); } else { afficherMessage("⏳ En attente que joueur1 initialise le jeu..."); setTimeout(detecterIdentite, 2000); } }) .catch(err => { console.warn("Erreur serveur :", err); setTimeout(detecterIdentite, 2000); }); } function afficherTableHorizontale() { /* 1) point de départ : la table verticale déjà construite */ const sourceTable = document.querySelector("#copieTableAffichage table"); const dest = document.getElementById("tableAffichageHoriz"); if (!sourceTable || !dest) { console.warn("Table source ou destination manquante."); return; } /* 2) récupérer tous les accords (on ignore la 1re ligne d’en-tête) */ const accords = Array.from(sourceTable.querySelectorAll("tr")) .slice(1) // ⤷ saute l’en-tête .map(tr => tr.cells[0]?.textContent.trim()) .filter(txt => txt.length); // enlève les lignes vides if (accords.length === 0) { dest.innerHTML = "
(Aucun accord à afficher)
"; return; } /* 3) créer la nouvelle table horizontale */ const newTable = document.createElement("table"); const row = document.createElement("tr"); accords.forEach(ac => { const td = document.createElement("td"); td.textContent = ac; row.appendChild(td); }); newTable.appendChild(row); /* 4) injecter le résultat */ dest.innerHTML = ""; // nettoie l’ancienne version dest.appendChild(newTable); // affiche la nouvelle } function afficherTableHorizontaleImitateur() { /* 1) point de départ : la table verticale déjà construite */ const sourceTable = document.querySelector("#copieTableAffichageImitateur table"); const dest = document.getElementById("tableAffichageHorizImitateur"); if (!sourceTable || !dest) { console.warn("Table source ou destination manquante."); return; } /* 2) récupérer tous les accords (on ignore la 1re ligne d’en-tête) */ const accords = Array.from(sourceTable.querySelectorAll("tr")) .slice(1) .map(tr => tr.cells[0]?.textContent.trim()) .filter(txt => txt.length); if (accords.length === 0) { dest.innerHTML = "
(Aucun accord à afficher)
"; return; } /* 3) créer la nouvelle table horizontale */ const newTable = document.createElement("table"); const row = document.createElement("tr"); accords.forEach(ac => { const td = document.createElement("td"); td.textContent = ac; row.appendChild(td); }); newTable.appendChild(row); /* 4) injecter le résultat */ dest.innerHTML = ""; dest.appendChild(newTable); } //if (typeof preparerDesactivationAuto === "function") { //preparerDesactivationAuto(); //} function configBoutonsDebutJeu() { afficherBoutons(["btnDebutJeu"]); masquerBoutons([ "btnDemarrer", "btnEcouter", "imiterBtn", "btnFinTour", "btnValiderTempo", "tempoInput" ]); } function configBoutonsModeleVsImitateur() { setTimeout(() => { const estModele = (window.modeleEstJoueur1 && window.joueur === "joueur1") || (!window.modeleEstJoueur1 && window.joueur === "joueur2"); if (estModele) { afficherBoutons(["btnDemarrer", "btnValiderTempo", "tempoInput"]); masquerBoutons(["btnDebutJeu", "btnEcouter", "imiterBtn", "btnFinTour"]); } else { afficherBoutons(["btnEcouter", "imiterBtn", "btnFinTour"]); masquerBoutons(["btnDebutJeu", "btnDemarrer", "btnValiderTempo", "tempoInput"]); } }, 800); // 0,5 s de délai } configBoutonsDebutJeu() (async function () { const BASE_URL = "sounds/xylophoneNotes/"; const NOTES = ['C', 'Cd', 'D', 'Dd', 'E', 'F', 'Fd', 'G', 'Gd', 'A', 'Ad', 'B']; const audioCtx = new (window.AudioContext || window.webkitAudioContext)(); const cache = {}; function midiNoteToName(n) { const note = NOTES[n % 12]; const octave = Math.floor(n / 12) - 1; return `${note}${octave}`; } async function loadSample(name) { const url = `${BASE_URL}Xylo_${name}.wav`; try { const res = await fetch(url); if (!res.ok) throw new Error(`404: ${url}`); const buf = await res.arrayBuffer(); cache[name] = await audioCtx.decodeAudioData(buf); console.log("✔ Préchargé :", name); } catch (err) { console.warn("⚠️ Erreur de chargement :", err.message); } } async function preloadAllSamples() { const promises = []; for (let n = 60; n <= 108; n++) { // MIDI notes de C4 à C8 const name = midiNoteToName(n); promises.push(loadSample(name)); } await Promise.all(promises); console.log("✅ Tous les sons de xylophone ont été chargés."); } function playNote(name) { const buf = cache[name]; if (!buf) return console.warn("❌ Non chargé :", name); const src = audioCtx.createBufferSource(); src.buffer = buf; src.connect(audioCtx.destination); src.start(); } function handleMessage(e) { const [cmd, note, velocity] = e.data; if ((cmd & 0xf0) === 0x90 && velocity > 0) { const name = midiNoteToName(note); playNote(name); } } // Activation du contexte audio si suspendu window.addEventListener("click", () => { if (audioCtx.state === "suspended") audioCtx.resume(); }); // Initialisation MIDI if (navigator.requestMIDIAccess) { const midi = await navigator.requestMIDIAccess(); for (const input of midi.inputs.values()) { input.addEventListener("midimessage", handleMessage); } console.log("🎹 MIDI prêt"); } else { console.warn("❌ Web MIDI non supporté"); } // Lancer le préchargement après un clic utilisateur (obligatoire pour AudioContext) window.addEventListener("click", async () => { if (!window.xyloCharged) { window.xyloCharged = true; await preloadAllSamples(); } }); })(); const bouton2 = document.getElementById("btnConversionTimestamps"); if (bouton2) { bouton2.addEventListener("click", conversionTimestampsEnBeats); } function conversionTimestampsEnBeats() { const tempo = window.tempo || 120; const beatMap = {}; const table = document.querySelector("table"); if (!table) { alert("Aucune table trouvée dans la page."); return; } const lignes = [...table.querySelectorAll("tbody tr")]; const headerRow = table.querySelector("thead tr"); // Ajout de la colonne "Beat" si absente if (headerRow && ![...headerRow.children].some(cell => cell.textContent.trim() === "Beat")) { const th = document.createElement("th"); th.textContent = "Beat"; headerRow.appendChild(th); } lignes.forEach(ligne => { const cellules = [...ligne.querySelectorAll("td")]; const id = cellules[0]?.textContent?.trim(); const timestampStr = cellules[2]?.textContent?.trim(); const timestamp = parseFloat(timestampStr); let beatTime = ""; if (id && Number.isFinite(timestamp)) { beatTime = timestamp * tempo / 60; beatMap[id] = beatTime; } // Ajout ou mise à jour de la cellule "Beat" if (cellules.length >= 4) { cellules[3].textContent = Number.isFinite(beatTime) ? beatTime.toFixed(12) : ""; } else { const newCell = document.createElement("td"); newCell.textContent = Number.isFinite(beatTime) ? beatTime.toFixed(12) : ""; ligne.appendChild(newCell); } }); // Stockage global window.timestampBeats = beatMap; console.log("Conversion réussie. timestampBeats =", window.timestampBeats); } window.conversionTimestampsEnBeats = conversionTimestampsEnBeats; document.addEventListener("DOMContentLoaded", () => { const bouton = document.getElementById("btnConversionTimestamps"); if (bouton) { bouton.addEventListener("click", conversionTimestampsEnBeats); } }); document.addEventListener("DOMContentLoaded", () => { document.getElementById("btnExporterTables").addEventListener("click", () => { const tables = document.querySelectorAll("table"); const exportData = {}; tables.forEach((table, index) => { const tableName = table.getAttribute("id") || `table_${index + 1}`; const headers = Array.from(table.querySelectorAll("thead th")).map(th => th.textContent.trim()); const rows = Array.from(table.querySelectorAll("tbody tr")); const rowData = rows.map(tr => { const cells = Array.from(tr.querySelectorAll("td")); const rowObj = {}; cells.forEach((td, i) => { rowObj[headers[i] || `col${i + 1}`] = td.textContent.trim(); }); return rowObj; }); exportData[tableName] = rowData; }); const blob = new Blob([JSON.stringify(exportData, null, 2)], { type: "application/json" }); const url = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = "export_tables.json"; document.body.appendChild(a); a.click(); a.remove(); URL.revokeObjectURL(url); }); });