Update app.py
Browse files
app.py
CHANGED
|
@@ -87,7 +87,7 @@ with gr.Blocks(title="šµ Strudel Generator") as app:
|
|
| 87 |
lines=8
|
| 88 |
)
|
| 89 |
|
| 90 |
-
# Simple audio player with
|
| 91 |
gr.HTML("""
|
| 92 |
<div id="player" style='
|
| 93 |
background: linear-gradient(135deg, #FF6B9D, #A855F7);
|
|
@@ -121,47 +121,51 @@ with gr.Blocks(title="šµ Strudel Generator") as app:
|
|
| 121 |
margin: 5px;
|
| 122 |
transition: all 0.3s;
|
| 123 |
'>ā¹ļø Stop</button>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 124 |
<div id="status" style='color: white; margin-top: 10px; font-size: 14px;'>
|
| 125 |
-
Click
|
| 126 |
</div>
|
| 127 |
</div>
|
| 128 |
|
| 129 |
-
<!-- Load Strudel
|
| 130 |
-
<script
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
// Make Strudel available globally
|
| 134 |
-
window.strudel = { repl, controls, mini, initAudioOnFirstClick };
|
| 135 |
-
window.strudelReady = true;
|
| 136 |
-
|
| 137 |
-
console.log('Strudel.js loaded successfully!');
|
| 138 |
-
updateStatus('šµ Strudel.js ready! Generate code and play!', 'white');
|
| 139 |
-
</script>
|
| 140 |
|
| 141 |
<script>
|
| 142 |
let isPlaying = false;
|
| 143 |
-
let
|
| 144 |
let strudelInitialized = false;
|
|
|
|
| 145 |
|
| 146 |
function getCurrentCode() {
|
| 147 |
-
//
|
| 148 |
-
const
|
| 149 |
-
|
| 150 |
-
|
| 151 |
-
'textarea',
|
| 152 |
-
'code',
|
| 153 |
-
'[data-testid="code"] textarea'
|
| 154 |
-
];
|
| 155 |
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
|
| 159 |
-
|
| 160 |
-
|
| 161 |
-
|
| 162 |
-
|
|
|
|
| 163 |
}
|
| 164 |
}
|
|
|
|
| 165 |
return '';
|
| 166 |
}
|
| 167 |
|
|
@@ -171,6 +175,7 @@ with gr.Blocks(title="šµ Strudel Generator") as app:
|
|
| 171 |
status.textContent = message;
|
| 172 |
status.style.color = color;
|
| 173 |
}
|
|
|
|
| 174 |
}
|
| 175 |
|
| 176 |
function updatePlayButton(playing) {
|
|
@@ -180,36 +185,37 @@ with gr.Blocks(title="šµ Strudel Generator") as app:
|
|
| 180 |
}
|
| 181 |
}
|
| 182 |
|
| 183 |
-
async function
|
| 184 |
-
if (strudelInitialized) return true;
|
| 185 |
-
|
| 186 |
try {
|
| 187 |
-
|
| 188 |
-
|
| 189 |
-
|
| 190 |
-
|
| 191 |
-
|
|
|
|
|
|
|
|
|
|
| 192 |
}
|
| 193 |
|
| 194 |
-
if
|
| 195 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 196 |
}
|
| 197 |
-
|
| 198 |
-
// Initialize audio on first click
|
| 199 |
-
await window.strudel.initAudioOnFirstClick();
|
| 200 |
-
strudelInitialized = true;
|
| 201 |
-
updateStatus('šµ Audio ready! Generate code and play!', '#ccffcc');
|
| 202 |
-
return true;
|
| 203 |
} catch (error) {
|
| 204 |
-
console.error('
|
| 205 |
updateStatus('ā ļø Audio init failed - using demo mode', '#ffcccc');
|
| 206 |
-
|
| 207 |
}
|
| 208 |
}
|
| 209 |
|
| 210 |
async function togglePlay() {
|
| 211 |
if (isPlaying) {
|
| 212 |
-
|
| 213 |
} else {
|
| 214 |
await startPlay();
|
| 215 |
}
|
|
@@ -218,92 +224,87 @@ with gr.Blocks(title="šµ Strudel Generator") as app:
|
|
| 218 |
async function startPlay() {
|
| 219 |
const code = getCurrentCode();
|
| 220 |
|
| 221 |
-
if (!code
|
| 222 |
-
updateStatus('No code
|
| 223 |
return;
|
| 224 |
}
|
| 225 |
|
| 226 |
try {
|
| 227 |
-
|
| 228 |
-
const initialized = await initializeStrudel();
|
| 229 |
-
|
| 230 |
-
if (!initialized || !window.strudel) {
|
| 231 |
-
// Fallback to demo mode
|
| 232 |
-
updateStatus('š® Demo: Playing ' + code.split('\\n')[0] + '...', '#ffffcc');
|
| 233 |
-
isPlaying = true;
|
| 234 |
-
updatePlayButton(true);
|
| 235 |
-
|
| 236 |
-
setTimeout(() => {
|
| 237 |
-
stopPlay();
|
| 238 |
-
updateStatus('Demo playback finished!', 'white');
|
| 239 |
-
}, 8000);
|
| 240 |
-
return;
|
| 241 |
-
}
|
| 242 |
-
|
| 243 |
-
// Stop any current pattern
|
| 244 |
-
if (currentPattern) {
|
| 245 |
-
currentPattern.stop();
|
| 246 |
-
}
|
| 247 |
-
|
| 248 |
-
updateStatus('šµ Evaluating code...', '#ffffcc');
|
| 249 |
-
|
| 250 |
-
// Clean the code (remove comments)
|
| 251 |
-
const cleanCode = code
|
| 252 |
-
.split('\\n')
|
| 253 |
-
.filter(line => !line.trim().startsWith('//'))
|
| 254 |
-
.join('\\n')
|
| 255 |
-
.trim();
|
| 256 |
|
| 257 |
-
|
| 258 |
-
|
| 259 |
-
|
| 260 |
-
|
| 261 |
-
|
| 262 |
-
|
| 263 |
-
|
| 264 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 265 |
|
| 266 |
-
isPlaying = true;
|
| 267 |
-
updatePlayButton(true);
|
| 268 |
-
updateStatus('šµ Playing your music! š¶', '#ccffcc');
|
| 269 |
} else {
|
| 270 |
-
|
|
|
|
|
|
|
| 271 |
}
|
| 272 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 273 |
} catch (error) {
|
| 274 |
console.error('Playback error:', error);
|
| 275 |
-
updateStatus('ā
|
| 276 |
-
|
| 277 |
-
|
|
|
|
|
|
|
|
|
|
| 278 |
|
| 279 |
-
// Try demo mode as fallback
|
| 280 |
setTimeout(() => {
|
| 281 |
-
|
| 282 |
-
|
| 283 |
-
|
| 284 |
-
|
| 285 |
-
setTimeout(() => {
|
| 286 |
-
stopPlay();
|
| 287 |
-
updateStatus('Demo playback finished!', 'white');
|
| 288 |
-
}, 5000);
|
| 289 |
-
}, 1000);
|
| 290 |
-
}
|
| 291 |
-
}
|
| 292 |
-
|
| 293 |
-
function pausePlay() {
|
| 294 |
-
if (currentPattern && typeof currentPattern.stop === 'function') {
|
| 295 |
-
currentPattern.stop();
|
| 296 |
}
|
| 297 |
-
|
| 298 |
-
isPlaying = false;
|
| 299 |
-
updatePlayButton(false);
|
| 300 |
-
updateStatus('āøļø Paused', '#ffffcc');
|
| 301 |
}
|
| 302 |
|
| 303 |
function stopPlay() {
|
| 304 |
-
if (
|
| 305 |
-
|
| 306 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 307 |
}
|
| 308 |
|
| 309 |
isPlaying = false;
|
|
@@ -311,8 +312,11 @@ with gr.Blocks(title="šµ Strudel Generator") as app:
|
|
| 311 |
updateStatus('ā¹ļø Stopped', 'white');
|
| 312 |
}
|
| 313 |
|
| 314 |
-
//
|
| 315 |
document.addEventListener('DOMContentLoaded', function() {
|
|
|
|
|
|
|
|
|
|
| 316 |
const buttons = document.querySelectorAll('#player button');
|
| 317 |
buttons.forEach(btn => {
|
| 318 |
btn.addEventListener('mouseenter', function() {
|
|
@@ -325,15 +329,21 @@ with gr.Blocks(title="šµ Strudel Generator") as app:
|
|
| 325 |
});
|
| 326 |
});
|
| 327 |
|
| 328 |
-
//
|
| 329 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 330 |
});
|
| 331 |
|
| 332 |
-
//
|
| 333 |
-
|
| 334 |
-
|
| 335 |
-
pausePlay();
|
| 336 |
-
}
|
| 337 |
});
|
| 338 |
</script>
|
| 339 |
""")
|
|
|
|
| 87 |
lines=8
|
| 88 |
)
|
| 89 |
|
| 90 |
+
# Simple audio player with working Strudel.js
|
| 91 |
gr.HTML("""
|
| 92 |
<div id="player" style='
|
| 93 |
background: linear-gradient(135deg, #FF6B9D, #A855F7);
|
|
|
|
| 121 |
margin: 5px;
|
| 122 |
transition: all 0.3s;
|
| 123 |
'>ā¹ļø Stop</button>
|
| 124 |
+
<button onclick="initAudio()" style='
|
| 125 |
+
background: rgba(255,255,255,0.6);
|
| 126 |
+
color: #FF6B9D;
|
| 127 |
+
border: none;
|
| 128 |
+
padding: 8px 20px;
|
| 129 |
+
border-radius: 20px;
|
| 130 |
+
font-size: 14px;
|
| 131 |
+
font-weight: bold;
|
| 132 |
+
cursor: pointer;
|
| 133 |
+
margin: 5px;
|
| 134 |
+
transition: all 0.3s;
|
| 135 |
+
'>š Enable Audio</button>
|
| 136 |
<div id="status" style='color: white; margin-top: 10px; font-size: 14px;'>
|
| 137 |
+
Click "Enable Audio" first, then generate code and play!
|
| 138 |
</div>
|
| 139 |
</div>
|
| 140 |
|
| 141 |
+
<!-- Load Strudel from official CDN -->
|
| 142 |
+
<script src="https://unpkg.com/@strudel.cycles/core@latest/dist/index.js"></script>
|
| 143 |
+
<script src="https://unpkg.com/@strudel.cycles/webaudio@latest/dist/index.js"></script>
|
| 144 |
+
<script src="https://unpkg.com/@strudel.cycles/mini@latest/dist/index.js"></script>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 145 |
|
| 146 |
<script>
|
| 147 |
let isPlaying = false;
|
| 148 |
+
let currentHap = null;
|
| 149 |
let strudelInitialized = false;
|
| 150 |
+
let audioContext = null;
|
| 151 |
|
| 152 |
function getCurrentCode() {
|
| 153 |
+
// More aggressive code detection
|
| 154 |
+
const allTextareas = document.querySelectorAll('textarea');
|
| 155 |
+
const allCodes = document.querySelectorAll('code');
|
| 156 |
+
const allPres = document.querySelectorAll('pre');
|
|
|
|
|
|
|
|
|
|
|
|
|
| 157 |
|
| 158 |
+
// Check all possible containers
|
| 159 |
+
const elements = [...allTextareas, ...allCodes, ...allPres];
|
| 160 |
+
|
| 161 |
+
for (let element of elements) {
|
| 162 |
+
const code = element.value || element.textContent || element.innerText || '';
|
| 163 |
+
if (code && code.includes('.cpm(') && !code.includes('Please describe')) {
|
| 164 |
+
console.log('Found code:', code);
|
| 165 |
+
return code;
|
| 166 |
}
|
| 167 |
}
|
| 168 |
+
console.log('No valid code found');
|
| 169 |
return '';
|
| 170 |
}
|
| 171 |
|
|
|
|
| 175 |
status.textContent = message;
|
| 176 |
status.style.color = color;
|
| 177 |
}
|
| 178 |
+
console.log('Status:', message);
|
| 179 |
}
|
| 180 |
|
| 181 |
function updatePlayButton(playing) {
|
|
|
|
| 185 |
}
|
| 186 |
}
|
| 187 |
|
| 188 |
+
async function initAudio() {
|
|
|
|
|
|
|
| 189 |
try {
|
| 190 |
+
updateStatus('š§ Initializing audio...', '#ffffcc');
|
| 191 |
+
|
| 192 |
+
// Create audio context
|
| 193 |
+
audioContext = new (window.AudioContext || window.webkitAudioContext)();
|
| 194 |
+
|
| 195 |
+
// Resume if suspended
|
| 196 |
+
if (audioContext.state === 'suspended') {
|
| 197 |
+
await audioContext.resume();
|
| 198 |
}
|
| 199 |
|
| 200 |
+
// Check if Strudel is available
|
| 201 |
+
if (typeof strudel !== 'undefined') {
|
| 202 |
+
updateStatus('šµ Strudel audio ready! Generate code and play!', '#ccffcc');
|
| 203 |
+
strudelInitialized = true;
|
| 204 |
+
} else {
|
| 205 |
+
updateStatus('š® Demo mode ready (Strudel not loaded)', '#ffffcc');
|
| 206 |
+
strudelInitialized = false;
|
| 207 |
}
|
| 208 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 209 |
} catch (error) {
|
| 210 |
+
console.error('Audio init error:', error);
|
| 211 |
updateStatus('ā ļø Audio init failed - using demo mode', '#ffcccc');
|
| 212 |
+
strudelInitialized = false;
|
| 213 |
}
|
| 214 |
}
|
| 215 |
|
| 216 |
async function togglePlay() {
|
| 217 |
if (isPlaying) {
|
| 218 |
+
stopPlay();
|
| 219 |
} else {
|
| 220 |
await startPlay();
|
| 221 |
}
|
|
|
|
| 224 |
async function startPlay() {
|
| 225 |
const code = getCurrentCode();
|
| 226 |
|
| 227 |
+
if (!code) {
|
| 228 |
+
updateStatus('ā No code found! Generate some first.', '#ffcccc');
|
| 229 |
return;
|
| 230 |
}
|
| 231 |
|
| 232 |
try {
|
| 233 |
+
updateStatus('šµ Starting playback...', '#ffffcc');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 234 |
|
| 235 |
+
if (strudelInitialized && typeof strudel !== 'undefined') {
|
| 236 |
+
// Try real Strudel playback
|
| 237 |
+
console.log('Attempting Strudel playback');
|
| 238 |
+
|
| 239 |
+
// Clean code
|
| 240 |
+
const cleanCode = code
|
| 241 |
+
.split('\\n')
|
| 242 |
+
.filter(line => !line.trim().startsWith('//') && line.trim() !== '')
|
| 243 |
+
.join('\\n')
|
| 244 |
+
.trim();
|
| 245 |
+
|
| 246 |
+
console.log('Clean code:', cleanCode);
|
| 247 |
+
|
| 248 |
+
// Try to evaluate and play
|
| 249 |
+
try {
|
| 250 |
+
// Simple evaluation approach
|
| 251 |
+
const result = eval(cleanCode);
|
| 252 |
+
console.log('Eval result:', result);
|
| 253 |
+
|
| 254 |
+
if (result && typeof result.play === 'function') {
|
| 255 |
+
currentHap = result;
|
| 256 |
+
await result.play();
|
| 257 |
+
updateStatus('šµ Playing with Strudel! š¶', '#ccffcc');
|
| 258 |
+
} else {
|
| 259 |
+
throw new Error('Not a playable pattern');
|
| 260 |
+
}
|
| 261 |
+
} catch (evalError) {
|
| 262 |
+
console.error('Eval error:', evalError);
|
| 263 |
+
throw evalError;
|
| 264 |
+
}
|
| 265 |
|
|
|
|
|
|
|
|
|
|
| 266 |
} else {
|
| 267 |
+
// Demo mode
|
| 268 |
+
console.log('Using demo mode');
|
| 269 |
+
updateStatus('š® Demo: Playing your pattern...', '#ffffcc');
|
| 270 |
}
|
| 271 |
|
| 272 |
+
isPlaying = true;
|
| 273 |
+
updatePlayButton(true);
|
| 274 |
+
|
| 275 |
+
// Auto-stop after 30 seconds
|
| 276 |
+
setTimeout(() => {
|
| 277 |
+
if (isPlaying) {
|
| 278 |
+
stopPlay();
|
| 279 |
+
updateStatus('ā° Auto-stopped after 30s', 'white');
|
| 280 |
+
}
|
| 281 |
+
}, 30000);
|
| 282 |
+
|
| 283 |
} catch (error) {
|
| 284 |
console.error('Playback error:', error);
|
| 285 |
+
updateStatus('ā Playback error: ' + error.message, '#ffcccc');
|
| 286 |
+
|
| 287 |
+
// Fallback to demo mode
|
| 288 |
+
isPlaying = true;
|
| 289 |
+
updatePlayButton(true);
|
| 290 |
+
updateStatus('š® Demo mode: Simulating playback...', '#ffffcc');
|
| 291 |
|
|
|
|
| 292 |
setTimeout(() => {
|
| 293 |
+
stopPlay();
|
| 294 |
+
updateStatus('Demo finished!', 'white');
|
| 295 |
+
}, 8000);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 296 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 297 |
}
|
| 298 |
|
| 299 |
function stopPlay() {
|
| 300 |
+
if (currentHap && typeof currentHap.stop === 'function') {
|
| 301 |
+
try {
|
| 302 |
+
currentHap.stop();
|
| 303 |
+
console.log('Stopped Strudel pattern');
|
| 304 |
+
} catch (e) {
|
| 305 |
+
console.error('Error stopping:', e);
|
| 306 |
+
}
|
| 307 |
+
currentHap = null;
|
| 308 |
}
|
| 309 |
|
| 310 |
isPlaying = false;
|
|
|
|
| 312 |
updateStatus('ā¹ļø Stopped', 'white');
|
| 313 |
}
|
| 314 |
|
| 315 |
+
// Initialize on page load
|
| 316 |
document.addEventListener('DOMContentLoaded', function() {
|
| 317 |
+
updateStatus('Click "Enable Audio" to start!', 'white');
|
| 318 |
+
|
| 319 |
+
// Add hover effects
|
| 320 |
const buttons = document.querySelectorAll('#player button');
|
| 321 |
buttons.forEach(btn => {
|
| 322 |
btn.addEventListener('mouseenter', function() {
|
|
|
|
| 329 |
});
|
| 330 |
});
|
| 331 |
|
| 332 |
+
// Check if Strudel loaded
|
| 333 |
+
setTimeout(() => {
|
| 334 |
+
if (typeof strudel !== 'undefined') {
|
| 335 |
+
console.log('Strudel detected!', strudel);
|
| 336 |
+
updateStatus('šµ Strudel loaded! Click "Enable Audio" then play!', '#ccffcc');
|
| 337 |
+
} else {
|
| 338 |
+
console.log('Strudel not found, demo mode only');
|
| 339 |
+
updateStatus('š® Demo mode ready - click "Enable Audio"!', '#ffffcc');
|
| 340 |
+
}
|
| 341 |
+
}, 2000);
|
| 342 |
});
|
| 343 |
|
| 344 |
+
// Global error handler
|
| 345 |
+
window.addEventListener('error', function(e) {
|
| 346 |
+
console.error('Global error:', e.error);
|
|
|
|
|
|
|
| 347 |
});
|
| 348 |
</script>
|
| 349 |
""")
|