sligthly better
This commit is contained in:
@ -1,23 +1,40 @@
|
|||||||
import { Csound } from '@csound/browser';
|
import { Csound } from '@csound/browser';
|
||||||
|
|
||||||
|
export type InstanceMode = 'persistent' | 'ephemeral';
|
||||||
|
|
||||||
export interface CsoundEngineOptions {
|
export interface CsoundEngineOptions {
|
||||||
onMessage?: (message: string) => void;
|
onMessage?: (message: string) => void;
|
||||||
onError?: (error: string) => void;
|
onError?: (error: string) => void;
|
||||||
onPerformanceEnd?: () => void;
|
onPerformanceEnd?: () => void;
|
||||||
onAnalyserNodeCreated?: (node: AnalyserNode) => void;
|
onAnalyserNodeCreated?: (node: AnalyserNode) => void;
|
||||||
|
instanceMode?: InstanceMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PerformanceMetrics {
|
||||||
|
kperiodCount: number;
|
||||||
|
currentTime: number;
|
||||||
|
isRunning: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CompilationResult {
|
||||||
|
success: boolean;
|
||||||
|
errorMessage?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CsoundEngine {
|
export class CsoundEngine {
|
||||||
private csound: Csound | null = null;
|
private csound: Csound | null = null;
|
||||||
private initialized = false;
|
private initialized = false;
|
||||||
|
private compiled = false;
|
||||||
private running = false;
|
private running = false;
|
||||||
private options: CsoundEngineOptions;
|
private options: CsoundEngineOptions;
|
||||||
private scopeNode: AnalyserNode | null = null;
|
private scopeNode: AnalyserNode | null = null;
|
||||||
private audioNode: AudioNode | null = null;
|
private audioNode: AudioNode | null = null;
|
||||||
private audioContext: AudioContext | null = null;
|
private audioContext: AudioContext | null = null;
|
||||||
|
private instanceMode: InstanceMode;
|
||||||
|
|
||||||
constructor(options: CsoundEngineOptions = {}) {
|
constructor(options: CsoundEngineOptions = {}) {
|
||||||
this.options = options;
|
this.options = options;
|
||||||
|
this.instanceMode = options.instanceMode ?? 'ephemeral';
|
||||||
}
|
}
|
||||||
|
|
||||||
async init(): Promise<void> {
|
async init(): Promise<void> {
|
||||||
@ -27,7 +44,98 @@ export class CsoundEngine {
|
|||||||
this.log('Csound ready');
|
this.log('Csound ready');
|
||||||
}
|
}
|
||||||
|
|
||||||
async evaluateCode(code: string): Promise<void> {
|
private async ensureCsoundInstance(): Promise<void> {
|
||||||
|
if (!this.csound) {
|
||||||
|
this.log('Creating Csound instance...');
|
||||||
|
this.csound = await Csound();
|
||||||
|
this.setupCallbacks();
|
||||||
|
await this.csound.setOption('-odac');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private setupCallbacks(): void {
|
||||||
|
if (!this.csound) return;
|
||||||
|
|
||||||
|
this.csound.on('message', (message: string) => {
|
||||||
|
this.options.onMessage?.(message);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.csound.on('onAudioNodeCreated', (node: AudioNode) => {
|
||||||
|
this.audioNode = node;
|
||||||
|
this.audioContext = node.context as AudioContext;
|
||||||
|
this.log('Audio node created and captured');
|
||||||
|
});
|
||||||
|
|
||||||
|
this.csound.on('realtimePerformanceEnded', async () => {
|
||||||
|
this.running = false;
|
||||||
|
this.log('Performance complete');
|
||||||
|
this.options.onPerformanceEnd?.();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async compileOrchestra(orchestra: string): Promise<CompilationResult> {
|
||||||
|
if (!this.initialized) {
|
||||||
|
throw new Error('Csound not initialized. Call init() first.');
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.ensureCsoundInstance();
|
||||||
|
|
||||||
|
this.log('Compiling orchestra...');
|
||||||
|
const result = await this.csound!.compileOrc(orchestra);
|
||||||
|
|
||||||
|
if (result !== 0) {
|
||||||
|
const errorMsg = 'Orchestra compilation failed';
|
||||||
|
this.error(errorMsg);
|
||||||
|
return { success: false, errorMessage: errorMsg };
|
||||||
|
}
|
||||||
|
|
||||||
|
this.compiled = true;
|
||||||
|
this.log('Orchestra compiled successfully');
|
||||||
|
return { success: true };
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
const errorMsg = error instanceof Error ? error.message : 'Compilation failed';
|
||||||
|
this.error(errorMsg);
|
||||||
|
return { success: false, errorMessage: errorMsg };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async startPerformance(): Promise<void> {
|
||||||
|
if (!this.compiled) {
|
||||||
|
throw new Error('No orchestra compiled. Call compileOrchestra() first.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.running) {
|
||||||
|
this.log('Already running');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.log('Starting performance...');
|
||||||
|
await this.csound!.start();
|
||||||
|
this.running = true;
|
||||||
|
this.setupAnalyser();
|
||||||
|
this.log('Performance started');
|
||||||
|
}
|
||||||
|
|
||||||
|
async readScore(score: string): Promise<void> {
|
||||||
|
if (!this.csound) {
|
||||||
|
throw new Error('No Csound instance available');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.log('Reading score...');
|
||||||
|
await this.csound.readScore(score);
|
||||||
|
}
|
||||||
|
|
||||||
|
async sendScoreEvent(event: string): Promise<void> {
|
||||||
|
if (!this.running) {
|
||||||
|
throw new Error('Csound not running. Call startPerformance() first.');
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.csound!.readScore(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
async evaluateCode(code: string, forceNewInstance = false): Promise<void> {
|
||||||
if (!this.initialized) {
|
if (!this.initialized) {
|
||||||
throw new Error('Csound not initialized. Call init() first.');
|
throw new Error('Csound not initialized. Call init() first.');
|
||||||
}
|
}
|
||||||
@ -37,32 +145,21 @@ export class CsoundEngine {
|
|||||||
await this.stop();
|
await this.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const needsNewInstance = forceNewInstance ||
|
||||||
|
this.instanceMode === 'ephemeral' ||
|
||||||
|
!this.csound;
|
||||||
|
|
||||||
|
if (needsNewInstance && this.csound) {
|
||||||
|
this.log('Destroying existing instance...');
|
||||||
|
await this.cleanupInstance();
|
||||||
|
} else if (this.csound && this.compiled) {
|
||||||
|
this.log('Resetting Csound for new performance...');
|
||||||
|
await this.resetForNewPerformance();
|
||||||
|
}
|
||||||
|
|
||||||
this.scopeNode = null;
|
this.scopeNode = null;
|
||||||
|
|
||||||
this.log('Creating new Csound instance...');
|
await this.ensureCsoundInstance();
|
||||||
this.csound = await Csound();
|
|
||||||
|
|
||||||
this.csound.on('message', (message: string) => {
|
|
||||||
this.options.onMessage?.(message);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.csound.on('onAudioNodeCreated', (node: AudioNode) => {
|
|
||||||
this.audioNode = node;
|
|
||||||
this.audioContext = node.context as AudioContext;
|
|
||||||
this.log('Audio node created and captured');
|
|
||||||
});
|
|
||||||
|
|
||||||
this.csound.on('realtimePerformanceEnded', async () => {
|
|
||||||
try {
|
|
||||||
await this.csound?.cleanup();
|
|
||||||
} catch {}
|
|
||||||
this.running = false;
|
|
||||||
this.log('Performance complete');
|
|
||||||
this.options.onPerformanceEnd?.();
|
|
||||||
});
|
|
||||||
|
|
||||||
this.log('Setting audio output...');
|
|
||||||
await this.csound.setOption('-odac');
|
|
||||||
|
|
||||||
const orcMatch = code.match(/<CsInstruments>([\s\S]*?)<\/CsInstruments>/);
|
const orcMatch = code.match(/<CsInstruments>([\s\S]*?)<\/CsInstruments>/);
|
||||||
const scoMatch = code.match(/<CsScore>([\s\S]*?)<\/CsScore>/);
|
const scoMatch = code.match(/<CsScore>([\s\S]*?)<\/CsScore>/);
|
||||||
@ -74,42 +171,125 @@ export class CsoundEngine {
|
|||||||
const orc = orcMatch[1].trim();
|
const orc = orcMatch[1].trim();
|
||||||
const sco = scoMatch[1].trim();
|
const sco = scoMatch[1].trim();
|
||||||
|
|
||||||
this.log('Compiling orchestra...');
|
const compileResult = await this.compileOrchestra(orc);
|
||||||
const compileResult = await this.csound.compileOrc(orc);
|
if (!compileResult.success) {
|
||||||
|
throw new Error(compileResult.errorMessage || 'Compilation failed');
|
||||||
if (compileResult !== 0) {
|
|
||||||
throw new Error('Failed to compile orchestra');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.log('Reading score...');
|
await this.readScore(sco);
|
||||||
await this.csound.readScore(sco);
|
await this.startPerformance();
|
||||||
|
|
||||||
this.log('Starting performance...');
|
|
||||||
this.running = true;
|
|
||||||
await this.csound.start();
|
|
||||||
|
|
||||||
this.setupAnalyser();
|
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.running = false;
|
this.running = false;
|
||||||
if (this.csound) {
|
this.compiled = false;
|
||||||
try {
|
|
||||||
await this.csound.cleanup();
|
|
||||||
} catch {}
|
|
||||||
}
|
|
||||||
const errorMsg = error instanceof Error ? error.message : 'Evaluation failed';
|
const errorMsg = error instanceof Error ? error.message : 'Evaluation failed';
|
||||||
this.error(errorMsg);
|
this.error(errorMsg);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async setControlChannel(name: string, value: number): Promise<void> {
|
||||||
|
if (!this.csound) {
|
||||||
|
throw new Error('No Csound instance available');
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.csound.setControlChannel(name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getControlChannel(name: string): Promise<number> {
|
||||||
|
if (!this.csound) {
|
||||||
|
throw new Error('No Csound instance available');
|
||||||
|
}
|
||||||
|
|
||||||
|
return await this.csound.getControlChannel(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
async setStringChannel(name: string, value: string): Promise<void> {
|
||||||
|
if (!this.csound) {
|
||||||
|
throw new Error('No Csound instance available');
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.csound.setStringChannel(name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getStringChannel(name: string): Promise<string> {
|
||||||
|
if (!this.csound) {
|
||||||
|
throw new Error('No Csound instance available');
|
||||||
|
}
|
||||||
|
|
||||||
|
return await this.csound.getStringChannel(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getTable(tableNumber: number): Promise<Float32Array> {
|
||||||
|
if (!this.csound) {
|
||||||
|
throw new Error('No Csound instance available');
|
||||||
|
}
|
||||||
|
|
||||||
|
return await this.csound.getTable(tableNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
async setTable(tableNumber: number, data: Float32Array | number[]): Promise<void> {
|
||||||
|
if (!this.csound) {
|
||||||
|
throw new Error('No Csound instance available');
|
||||||
|
}
|
||||||
|
|
||||||
|
const tableData = data instanceof Float32Array ? data : new Float32Array(data);
|
||||||
|
await this.csound.setTable(tableNumber, tableData);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getPerformanceMetrics(): Promise<PerformanceMetrics> {
|
||||||
|
if (!this.csound) {
|
||||||
|
return {
|
||||||
|
kperiodCount: 0,
|
||||||
|
currentTime: 0,
|
||||||
|
isRunning: this.running
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const currentTime = await this.csound.getCurrentTimeSamples();
|
||||||
|
|
||||||
|
return {
|
||||||
|
kperiodCount: 0,
|
||||||
|
currentTime: currentTime / (await this.csound.getSr()),
|
||||||
|
isRunning: this.running
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
return {
|
||||||
|
kperiodCount: 0,
|
||||||
|
currentTime: 0,
|
||||||
|
isRunning: this.running
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getSampleRate(): Promise<number> {
|
||||||
|
if (!this.csound) {
|
||||||
|
throw new Error('No Csound instance available');
|
||||||
|
}
|
||||||
|
return await this.csound.getSr();
|
||||||
|
}
|
||||||
|
|
||||||
|
async getKsmps(): Promise<number> {
|
||||||
|
if (!this.csound) {
|
||||||
|
throw new Error('No Csound instance available');
|
||||||
|
}
|
||||||
|
return await this.csound.getKsmps();
|
||||||
|
}
|
||||||
|
|
||||||
|
async getOutputChannelCount(): Promise<number> {
|
||||||
|
if (!this.csound) {
|
||||||
|
throw new Error('No Csound instance available');
|
||||||
|
}
|
||||||
|
return await this.csound.getNchnls();
|
||||||
|
}
|
||||||
|
|
||||||
async stop(): Promise<void> {
|
async stop(): Promise<void> {
|
||||||
if (!this.csound || !this.running) return;
|
if (!this.csound || !this.running) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.log('Stopping...');
|
this.log('Stopping performance...');
|
||||||
await this.csound.stop();
|
await this.csound.stop();
|
||||||
await this.csound.cleanup();
|
|
||||||
this.running = false;
|
this.running = false;
|
||||||
this.log('Stopped');
|
this.log('Stopped');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -118,6 +298,49 @@ export class CsoundEngine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async reset(): Promise<void> {
|
||||||
|
this.log('Resetting Csound...');
|
||||||
|
await this.stop();
|
||||||
|
await this.cleanupInstance();
|
||||||
|
this.compiled = false;
|
||||||
|
await this.ensureCsoundInstance();
|
||||||
|
this.log('Reset complete');
|
||||||
|
}
|
||||||
|
|
||||||
|
private async resetForNewPerformance(): Promise<void> {
|
||||||
|
if (!this.csound) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.csound.reset();
|
||||||
|
await this.csound.setOption('-odac');
|
||||||
|
this.compiled = false;
|
||||||
|
this.running = false;
|
||||||
|
this.audioNode = null;
|
||||||
|
this.audioContext = null;
|
||||||
|
this.scopeNode = null;
|
||||||
|
} catch (error) {
|
||||||
|
this.log('Reset failed, creating new instance...');
|
||||||
|
await this.cleanupInstance();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async cleanupInstance(): Promise<void> {
|
||||||
|
if (!this.csound) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (this.running) {
|
||||||
|
await this.csound.stop();
|
||||||
|
}
|
||||||
|
await this.csound.cleanup();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Cleanup error:', error);
|
||||||
|
} finally {
|
||||||
|
this.csound = null;
|
||||||
|
this.running = false;
|
||||||
|
this.compiled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
isRunning(): boolean {
|
isRunning(): boolean {
|
||||||
return this.running;
|
return this.running;
|
||||||
}
|
}
|
||||||
@ -126,6 +349,10 @@ export class CsoundEngine {
|
|||||||
return this.initialized;
|
return this.initialized;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isCompiled(): boolean {
|
||||||
|
return this.compiled;
|
||||||
|
}
|
||||||
|
|
||||||
getAudioContext(): AudioContext | null {
|
getAudioContext(): AudioContext | null {
|
||||||
return this.audioContext;
|
return this.audioContext;
|
||||||
}
|
}
|
||||||
@ -166,10 +393,7 @@ export class CsoundEngine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async destroy(): Promise<void> {
|
async destroy(): Promise<void> {
|
||||||
if (this.running) {
|
await this.cleanupInstance();
|
||||||
await this.stop();
|
|
||||||
}
|
|
||||||
this.csound = null;
|
|
||||||
this.initialized = false;
|
this.initialized = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,9 @@
|
|||||||
export { CsoundEngine } from './engine';
|
export { CsoundEngine } from './engine';
|
||||||
export type { CsoundEngineOptions } from './engine';
|
export type {
|
||||||
|
CsoundEngineOptions,
|
||||||
|
InstanceMode,
|
||||||
|
PerformanceMetrics,
|
||||||
|
CompilationResult
|
||||||
|
} from './engine';
|
||||||
export { createCsoundStore, createCsoundDerivedStores } from './store';
|
export { createCsoundStore, createCsoundDerivedStores } from './store';
|
||||||
export type { LogEntry, CsoundStore } from './store';
|
export type { LogEntry, CsoundStore } from './store';
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { writable, derived, get } from 'svelte/store';
|
import { writable, derived, get } from 'svelte/store';
|
||||||
import { CsoundEngine } from './engine';
|
import { CsoundEngine, type InstanceMode, type PerformanceMetrics, type CompilationResult } from './engine';
|
||||||
|
|
||||||
export interface LogEntry {
|
export interface LogEntry {
|
||||||
timestamp: Date;
|
timestamp: Date;
|
||||||
@ -9,6 +9,7 @@ export interface LogEntry {
|
|||||||
|
|
||||||
interface CsoundState {
|
interface CsoundState {
|
||||||
initialized: boolean;
|
initialized: boolean;
|
||||||
|
compiled: boolean;
|
||||||
running: boolean;
|
running: boolean;
|
||||||
logs: LogEntry[];
|
logs: LogEntry[];
|
||||||
}
|
}
|
||||||
@ -16,8 +17,23 @@ interface CsoundState {
|
|||||||
export interface CsoundStore {
|
export interface CsoundStore {
|
||||||
subscribe: (run: (value: CsoundState) => void) => () => void;
|
subscribe: (run: (value: CsoundState) => void) => () => void;
|
||||||
init: () => Promise<void>;
|
init: () => Promise<void>;
|
||||||
evaluate: (code: string) => Promise<void>;
|
compileOrchestra: (orchestra: string) => Promise<CompilationResult>;
|
||||||
|
startPerformance: () => Promise<void>;
|
||||||
|
readScore: (score: string) => Promise<void>;
|
||||||
|
sendScoreEvent: (event: string) => Promise<void>;
|
||||||
|
evaluate: (code: string, forceNewInstance?: boolean) => Promise<void>;
|
||||||
stop: () => Promise<void>;
|
stop: () => Promise<void>;
|
||||||
|
reset: () => Promise<void>;
|
||||||
|
setControlChannel: (name: string, value: number) => Promise<void>;
|
||||||
|
getControlChannel: (name: string) => Promise<number>;
|
||||||
|
setStringChannel: (name: string, value: string) => Promise<void>;
|
||||||
|
getStringChannel: (name: string) => Promise<string>;
|
||||||
|
getTable: (tableNumber: number) => Promise<Float32Array>;
|
||||||
|
setTable: (tableNumber: number, data: Float32Array | number[]) => Promise<void>;
|
||||||
|
getPerformanceMetrics: () => Promise<PerformanceMetrics>;
|
||||||
|
getSampleRate: () => Promise<number>;
|
||||||
|
getKsmps: () => Promise<number>;
|
||||||
|
getOutputChannelCount: () => Promise<number>;
|
||||||
clearLogs: () => void;
|
clearLogs: () => void;
|
||||||
getAudioContext: () => AudioContext | null;
|
getAudioContext: () => AudioContext | null;
|
||||||
getAnalyserNode: () => AnalyserNode | null;
|
getAnalyserNode: () => AnalyserNode | null;
|
||||||
@ -25,9 +41,10 @@ export interface CsoundStore {
|
|||||||
destroy: () => Promise<void>;
|
destroy: () => Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createCsoundStore(): CsoundStore {
|
export function createCsoundStore(instanceMode?: InstanceMode): CsoundStore {
|
||||||
const initialState: CsoundState = {
|
const initialState: CsoundState = {
|
||||||
initialized: false,
|
initialized: false,
|
||||||
|
compiled: false,
|
||||||
running: false,
|
running: false,
|
||||||
logs: []
|
logs: []
|
||||||
};
|
};
|
||||||
@ -54,9 +71,13 @@ export function createCsoundStore(): CsoundStore {
|
|||||||
engine = new CsoundEngine({
|
engine = new CsoundEngine({
|
||||||
onMessage: (msg) => addLog(msg, 'info'),
|
onMessage: (msg) => addLog(msg, 'info'),
|
||||||
onError: (err) => addLog(err, 'error'),
|
onError: (err) => addLog(err, 'error'),
|
||||||
|
onPerformanceEnd: () => {
|
||||||
|
update(state => ({ ...state, running: false }));
|
||||||
|
},
|
||||||
onAnalyserNodeCreated: (node) => {
|
onAnalyserNodeCreated: (node) => {
|
||||||
analyserNodeListeners.forEach(listener => listener(node));
|
analyserNodeListeners.forEach(listener => listener(node));
|
||||||
}
|
},
|
||||||
|
instanceMode
|
||||||
});
|
});
|
||||||
|
|
||||||
await engine.init();
|
await engine.init();
|
||||||
@ -72,20 +93,70 @@ export function createCsoundStore(): CsoundStore {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
async evaluate(code: string) {
|
async compileOrchestra(orchestra: string): Promise<CompilationResult> {
|
||||||
if (!engine) {
|
if (!engine) {
|
||||||
throw new Error('CSound engine not initialized');
|
throw new Error('Csound engine not initialized');
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
const result = await engine.compileOrchestra(orchestra);
|
||||||
|
update(state => ({ ...state, compiled: result.success }));
|
||||||
|
return result;
|
||||||
|
} catch (error) {
|
||||||
|
const errorMsg = error instanceof Error ? error.message : 'Compilation failed';
|
||||||
|
addLog(errorMsg, 'error');
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async startPerformance() {
|
||||||
|
if (!engine) {
|
||||||
|
throw new Error('Csound engine not initialized');
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await engine.startPerformance();
|
||||||
update(state => ({ ...state, running: true }));
|
update(state => ({ ...state, running: true }));
|
||||||
await engine.evaluateCode(code);
|
} catch (error) {
|
||||||
|
const errorMsg = error instanceof Error ? error.message : 'Failed to start performance';
|
||||||
|
addLog(errorMsg, 'error');
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async readScore(score: string) {
|
||||||
|
if (!engine) {
|
||||||
|
throw new Error('Csound engine not initialized');
|
||||||
|
}
|
||||||
|
|
||||||
|
await engine.readScore(score);
|
||||||
|
},
|
||||||
|
|
||||||
|
async sendScoreEvent(event: string) {
|
||||||
|
if (!engine) {
|
||||||
|
throw new Error('Csound engine not initialized');
|
||||||
|
}
|
||||||
|
|
||||||
|
await engine.sendScoreEvent(event);
|
||||||
|
},
|
||||||
|
|
||||||
|
async evaluate(code: string, forceNewInstance = false) {
|
||||||
|
if (!engine) {
|
||||||
|
throw new Error('Csound engine not initialized');
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await engine.evaluateCode(code, forceNewInstance);
|
||||||
|
update(state => ({
|
||||||
|
...state,
|
||||||
|
compiled: engine!.isCompiled(),
|
||||||
|
running: engine!.isRunning()
|
||||||
|
}));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const errorMsg = error instanceof Error ? error.message : 'Evaluation failed';
|
const errorMsg = error instanceof Error ? error.message : 'Evaluation failed';
|
||||||
addLog(errorMsg, 'error');
|
addLog(errorMsg, 'error');
|
||||||
|
update(state => ({ ...state, compiled: false, running: false }));
|
||||||
throw error;
|
throw error;
|
||||||
} finally {
|
|
||||||
update(state => ({ ...state, running: false }));
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -101,6 +172,88 @@ export function createCsoundStore(): CsoundStore {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async reset() {
|
||||||
|
if (!engine) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await engine.reset();
|
||||||
|
update(state => ({ ...state, compiled: false, running: false }));
|
||||||
|
} catch (error) {
|
||||||
|
const errorMsg = error instanceof Error ? error.message : 'Reset failed';
|
||||||
|
addLog(errorMsg, 'error');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async setControlChannel(name: string, value: number) {
|
||||||
|
if (!engine) {
|
||||||
|
throw new Error('Csound engine not initialized');
|
||||||
|
}
|
||||||
|
await engine.setControlChannel(name, value);
|
||||||
|
},
|
||||||
|
|
||||||
|
async getControlChannel(name: string): Promise<number> {
|
||||||
|
if (!engine) {
|
||||||
|
throw new Error('Csound engine not initialized');
|
||||||
|
}
|
||||||
|
return await engine.getControlChannel(name);
|
||||||
|
},
|
||||||
|
|
||||||
|
async setStringChannel(name: string, value: string) {
|
||||||
|
if (!engine) {
|
||||||
|
throw new Error('Csound engine not initialized');
|
||||||
|
}
|
||||||
|
await engine.setStringChannel(name, value);
|
||||||
|
},
|
||||||
|
|
||||||
|
async getStringChannel(name: string): Promise<string> {
|
||||||
|
if (!engine) {
|
||||||
|
throw new Error('Csound engine not initialized');
|
||||||
|
}
|
||||||
|
return await engine.getStringChannel(name);
|
||||||
|
},
|
||||||
|
|
||||||
|
async getTable(tableNumber: number): Promise<Float32Array> {
|
||||||
|
if (!engine) {
|
||||||
|
throw new Error('Csound engine not initialized');
|
||||||
|
}
|
||||||
|
return await engine.getTable(tableNumber);
|
||||||
|
},
|
||||||
|
|
||||||
|
async setTable(tableNumber: number, data: Float32Array | number[]) {
|
||||||
|
if (!engine) {
|
||||||
|
throw new Error('Csound engine not initialized');
|
||||||
|
}
|
||||||
|
await engine.setTable(tableNumber, data);
|
||||||
|
},
|
||||||
|
|
||||||
|
async getPerformanceMetrics(): Promise<PerformanceMetrics> {
|
||||||
|
if (!engine) {
|
||||||
|
throw new Error('Csound engine not initialized');
|
||||||
|
}
|
||||||
|
return await engine.getPerformanceMetrics();
|
||||||
|
},
|
||||||
|
|
||||||
|
async getSampleRate(): Promise<number> {
|
||||||
|
if (!engine) {
|
||||||
|
throw new Error('Csound engine not initialized');
|
||||||
|
}
|
||||||
|
return await engine.getSampleRate();
|
||||||
|
},
|
||||||
|
|
||||||
|
async getKsmps(): Promise<number> {
|
||||||
|
if (!engine) {
|
||||||
|
throw new Error('Csound engine not initialized');
|
||||||
|
}
|
||||||
|
return await engine.getKsmps();
|
||||||
|
},
|
||||||
|
|
||||||
|
async getOutputChannelCount(): Promise<number> {
|
||||||
|
if (!engine) {
|
||||||
|
throw new Error('Csound engine not initialized');
|
||||||
|
}
|
||||||
|
return await engine.getOutputChannelCount();
|
||||||
|
},
|
||||||
|
|
||||||
clearLogs() {
|
clearLogs() {
|
||||||
update(state => ({
|
update(state => ({
|
||||||
...state,
|
...state,
|
||||||
@ -142,6 +295,7 @@ export function createCsoundDerivedStores(csound: CsoundStore) {
|
|||||||
return {
|
return {
|
||||||
logs: derived(csound, $csound => $csound.logs),
|
logs: derived(csound, $csound => $csound.logs),
|
||||||
initialized: derived(csound, $csound => $csound.initialized),
|
initialized: derived(csound, $csound => $csound.initialized),
|
||||||
|
compiled: derived(csound, $csound => $csound.compiled),
|
||||||
running: derived(csound, $csound => $csound.running)
|
running: derived(csound, $csound => $csound.running)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user