Spaces:
				
			
			
	
			
			
		Sleeping
		
	
	
	
			
			
	
	
	
	
		
		
		Sleeping
		
	| 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 = ` | |
| <form | |
| action="/submit" | |
| method="POST" | |
| class="flex items-center space-x-2 w-full sm:w-auto" | |
| onsubmit="event.preventDefault();" | |
| > | |
| <input | |
| type="text" | |
| name="name" | |
| placeholder="名前を入力" | |
| class="flex-1 px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 bg-gray-700 text-white" | |
| /> | |
| <button type="button" class="record-button" aria-label="音声録音開始"> | |
| <div class="record-icon"></div> | |
| </button> | |
| </form> | |
| `; | |
| newItem.addEventListener("click", handleRecording); | |
| document.getElementById("people-list").appendChild(newItem); | |
| userCount++; // 新しいメンバーを追加するたびにカウントを増やす | |
| }); | |