Spaces:
Running
Running
fix: add proper loading screen and fix initial music generation
Browse files- src/audio/audio_generator.py +5 -5
- src/css.py +22 -0
- src/game_constructor.py +11 -0
- src/main.py +38 -16
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=
|
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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
108 |
)
|
109 |
-
|
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",
|
|
|
|
|
122 |
) as demo:
|
|
|
|
|
|
|
|
|
123 |
# Constructor Interface (visible by default)
|
124 |
-
with gr.Column(
|
|
|
|
|
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=[
|
|
|
|
|
|
|
|
|
|
|
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(
|