106 lines
2.8 KiB
TypeScript
106 lines
2.8 KiB
TypeScript
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('<CsoundSynthesizer>') &&
|
|
code.includes('<CsInstruments>');
|
|
}
|
|
|
|
/**
|
|
* Execute entire file (composition mode: full restart)
|
|
*/
|
|
async executeFile(): Promise<void> {
|
|
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<void> {
|
|
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<void> {
|
|
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<void> {
|
|
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);
|
|
}
|
|
}
|