rinabuoy commited on
Commit
25bf378
Β·
1 Parent(s): a39e3fc

Add audio set selection screen - users can choose between Khmer and Korean audio

Browse files
Files changed (1) hide show
  1. app.py +98 -50
app.py CHANGED
@@ -3,17 +3,15 @@ import os
3
  import random
4
  from pathlib import Path
5
 
6
- # Get all audio clips and images from the same folder
7
- audio_dir = "audio_clips"
8
- audio_files = sorted([f for f in os.listdir(audio_dir) if f.endswith('.mp3')])
9
- image_files = sorted([f for f in os.listdir(audio_dir) if f.endswith('.png')])
10
-
11
- print(f"Found {len(audio_files)} audio files")
12
- print(f"Found {len(image_files)} image files")
13
-
14
  # Build a mapping of audio clips to their corresponding images
15
- def build_clip_mapping():
16
  """Map each audio file to its corresponding image based on matching base names"""
 
 
 
 
 
 
17
  mapping = {}
18
 
19
  for audio_file in audio_files:
@@ -23,36 +21,62 @@ def build_clip_mapping():
23
  if audio_base == image_base:
24
  mapping[audio_file] = image_file
25
  break
 
 
26
  return mapping
27
 
28
- clip_to_image = build_clip_mapping()
29
- print(f"Successfully mapped {len(clip_to_image)} audio-image pairs")
30
- print(f"Sample pairs: {list(clip_to_image.items())[:3]}")
31
-
32
- valid_clips = list(clip_to_image.keys())
33
-
34
  class QuizApp:
35
  def __init__(self):
36
  self.current_index = 0
37
  self.score = 0
38
- self.total_questions = len(valid_clips)
39
  self.selected_image = None
40
  self.current_correct_index = None
 
 
 
 
 
 
 
 
 
 
41
  self.shuffled_clips = valid_clips.copy()
42
  random.shuffle(self.shuffled_clips)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
 
44
  def get_current_clip(self):
45
  if self.current_index < len(self.shuffled_clips):
46
  clip_name = self.shuffled_clips[self.current_index]
47
- clip_path = os.path.join(audio_dir, clip_name)
48
- correct_image = clip_to_image[clip_name]
49
- all_images = [img for audio, img in clip_to_image.items() if audio != clip_name]
50
  incorrect_images = random.sample(all_images, min(3, len(all_images)))
51
  all_options = [correct_image] + incorrect_images
52
  random.shuffle(all_options)
53
  correct_index = all_options.index(correct_image)
54
  self.current_correct_index = correct_index
55
- image_paths = [os.path.join(audio_dir, img) for img in all_options]
56
  question = f"What did you hear? ({self.current_index + 1}/{self.total_questions})"
57
  return clip_path, question, image_paths, correct_index
58
 
@@ -117,21 +141,25 @@ class QuizApp:
117
  )
118
 
119
  def reset(self):
 
120
  self.current_index = 0
121
  self.score = 0
122
  self.selected_image = None
123
- random.shuffle(self.shuffled_clips)
124
- clip_path, question, image_paths, _ = self.get_current_clip()
125
- image_updates = []
126
- for i in range(4):
127
- if i < len(image_paths):
128
- image_updates.append(gr.Image(value=image_paths[i], visible=True))
129
- else:
130
- image_updates.append(gr.Image(visible=False))
131
- return "", clip_path, gr.Markdown(f"### {question}"), *image_updates, ""
 
 
 
 
132
 
133
  quiz = QuizApp()
134
- initial_clip, initial_question, initial_images, _ = quiz.get_current_clip()
135
 
136
  def make_select_handler(index):
137
  def handler():
@@ -161,30 +189,50 @@ custom_css = """
161
 
162
  with gr.Blocks(title="Khmer Learning Quiz", theme=gr.themes.Base(), css=custom_css) as demo:
163
  gr.Markdown("# 🎡 Khmer Audio Learning Quiz")
164
- gr.Markdown("Listen to each audio clip and select the correct image!")
165
 
166
- audio_player = gr.Audio(value=initial_clip, label="πŸ”Š Listen to the audio clip", autoplay=False)
167
- question_text = gr.Markdown(f"### {initial_question}")
168
- gr.Markdown("### Click on an image to select:")
 
 
 
 
 
 
 
 
 
 
169
 
170
- with gr.Row():
171
- image_options = []
172
- for i in range(4):
173
- if initial_images and i < len(initial_images):
174
- img = gr.Image(value=initial_images[i], show_label=False, interactive=False,
175
- height=200, visible=True, elem_classes="clickable-image")
176
- else:
 
 
 
 
177
  img = gr.Image(show_label=False, interactive=False, height=200,
178
  visible=False, elem_classes="clickable-image")
179
- image_options.append(img)
180
-
181
- selection_status = gr.Textbox(label="Your Selection", value="", interactive=False)
182
-
183
- with gr.Row():
184
- submit_btn = gr.Button("βœ“ Submit Answer", variant="primary", size="lg")
185
- reset_btn = gr.Button("πŸ”„ Reset Quiz", variant="secondary")
 
 
186
 
187
- feedback_text = gr.Markdown("", elem_classes="feedback-box")
 
 
 
 
 
188
 
189
  for i in range(4):
190
  image_options[i].select(fn=make_select_handler(i), inputs=[], outputs=[selection_status])
@@ -193,7 +241,7 @@ with gr.Blocks(title="Khmer Learning Quiz", theme=gr.themes.Base(), css=custom_c
193
  outputs=[feedback_text, audio_player, question_text] + image_options + [selection_status])
194
 
195
  reset_btn.click(fn=quiz.reset, inputs=[],
196
- outputs=[feedback_text, audio_player, question_text] + image_options + [selection_status])
197
 
198
  if __name__ == "__main__":
199
  demo.launch()
 
3
  import random
4
  from pathlib import Path
5
 
 
 
 
 
 
 
 
 
6
  # Build a mapping of audio clips to their corresponding images
7
+ def build_clip_mapping(audio_dir):
8
  """Map each audio file to its corresponding image based on matching base names"""
9
+ audio_files = sorted([f for f in os.listdir(audio_dir) if f.endswith('.mp3')])
10
+ image_files = sorted([f for f in os.listdir(audio_dir) if f.endswith('.png')])
11
+
12
+ print(f"Found {len(audio_files)} audio files in {audio_dir}")
13
+ print(f"Found {len(image_files)} image files in {audio_dir}")
14
+
15
  mapping = {}
16
 
17
  for audio_file in audio_files:
 
21
  if audio_base == image_base:
22
  mapping[audio_file] = image_file
23
  break
24
+
25
+ print(f"Successfully mapped {len(mapping)} audio-image pairs")
26
  return mapping
27
 
 
 
 
 
 
 
28
  class QuizApp:
29
  def __init__(self):
30
  self.current_index = 0
31
  self.score = 0
 
32
  self.selected_image = None
33
  self.current_correct_index = None
34
+ self.audio_dir = None
35
+ self.clip_to_image = {}
36
+ self.shuffled_clips = []
37
+ self.total_questions = 0
38
+
39
+ def start_quiz(self, audio_set):
40
+ """Initialize quiz with selected audio set"""
41
+ self.audio_dir = "audio_clips" if audio_set == "Khmer Script (αžαŸ’αž˜αŸ‚αžš)" else "audio_clips_ko"
42
+ self.clip_to_image = build_clip_mapping(self.audio_dir)
43
+ valid_clips = list(self.clip_to_image.keys())
44
  self.shuffled_clips = valid_clips.copy()
45
  random.shuffle(self.shuffled_clips)
46
+ self.total_questions = len(self.shuffled_clips)
47
+ self.current_index = 0
48
+ self.score = 0
49
+ self.selected_image = None
50
+
51
+ clip_path, question, image_paths, _ = self.get_current_clip()
52
+ image_updates = []
53
+ for i in range(4):
54
+ if i < len(image_paths):
55
+ image_updates.append(gr.Image(value=image_paths[i], visible=True))
56
+ else:
57
+ image_updates.append(gr.Image(visible=False))
58
+
59
+ return (
60
+ gr.update(visible=False), # Hide start screen
61
+ gr.update(visible=True), # Show quiz screen
62
+ clip_path,
63
+ gr.Markdown(f"### {question}"),
64
+ *image_updates,
65
+ ""
66
+ )
67
 
68
  def get_current_clip(self):
69
  if self.current_index < len(self.shuffled_clips):
70
  clip_name = self.shuffled_clips[self.current_index]
71
+ clip_path = os.path.join(self.audio_dir, clip_name)
72
+ correct_image = self.clip_to_image[clip_name]
73
+ all_images = [img for audio, img in self.clip_to_image.items() if audio != clip_name]
74
  incorrect_images = random.sample(all_images, min(3, len(all_images)))
75
  all_options = [correct_image] + incorrect_images
76
  random.shuffle(all_options)
77
  correct_index = all_options.index(correct_image)
78
  self.current_correct_index = correct_index
79
+ image_paths = [os.path.join(self.audio_dir, img) for img in all_options]
80
  question = f"What did you hear? ({self.current_index + 1}/{self.total_questions})"
81
  return clip_path, question, image_paths, correct_index
82
 
 
141
  )
142
 
143
  def reset(self):
144
+ """Reset quiz to start screen"""
145
  self.current_index = 0
146
  self.score = 0
147
  self.selected_image = None
148
+ self.audio_dir = None
149
+ self.clip_to_image = {}
150
+ self.shuffled_clips = []
151
+
152
+ return (
153
+ gr.update(visible=True), # Show start screen
154
+ gr.update(visible=False), # Hide quiz screen
155
+ None,
156
+ gr.Markdown(""),
157
+ *[gr.Image(visible=False) for _ in range(4)],
158
+ "",
159
+ ""
160
+ )
161
 
162
  quiz = QuizApp()
 
163
 
164
  def make_select_handler(index):
165
  def handler():
 
189
 
190
  with gr.Blocks(title="Khmer Learning Quiz", theme=gr.themes.Base(), css=custom_css) as demo:
191
  gr.Markdown("# 🎡 Khmer Audio Learning Quiz")
 
192
 
193
+ # Start Screen
194
+ with gr.Column(visible=True) as start_screen:
195
+ gr.Markdown("## Welcome! πŸ‘‹")
196
+ gr.Markdown("### Choose your learning mode:")
197
+
198
+ audio_set_choice = gr.Radio(
199
+ choices=["Khmer Script (αžαŸ’αž˜αŸ‚αžš)", "Korean (ν•œκΈ€/ko)"],
200
+ value="Khmer Script (αžαŸ’αž˜αŸ‚αžš)",
201
+ label="Select Audio Set",
202
+ info="Choose which set of audio clips you want to practice with"
203
+ )
204
+
205
+ start_btn = gr.Button("πŸš€ Start Quiz", variant="primary", size="lg")
206
 
207
+ # Quiz Screen
208
+ with gr.Column(visible=False) as quiz_screen:
209
+ gr.Markdown("Listen to each audio clip and select the correct image!")
210
+
211
+ audio_player = gr.Audio(label="πŸ”Š Listen to the audio clip", autoplay=False)
212
+ question_text = gr.Markdown("")
213
+ gr.Markdown("### Click on an image to select:")
214
+
215
+ with gr.Row():
216
+ image_options = []
217
+ for i in range(4):
218
  img = gr.Image(show_label=False, interactive=False, height=200,
219
  visible=False, elem_classes="clickable-image")
220
+ image_options.append(img)
221
+
222
+ selection_status = gr.Textbox(label="Your Selection", value="", interactive=False)
223
+
224
+ with gr.Row():
225
+ submit_btn = gr.Button("βœ“ Submit Answer", variant="primary", size="lg")
226
+ reset_btn = gr.Button("πŸ”„ Back to Start", variant="secondary")
227
+
228
+ feedback_text = gr.Markdown("", elem_classes="feedback-box")
229
 
230
+ # Event handlers
231
+ start_btn.click(
232
+ fn=quiz.start_quiz,
233
+ inputs=[audio_set_choice],
234
+ outputs=[start_screen, quiz_screen, audio_player, question_text] + image_options + [selection_status]
235
+ )
236
 
237
  for i in range(4):
238
  image_options[i].select(fn=make_select_handler(i), inputs=[], outputs=[selection_status])
 
241
  outputs=[feedback_text, audio_player, question_text] + image_options + [selection_status])
242
 
243
  reset_btn.click(fn=quiz.reset, inputs=[],
244
+ outputs=[start_screen, quiz_screen, audio_player, question_text] + image_options + [selection_status, feedback_text])
245
 
246
  if __name__ == "__main__":
247
  demo.launch()