gsavin commited on
Commit
c55ccab
·
1 Parent(s): 86b351a

fix: add proper loading screen and fix initial music generation

Browse files
src/audio/audio_generator.py CHANGED
@@ -15,7 +15,7 @@ logger = logging.getLogger(__name__)
15
  client = genai.Client(api_key=settings.gemini_api_key.get_secret_value(), http_options={'api_version': 'v1alpha'})
16
  audio_queue = queue.Queue(maxsize=1)
17
 
18
- async def generate_music(request: gr.Request, receive_audio):
19
  async with (
20
  client.aio.live.music.connect(model='models/lyria-realtime-exp') as session,
21
  asyncio.TaskGroup() as tg,
@@ -26,14 +26,14 @@ async def generate_music(request: gr.Request, receive_audio):
26
  # Send initial prompts and config
27
  await session.set_weighted_prompts(
28
  prompts=[
29
- types.WeightedPrompt(text='The mysterious music of the forest', weight=1.0),
30
  ]
31
  )
32
  await session.set_music_generation_config(
33
  config=types.LiveMusicGenerationConfig(bpm=90, temperature=1.0)
34
  )
35
  await session.play()
36
- logger.info(f"Started music generation for session {request.session_hash}")
37
  sessions[request.session_hash] = session
38
 
39
  async def change_music_tone(request: gr.Request, new_tone):
@@ -65,9 +65,9 @@ async def receive_audio(session):
65
 
66
  sessions = {}
67
 
68
- async def start_music_generation(request: gr.Request):
69
  """Start the music generation in a separate thread."""
70
- await generate_music(request, receive_audio)
71
 
72
  async def cleanup_music_session(request: gr.Request):
73
  if request.session_hash in sessions:
 
15
  client = genai.Client(api_key=settings.gemini_api_key.get_secret_value(), http_options={'api_version': 'v1alpha'})
16
  audio_queue = queue.Queue(maxsize=1)
17
 
18
+ async def generate_music(request: gr.Request, music_tone: str, receive_audio):
19
  async with (
20
  client.aio.live.music.connect(model='models/lyria-realtime-exp') as session,
21
  asyncio.TaskGroup() as tg,
 
26
  # Send initial prompts and config
27
  await session.set_weighted_prompts(
28
  prompts=[
29
+ types.WeightedPrompt(text=music_tone, weight=1.0),
30
  ]
31
  )
32
  await session.set_music_generation_config(
33
  config=types.LiveMusicGenerationConfig(bpm=90, temperature=1.0)
34
  )
35
  await session.play()
36
+ logger.info(f"Started music generation for session {request.session_hash}, music tone: {music_tone}")
37
  sessions[request.session_hash] = session
38
 
39
  async def change_music_tone(request: gr.Request, new_tone):
 
65
 
66
  sessions = {}
67
 
68
+ async def start_music_generation(request: gr.Request, music_tone: str):
69
  """Start the music generation in a separate thread."""
70
+ await generate_music(request, music_tone, receive_audio)
71
 
72
  async def cleanup_music_session(request: gr.Request):
73
  if request.session_hash in sessions:
src/css.py CHANGED
@@ -123,4 +123,26 @@ img {
123
  .overlay-content .form {
124
  background: transparent !important;
125
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
126
  """
 
123
  .overlay-content .form {
124
  background: transparent !important;
125
  }
126
+ """
127
+
128
+ # CSS for the loading indicator
129
+ loading_css_styles = """
130
+ #loading-indicator {
131
+ position: fixed;
132
+ top: 0;
133
+ left: 0;
134
+ width: 100%;
135
+ height: 100%;
136
+ background-color: rgba(0, 0, 0, 0.8); /* Semi-transparent black */
137
+ /* When Gradio makes this gr.Column visible, it will set display:flex !important; (or similar). */
138
+ /* These properties will then apply to center the content of the Column: */
139
+ justify-content: center;
140
+ align-items: center;
141
+ z-index: 9999; /* Ensure it's on top */
142
+ }
143
+ #loading-indicator .loading-text { /* Style for the text inside */
144
+ color: white;
145
+ font-size: 2em;
146
+ text-align: center;
147
+ }
148
  """
src/game_constructor.py CHANGED
@@ -5,6 +5,8 @@ from game_setting import Character, GameSetting
5
  from game_state import story, state, get_current_scene
6
  from agent.llm_agent import process_user_input
7
  from images.image_generator import generate_image
 
 
8
 
9
  # Predefined suggestions for demo
10
  SETTING_SUGGESTIONS = [
@@ -106,6 +108,7 @@ def save_game_config(
106
  return f"❌ Error saving configuration: {str(e)}"
107
 
108
  async def start_game_with_settings(
 
109
  setting_desc: str,
110
  char_name: str,
111
  char_age: str,
@@ -119,6 +122,7 @@ async def start_game_with_settings(
119
  ):
120
  return (
121
  gr.update(visible=True), # constructor_interface
 
122
  gr.update(visible=False), # game_interface
123
  gr.update(
124
  value="❌ Please fill in all fields before starting the game.",
@@ -155,8 +159,14 @@ NOTE FOR THE ASSISTANT: YOU HAVE TO GENERATE THE IMAGE FOR THE START SCENE.
155
  """
156
 
157
  response = await process_user_input(initial_story)
 
 
 
 
 
158
 
159
  img = "forest.jpg"
 
160
  if response.change_scene.change_scene:
161
  img_path, _ = await generate_image(response.change_scene.scene_description)
162
  if img_path:
@@ -174,6 +184,7 @@ NOTE FOR THE ASSISTANT: YOU HAVE TO GENERATE THE IMAGE FOR THE START SCENE.
174
  scene_text, scene_image, scene_choices = get_current_scene()
175
 
176
  return (
 
177
  gr.update(visible=False), # constructor_interface
178
  gr.update(visible=True), # game_interface
179
  gr.update(visible=False), # error_message
 
5
  from game_state import story, state, get_current_scene
6
  from agent.llm_agent import process_user_input
7
  from images.image_generator import generate_image
8
+ from audio.audio_generator import start_music_generation
9
+ import asyncio
10
 
11
  # Predefined suggestions for demo
12
  SETTING_SUGGESTIONS = [
 
108
  return f"❌ Error saving configuration: {str(e)}"
109
 
110
  async def start_game_with_settings(
111
+ request: gr.Request,
112
  setting_desc: str,
113
  char_name: str,
114
  char_age: str,
 
122
  ):
123
  return (
124
  gr.update(visible=True), # constructor_interface
125
+ gr.update(visible=False), # loading indicator
126
  gr.update(visible=False), # game_interface
127
  gr.update(
128
  value="❌ Please fill in all fields before starting the game.",
 
159
  """
160
 
161
  response = await process_user_input(initial_story)
162
+
163
+ music_tone = response.change_music.music_description
164
+
165
+ if music_tone:
166
+ asyncio.create_task(start_music_generation(request, music_tone))
167
 
168
  img = "forest.jpg"
169
+
170
  if response.change_scene.change_scene:
171
  img_path, _ = await generate_image(response.change_scene.scene_description)
172
  if img_path:
 
184
  scene_text, scene_image, scene_choices = get_current_scene()
185
 
186
  return (
187
+ gr.update(visible=False), # loading indicator
188
  gr.update(visible=False), # constructor_interface
189
  gr.update(visible=True), # game_interface
190
  gr.update(visible=False), # error_message
src/main.py CHANGED
@@ -1,8 +1,7 @@
1
  import gradio as gr
2
- from css import custom_css
3
  from audio.audio_generator import (
4
  update_audio,
5
- start_music_generation,
6
  change_music_tone,
7
  cleanup_music_session,
8
  )
@@ -25,8 +24,9 @@ logger = logging.getLogger(__name__)
25
 
26
 
27
  def return_to_constructor():
28
- """Return to the game constructor interface"""
29
  return (
 
30
  gr.update(visible=True), # constructor_interface
31
  gr.update(visible=False), # game_interface
32
  gr.update(visible=False), # error_message
@@ -102,26 +102,42 @@ async def start_game_with_music(
102
  genre: str,
103
  ):
104
  """Start the game with custom settings and initialize music"""
 
 
 
 
 
 
 
 
 
 
105
  # First, get the game interface updates
106
  result = await start_game_with_settings(
107
- setting_desc, char_name, char_age, char_background, char_personality, genre
 
 
 
 
 
 
108
  )
109
- # If game is starting successfully (interface is switching), start music
110
- if len(result) >= 2 and result[1].get(
111
- "visible", False
112
- ): # game_interface becoming visible
113
- asyncio.create_task(start_music_generation(request))
114
-
115
- if state["scene"] in story and "music_tone" in story[state["scene"]]:
116
- await change_music_tone(request, story[state["scene"]]["music_tone"])
117
 
118
- return result
119
 
120
  with gr.Blocks(
121
- theme="soft", title="Game Constructor & Visual Novel", css=custom_css
 
 
122
  ) as demo:
 
 
 
 
123
  # Constructor Interface (visible by default)
124
- with gr.Column(visible=True, elem_id="constructor-interface") as constructor_interface:
 
 
125
  gr.Markdown("# 🎮 Interactive Game Constructor")
126
  gr.Markdown(
127
  "Create your own interactive story game by defining the setting, character, and genre!"
@@ -289,6 +305,7 @@ with gr.Blocks(
289
  genre_selection,
290
  ],
291
  outputs=[
 
292
  constructor_interface,
293
  game_interface,
294
  error_message,
@@ -301,7 +318,12 @@ with gr.Blocks(
301
  back_btn.click(
302
  fn=return_to_constructor,
303
  inputs=[],
304
- outputs=[constructor_interface, game_interface, error_message],
 
 
 
 
 
305
  )
306
 
307
  game_choices.change(
 
1
  import gradio as gr
2
+ from css import custom_css, loading_css_styles
3
  from audio.audio_generator import (
4
  update_audio,
 
5
  change_music_tone,
6
  cleanup_music_session,
7
  )
 
24
 
25
 
26
  def return_to_constructor():
27
+ """Return to the game constructor interface, ensure loading is hidden."""
28
  return (
29
+ gr.update(visible=False), # loading_indicator
30
  gr.update(visible=True), # constructor_interface
31
  gr.update(visible=False), # game_interface
32
  gr.update(visible=False), # error_message
 
102
  genre: str,
103
  ):
104
  """Start the game with custom settings and initialize music"""
105
+ yield (
106
+ gr.update(visible=True), # loading indicator
107
+ gr.update(), # constructor_interface
108
+ gr.update(), # game_interface
109
+ gr.update(), # error_message
110
+ gr.update(),
111
+ gr.update(),
112
+ gr.update(), # game components unchanged
113
+ )
114
+
115
  # First, get the game interface updates
116
  result = await start_game_with_settings(
117
+ request,
118
+ setting_desc,
119
+ char_name,
120
+ char_age,
121
+ char_background,
122
+ char_personality,
123
+ genre,
124
  )
125
+ yield result
 
 
 
 
 
 
 
126
 
 
127
 
128
  with gr.Blocks(
129
+ theme="soft",
130
+ title="Game Constructor & Visual Novel",
131
+ css=custom_css + loading_css_styles,
132
  ) as demo:
133
+ # Fullscreen Loading Indicator (hidden by default)
134
+ with gr.Column(visible=False, elem_id="loading-indicator") as loading_indicator:
135
+ gr.HTML("<div class='loading-text'>🚀 Starting your adventure...</div>")
136
+
137
  # Constructor Interface (visible by default)
138
+ with gr.Column(
139
+ visible=True, elem_id="constructor-interface"
140
+ ) as constructor_interface:
141
  gr.Markdown("# 🎮 Interactive Game Constructor")
142
  gr.Markdown(
143
  "Create your own interactive story game by defining the setting, character, and genre!"
 
305
  genre_selection,
306
  ],
307
  outputs=[
308
+ loading_indicator,
309
  constructor_interface,
310
  game_interface,
311
  error_message,
 
318
  back_btn.click(
319
  fn=return_to_constructor,
320
  inputs=[],
321
+ outputs=[
322
+ loading_indicator,
323
+ constructor_interface,
324
+ game_interface,
325
+ error_message,
326
+ ],
327
  )
328
 
329
  game_choices.change(