import type { CsoundStore } from './store'; export type EvalSource = 'selection' | 'block' | 'file'; export class ExecutionContext { private contentProvider: (() => string) | null = null; private initialized = false; constructor(private csound: CsoundStore) {} setContentProvider(provider: () => string): void { this.contentProvider = provider; } /** * Check if content is a complete CSD file */ private isCSDContent(code: string): boolean { return code.includes('') && code.includes(''); } /** * Execute entire file (composition mode: full restart) */ async executeFile(): Promise { const content = this.contentProvider?.() ?? ''; console.log('[ExecutionContext] Content from provider:', content.substring(0, 100)); console.log('[ExecutionContext] Content length:', content.length); if (!content.trim()) { console.log('[ExecutionContext] Content is empty, aborting'); return; } await this.csound.stop(); await this.csound.evaluate(content); this.initialized = true; } /** * Execute code block (live coding mode: incremental evaluation) * If the code is a complete CSD file, routes to executeFile() instead */ async executeBlock(code: string): Promise { if (!code.trim()) { return; } // If this is a complete CSD file, use composition mode if (this.isCSDContent(code)) { return await this.executeFile(); } if (!this.initialized) { await this.csound.restart(); this.initialized = true; } await this.evaluateCodeBlock(code); } /** * Execute selected text (live coding mode: incremental evaluation) */ async executeSelection(code: string): Promise { if (!code.trim()) { return; } if (!this.initialized) { await this.csound.restart(); this.initialized = true; } await this.evaluateCodeBlock(code); } /** * Smart detection and evaluation of code blocks */ private async evaluateCodeBlock(code: string): Promise { const trimmed = code.trim(); // Score event detection: starts with i, f, e, or a // Examples: "i 1 0 1", "f 1 0 8192 10 1", "e", "a 0 0 1" if (/^[ifea]\s+[\d\-]/.test(trimmed)) { await this.csound.sendScoreEvent(trimmed); return; } // Channel assignment detection: "varname = value" // Examples: "gkFreq = 440", "gkAmp = 0.5" const channelMatch = trimmed.match(/^(\w+)\s*=\s*([\d\.\-]+)/); if (channelMatch) { const [, channelName, valueStr] = channelMatch; await this.csound.setControlChannel(channelName, parseFloat(valueStr)); return; } // Default: orchestra code (instrument definitions, opcodes, etc.) // Use evalCode() to maintain macro context from initial CSD compilation await this.csound.evalCode(trimmed); } }