buletomato25 commited on
Commit
6cfbac7
·
2 Parent(s): 8bf13ec f681e2e

Merge branch 'adduser'

Browse files
app.py CHANGED
@@ -8,11 +8,13 @@ from process import AudioProcessor
8
  process=AudioProcessor()
9
  app = Flask(__name__)
10
 
 
 
11
  # トップページ(テンプレート: index.html)
12
  @app.route('/')
13
  @app.route('/index', methods=['GET', 'POST'])
14
  def index():
15
- return render_template('index.html')
16
 
17
  # フィードバック画面(テンプレート: feedback.html)
18
  @app.route('/feedback', methods=['GET', 'POST'])
@@ -35,25 +37,35 @@ def userregister():
35
  def upload_audio():
36
  try:
37
  data = request.get_json()
38
- if not data or 'audio_data' not in data:
39
- return jsonify({"error": "音声データがありません"}), 400
40
 
41
- audio_path=process.save_audio_from_base64(
42
- base64_audio=data['audio_data'], # 音声データ
43
- output_dir= "/tmp/data", #保存先
44
- output_filename="recorded_audio.wav" # 固定ファイル名(必要に応じて generate_filename() で一意のファイル名に変更可能)
45
- )
 
 
 
 
 
46
 
47
  # 参照音声ファイルのパスを指定(sample.wav を正しい場所に配置すること)
48
- reference_audio = os.path.abspath('/tmp/data/base_audio/recorded_base_audio.wav')
 
49
  if not os.path.exists(reference_audio):
50
  return jsonify({"error": "参照音声ファイルが見つかりません", "details": reference_audio}), 500
51
 
52
  # 音声解析:参照音声とアップロードされた音声との類似度をセグメント毎に計算
53
  # threshold の値は調整可能です(例: 0.1)
54
- matched_time, unmatched_time = process.process_audio(reference_audio, audio_path, threshold=0.05)
55
- total_time = matched_time + unmatched_time
56
- rate = (matched_time / total_time) * 100 if total_time > 0 else 0
 
 
 
 
57
 
58
  return jsonify({"rate": rate}), 200
59
  except Exception as e:
@@ -65,11 +77,13 @@ def upload_base_audio():
65
  data = request.get_json()
66
  if not data or 'audio_data' not in data or 'name' not in data:
67
  return jsonify({"error": "音声データまたは名前がありません"}), 400
 
 
68
 
69
  audio_path=process.save_audio_from_base64(
70
  base64_audio=data['audio_data'], # 音声データ
71
  output_dir= "/tmp/data", #保存先
72
- output_filename="recorded_audio.wav" # 固定ファイル名(必要に応じて generate_filename() で一意のファイル名に変更可能)
73
  )
74
  return jsonify({"state": "Registration Success!", "path": audio_path}), 200
75
  except Exception as e:
 
8
  process=AudioProcessor()
9
  app = Flask(__name__)
10
 
11
+ users = ["ccc"]
12
+
13
  # トップページ(テンプレート: index.html)
14
  @app.route('/')
15
  @app.route('/index', methods=['GET', 'POST'])
16
  def index():
17
+ return render_template('index.html', users = users)
18
 
19
  # フィードバック画面(テンプレート: feedback.html)
20
  @app.route('/feedback', methods=['GET', 'POST'])
 
37
  def upload_audio():
38
  try:
39
  data = request.get_json()
40
+ if not data or 'audio_data' not in data or 'name' not in data:
41
+ return jsonify({"error": "音声データまたは名前がありません"}), 400
42
 
43
+ # Base64デコードして音声バイナリを取得
44
+ audio_binary = base64.b64decode(data['audio_data'])
45
+ name = data['name'] # 名前を取得
46
+ audio_dir = "/tmp/data"
47
+ os.makedirs(audio_dir, exist_ok=True)
48
+ # 固定ファイル名(必要に応じて generate_filename() で一意のファイル名に変更可能)
49
+ audio_path = os.path.join(audio_dir, f"{name}.wav")
50
+
51
+ with open(audio_path, 'wb') as f:
52
+ f.write(audio_binary)
53
 
54
  # 参照音声ファイルのパスを指定(sample.wav を正しい場所に配置すること)
55
+ reference_audio = os.path.abspath("/tmp/data/base_audio/", f"{name}.wav")
56
+
57
  if not os.path.exists(reference_audio):
58
  return jsonify({"error": "参照音声ファイルが見つかりません", "details": reference_audio}), 500
59
 
60
  # 音声解析:参照音声とアップロードされた音声との類似度をセグメント毎に計算
61
  # threshold の値は調整可能です(例: 0.1)
62
+ if(users.length > 2):
63
+ print("複数人の場合の処理")
64
+ else:
65
+ matched_time, unmatched_time = process.process_audio(reference_audio, audio_path, threshold=0.05)
66
+ total_time = matched_time + unmatched_time
67
+ rate = (matched_time / total_time) * 100 if total_time > 0 else 0
68
+
69
 
70
  return jsonify({"rate": rate}), 200
71
  except Exception as e:
 
77
  data = request.get_json()
78
  if not data or 'audio_data' not in data or 'name' not in data:
79
  return jsonify({"error": "音声データまたは名前がありません"}), 400
80
+ name = data['name'] # 名前を取得
81
+ users.append(name)
82
 
83
  audio_path=process.save_audio_from_base64(
84
  base64_audio=data['audio_data'], # 音声データ
85
  output_dir= "/tmp/data", #保存先
86
+ output_filename=f"{name}.wav" # 固定ファイル名(必要に応じて generate_filename() で一意のファイル名に変更可能)
87
  )
88
  return jsonify({"state": "Registration Success!", "path": audio_path}), 200
89
  except Exception as e:
requirements.txt CHANGED
@@ -1,4 +1,5 @@
1
  Flask==2.2.5
 
2
  pyannote.audio==2.1.1
3
  numpy==1.23.5
4
  pydub==0.25.1
@@ -13,4 +14,6 @@ google-auth==2.38.0
13
  google-auth-oauthlib==1.2.1
14
  google-auth-httplib2==0.2.0
15
  faster-whisper
 
 
16
 
 
1
  Flask==2.2.5
2
+ Flask-WTF
3
  pyannote.audio==2.1.1
4
  numpy==1.23.5
5
  pydub==0.25.1
 
14
  google-auth-oauthlib==1.2.1
15
  google-auth-httplib2==0.2.0
16
  faster-whisper
17
+ Flask-Migrate
18
+ requests
19
 
static/register_record.js ADDED
@@ -0,0 +1,124 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ let mediaRecorder;
2
+ let audioChunks = [];
3
+ let userCount = 0; // 追加されたメンバー数を保持
4
+ let isRecording = false; // 録音中かどうかを判定するフラグ
5
+ let currentRecordingButton = null; // 現在録音中のボタンを保持
6
+ let userNames = [];
7
+
8
+ function toggleRecording(button) {
9
+ button.classList.toggle("recording");
10
+ }
11
+
12
+ async function startRecording(button) {
13
+ if (isRecording && currentRecordingButton !== button) return; // 他の人が録音中なら何もしない
14
+ isRecording = true; // 録音中に設定
15
+ currentRecordingButton = button; // 録音中のボタンを記録
16
+
17
+ try {
18
+ const stream = await navigator.mediaDevices.getUserMedia({
19
+ audio: true,
20
+ });
21
+ mediaRecorder = new MediaRecorder(stream, { mimeType: "audio/webm" });
22
+ audioChunks = [];
23
+ mediaRecorder.ondataavailable = (e) => audioChunks.push(e.data);
24
+ mediaRecorder.onstop = () => {
25
+ sendAudioChunks(audioChunks, button); // ボタン情報を渡す
26
+ audioChunks = [];
27
+ isRecording = false; // 録音停止後はフラグを戻す
28
+ currentRecordingButton = null; // 録音ボタンを解除
29
+ };
30
+ mediaRecorder.start();
31
+ toggleRecording(button);
32
+ } catch (err) {
33
+ console.error("マイクアクセスに失敗しました:", err);
34
+ isRecording = false; // エラー発生時もフラグを戻す
35
+ currentRecordingButton = null;
36
+ }
37
+ }
38
+
39
+ function stopRecording(button) {
40
+ if (!isRecording) return; // 録音中でない場合は停止しない
41
+ mediaRecorder.stop();
42
+ toggleRecording(button);
43
+ }
44
+
45
+ function handleRecording(e) {
46
+ const button = e.target.closest(".record-button");
47
+ if (button) {
48
+ if (isRecording && currentRecordingButton !== button) {
49
+ // 他の人が録音中なら反応しない
50
+ return;
51
+ }
52
+ if (mediaRecorder && mediaRecorder.state === "recording") {
53
+ stopRecording(button);
54
+ } else {
55
+ startRecording(button);
56
+ }
57
+ }
58
+ }
59
+
60
+ function sendAudioChunks(chunks, button) {
61
+ // 引数に button を追加
62
+ const audioBlob = new Blob(chunks, { type: "audio/wav" });
63
+ const reader = new FileReader();
64
+ reader.onloadend = () => {
65
+ const base64String = reader.result.split(",")[1]; // Base64エンコードされた音声データ
66
+ const form = button.closest("form");
67
+ const nameInput = form.querySelector('input[name="name"]');
68
+ const name = nameInput ? nameInput.value : "unknown"; // 名前がない
69
+ fetch("/upload_base_audio", {
70
+ method: "POST",
71
+ headers: {
72
+ "Content-Type": "application/json",
73
+ },
74
+ body: JSON.stringify({ audio_data: base64String, name: name }),
75
+ })
76
+ .then((response) => response.json())
77
+ .then((data) => {
78
+ // エラー処理のみ残す
79
+ if (data.error) {
80
+ alert("エラー: " + data.error);
81
+ console.error(data.details);
82
+ }
83
+ // 成功時の処理(ボタンの有効化など)
84
+ else {
85
+ console.log("音声データ送信成功:", data);
86
+ userNames.push(name);
87
+ // 必要に応じて、ここでUIの変更(ボタンの有効化など)を行う
88
+ // 例: button.disabled = true; // 送信ボタンを無効化
89
+ // 例: button.classList.remove("recording"); //録音中のスタイルを解除
90
+ }
91
+ })
92
+ .catch((error) => {
93
+ console.error("エラー:", error);
94
+ });
95
+ };
96
+ reader.readAsDataURL(audioBlob);
97
+ }
98
+
99
+ document.getElementById("add-btn").addEventListener("click", () => {
100
+ const newItem = document.createElement("div");
101
+ newItem.className = "flex items-center gap-3 flex-wrap";
102
+ newItem.innerHTML = `
103
+ <form
104
+ action="/submit"
105
+ method="POST"
106
+ class="flex items-center space-x-2 w-full sm:w-auto"
107
+ onsubmit="event.preventDefault();"
108
+ >
109
+ <input
110
+ type="text"
111
+ name="name"
112
+ placeholder="名前を入力"
113
+ 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"
114
+ />
115
+
116
+ <button type="button" class="record-button" aria-label="音声録音開始">
117
+ <div class="record-icon"></div>
118
+ </button>
119
+ </form>
120
+ `;
121
+ newItem.addEventListener("click", handleRecording);
122
+ document.getElementById("people-list").appendChild(newItem);
123
+ userCount++; // 新しいメンバーを追加するたびにカウントを増やす
124
+ });
{templates → static}/style.css RENAMED
File without changes
templates/index.html CHANGED
@@ -25,11 +25,30 @@
25
  <div class="chart w-72 h-72 mb-5">
26
  <canvas id="speechChart"></canvas>
27
  </div>
28
-
29
- <!-- 録音ボタン -->
30
- <button class="record-button" id="recordButton" onclick="toggleRecording()">
31
- <div class="record-icon" id="recordIcon"></div>
32
- </button>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
 
34
  <!-- 結果ボタン -->
35
  <div class="flex mt-5">
@@ -88,7 +107,8 @@
88
  let before_rate = 0;
89
 
90
  // 初期設定:人数と名前を受け取って円グラフを作成
91
- let members = ["自分", "ahaha", "nufufu", "不明"];
 
92
  let voiceData = [50, 20, 20, 10]; // 自分と不明の割合を仮設定
93
 
94
  // Chart.js の初期化
@@ -180,12 +200,16 @@
180
  const reader = new FileReader();
181
  reader.onloadend = () => {
182
  const base64String = reader.result.split(",")[1]; // Base64エンコードされた音声データ
 
 
 
 
183
  fetch("/upload_audio", {
184
  method: "POST",
185
  headers: {
186
  "Content-Type": "application/json",
187
  },
188
- body: JSON.stringify({ audio_data: base64String }),
189
  })
190
  .then((response) => response.json())
191
  .then((data) => {
 
25
  <div class="chart w-72 h-72 mb-5">
26
  <canvas id="speechChart"></canvas>
27
  </div>
28
+ <form
29
+ id="recordForm"
30
+ action="/submit"
31
+ method="POST"
32
+ class="flex items-center space-x-2 w-full sm:w-auto"
33
+ onsubmit="event.preventDefault();"
34
+ >
35
+ <input
36
+ type="text"
37
+ name="name"
38
+ placeholder="名前を入力"
39
+ 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"
40
+ />
41
+ <!-- 録音ボタン -->
42
+ <button
43
+ type="button"
44
+ class="record-button"
45
+ id="recordButton"
46
+ onclick="toggleRecording()"
47
+ >
48
+ <div class="record-icon" id="recordIcon"></div>
49
+ </button>
50
+ </form>
51
+ <!-- 名前入力 -->
52
 
53
  <!-- 結果ボタン -->
54
  <div class="flex mt-5">
 
107
  let before_rate = 0;
108
 
109
  // 初期設定:人数と名前を受け取って円グラフを作成
110
+ let members = ["aaa", "bbb", "ccc", "その他"];
111
+
112
  let voiceData = [50, 20, 20, 10]; // 自分と不明の割合を仮設定
113
 
114
  // Chart.js の初期化
 
200
  const reader = new FileReader();
201
  reader.onloadend = () => {
202
  const base64String = reader.result.split(",")[1]; // Base64エンコードされた音声データ
203
+ // フォーム要素を取得
204
+ const form = document.getElementById("recordForm");
205
+ const nameInput = form.querySelector('input[name="name"]');
206
+ const name = nameInput ? nameInput.value : "unknown"; // 名前がない
207
  fetch("/upload_audio", {
208
  method: "POST",
209
  headers: {
210
  "Content-Type": "application/json",
211
  },
212
+ body: JSON.stringify({ audio_data: base64String, name: name }),
213
  })
214
  .then((response) => response.json())
215
  .then((data) => {
templates/userRegister.html CHANGED
@@ -67,158 +67,18 @@
67
  <!-- 録音画面に戻るボタン -->
68
  <button
69
  id="backButton"
 
70
  class="mt-6 px-6 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600 transition-colors"
71
  >
72
  録音画面に戻る
73
  </button>
74
  </div>
75
 
 
76
  <script>
77
- let mediaRecorder;
78
- let audioChunks = [];
79
- let userCount = 0; // 追加されたメンバー数を保持
80
- let isRecording = false; // 録音中かどうかを判定するフラグ
81
- let currentRecordingButton = null; // 現在録音中のボタンを保持
82
-
83
- function toggleRecording(button) {
84
- button.classList.toggle("recording");
85
- }
86
-
87
- async function startRecording(button) {
88
- if (isRecording && currentRecordingButton !== button) return; // 他の人が録音中なら何もしない
89
- isRecording = true; // 録音中に設定
90
- currentRecordingButton = button; // 録音中のボタンを記録
91
-
92
- try {
93
- const stream = await navigator.mediaDevices.getUserMedia({
94
- audio: true,
95
- });
96
- mediaRecorder = new MediaRecorder(stream, { mimeType: "audio/webm" });
97
- audioChunks = [];
98
- mediaRecorder.ondataavailable = (e) => audioChunks.push(e.data);
99
- mediaRecorder.onstop = () => {
100
- sendAudioChunks(audioChunks, button); // ボタン情報を渡す
101
- audioChunks = [];
102
- isRecording = false; // 録音停止後はフラグを戻す
103
- currentRecordingButton = null; // 録音ボタンを解除
104
- };
105
- mediaRecorder.start();
106
- toggleRecording(button);
107
- } catch (err) {
108
- console.error("マイクアクセスに失敗しました:", err);
109
- isRecording = false; // エラー発生時もフラグを戻す
110
- currentRecordingButton = null;
111
- }
112
- }
113
-
114
- function stopRecording(button) {
115
- if (!isRecording) return; // 録音中でない場合は停止しない
116
- mediaRecorder.stop();
117
- toggleRecording(button);
118
- }
119
-
120
- function handleRecording(e) {
121
- const button = e.target.closest(".record-button");
122
- if (button) {
123
- if (isRecording && currentRecordingButton !== button) {
124
- // 他の人が録音中なら反応しない
125
- return;
126
- }
127
- if (mediaRecorder && mediaRecorder.state === "recording") {
128
- stopRecording(button);
129
- } else {
130
- startRecording(button);
131
- }
132
- }
133
- }
134
-
135
- function sendAudioChunks(chunks, button) {
136
- // 引数に button を追加
137
- const audioBlob = new Blob(chunks, { type: "audio/wav" });
138
- const reader = new FileReader();
139
- reader.onloadend = () => {
140
- const base64String = reader.result.split(",")[1]; // Base64エンコードされた音声データ
141
- const form = button.closest("form");
142
- const nameInput = form.querySelector('input[name="name"]');
143
- const name = nameInput ? nameInput.value : "unknown"; // 名前がない
144
- fetch("/upload_base_audio", {
145
- method: "POST",
146
- headers: {
147
- "Content-Type": "application/json",
148
- },
149
- body: JSON.stringify({ audio_data: base64String, name: name }),
150
- })
151
- .then((response) => response.json())
152
- .then((data) => {
153
- // エラー処理のみ残す
154
- if (data.error) {
155
- alert("エラー: " + data.error);
156
- console.error(data.details);
157
- }
158
- // 成功時の処理(ボタンの有効化など)
159
- else {
160
- console.log("音声データ送信成功:", data);
161
- // 必要に応じて、ここでUIの変更(ボタンの有効化など)を行う
162
- // 例: button.disabled = true; // 送信ボタンを無効化
163
- // 例: button.classList.remove("recording"); //録音中のスタイルを解除
164
- }
165
- })
166
- .catch((error) => {
167
- console.error("エラー:", error);
168
- });
169
- };
170
- reader.readAsDataURL(audioBlob);
171
- }
172
-
173
- document.getElementById("add-btn").addEventListener("click", () => {
174
- const newItem = document.createElement("div");
175
- newItem.className = "flex items-center gap-3 flex-wrap";
176
- newItem.innerHTML = `
177
- <form
178
- action="/submit"
179
- method="POST"
180
- class="flex items-center space-x-2 w-full sm:w-auto"
181
- onsubmit="event.preventDefault();"
182
- >
183
- <input
184
- type="text"
185
- name="name"
186
- placeholder="名前を入力"
187
- 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"
188
- />
189
-
190
- <button type="button" class="record-button" aria-label="音声録音開始">
191
- <div class="record-icon"></div>
192
- </button>
193
-
194
- <button
195
- type="submit"
196
- class="submit-button px-4 py-2 border rounded-lg bg-blue-500 text-white hover:bg-blue-600"
197
- >
198
- 送信
199
- </button>
200
- </form>
201
- `;
202
- newItem.addEventListener("click", handleRecording);
203
- document.getElementById("people-list").appendChild(newItem);
204
- userCount++; // 新しいメンバーを追加するたびにカウントを増やす
205
- });
206
-
207
- // 「録音画面に戻る」ボタンの処理
208
- document
209
- .getElementById("backButton")
210
- .addEventListener("click", function () {
211
- // メンバーの人数を送信する
212
- sendUserCount();
213
-
214
- // index.htmlに戻る
215
- window.location.href = "index.html";
216
- });
217
-
218
- // メンバーの人数を送信する関数
219
- function sendUserCount() {
220
- console.log(`追加された人数: ${userCount}`);
221
- // ここで人数を送信する処理を実行(例: fetchを使ってサーバーに送信)
222
  }
223
  </script>
224
  </body>
 
67
  <!-- 録音画面に戻るボタン -->
68
  <button
69
  id="backButton"
70
+ onclick="showRecorder()"
71
  class="mt-6 px-6 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600 transition-colors"
72
  >
73
  録音画面に戻る
74
  </button>
75
  </div>
76
 
77
+ <script src="{{ url_for('static', filename='register_record.js') }}"></script>
78
  <script>
79
+ function showRecorder() {
80
+ // 録音画面へ遷移
81
+ window.location.href = "index";
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
82
  }
83
  </script>
84
  </body>