mizzzuno commited on
Commit
f525763
·
verified ·
1 Parent(s): e501bac

Update templates/index.html

Browse files
Files changed (1) hide show
  1. templates/index.html +210 -203
templates/index.html CHANGED
@@ -1,212 +1,219 @@
1
  <!DOCTYPE html>
2
  <html lang="ja">
3
- <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Voice Recorder Interface</title>
7
- <style>
8
- body {
9
- display: flex;
10
- flex-direction: column;
11
- justify-content: center;
12
- align-items: center;
13
- height: 100vh;
14
- margin: 0;
15
- background-color: #121212;
16
- color: white;
17
- }
18
- .chart {
19
- width: 300px;
20
- height: 300px;
21
- margin-bottom: 20px;
22
- }
23
- .record-button {
24
- position: fixed;
25
- bottom: 30px;
26
- width: 80px;
27
- height: 80px;
28
- background-color: transparent;
29
- border-radius: 50%;
30
- border: 4px solid white;
31
- display: flex;
32
- justify-content: center;
33
- align-items: center;
34
- cursor: pointer;
35
- box-shadow: 0 4px 6px rgba(0, 0, 0, 0.4);
36
- transition: all 0.2s ease;
37
- }
38
- .record-icon {
39
- width: 60px;
40
- height: 60px;
41
- background-color: #d32f2f;
42
- border-radius: 50%;
43
- transition: all 0.2s ease;
44
- }
45
- .recording .record-icon {
46
- width: 40px;
47
- height: 40px;
48
- border-radius: 10%;
49
- }
50
- .result-buttons {
51
- margin-top: 20px;
52
- display: flex;
53
- gap: 10px;
54
- }
55
- .result-button {
56
- padding: 10px 20px;
57
- background-color: #4caf50;
58
- border: none;
59
- border-radius: 5px;
60
- color: white;
61
- cursor: pointer;
62
- box-shadow: 0 4px 6px rgba(0, 0, 0, 0.4);
63
- transition: background-color 0.2s ease;
64
- }
65
- .result-button:hover {
66
- background-color: #388e3c;
67
- }
68
- </style>
69
- <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
70
- </head>
71
- <body>
72
- <div class="chart">
73
- <canvas id="speechChart"></canvas>
74
- </div>
75
-
76
- <button class="record-button" id="recordButton" onclick="toggleRecording()">
77
- <div class="record-icon" id="recordIcon"></div>
78
- </button>
79
-
80
- <div class="result-buttons">
81
- <button class="result-button" id="historyButton" onclick="showHistory()">会話履歴を表示</button>
82
- <button class="result-button" id="feedbackButton" onclick="showResults()">フィードバック画面を表示</button>
83
- </div>
84
-
85
- <script>
86
- let isRecording = false;
87
- let mediaRecorder;
88
- let audioChunks = [];
89
- let recordingInterval;
90
-
91
- // Chart.js の初期化
92
- const ctx = document.getElementById('speechChart').getContext('2d');
93
- const speechChart = new Chart(ctx, {
94
- type: 'doughnut',
95
- data: {
96
- labels: ['自分', '他の人'],
97
- datasets: [{
98
- data: [30, 70],
99
- backgroundColor: ['#4caf50', '#757575'],
100
- }],
101
- },
102
- options: {
103
- responsive: true,
104
- plugins: {
105
- legend: {
106
- display: true,
107
- position: 'bottom',
108
- labels: { color: 'white' }
109
- }
110
- }
111
  }
112
- });
113
-
114
- async function toggleRecording() {
115
- const recordButton = document.getElementById('recordButton');
116
-
117
- if (!isRecording) {
118
- // 録音開始
119
- isRecording = true;
120
- recordButton.classList.add('recording');
121
- try {
122
- const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
123
- mediaRecorder = new MediaRecorder(stream);
124
- audioChunks = [];
125
-
126
- mediaRecorder.ondataavailable = event => {
127
- if (event.data.size > 0) {
128
- audioChunks.push(event.data);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
129
  }
130
- };
131
-
132
- mediaRecorder.onstop = () => {
133
- sendAudioChunks([...audioChunks]);
 
 
 
 
 
 
 
 
 
 
134
  audioChunks = [];
135
- };
136
-
137
- mediaRecorder.start();
138
- // 10秒ごとに自動で停止して送信
139
- recordingInterval = setInterval(() => {
140
- if (mediaRecorder && mediaRecorder.state === "recording") {
141
- mediaRecorder.stop();
142
- }
143
- }, 10000);
144
- } catch (error) {
145
- console.error('マイクへのアクセスに失敗しました:', error);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
146
  isRecording = false;
147
  recordButton.classList.remove('recording');
148
- }
149
- } else {
150
- // 手動停止
151
- isRecording = false;
152
- recordButton.classList.remove('recording');
153
- clearInterval(recordingInterval);
154
- if (mediaRecorder && mediaRecorder.state === "recording") {
155
- mediaRecorder.stop();
156
  }
157
  }
158
- }
159
-
160
- function sendAudioChunks(chunks) {
161
- const audioBlob = new Blob(chunks, { type: 'audio/wav' });
162
- const reader = new FileReader();
163
- reader.onloadend = () => {
164
- const base64String = reader.result.split(',')[1]; // Base64エンコードされた音声データ
165
- fetch('/upload_audio', {
166
- method: 'POST',
167
- headers: {
168
- 'Content-Type': 'application/json',
169
- },
170
- body: JSON.stringify({ audio_data: base64String }),
171
- })
172
- .then(response => response.json())
173
- .then(data => {
174
- if (data.error) {
175
- alert('エラー: ' + data.error);
176
- console.error(data.details);
177
- } else if (data.rate !== undefined) {
178
- // 解析結果が返ってきた場合はチャートを更新
179
- speechChart.data.datasets[0].data = [data.rate, 100 - data.rate];
180
- speechChart.update();
181
- alert('音声の解析が完了しました。自分の音声: ' + data.rate.toFixed(2) + '%, 他の人: ' + (100 - data.rate).toFixed(2) + '%');
182
- } else {
183
- alert('音声がバックエンドに送信されました。');
184
- }
185
- // 録音が継続中であれば、再度録音を開始(自動録音の連続処理)
186
- if (isRecording && mediaRecorder && mediaRecorder.state === "inactive") {
187
- mediaRecorder.start();
188
- }
189
- })
190
- .catch(error => {
191
- console.error('エラー:', error);
192
- if (isRecording && mediaRecorder && mediaRecorder.state === "inactive") {
193
- mediaRecorder.start();
194
- }
195
- });
196
- };
197
- reader.readAsDataURL(audioBlob);
198
- }
199
-
200
- function showHistory() {
201
- // 会話履歴表示の画面があれば、そのページへ遷移する例
202
- // window.location.href = 'history';
203
- alert('会話履歴を表示する機能は未実装です。');
204
- }
205
-
206
- function showResults() {
207
- // フィードバック画面へ遷移
208
- window.location.href = 'feedback';
209
- }
210
- </script>
211
- </body>
212
- </html>
 
1
  <!DOCTYPE html>
2
  <html lang="ja">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Voice Recorder Interface</title>
7
+ <style>
8
+ body {
9
+ display: flex;
10
+ flex-direction: column;
11
+ justify-content: center;
12
+ align-items: center;
13
+ height: 100vh;
14
+ margin: 0;
15
+ background-color: #121212;
16
+ color: white;
17
+ }
18
+
19
+ .chart {
20
+ width: 300px;
21
+ height: 300px;
22
+ margin-bottom: 50px; /* 円グラフとボタンの間隔を狭く */
23
+ }
24
+
25
+ .record-button {
26
+ position: fixed;
27
+ bottom: 30px;
28
+ width: 80px;
29
+ height: 80px;
30
+ background-color: transparent;
31
+ border-radius: 50%;
32
+ border: 4px solid white;
33
+ display: flex;
34
+ justify-content: center;
35
+ align-items: center;
36
+ cursor: pointer;
37
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.4);
38
+ transition: all 0.2s ease;
39
+ }
40
+
41
+ .record-icon {
42
+ width: 60px;
43
+ height: 60px;
44
+ background-color: #d32f2f;
45
+ border-radius: 50%;
46
+ transition: all 0.2s ease;
47
+ }
48
+
49
+ .recording .record-icon {
50
+ width: 40px;
51
+ height: 40px;
52
+ border-radius: 10%;
53
+ }
54
+
55
+ .result-buttons {
56
+ margin-top: 5px; /* ボタン間の距離を少し縮める */
57
+ display: flex;
58
+ gap: 10px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
  }
60
+
61
+ .result-button {
62
+ padding: 10px 20px;
63
+ background-color: #4caf50;
64
+ border: none;
65
+ border-radius: 5px;
66
+ color: white;
67
+ cursor: pointer;
68
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.4);
69
+ transition: background-color 0.2s ease;
70
+ }
71
+
72
+ .result-button:hover {
73
+ background-color: #388e3c;
74
+ }
75
+ </style>
76
+ <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
77
+ </head>
78
+ <body>
79
+ <div class="chart">
80
+ <canvas id="speechChart"></canvas>
81
+ </div>
82
+
83
+ <button class="record-button" id="recordButton" onclick="toggleRecording()">
84
+ <div class="record-icon" id="recordIcon"></div>
85
+ </button>
86
+
87
+ <div class="result-buttons">
88
+ <button class="result-button" id="historyButton" onclick="showHistory()">会話履歴を表示</button>
89
+ <button class="result-button" id="feedbackButton" onclick="showResults()">フィードバック画面を表示</button>
90
+ </div>
91
+
92
+ <script>
93
+ let isRecording = false;
94
+ let mediaRecorder;
95
+ let audioChunks = [];
96
+ let recordingInterval;
97
+
98
+ // Chart.js の初期化
99
+ const ctx = document.getElementById('speechChart').getContext('2d');
100
+ const speechChart = new Chart(ctx, {
101
+ type: 'doughnut',
102
+ data: {
103
+ labels: ['自分', '他の人'],
104
+ datasets: [{
105
+ data: [30, 70],
106
+ backgroundColor: ['#4caf50', '#757575'],
107
+ }],
108
+ },
109
+ options: {
110
+ responsive: true,
111
+ plugins: {
112
+ legend: {
113
+ display: true,
114
+ position: 'bottom',
115
+ labels: { color: 'white' }
116
  }
117
+ }
118
+ }
119
+ });
120
+
121
+ async function toggleRecording() {
122
+ const recordButton = document.getElementById('recordButton');
123
+
124
+ if (!isRecording) {
125
+ // 録音開始
126
+ isRecording = true;
127
+ recordButton.classList.add('recording');
128
+ try {
129
+ const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
130
+ mediaRecorder = new MediaRecorder(stream);
131
  audioChunks = [];
132
+
133
+ mediaRecorder.ondataavailable = event => {
134
+ if (event.data.size > 0) {
135
+ audioChunks.push(event.data);
136
+ }
137
+ };
138
+
139
+ mediaRecorder.onstop = () => {
140
+ sendAudioChunks([...audioChunks]);
141
+ audioChunks = [];
142
+ };
143
+
144
+ mediaRecorder.start();
145
+ // 10秒ごとに自動で停止して送信
146
+ recordingInterval = setInterval(() => {
147
+ if (mediaRecorder && mediaRecorder.state === "recording") {
148
+ mediaRecorder.stop();
149
+ }
150
+ }, 10000);
151
+ } catch (error) {
152
+ console.error('マイクへのアクセスに失敗しました:', error);
153
+ isRecording = false;
154
+ recordButton.classList.remove('recording');
155
+ }
156
+ } else {
157
+ // 手動停止
158
  isRecording = false;
159
  recordButton.classList.remove('recording');
160
+ clearInterval(recordingInterval);
161
+ if (mediaRecorder && mediaRecorder.state === "recording") {
162
+ mediaRecorder.stop();
163
+ }
 
 
 
 
164
  }
165
  }
166
+
167
+ function sendAudioChunks(chunks) {
168
+ const audioBlob = new Blob(chunks, { type: 'audio/wav' });
169
+ const reader = new FileReader();
170
+ reader.onloadend = () => {
171
+ const base64String = reader.result.split(',')[1]; // Base64エンコードされた音声データ
172
+ fetch('/upload_audio', {
173
+ method: 'POST',
174
+ headers: {
175
+ 'Content-Type': 'application/json',
176
+ },
177
+ body: JSON.stringify({ audio_data: base64String }),
178
+ })
179
+ .then(response => response.json())
180
+ .then(data => {
181
+ if (data.error) {
182
+ alert('エラー: ' + data.error);
183
+ console.error(data.details);
184
+ } else if (data.rate !== undefined) {
185
+ // 解析結果が返ってきた場合はチャートを更新
186
+ speechChart.data.datasets[0].data = [data.rate, 100 - data.rate];
187
+ speechChart.update();
188
+ alert('音声の解析が完了しました。自分の音声: ' + data.rate.toFixed(2) + '%, 他の人: ' + (100 - data.rate).toFixed(2) + '%');
189
+ } else {
190
+ alert('音声がバックエンドに送信されました。');
191
+ }
192
+ // 録音が継続中であれば、再度録音を開始(自動録音の連続処理)
193
+ if (isRecording && mediaRecorder && mediaRecorder.state === "inactive") {
194
+ mediaRecorder.start();
195
+ }
196
+ })
197
+ .catch(error => {
198
+ console.error('エラー:', error);
199
+ if (isRecording && mediaRecorder && mediaRecorder.state === "inactive") {
200
+ mediaRecorder.start();
201
+ }
202
+ });
203
+ };
204
+ reader.readAsDataURL(audioBlob);
205
+ }
206
+
207
+ function showHistory() {
208
+ // 会話履歴表示の画面があれば、そのページへ遷移する例
209
+ // window.location.href = 'history';
210
+ alert('会話履歴を表示する機能は未実装です。');
211
+ }
212
+
213
+ function showResults() {
214
+ // フィードバック画面へ遷移
215
+ window.location.href = 'feedback';
216
+ }
217
+ </script>
218
+ </body>
219
+ </html>