ciaochris commited on
Commit
0803119
·
verified ·
1 Parent(s): 80dc4c4

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +222 -133
app.py CHANGED
@@ -1,202 +1,291 @@
1
  import os
2
  import gradio as gr
3
  import numpy as np
 
 
4
  import matplotlib.pyplot as plt
5
  from PIL import Image
6
  import soundfile as sf
7
  import tempfile
8
  import time
9
- from rhythma import RhythmaModulationEngine, RhythmaSymphAICore
10
 
11
- # Check if Groq API key is available
12
- has_groq_key = os.environ.get("GROQ_API_KEY") is not None
13
- use_groq = has_groq_key
14
 
15
- # If Groq API key is not set, show a message
16
- if not has_groq_key:
17
- print("⚠️ GROQ_API_KEY not found in environment variables.")
18
- print("To enable Groq LLM and audio transcription features, set the GROQ_API_KEY environment variable.")
19
- print("Running with limited functionality.")
 
 
 
 
20
 
21
- # Initialize the SymphAI Core
22
- symphai_core = RhythmaSymphAICore(use_groq=use_groq)
23
 
 
 
 
 
 
 
 
 
 
 
 
 
24
  def analyze_input(input_text=None, audio_input=None):
25
- """Analyze user input for emotional state and rhythm patterns"""
 
 
 
 
 
 
26
  # Pass to SymphAI Core for analysis
27
- return symphai_core.analyze_input(input_text, audio_input)
 
 
28
 
29
  def generate_modulated_experience(analysis_result, base_freq=None, modulation_type="sine", rhythm_pattern=None, duration=5):
30
- """Generate a complete modulated experience based on analysis and parameters"""
31
- if "error" in analysis_result:
32
- return analysis_result["error"], None, None, None, None
33
-
34
- # Create a unique timestamp for file naming
35
- timestamp = int(time.time())
36
-
37
- # Use emotional state and rhythm pattern from analysis if not manually overridden
38
- emotional_state = analysis_result["emotional_state"]
39
- rhythm_pattern = rhythm_pattern or analysis_result["rhythm_pattern"]
40
-
41
- # Initialize the Rhythma Engine
42
- engine = RhythmaModulationEngine(
43
- base_freq=base_freq, # Will be overridden by emotional state if None
44
- modulation_type=modulation_type,
45
- rhythm_pattern=rhythm_pattern,
46
- emotional_state=emotional_state
47
- )
48
-
49
- # Generate modulated audio
50
- temp_dir = tempfile.gettempdir()
51
- audio_file = os.path.join(temp_dir, f"rhythma_{timestamp}.wav")
52
- engine.save_audio(duration, audio_file)
53
-
54
- # Generate waveform visualization
55
- fig = engine.visualize_waveform(duration)
56
-
57
- # Get simple waveform image
58
- waveform_image = engine.get_waveform_image()
59
-
60
- # Get complete analysis text
61
- analysis_text = engine.get_complete_analysis()
62
-
63
- # Get symbolic interpretation
64
- symbolic = engine.get_symbolic_interpretation()
65
-
66
- return analysis_text, audio_file, fig, waveform_image, symbolic
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
 
68
  def rhythma_experience(
69
- input_text, audio_input,
70
- override_freq=None,
71
- override_modulation="sine",
72
- override_rhythm=None,
73
  duration=5
74
  ):
75
- """Complete Rhythma experience pipeline"""
76
- # Step 1: Analyze input
 
 
 
 
 
 
 
 
 
 
77
  analysis = analyze_input(input_text, audio_input)
78
-
79
- # Step 2: Generate modulated experience
 
80
  analysis_text, audio_file, fig, waveform_image, symbolic = generate_modulated_experience(
81
- analysis,
82
- base_freq=override_freq,
83
  modulation_type=override_modulation,
84
- rhythm_pattern=override_rhythm,
85
  duration=duration
86
  )
87
-
88
- # Return all outputs
 
89
  transcription = analysis.get("transcription", "") if isinstance(analysis, dict) else ""
90
-
91
- return analysis_text, audio_file, fig, waveform_image, symbolic, transcription
 
 
 
 
 
 
 
92
 
93
- # Create the Gradio interface
94
  def create_interface():
95
- with gr.Blocks(title="Rhythma: The Living Modulation Engine") as demo:
96
  gr.Markdown("# Rhythma: The Living Modulation Engine")
97
  gr.Markdown("### Dynamic rhythm-based sound modulation for wellbeing")
98
-
99
  if not use_groq:
100
- gr.Markdown("⚠️ **Running with limited functionality:** GROQ_API_KEY not found. Advanced emotion detection and audio transcription disabled.")
101
-
 
102
  with gr.Row():
103
  with gr.Column(scale=1):
104
- # Input section
105
  input_text = gr.Textbox(
106
- label="How are you feeling?",
107
- placeholder="Describe your emotional state or intention..."
 
108
  )
 
 
109
  audio_input = gr.Audio(
110
- type="filepath",
111
- label="Or speak about your feelings" if use_groq else "Audio input (requires Groq API for transcription)"
 
 
112
  )
113
-
114
- with gr.Accordion("Advanced Settings", open=False):
115
  override_freq = gr.Slider(
116
- minimum=0, maximum=1000, value=0,
117
- label="Override Frequency (Hz, leave at 0 for automatic)"
 
118
  )
119
  override_modulation = gr.Dropdown(
120
- choices=["sine", "pulse", "chirp"],
121
  value="sine",
122
- label="Modulation Type"
123
  )
 
 
124
  override_rhythm = gr.Dropdown(
125
- choices=[None, "calm", "active", "focused", "relaxed"],
126
- value=None,
127
- label="Override Rhythm Pattern (leave blank for automatic)"
 
128
  )
129
  duration = gr.Slider(
130
- minimum=1, maximum=30, value=5, step=1,
131
  label="Duration (seconds)"
132
  )
133
-
134
- # Generate button
135
- generate_button = gr.Button("Generate Rhythma Experience", variant="primary")
136
-
137
  with gr.Column(scale=2):
138
- # Outputs
139
- analysis_output = gr.Textbox(label="Rhythma Analysis", lines=10)
140
  with gr.Row():
141
- audio_output = gr.Audio(label="Modulated Audio", type="filepath")
142
- waveform_simple = gr.Image(label="Basic Waveform")
143
- waveform_plot = gr.Plot(label="Detailed Waveform Analysis")
144
- symbolic_output = gr.Textbox(label="Symbolic Interpretation")
145
- transcription_output = gr.Textbox(label="Transcribed Audio (if provided)", visible=use_groq)
 
 
 
 
 
146
 
147
- # Button action
148
  generate_button.click(
149
- rhythma_experience,
150
  inputs=[
151
- input_text, audio_input,
152
- override_freq, override_modulation, override_rhythm,
153
  duration
154
  ],
155
  outputs=[
156
- analysis_output, audio_output,
157
  waveform_plot, waveform_simple, symbolic_output,
158
  transcription_output
159
  ]
160
  )
161
-
162
- # Examples
163
  gr.Examples(
164
  examples=[
165
- ["I'm feeling anxious about my upcoming presentation.", None],
166
- ["I feel at peace and grounded today.", None],
167
- ["I need to focus on my work but keep getting distracted.", None],
168
- ["I'm feeling overwhelmed with all my responsibilities.", None],
169
- ["I'm excited about my vacation next week!", None]
 
 
170
  ],
171
- inputs=[input_text, audio_input]
 
 
 
172
  )
173
-
 
174
  gr.Markdown("""
175
- ## About Rhythma: The Living Modulation Engine
176
-
177
- Rhythma is a dynamic rhythm-based enhancer that creates responsive sound experiences
178
- based on your emotional state and intentions.
179
-
180
- ### Key Features:
181
-
182
- 1. **Input Layer** – Senses the rhythm through text descriptions and audio input
183
- 2. **SymphAI Core** – Interprets your emotional state and maps it to rhythm patterns
184
- 3. **Modulation Engine** – Creates dynamic audio experiences with frequency modulation
185
- 4. **Output Layer** – Delivers audio-visual feedback tailored to your state
186
-
187
- Rhythma uses principles of Floquet modulation and rhythm analysis to generate audio that
188
- resonates with your current state or intention.
189
-
190
- ### Advanced Features (require Groq API):
191
- - Enhanced emotion detection with large language models
192
- - Audio transcription for voice input
193
-
194
- © 2025 Rhythma Technologies
195
  """)
196
 
197
  return demo
198
 
199
- # Run Gradio interface
200
  if __name__ == "__main__":
201
- demo = create_interface()
202
- demo.launch()
 
 
 
 
 
 
 
1
  import os
2
  import gradio as gr
3
  import numpy as np
4
+ import matplotlib
5
+ matplotlib.use('Agg') # Set backend BEFORE importing pyplot
6
  import matplotlib.pyplot as plt
7
  from PIL import Image
8
  import soundfile as sf
9
  import tempfile
10
  import time
11
+ from rhythma import RhythmaModulationEngine, RhythmaSymphAICore # Assuming rhythma.py is in the same directory
12
 
13
+ # --- Environment Variable Check ---
14
+ GROQ_API_KEY = os.environ.get("GROQ_API_KEY")
15
+ use_groq = bool(GROQ_API_KEY) # True only if key exists and is not empty
16
 
17
+ if not use_groq:
18
+ print("*"*40)
19
+ print("⚠️ WARNING: GROQ_API_KEY not found or empty in environment variables.")
20
+ print(" Groq LLM analysis and audio transcription features will be disabled.")
21
+ print(" Falling back to local analysis methods.")
22
+ print("*"*40)
23
+ else:
24
+ print("✅ GROQ_API_KEY found. Enabling Groq features.")
25
+ # --- End Environment Variable Check ---
26
 
 
 
27
 
28
+ # --- Initialize Core Components ---
29
+ try:
30
+ # Pass the determined use_groq flag to the core
31
+ symphai_core = RhythmaSymphAICore(use_groq=use_groq)
32
+ except Exception as e:
33
+ print(f"❌ FATAL ERROR: Could not initialize RhythmaSymphAICore: {e}")
34
+ # Handle fatal error appropriately - maybe exit or disable functionality
35
+ symphai_core = None # Indicate failure
36
+ # --- End Initialization ---
37
+
38
+
39
+ # --- Core Functions ---
40
  def analyze_input(input_text=None, audio_input=None):
41
+ """Analyze user input using the SymphAI Core."""
42
+ if symphai_core is None:
43
+ return {"error": "Analysis Core failed to initialize."}
44
+
45
+ # Ensure audio_input is a filepath string or None
46
+ audio_filepath = audio_input if isinstance(audio_input, str) else None
47
+
48
  # Pass to SymphAI Core for analysis
49
+ # Add default empty string for input_text if None, as core expects string or None
50
+ return symphai_core.analyze_input(input_text or "", audio_filepath)
51
+
52
 
53
  def generate_modulated_experience(analysis_result, base_freq=None, modulation_type="sine", rhythm_pattern=None, duration=5):
54
+ """Generate a complete modulated experience based on analysis and parameters."""
55
+ print(f"DEBUG: generate_modulated_experience received analysis: {analysis_result}")
56
+ print(f"DEBUG: Overrides - Freq: {base_freq}, Mod: {modulation_type}, Rhythm: {rhythm_pattern}, Dur: {duration}")
57
+
58
+ # --- Input Validation ---
59
+ if not isinstance(analysis_result, dict):
60
+ error_msg = "Internal Error: Analysis result is not in the expected format."
61
+ print(f"❌ {error_msg}")
62
+ return error_msg, None, None, None, None
63
+
64
+ if "error" in analysis_result and analysis_result["error"]:
65
+ error_msg = f"Analysis Error: {analysis_result['error']}"
66
+ print(f"❌ {error_msg}")
67
+ # Return the error message clearly for the analysis output
68
+ return error_msg, None, None, None, None
69
+
70
+ # Ensure required keys exist, even if defaults were used in analysis
71
+ emotional_state = analysis_result.get("emotional_state", "neutral") # Default if missing
72
+ rhythm_pattern_from_analysis = analysis_result.get("rhythm_pattern", "calm") # Default if missing
73
+
74
+ # --- Determine Final Parameters ---
75
+ # Use manual override if provided and valid, otherwise use analysis result
76
+ final_rhythm_pattern = rhythm_pattern if rhythm_pattern else rhythm_pattern_from_analysis
77
+ # Use manual frequency override ONLY if it's > 0
78
+ final_base_freq = base_freq if base_freq and base_freq > 0 else None # Pass None to let engine use emotion/default
79
+
80
+ print(f"DEBUG: Engine Params - Emotion: {emotional_state}, Freq Override: {final_base_freq}, Rhythm: {final_rhythm_pattern}, Mod: {modulation_type}")
81
+
82
+ try:
83
+ # --- Initialize the Rhythma Engine ---
84
+ engine = RhythmaModulationEngine(
85
+ base_freq=final_base_freq, # Engine handles None: uses emotional_state or default
86
+ modulation_type=modulation_type,
87
+ rhythm_pattern=final_rhythm_pattern,
88
+ emotional_state=emotional_state if not final_base_freq else None # Pass emotion only if freq isn't overridden
89
+ )
90
+
91
+ # --- Generate Outputs ---
92
+ timestamp = int(time.time())
93
+ temp_dir = tempfile.gettempdir()
94
+ # Ensure temp_dir exists (useful in some restricted environments)
95
+ os.makedirs(temp_dir, exist_ok=True)
96
+ audio_file = os.path.join(temp_dir, f"rhythma_{timestamp}.wav")
97
+
98
+ # Generate and save audio
99
+ saved_audio_path = engine.save_audio(duration, audio_file)
100
+ if not saved_audio_path: # Check if saving failed
101
+ raise RuntimeError("Failed to save generated audio file.")
102
+
103
+ # Generate waveform visualization (Plot)
104
+ fig = engine.visualize_waveform(duration)
105
+
106
+ # Get simple waveform image (PIL Image)
107
+ waveform_image = engine.get_waveform_image()
108
+
109
+ # Get complete analysis text from the engine's perspective
110
+ analysis_text = engine.get_complete_analysis()
111
+
112
+ # Get symbolic interpretation
113
+ symbolic = engine.get_symbolic_interpretation()
114
+
115
+ print("✅ Modulation experience generated successfully.")
116
+ return analysis_text, saved_audio_path, fig, waveform_image, symbolic
117
+
118
+ except Exception as e:
119
+ error_msg = f"Error during Rhythma generation: {e}"
120
+ print(f"❌ {error_msg}")
121
+ import traceback
122
+ traceback.print_exc()
123
+ # Return error message for analysis, and None for other outputs
124
+ return error_msg, None, None, None, None
125
+
126
 
127
  def rhythma_experience(
128
+ input_text, audio_input,
129
+ override_freq=None,
130
+ override_modulation="sine",
131
+ override_rhythm=None,
132
  duration=5
133
  ):
134
+ """Complete Rhythma experience pipeline: Analysis -> Generation"""
135
+ print("\n--- Starting New Rhythma Experience ---")
136
+ # Clean up input text
137
+ input_text = input_text.strip() if input_text else ""
138
+
139
+ # --- Step 1: Analyze input ---
140
+ # Ensure override_freq is float or None
141
+ try:
142
+ freq_override_value = float(override_freq) if override_freq is not None else 0.0
143
+ except (ValueError, TypeError):
144
+ freq_override_value = 0.0 # Default to 0 if invalid input
145
+
146
  analysis = analyze_input(input_text, audio_input)
147
+
148
+ # --- Step 2: Generate modulated experience ---
149
+ # Pass analysis results and overrides to the generation function
150
  analysis_text, audio_file, fig, waveform_image, symbolic = generate_modulated_experience(
151
+ analysis,
152
+ base_freq=freq_override_value, # Pass the validated float/int
153
  modulation_type=override_modulation,
154
+ rhythm_pattern=override_rhythm if override_rhythm else None, # Pass None if dropdown default is selected
155
  duration=duration
156
  )
157
+
158
+ # --- Step 3: Prepare Outputs ---
159
+ # Get transcription from analysis result (will be empty string if no audio/transcription)
160
  transcription = analysis.get("transcription", "") if isinstance(analysis, dict) else ""
161
+ # If analysis itself failed, analysis_text will contain the error message from generate_modulated_experience
162
+ # If only transcription failed, it might be in the transcription field
163
+
164
+ # Handle potential None figure if generation failed
165
+ plot_output = fig if fig else None # Gradio handles None for Plot output
166
+
167
+ print("--- Rhythma Experience Complete ---")
168
+ # Return all outputs for Gradio interface
169
+ return analysis_text, audio_file, plot_output, waveform_image, symbolic, transcription
170
 
171
+ # --- Create the Gradio Interface ---
172
  def create_interface():
173
+ with gr.Blocks(theme=gr.themes.Soft(), title="Rhythma: The Living Modulation Engine") as demo:
174
  gr.Markdown("# Rhythma: The Living Modulation Engine")
175
  gr.Markdown("### Dynamic rhythm-based sound modulation for wellbeing")
176
+
177
  if not use_groq:
178
+ gr.Warning("Running with limited functionality: GROQ_API_KEY not found. "
179
+ "Advanced AI analysis and audio transcription are disabled.")
180
+
181
  with gr.Row():
182
  with gr.Column(scale=1):
183
+ gr.Markdown("**1. Describe Your State or Intention**")
184
  input_text = gr.Textbox(
185
+ label="How are you feeling, or what is your intention?",
186
+ placeholder="e.g., 'feeling stressed about work', 'want to relax', 'need focus'...",
187
+ lines=3
188
  )
189
+
190
+ gr.Markdown("**Optional: Use Your Voice (Requires Groq API Key)**")
191
  audio_input = gr.Audio(
192
+ sources=["microphone"], # Prioritize microphone
193
+ type="filepath", # RhythmaSymphAICore expects a filepath
194
+ label="Record or Upload Audio" if use_groq else "Audio Input (Disabled)",
195
+ interactive=use_groq # Disable if Groq not available
196
  )
197
+
198
+ with gr.Accordion("Advanced Settings (Optional Overrides)", open=False):
199
  override_freq = gr.Slider(
200
+ minimum=0, maximum=1000, value=0, step=1,
201
+ label="Override Frequency (Hz)",
202
+ info="Leave at 0 to use automatic frequency based on analysis."
203
  )
204
  override_modulation = gr.Dropdown(
205
+ choices=["sine", "pulse", "chirp"],
206
  value="sine",
207
+ label="Override Modulation Type"
208
  )
209
+ # Get available patterns from the engine instance
210
+ available_patterns = list(RhythmaModulationEngine().rhythm_configs.keys())
211
  override_rhythm = gr.Dropdown(
212
+ choices=[None] + available_patterns, # Add None option for automatic
213
+ value=None, # Default to automatic
214
+ label="Override Rhythm Pattern",
215
+ info="Leave blank to use automatic pattern based on analysis."
216
  )
217
  duration = gr.Slider(
218
+ minimum=3, maximum=60, value=10, step=1,
219
  label="Duration (seconds)"
220
  )
221
+
222
+ generate_button = gr.Button("Generate Rhythma Experience", variant="primary", scale=2)
223
+
 
224
  with gr.Column(scale=2):
225
+ gr.Markdown("**2. Experience Your Rhythma Soundscape**")
226
+ analysis_output = gr.Textbox(label="Rhythma Analysis & Guidance", lines=8, interactive=False)
227
  with gr.Row():
228
+ audio_output = gr.Audio(label="Modulated Audio", type="filepath", interactive=False)
229
+ waveform_simple = gr.Image(label="Base Waveform", interactive=False, height=100, width=200)
230
+ waveform_plot = gr.Plot(label="Detailed Waveform & Spectrogram", interactive=False)
231
+ symbolic_output = gr.Textbox(label="Symbolic Interpretation", interactive=False)
232
+ # Conditionally visible transcription output
233
+ transcription_output = gr.Textbox(
234
+ label="Transcribed Audio (If Provided)",
235
+ interactive=False,
236
+ visible=use_groq # Only show if Groq is potentially usable
237
+ )
238
 
239
+ # Define button action
240
  generate_button.click(
241
+ fn=rhythma_experience,
242
  inputs=[
243
+ input_text, audio_input,
244
+ override_freq, override_modulation, override_rhythm,
245
  duration
246
  ],
247
  outputs=[
248
+ analysis_output, audio_output,
249
  waveform_plot, waveform_simple, symbolic_output,
250
  transcription_output
251
  ]
252
  )
253
+
254
+ # Add Examples
255
  gr.Examples(
256
  examples=[
257
+ ["I'm feeling anxious about my upcoming presentation.", None, 0, "sine", None, 10],
258
+ ["I feel at peace and grounded today.", None, 0, "sine", None, 15],
259
+ ["I need to focus on my work but keep getting distracted.", None, 0, "sine", None, 20],
260
+ ["Feeling overwhelmed with responsibilities.", None, 0, "sine", None, 10],
261
+ ["Excited about my vacation next week!", None, 0, "sine", None, 10],
262
+ ["Just want to relax after a long day.", None, 0, "sine", "relaxed", 30], # Example with override
263
+ ["Feeling sad and low energy.", None, 0, "sine", None, 15],
264
  ],
265
+ inputs=[input_text, audio_input, override_freq, override_modulation, override_rhythm, duration],
266
+ outputs=[analysis_output, audio_output, waveform_plot, waveform_simple, symbolic_output, transcription_output],
267
+ fn=rhythma_experience, # Ensure examples also run the main function
268
+ cache_examples=False # Maybe disable caching during development
269
  )
270
+
271
+ gr.Markdown("---")
272
  gr.Markdown("""
273
+ ## About Rhythma
274
+ Rhythma creates personalized soundscapes using frequency modulation based on your described emotional state or intention.
275
+ It leverages AI analysis (enhanced with Groq if available) and principles of rhythmic sound design.
276
+ **Note:** This is an experimental tool. The frequencies and interpretations are based on various theories and are not medical advice.
277
+ © 2024 Your Rhythma Project
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
278
  """)
279
 
280
  return demo
281
 
282
+ # --- Run the Gradio App ---
283
  if __name__ == "__main__":
284
+ if symphai_core is None:
285
+ print("\n❌ Cannot launch Gradio app because RhythmaSymphAICore failed to initialize.\n")
286
+ else:
287
+ print("\n🚀 Launching Rhythma Gradio Interface...")
288
+ app_demo = create_interface()
289
+ # Set share=True if you need a public link (useful for testing deployment)
290
+ # Set debug=True for more verbose logs during development
291
+ app_demo.launch()#debug=True)