let mediaRecorder; let audioChunks = []; let userCount = 0; // 追加されたメンバー数を保持 let isRecording = false; // 録音中かどうかを判定するフラグ let currentRecordingButton = null; // 現在録音中のボタンを保持 let userNames = []; function toggleRecording(button) { button.classList.toggle("recording"); } async function startRecording(button) { if (isRecording && currentRecordingButton !== button) return; // 他の人が録音中なら何もしない isRecording = true; // 録音中に設定 currentRecordingButton = button; // 録音中のボタンを記録 try { const stream = await navigator.mediaDevices.getUserMedia({ audio: true, }); mediaRecorder = new MediaRecorder(stream, { mimeType: "audio/webm" }); audioChunks = []; mediaRecorder.ondataavailable = (e) => audioChunks.push(e.data); mediaRecorder.onstop = () => { sendAudioChunks(audioChunks, button); // ボタン情報を渡す audioChunks = []; isRecording = false; // 録音停止後はフラグを戻す currentRecordingButton = null; // 録音ボタンを解除 }; mediaRecorder.start(); toggleRecording(button); } catch (err) { console.error("マイクアクセスに失敗しました:", err); isRecording = false; // エラー発生時もフラグを戻す currentRecordingButton = null; } } function stopRecording(button) { if (!isRecording) return; // 録音中でない場合は停止しない mediaRecorder.stop(); toggleRecording(button); } function handleRecording(e) { const button = e.target.closest(".record-button"); if (button) { if (isRecording && currentRecordingButton !== button) { // 他の人が録音中なら反応しない return; } if (mediaRecorder && mediaRecorder.state === "recording") { stopRecording(button); } else { startRecording(button); } } } function sendAudioChunks(chunks, button) { // 引数に button を追加 const audioBlob = new Blob(chunks, { type: "audio/wav" }); const reader = new FileReader(); reader.onloadend = () => { const base64String = reader.result.split(",")[1]; // Base64エンコードされた音声データ const form = button.closest("form"); const nameInput = form.querySelector('input[name="name"]'); const name = nameInput ? nameInput.value : "unknown"; // 名前がない fetch("/upload_base_audio", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ audio_data: base64String, name: name }), }) .then((response) => response.json()) .then((data) => { // エラー処理のみ残す if (data.error) { alert("エラー: " + data.error); console.error(data.details); } // 成功時の処理(ボタンの有効化など) else { console.log("音声データ送信成功:", data); userNames.push(name); // 必要に応じて、ここでUIの変更(ボタンの有効化など)を行う // 例: button.disabled = true; // 送信ボタンを無効化 // 例: button.classList.remove("recording"); //録音中のスタイルを解除 } }) .catch((error) => { console.error("エラー:", error); }); }; reader.readAsDataURL(audioBlob); } document.getElementById("add-btn").addEventListener("click", () => { const newItem = document.createElement("div"); newItem.className = "flex items-center gap-3 flex-wrap"; newItem.innerHTML = `
`; newItem.addEventListener("click", handleRecording); document.getElementById("people-list").appendChild(newItem); userCount++; // 新しいメンバーを追加するたびにカウントを増やす });