haepada commited on
Commit
eefb51e
·
verified ·
1 Parent(s): 7ca0d73

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +149 -74
app.py CHANGED
@@ -6,8 +6,6 @@ from datetime import datetime
6
  import os
7
  import requests
8
  import json
9
- import time
10
- import threading
11
  from dotenv import load_dotenv
12
 
13
  # 환경변수 로드
@@ -116,8 +114,8 @@ def calculate_baseline_features(audio_data):
116
 
117
  features = {
118
  "energy": float(np.mean(librosa.feature.rms(y=y))),
119
- "tempo": float(librosa.beat.tempo(y, sr=sr)[0]),
120
- "pitch": float(np.mean(librosa.feature.zero_crossing_rate(y))),
121
  "volume": float(np.mean(np.abs(y))),
122
  "mfcc": librosa.feature.mfcc(y=y, sr=sr, n_mfcc=13).mean(axis=1).tolist()
123
  }
@@ -148,9 +146,12 @@ def map_acoustic_to_emotion(features, baseline_features=None):
148
  pitch_norm = min(features["pitch"] * 2, 1)
149
 
150
  if baseline_features:
151
- energy_norm = (features["energy"] / baseline_features["energy"]) * 50
152
- tempo_norm = (features["tempo"] / baseline_features["tempo"])
153
- pitch_norm = (features["pitch"] / baseline_features["pitch"])
 
 
 
154
 
155
  emotions = {
156
  "primary": "",
@@ -218,7 +219,7 @@ def analyze_voice(audio_data, state):
218
  return state, "오디오 형식을 지원하지 않습니다.", "", "", ""
219
 
220
  # 음향학적 특성 분석
221
- acoustic_features = calculate_baseline_features(audio_data)
222
  if acoustic_features is None:
223
  return state, "음성 분석에 실패했습니다.", "", "", ""
224
 
@@ -294,7 +295,7 @@ def generate_image_from_prompt(prompt):
294
  """이미지 생성 함수"""
295
  if not prompt:
296
  print("No prompt provided")
297
- return None, None
298
 
299
  try:
300
  response = requests.post(
@@ -311,18 +312,15 @@ def generate_image_from_prompt(prompt):
311
  )
312
 
313
  if response.status_code == 200:
314
- timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
315
- image_path = f"generated_images/{timestamp}.png"
316
- with open(image_path, "wb") as f:
317
- f.write(response.content)
318
- return response.content, image_path
319
  else:
320
  print(f"Error: {response.status_code}")
321
  print(f"Response: {response.text}")
322
- return None, None
323
  except Exception as e:
324
  print(f"Error generating image: {str(e)}")
325
- return None, None
326
 
327
  def save_reflection(text, state):
328
  """감상 저장"""
@@ -331,9 +329,13 @@ def save_reflection(text, state):
331
 
332
  try:
333
  current_time = datetime.now().strftime("%H:%M:%S")
334
- sentiment = text_analyzer(text)[0] if text_analyzer else {"label": "unknown", "score": 0.0}
335
- new_reflection = [current_time, text, f"{sentiment['label']} ({sentiment['score']:.2f})"]
 
 
 
336
 
 
337
  reflections = state.get("reflections", [])
338
  reflections.append(new_reflection)
339
  state = {**state, "reflections": reflections}
@@ -351,34 +353,36 @@ def create_interface():
351
  "reflections": [],
352
  "wish": None,
353
  "final_prompt": "",
354
- "image_path": None
 
355
  }
356
 
357
  with gr.Blocks(theme=gr.themes.Soft()) as app:
358
  state = gr.State(value=initial_state)
359
 
360
  gr.Markdown("# 디지털 굿판")
 
 
 
 
361
 
362
- with gr.Tabs(selected=0) as tabs:
363
  with gr.TabItem("입장") as tab_entrance:
364
- name_display = gr.Markdown(WELCOME_MESSAGE) # worldview_display 대신 하나의 Markdown 사용
365
  name_input = gr.Textbox(
366
  label="이름을 알려주세요",
367
  placeholder="이름을 입력해주세요",
368
  interactive=True
369
  )
370
- start_btn = gr.Button("여정 시작하기", variant="primary")
371
-
372
-
373
- # variant 추가
374
 
375
- with gr.TabItem("기준 설정") as tab_baseline:
376
- gr.Markdown("""### 축원의 문장을 평온한 마음으로 읽어주세요""")
377
  gr.Markdown("'당신의 건강과 행복이 늘 가득하기를'")
378
  baseline_audio = gr.Audio(
379
  label="축원 문장 녹음하기",
380
- sources="microphone", # source -> sources
381
- streaming=False,
382
  type="numpy"
383
  )
384
  set_baseline_btn = gr.Button("기준점 설정 완료", variant="primary")
@@ -389,26 +393,26 @@ def create_interface():
389
  play_music_btn = gr.Button("온천천의 소리 듣기", variant="secondary")
390
  with gr.Row():
391
  audio = gr.Audio(
392
- value="oncheoncheon_sound.wav",
393
  type="filepath",
394
  label="온천천의 소리",
395
  interactive=False,
396
- show_download_button=True, # 다운로드 버튼 추가
397
  visible=True
398
  )
399
  with gr.Column():
400
  reflection_input = gr.Textbox(
401
  label="지금 이 순간의 감상을 자유롭게 적어보세요",
402
  lines=3,
403
- max_lines=5 # 최대 라인 수 지정
404
  )
405
  save_btn = gr.Button("감상 저장하기", variant="secondary")
406
  reflections_display = gr.Dataframe(
407
  headers=["시간", "감상", "감정 분석"],
408
  label="기록된 감상들",
409
- value=[[]],
410
  interactive=False,
411
- wrap=True # 긴 텍스트 wrap 처리
412
  )
413
 
414
  with gr.TabItem("기원") as tab_wish:
@@ -417,8 +421,7 @@ def create_interface():
417
  with gr.Column():
418
  voice_input = gr.Audio(
419
  label="소원을 나누고 싶은 마음을 말해주세요",
420
- sources="microphone",
421
- streaming=False,
422
  type="numpy"
423
  )
424
  with gr.Row():
@@ -449,59 +452,125 @@ def create_interface():
449
  generate_btn = gr.Button("마음의 그림 그리기", variant="primary")
450
  result_image = gr.Image(
451
  label="생성된 이미지",
452
- show_download_button=True # 다운로드 버튼 추가
453
  )
454
-
455
- gr.Markdown("## 마지막 감상을 남겨주세요")
456
  final_reflection = gr.Textbox(
457
- label="마지막 감상",
458
- placeholder="한 줄로 남겨주세요...",
459
  max_lines=3
460
  )
461
- save_final_btn = gr.Button("감상 남기기", variant="primary")
462
- save_final_status = gr.Markdown("")
 
 
 
 
 
 
 
 
 
 
463
 
464
- # 이벤트 연결 - gr.on 사용
465
  def handle_start(name, current_state):
466
  if not name.strip():
467
- return "이름을 입력해주세요", gr.update(selected=0), current_state
 
 
 
 
468
  current_state = {**current_state, "user_name": name}
469
- return WORLDVIEW_MESSAGE, gr.update(selected=1), current_state
470
-
471
- def handle_baseline(audio_data, current_state):
 
 
 
 
 
 
 
472
  try:
473
- features = calculate_baseline_features(audio_data)
 
474
  if features:
475
  current_state = {**current_state, "baseline_features": features}
476
- return current_state, "기준점이 설정되었습니다."
477
- return current_state, "기준점 설정에 실패했습니다."
478
  except Exception as e:
479
- return current_state, f"오류 발생: {str(e)}"
 
480
 
481
- def handle_analysis(audio_data, current_state):
 
482
  try:
483
- result = analyze_voice(audio_data, current_state)
484
- if isinstance(result, tuple) and len(result) == 5:
485
- return result
486
- return current_state, "분석 실패", "", "", ""
487
  except Exception as e:
488
- return current_state, f"오류 발생: {str(e)}", "", "", ""
 
 
 
 
 
489
 
490
  def handle_image_generation(prompt):
491
- if not prompt:
492
- return None
 
 
 
 
 
493
  try:
494
- image_data = generate_image_from_prompt(prompt)
495
- return image_data[0] if image_data else None
 
 
 
 
 
 
 
 
 
 
496
  except Exception as e:
497
- print(f"Image generation error: {str(e)}")
498
- return None
499
 
500
- # 이벤트 연결
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
501
  start_btn.click(
502
  fn=handle_start,
503
  inputs=[name_input, state],
504
- outputs=[name_display, tabs, state]
505
  )
506
 
507
  set_baseline_btn.click(
@@ -510,6 +579,11 @@ def create_interface():
510
  outputs=[state, baseline_status]
511
  )
512
 
 
 
 
 
 
513
  analyze_btn.click(
514
  fn=handle_analysis,
515
  inputs=[voice_input, state],
@@ -522,15 +596,16 @@ def create_interface():
522
  outputs=[result_image]
523
  )
524
 
525
- save_final_btn.click(
526
- fn=lambda t, s: "감상이 저장되었습니다." if db.save_wish(s.get("user_name", "익명"), t) else "저장 실패",
527
- inputs=[final_reflection, state],
528
- outputs=[save_final_status]
529
  )
530
 
531
- play_music_btn.click(
532
- fn=lambda: "oncheoncheon_sound.wav",
533
- outputs=[audio]
 
534
  )
535
 
536
  return app
@@ -541,4 +616,4 @@ if __name__ == "__main__":
541
  debug=True,
542
  server_name="0.0.0.0",
543
  server_port=7860
544
- )
 
6
  import os
7
  import requests
8
  import json
 
 
9
  from dotenv import load_dotenv
10
 
11
  # 환경변수 로드
 
114
 
115
  features = {
116
  "energy": float(np.mean(librosa.feature.rms(y=y))),
117
+ "tempo": float(librosa.beat.tempo(y=y, sr=sr)[0]),
118
+ "pitch": float(np.mean(librosa.feature.zero_crossing_rate(y=y))),
119
  "volume": float(np.mean(np.abs(y))),
120
  "mfcc": librosa.feature.mfcc(y=y, sr=sr, n_mfcc=13).mean(axis=1).tolist()
121
  }
 
146
  pitch_norm = min(features["pitch"] * 2, 1)
147
 
148
  if baseline_features:
149
+ if baseline_features["energy"] == 0 or baseline_features["tempo"] == 0 or baseline_features["pitch"] == 0:
150
+ print("Invalid baseline features")
151
+ else:
152
+ energy_norm = (features["energy"] / baseline_features["energy"]) * 50
153
+ tempo_norm = (features["tempo"] / baseline_features["tempo"])
154
+ pitch_norm = (features["pitch"] / baseline_features["pitch"])
155
 
156
  emotions = {
157
  "primary": "",
 
219
  return state, "오디오 형식을 지원하지 않습니다.", "", "", ""
220
 
221
  # 음향학적 특성 분석
222
+ acoustic_features = calculate_baseline_features((sr, y))
223
  if acoustic_features is None:
224
  return state, "음성 분석에 실패했습니다.", "", "", ""
225
 
 
295
  """이미지 생성 함수"""
296
  if not prompt:
297
  print("No prompt provided")
298
+ return None
299
 
300
  try:
301
  response = requests.post(
 
312
  )
313
 
314
  if response.status_code == 200:
315
+ image_content = response.content
316
+ return image_content
 
 
 
317
  else:
318
  print(f"Error: {response.status_code}")
319
  print(f"Response: {response.text}")
320
+ return None
321
  except Exception as e:
322
  print(f"Error generating image: {str(e)}")
323
+ return None
324
 
325
  def save_reflection(text, state):
326
  """감상 저장"""
 
329
 
330
  try:
331
  current_time = datetime.now().strftime("%H:%M:%S")
332
+ if text_analyzer:
333
+ sentiment = text_analyzer(text)[0]
334
+ sentiment_text = f"{sentiment['label']} ({sentiment['score']:.2f})"
335
+ else:
336
+ sentiment_text = "분석 불가"
337
 
338
+ new_reflection = [current_time, text, sentiment_text]
339
  reflections = state.get("reflections", [])
340
  reflections.append(new_reflection)
341
  state = {**state, "reflections": reflections}
 
353
  "reflections": [],
354
  "wish": None,
355
  "final_prompt": "",
356
+ "image_path": None,
357
+ "current_tab": 0 # 탭 상태 추가
358
  }
359
 
360
  with gr.Blocks(theme=gr.themes.Soft()) as app:
361
  state = gr.State(value=initial_state)
362
 
363
  gr.Markdown("# 디지털 굿판")
364
+ gr.Markdown("""
365
+ 1. 입장 → 2. 축원(기준 설정) → 3. 청신 → 4. 기원 → 5. 송신
366
+ 순서대로 진행해주세요.
367
+ """)
368
 
369
+ with gr.Tabs() as tabs:
370
  with gr.TabItem("입장") as tab_entrance:
371
+ gr.Markdown(WELCOME_MESSAGE)
372
  name_input = gr.Textbox(
373
  label="이름을 알려주세요",
374
  placeholder="이름을 입력해주세요",
375
  interactive=True
376
  )
377
+ worldview_display = gr.Markdown(visible=False)
378
+ start_btn = gr.Button("여정 시작하기", variant="primary")
 
 
379
 
380
+ with gr.TabItem("축원") as tab_baseline:
381
+ gr.Markdown("### 축원의 문장을 평온한 마음으로 읽어주세요")
382
  gr.Markdown("'당신의 건강과 행복이 늘 가득하기를'")
383
  baseline_audio = gr.Audio(
384
  label="축원 문장 녹음하기",
385
+ source="microphone",
 
386
  type="numpy"
387
  )
388
  set_baseline_btn = gr.Button("기준점 설정 완료", variant="primary")
 
393
  play_music_btn = gr.Button("온천천의 소리 듣기", variant="secondary")
394
  with gr.Row():
395
  audio = gr.Audio(
396
+ value="assets/main_music.mp3", # 음악 파일 경로 수정
397
  type="filepath",
398
  label="온천천의 소리",
399
  interactive=False,
400
+ show_download_button=True,
401
  visible=True
402
  )
403
  with gr.Column():
404
  reflection_input = gr.Textbox(
405
  label="지금 이 순간의 감상을 자유롭게 적어보세요",
406
  lines=3,
407
+ max_lines=5
408
  )
409
  save_btn = gr.Button("감상 저장하기", variant="secondary")
410
  reflections_display = gr.Dataframe(
411
  headers=["시간", "감상", "감정 분석"],
412
  label="기록된 감상들",
413
+ value=[],
414
  interactive=False,
415
+ wrap=True
416
  )
417
 
418
  with gr.TabItem("기원") as tab_wish:
 
421
  with gr.Column():
422
  voice_input = gr.Audio(
423
  label="소원을 나누고 싶은 마음을 말해주세요",
424
+ source="microphone",
 
425
  type="numpy"
426
  )
427
  with gr.Row():
 
452
  generate_btn = gr.Button("마음의 그림 그리기", variant="primary")
453
  result_image = gr.Image(
454
  label="생성된 이미지",
455
+ show_download_button=True
456
  )
457
+
458
+ gr.Markdown("## 온천천에 전하고 싶은 소원을 남겨주세요")
459
  final_reflection = gr.Textbox(
460
+ label="소원",
461
+ placeholder="당신의 소원을 한 줄로 남겨주세요...",
462
  max_lines=3
463
  )
464
+ save_final_btn = gr.Button("소원 전하기", variant="primary")
465
+ gr.Markdown("""
466
+ 💫 여러분의 소원은 11월 25일 온천천 벽면에 설치될 소원나무에 전시될 예정입니다.
467
+ 따뜻한 마음을 담아 작성해주세요.
468
+ """)
469
+ wishes_display = gr.Dataframe(
470
+ headers=["시간", "소원", "감정 분석"],
471
+ label="기록된 소원들",
472
+ value=[],
473
+ interactive=False,
474
+ wrap=True
475
+ )
476
 
477
+ # 이벤트 연결
478
  def handle_start(name, current_state):
479
  if not name.strip():
480
+ return (
481
+ "이름을 입력해주세요",
482
+ gr.update(visible=False),
483
+ current_state
484
+ )
485
  current_state = {**current_state, "user_name": name}
486
+ return (
487
+ WORLDVIEW_MESSAGE,
488
+ gr.update(visible=True),
489
+ current_state
490
+ )
491
+
492
+ def handle_baseline(audio, current_state):
493
+ if audio is None:
494
+ return current_state, "음성을 먼저 녹음해주세요."
495
+
496
  try:
497
+ sr, y = audio
498
+ features = calculate_baseline_features((sr, y))
499
  if features:
500
  current_state = {**current_state, "baseline_features": features}
501
+ return current_state, "기준점이 설정되었습니다. 다음 단계로 진행해주세요."
502
+ return current_state, "기준점 설정에 실패했습니다. 다시 시도해주세요."
503
  except Exception as e:
504
+ print(f"Baseline error: {str(e)}")
505
+ return current_state, "오류가 발생했습니다. 다시 시도해주세요."
506
 
507
+ # 음악 재생 이벤트 핸들러 수정
508
+ def play_music():
509
  try:
510
+ return "assets/main_music.mp3"
 
 
 
511
  except Exception as e:
512
+ print(f"Error playing music: {e}")
513
+ return None
514
+
515
+ def handle_analysis(audio, current_state):
516
+ state, text, voice_result, text_result, prompt = analyze_voice(audio, current_state)
517
+ return state, text, voice_result, text_result, prompt
518
 
519
  def handle_image_generation(prompt):
520
+ image_content = generate_image_from_prompt(prompt)
521
+ return image_content
522
+
523
+ def save_reflection_fixed(text, state):
524
+ if not text.strip():
525
+ return state, []
526
+
527
  try:
528
+ current_time = datetime.now().strftime("%H:%M:%S")
529
+ if text_analyzer:
530
+ sentiment = text_analyzer(text)[0]
531
+ sentiment_text = f"{sentiment['label']} ({sentiment['score']:.2f})"
532
+ else:
533
+ sentiment_text = "분석 불가"
534
+
535
+ new_reflection = [current_time, text, sentiment_text]
536
+ reflections = state.get("reflections", [])
537
+ reflections.append(new_reflection)
538
+ state = {**state, "reflections": reflections}
539
+ return state, reflections
540
  except Exception as e:
541
+ print(f"Error saving reflection: {e}")
542
+ return state, state.get("reflections", [])
543
 
544
+ def save_wish(text, state):
545
+ if not text.strip():
546
+ return "소원을 입력해주세요.", []
547
+
548
+ try:
549
+ current_time = datetime.now().strftime("%H:%M:%S")
550
+ if text_analyzer:
551
+ sentiment = text_analyzer(text)[0]
552
+ sentiment_text = f"{sentiment['label']} ({sentiment['score']:.2f})"
553
+ else:
554
+ sentiment_text = "분석 불가"
555
+
556
+ new_wish = [current_time, text, sentiment_text]
557
+ wishes = state.get("wishes", [])
558
+ wishes.append(new_wish)
559
+ state = {**state, "wishes": wishes}
560
+
561
+ # DB에 저장
562
+ db.save_wish(state.get("user_name", "익명"), text)
563
+
564
+ return "소원이 저장되었습니다.", wishes
565
+ except Exception as e:
566
+ print(f"Error saving wish: {e}")
567
+ return "오류가 발생했습니다.", state.get("wishes", [])
568
+
569
+ # 버튼 이벤트 연결
570
  start_btn.click(
571
  fn=handle_start,
572
  inputs=[name_input, state],
573
+ outputs=[worldview_display, worldview_display, state]
574
  )
575
 
576
  set_baseline_btn.click(
 
579
  outputs=[state, baseline_status]
580
  )
581
 
582
+ play_music_btn.click(
583
+ fn=play_music,
584
+ outputs=[audio]
585
+ )
586
+
587
  analyze_btn.click(
588
  fn=handle_analysis,
589
  inputs=[voice_input, state],
 
596
  outputs=[result_image]
597
  )
598
 
599
+ save_btn.click(
600
+ fn=save_reflection_fixed,
601
+ inputs=[reflection_input, state],
602
+ outputs=[state, reflections_display]
603
  )
604
 
605
+ save_final_btn.click(
606
+ fn=save_wish,
607
+ inputs=[final_reflection, state],
608
+ outputs=[baseline_status, wishes_display]
609
  )
610
 
611
  return app
 
616
  debug=True,
617
  server_name="0.0.0.0",
618
  server_port=7860
619
+ )