Files
oldboy/src/lib/csound/execution-context.ts
2025-11-14 01:53:18 +01:00

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);
}
}