From cec7ed8bd401f7f11883d1ab49c292a10f2305f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Forment?= Date: Tue, 14 Oct 2025 22:30:05 +0200 Subject: [PATCH] Beginning of File System integration --- csound_browser_readme.md | 917 ++++++++++++++++++++++++++++++++++ src/App.svelte | 217 +++++++- src/lib/ConfirmDialog.svelte | 89 ++++ src/lib/Editor.svelte | 15 + src/lib/EditorWithLogs.svelte | 8 - src/lib/FileBrowser.svelte | 347 +++++++++++++ src/lib/InputDialog.svelte | 121 +++++ src/lib/LogPanel.svelte | 120 ++++- src/lib/Modal.svelte | 78 +++ src/lib/csound/engine.ts | 125 +++++ src/lib/csound/index.ts | 4 + src/lib/csound/store.ts | 109 ++++ 12 files changed, 2119 insertions(+), 31 deletions(-) create mode 100644 csound_browser_readme.md create mode 100644 src/lib/ConfirmDialog.svelte create mode 100644 src/lib/FileBrowser.svelte create mode 100644 src/lib/InputDialog.svelte create mode 100644 src/lib/Modal.svelte create mode 100644 src/lib/csound/engine.ts create mode 100644 src/lib/csound/index.ts create mode 100644 src/lib/csound/store.ts diff --git a/csound_browser_readme.md b/csound_browser_readme.md new file mode 100644 index 0000000..d98cd3b --- /dev/null +++ b/csound_browser_readme.md @@ -0,0 +1,917 @@ +# @csound/browser +[![npm (scoped with tag)](https://shields.shivering-isles.com/npm/v/@csound/browser/latest)](https://www.npmjs.com/package/@csound/browser) +[![GitHub Workflow Status](https://shields.shivering-isles.com/github/workflow/status/csound/csound/csound_wasm)](https://github.com/csound/csound/actions?query=workflow%3Acsound_wasm) +[![styled with prettier](https://img.shields.io/badge/styled_with-prettier-ff69b4.svg)](https://github.com/prettier/prettier) + + +## Api Documentation + +## Objects + +
+
CsoundObj : object
+

CsoundObj API.

+
+
+ +## Functions + +
+
Csound([params])Promise.<(CsoundObj|undefined)>
+

The default entry for @csound/wasm/browser module. +If loaded successfully, it returns CsoundObj, +otherwise undefined.

+
+
getTable(tableNum)Promise.<(Float64Array|undefined)>
+
+
+ +## Typedefs + +
+
CSOUND_PARAMS
+
+
CS_MIDIDEVICE
+
+
+ + + +## CsoundObj : object +CsoundObj API. + +**Kind**: global namespace + +* [CsoundObj](#CsoundObj) : object + * _global_ + * [getTable(tableNum)](#getTable) ⇒ Promise.<(Float64Array\|undefined)> + * _static_ + * [.fs](#CsoundObj.fs) : IFs:memfs + * [.eventNames()](#CsoundObj.eventNames) ⇒ Array.<string> + * [.listenerCount()](#CsoundObj.listenerCount) ⇒ number + * [.listeners(eventName)](#CsoundObj.listeners) ⇒ Array.<function()> + * [.off(eventName, listener)](#CsoundObj.off) ⇒ external:EventEmitter + * [.on(eventName, listener)](#CsoundObj.on) ⇒ external:EventEmitter + * [.addListener(eventName, listener)](#CsoundObj.addListener) ⇒ external:EventEmitter + * [.once(eventName, listener)](#CsoundObj.once) ⇒ external:EventEmitter + * [.removeAllListeners(eventName)](#CsoundObj.removeAllListeners) ⇒ external:EventEmitter + * [.removeListener(eventName, listener)](#CsoundObj.removeListener) ⇒ external:EventEmitter + * [.getSr()](#CsoundObj.getSr) ⇒ Promise.<number> + * [.getKr()](#CsoundObj.getKr) ⇒ Promise.<number> + * [.getKsmps()](#CsoundObj.getKsmps) ⇒ Promise.<number> + * [.getNchnls()](#CsoundObj.getNchnls) ⇒ Promise.<number> + * [.getNchnlsInput()](#CsoundObj.getNchnlsInput) ⇒ Promise.<number> + * [.get0dBFS()](#CsoundObj.get0dBFS) ⇒ Promise.<number> + * [.getA4()](#CsoundObj.getA4) ⇒ Promise.<number> + * [.getCurrentTimeSamples()](#CsoundObj.getCurrentTimeSamples) ⇒ Promise.<number> + * [.getSizeOfMYFLT()](#CsoundObj.getSizeOfMYFLT) ⇒ Promise.<number> + * [.setOption(option)](#CsoundObj.setOption) ⇒ Promise.<number> + * [.setParams(csoundParams)](#CsoundObj.setParams) ⇒ Promise.<undefined> + * [.getParams()](#CsoundObj.getParams) ⇒ [Promise.<CSOUND\_PARAMS>](#CSOUND_PARAMS) + * [.getDebug()](#CsoundObj.getDebug) ⇒ Promise.<number> + * [.setDebug(debug)](#CsoundObj.setDebug) ⇒ Promise.<undefined> + * [.inputMessage(scoreEvent)](#CsoundObj.inputMessage) ⇒ Promise.<number> + * [.inputMessageAsync(scoreEvent)](#CsoundObj.inputMessageAsync) ⇒ Promise.<number> + * [.getControlChannel(channelName)](#CsoundObj.getControlChannel) ⇒ Promise.<undefined> + * [.setControlChannel(channelName, value)](#CsoundObj.setControlChannel) ⇒ Promise.<undefined> + * [.getStringChannel(channelName)](#CsoundObj.getStringChannel) ⇒ Promise.<undefined> + * [.setStringChannel(channelName, value)](#CsoundObj.setStringChannel) ⇒ Promise.<undefined> + * [.getOutputName()](#CsoundObj.getOutputName) ⇒ Promise.<string> + * [.getInputName()](#CsoundObj.getInputName) ⇒ Promise.<string> + * [.destroy()](#CsoundObj.destroy) ⇒ Promise.<undefined> + * [.getAPIVersion()](#CsoundObj.getAPIVersion) ⇒ Promise.<number> + * [.getVersion()](#CsoundObj.getVersion) ⇒ Promise.<number> + * [.initialize()](#CsoundObj.initialize) ⇒ Promise.<number> + * [.parseOrc(orc)](#CsoundObj.parseOrc) ⇒ Promise.<object> + * [.compileTree(tree)](#CsoundObj.compileTree) ⇒ Promise.<number> + * [.compileOrc(orc)](#CsoundObj.compileOrc) ⇒ Promise.<number> + * [.evalCode(orc)](#CsoundObj.evalCode) ⇒ Promise.<number> + * [.start()](#CsoundObj.start) ⇒ Promise.<number> + * [.compileCsd(path)](#CsoundObj.compileCsd) ⇒ Promise.<number> + * [.compileCsdText(orc)](#CsoundObj.compileCsdText) ⇒ Promise.<number> + * [.perform()](#CsoundObj.perform) ⇒ Promise.<number> + * [.performKsmps()](#CsoundObj.performKsmps) ⇒ Promise.<number> + * [.performBuffer()](#CsoundObj.performBuffer) ⇒ Promise.<number> + * [.stop()](#CsoundObj.stop) ⇒ Promise.<undefined> + * [.cleanup()](#CsoundObj.cleanup) ⇒ Promise.<number> + * [.reset()](#CsoundObj.reset) ⇒ Promise.<number> + * [.getInputBufferSize()](#CsoundObj.getInputBufferSize) ⇒ Promise.<number> + * [.getOutputBufferSize()](#CsoundObj.getOutputBufferSize) ⇒ Promise.<number> + * [.getInputBuffer()](#CsoundObj.getInputBuffer) ⇒ Promise.<number> + * [.getOutputBuffer()](#CsoundObj.getOutputBuffer) ⇒ Promise.<number> + * [.getSpin()](#CsoundObj.getSpin) ⇒ Promise.<number> + * [.getSpout()](#CsoundObj.getSpout) ⇒ Promise.<number> + * [.getMIDIDevList(isOutput)](#CsoundObj.getMIDIDevList) ⇒ [Promise.<CS\_MIDIDEVICE>](#CS_MIDIDEVICE) + * [.getRtMidiName()](#CsoundObj.getRtMidiName) ⇒ Promise.<string> + * [.midiMessage(midi, midi, midi)](#CsoundObj.midiMessage) ⇒ Promise.<void> + * [.isScorePending()](#CsoundObj.isScorePending) ⇒ Promise.<number> + * [.setScorePending(pending)](#CsoundObj.setScorePending) ⇒ Promise.<undefined> + * [.readScore(score)](#CsoundObj.readScore) ⇒ Promise.<undefined> + * [.getScoreTime()](#CsoundObj.getScoreTime) ⇒ Promise.<number> + * [.getScoreOffsetSeconds()](#CsoundObj.getScoreOffsetSeconds) ⇒ Promise.<number> + * [.setScoreOffsetSeconds(time)](#CsoundObj.setScoreOffsetSeconds) ⇒ Promise.<number> + * [.rewindScore()](#CsoundObj.rewindScore) ⇒ Promise.<number> + * [.tableLength(tableNum)](#CsoundObj.tableLength) ⇒ Promise.<number> + * [.tableGet(tableNum, tableIndex)](#CsoundObj.tableGet) ⇒ Promise.<number> + * [.tableSet(tableNum, tableIndex, value)](#CsoundObj.tableSet) ⇒ Promise.<undefined> + * [.tableCopyIn(tableNum, tableIndex, array)](#CsoundObj.tableCopyIn) ⇒ Promise.<undefined> + * [.tableCopyOut(tableNum)](#CsoundObj.tableCopyOut) ⇒ Promise.<(Float64Array\|undefined)> + * [.getTableArgs(tableNum)](#CsoundObj.getTableArgs) ⇒ Promise.<(Float64Array\|undefined)> + * [.isNamedGEN(tableNum)](#CsoundObj.isNamedGEN) ⇒ Promise.<number> + * [.getNamedGEN(tableNum)](#CsoundObj.getNamedGEN) ⇒ Promise.<(string\|undefined)> + + + +### CsoundObjgetTable(tableNum) ⇒ Promise.<(Float64Array\|undefined)> +**Kind**: global method of [CsoundObj](#CsoundObj) + +| Param | Type | +| --- | --- | +| tableNum | string | + + + +### CsoundObj.fs : IFs:memfs +The in-browser filesystem based on nodejs's +built-in module "fs" + +**Kind**: static property of [CsoundObj](#CsoundObj) + + +### CsoundObj.eventNames() ⇒ Array.<string> +Returns an array listing the events for which the emitter has registered listeners. +The values in the array are strings. + +**Kind**: static method of [CsoundObj](#CsoundObj) + + +### CsoundObj.listenerCount() ⇒ number +Returns the number of listeners listening to the event named eventName. + +**Kind**: static method of [CsoundObj](#CsoundObj) + + +### CsoundObj.listeners(eventName) ⇒ Array.<function()> +Returns a copy of the array of listeners for the event named eventName. + +**Kind**: static method of [CsoundObj](#CsoundObj) + +| Param | Type | +| --- | --- | +| eventName | [PublicEvents](#PublicEvents) | + + + +### CsoundObj.off(eventName, listener) ⇒ external:EventEmitter +Alias for removeListener() + +**Kind**: static method of [CsoundObj](#CsoundObj) + +| Param | Type | +| --- | --- | +| eventName | [PublicEvents](#PublicEvents) | +| listener | function | + + + +### CsoundObj.on(eventName, listener) ⇒ external:EventEmitter +Adds the listener function to the end of the listeners array for the event named eventName. +No checks are made to see if the listener has already been added. +Multiple calls passing the same combination of eventName and listener +will result in the listener being added, and called, multiple times. + +**Kind**: static method of [CsoundObj](#CsoundObj) + +| Param | Type | +| --- | --- | +| eventName | [PublicEvents](#PublicEvents) | +| listener | function | + + + +### CsoundObj.addListener(eventName, listener) ⇒ external:EventEmitter +Alias for "on" + +**Kind**: static method of [CsoundObj](#CsoundObj) + +| Param | Type | +| --- | --- | +| eventName | [PublicEvents](#PublicEvents) | +| listener | function | + + + +### CsoundObj.once(eventName, listener) ⇒ external:EventEmitter +Adds a one-time listener function for the event named eventName. +The next time eventName is triggered, this listener is removed and then invoked. + +**Kind**: static method of [CsoundObj](#CsoundObj) + +| Param | Type | +| --- | --- | +| eventName | [PublicEvents](#PublicEvents) | +| listener | function | + + + +### CsoundObj.removeAllListeners(eventName) ⇒ external:EventEmitter +Removes all listeners, or those of the specified eventName. +It is bad practice to remove listeners added elsewhere in the code, +particularly when the EventEmitter instance was created by some other +component or module. +Returns a reference to the EventEmitter, so that calls can be chained. + +**Kind**: static method of [CsoundObj](#CsoundObj) + +| Param | Type | +| --- | --- | +| eventName | [PublicEvents](#PublicEvents) | + + + +### CsoundObj.removeListener(eventName, listener) ⇒ external:EventEmitter +Removes the specified listener from the listener array for the event named eventName. +removeListener() will remove, at most, one instance of a listener from the listener array. +If any single listener has been added multiple times to the listener array for the specified eventName, +then removeListener() must be called multiple times to remove each instance. +Removes the specified listener from the listener array for the event named eventName. + +**Kind**: static method of [CsoundObj](#CsoundObj) + +| Param | Type | +| --- | --- | +| eventName | [PublicEvents](#PublicEvents) | +| listener | function | + + + +### CsoundObj.getSr() ⇒ Promise.<number> +Returns the sample rate from Csound instance + +**Kind**: static method of [CsoundObj](#CsoundObj) + + +### CsoundObj.getKr() ⇒ Promise.<number> +Returns the control rate from Csound instance + +**Kind**: static method of [CsoundObj](#CsoundObj) + + +### CsoundObj.getKsmps() ⇒ Promise.<number> +Returns the ksmps value (kr/sr) from Csound instance + +**Kind**: static method of [CsoundObj](#CsoundObj) + + +### CsoundObj.getNchnls() ⇒ Promise.<number> +Returns the number of output channels from Csound instance + +**Kind**: static method of [CsoundObj](#CsoundObj) + + +### CsoundObj.getNchnlsInput() ⇒ Promise.<number> +Returns the number of input channels from Csound instance + +**Kind**: static method of [CsoundObj](#CsoundObj) + + +### CsoundObj.get0dBFS() ⇒ Promise.<number> +Returns the value of csoundGet0dBFS + +**Kind**: static method of [CsoundObj](#CsoundObj) + + +### CsoundObj.getA4() ⇒ Promise.<number> +Returns the A4 frequency reference + +**Kind**: static method of [CsoundObj](#CsoundObj) + + +### CsoundObj.getCurrentTimeSamples() ⇒ Promise.<number> +Return the current performance time in samples + +**Kind**: static method of [CsoundObj](#CsoundObj) + + +### CsoundObj.getSizeOfMYFLT() ⇒ Promise.<number> +Return the size of MYFLT in number of bytes + +**Kind**: static method of [CsoundObj](#CsoundObj) + + +### CsoundObj.setOption(option) ⇒ Promise.<number> +Set a single csound option (flag), +no spaces are allowed in the string. + +**Kind**: static method of [CsoundObj](#CsoundObj) + +| Param | Type | +| --- | --- | +| option | string | + + + +### CsoundObj.setParams(csoundParams) ⇒ Promise.<undefined> +Configure Csound with a given set of +parameters defined in the CSOUND_PARAMS structure. +These parameters are the part of the OPARMS struct +that are configurable through command line flags. +The CSOUND_PARAMS structure can be obtained using +csoundGetParams(). +These options should only be changed before +performance has started. + +**Kind**: static method of [CsoundObj](#CsoundObj) + +| Param | Type | Description | +| --- | --- | --- | +| csoundParams | [CSOUND\_PARAMS](#CSOUND_PARAMS) | csoundParams object | + + + +### CsoundObj.getParams() ⇒ [Promise.<CSOUND\_PARAMS>](#CSOUND_PARAMS) +Get the current set of parameters +from a Csound instance +in a CSOUND_PARAMS structure. + +**Kind**: static method of [CsoundObj](#CsoundObj) +**Returns**: [Promise.<CSOUND\_PARAMS>](#CSOUND_PARAMS) - - CSOUND_PARAMS object + + +### CsoundObj.getDebug() ⇒ Promise.<number> +Returns whether Csound is set to print debug messages +sent through the DebugMsg() internal API function. +Anything different to 0 means true. + +**Kind**: static method of [CsoundObj](#CsoundObj) + + +### CsoundObj.setDebug(debug) ⇒ Promise.<undefined> +Return the size of MYFLT in number of bytes + +**Kind**: static method of [CsoundObj](#CsoundObj) + +| Param | Type | +| --- | --- | +| debug | number | + + + +### CsoundObj.inputMessage(scoreEvent) ⇒ Promise.<number> +Inputs an immediate score event +without any pre-process parsing + +**Kind**: static method of [CsoundObj](#CsoundObj) + +| Param | Type | +| --- | --- | +| scoreEvent | string | + + + +### CsoundObj.inputMessageAsync(scoreEvent) ⇒ Promise.<number> +Inputs an immediate score event +without any pre-process parsing + +**Kind**: static method of [CsoundObj](#CsoundObj) + +| Param | Type | +| --- | --- | +| scoreEvent | string | + + + +### CsoundObj.getControlChannel(channelName) ⇒ Promise.<undefined> +Retrieves the value of control channel identified by channelName. +If the err argument is not NULL, the error (or success) code finding +or accessing the channel is stored in it. + +**Kind**: static method of [CsoundObj](#CsoundObj) + +| Param | Type | +| --- | --- | +| channelName | string | + + + +### CsoundObj.setControlChannel(channelName, value) ⇒ Promise.<undefined> +Sets the value of control channel identified by channelName + +**Kind**: static method of [CsoundObj](#CsoundObj) + +| Param | Type | +| --- | --- | +| channelName | string | +| value | number | + + + +### CsoundObj.getStringChannel(channelName) ⇒ Promise.<undefined> +Retrieves the string channel identified by channelName + +**Kind**: static method of [CsoundObj](#CsoundObj) + +| Param | Type | +| --- | --- | +| channelName | string | + + + +### CsoundObj.setStringChannel(channelName, value) ⇒ Promise.<undefined> +Sets the string channel value identified by channelName + +**Kind**: static method of [CsoundObj](#CsoundObj) + +| Param | Type | +| --- | --- | +| channelName | string | +| value | string | + + + +### CsoundObj.getOutputName() ⇒ Promise.<string> +Returns the audio output name (-o) + +**Kind**: static method of [CsoundObj](#CsoundObj) + + +### CsoundObj.getInputName() ⇒ Promise.<string> +Returns the audio input name (-i) + +**Kind**: static method of [CsoundObj](#CsoundObj) + + +### CsoundObj.destroy() ⇒ Promise.<undefined> +Destroys an instance of Csound and frees memory + +**Kind**: static method of [CsoundObj](#CsoundObj) + + +### CsoundObj.getAPIVersion() ⇒ Promise.<number> +Returns the API version as int + +**Kind**: static method of [CsoundObj](#CsoundObj) + + +### CsoundObj.getVersion() ⇒ Promise.<number> +Returns the Csound version as int + +**Kind**: static method of [CsoundObj](#CsoundObj) + + +### CsoundObj.initialize() ⇒ Promise.<number> +Initialise Csound with specific flags. +This function is called internally by csoundCreate(), +so there is generally no need to use it explicitly +unless you need to avoid default initilization that +sets signal handlers and atexit() callbacks. + +**Kind**: static method of [CsoundObj](#CsoundObj) +**Returns**: Promise.<number> - - Return value is zero on success, + positive if initialisation was done already, and negative on error. + + +### CsoundObj.parseOrc(orc) ⇒ Promise.<object> +Parses a csound orchestra string + +**Kind**: static method of [CsoundObj](#CsoundObj) + +| Param | Type | +| --- | --- | +| orc | string | + + + +### CsoundObj.compileTree(tree) ⇒ Promise.<number> +Compiles AST tree + +**Kind**: static method of [CsoundObj](#CsoundObj) + +| Param | Type | +| --- | --- | +| tree | object | + + + +### CsoundObj.compileOrc(orc) ⇒ Promise.<number> +Compiles a csound orchestra string + +**Kind**: static method of [CsoundObj](#CsoundObj) + +| Param | Type | +| --- | --- | +| orc | string | + + + +### CsoundObj.evalCode(orc) ⇒ Promise.<number> +Compiles a csound orchestra string + +**Kind**: static method of [CsoundObj](#CsoundObj) + +| Param | Type | +| --- | --- | +| orc | string | + + + +### CsoundObj.start() ⇒ Promise.<number> +Prepares Csound for performance + +**Kind**: static method of [CsoundObj](#CsoundObj) + + +### CsoundObj.compileCsd(path) ⇒ Promise.<number> +Compiles a Csound input file but does not perform it. + +**Kind**: static method of [CsoundObj](#CsoundObj) + +| Param | Type | +| --- | --- | +| path | string | + + + +### CsoundObj.compileCsdText(orc) ⇒ Promise.<number> +Compiles a CSD string but does not perform it. + +**Kind**: static method of [CsoundObj](#CsoundObj) + +| Param | Type | +| --- | --- | +| orc | string | + + + +### CsoundObj.perform() ⇒ Promise.<number> +Performs(plays) audio until end is reached + +**Kind**: static method of [CsoundObj](#CsoundObj) + + +### CsoundObj.performKsmps() ⇒ Promise.<number> +Performs(plays) 1 ksmps worth of sample(s) + +**Kind**: static method of [CsoundObj](#CsoundObj) + + +### CsoundObj.performBuffer() ⇒ Promise.<number> +Performs(plays) 1 buffer worth of audio + +**Kind**: static method of [CsoundObj](#CsoundObj) + + +### CsoundObj.stop() ⇒ Promise.<undefined> +Stops a csoundPerform + +**Kind**: static method of [CsoundObj](#CsoundObj) + + +### CsoundObj.cleanup() ⇒ Promise.<number> +Prints information about the end of a performance, +and closes audio and MIDI devices. + +**Kind**: static method of [CsoundObj](#CsoundObj) + + +### CsoundObj.reset() ⇒ Promise.<number> +Prints information about the end of a performance, +and closes audio and MIDI devices. + +**Kind**: static method of [CsoundObj](#CsoundObj) + + +### CsoundObj.getInputBufferSize() ⇒ Promise.<number> +Returns the number of samples in Csound's input buffer. + +**Kind**: static method of [CsoundObj](#CsoundObj) + + +### CsoundObj.getOutputBufferSize() ⇒ Promise.<number> +Returns the number of samples in Csound's output buffer. + +**Kind**: static method of [CsoundObj](#CsoundObj) + + +### CsoundObj.getInputBuffer() ⇒ Promise.<number> +Returns the address of the Csound audio input buffer. + +**Kind**: static method of [CsoundObj](#CsoundObj) + + +### CsoundObj.getOutputBuffer() ⇒ Promise.<number> +Returns the address of the Csound audio output buffer. + +**Kind**: static method of [CsoundObj](#CsoundObj) + + +### CsoundObj.getSpin() ⇒ Promise.<number> +Returns the address of the Csound audio input working buffer (spin). +Enables external software to write audio into Csound before calling csoundPerformKsmps. + +**Kind**: static method of [CsoundObj](#CsoundObj) + + +### CsoundObj.getSpout() ⇒ Promise.<number> +Returns the address of the Csound audio output working buffer (spout). +Enables external software to read audio from Csound after calling csoundPerformKsmps. + +**Kind**: static method of [CsoundObj](#CsoundObj) + + +### CsoundObj.getMIDIDevList(isOutput) ⇒ [Promise.<CS\_MIDIDEVICE>](#CS_MIDIDEVICE) +This function can be called to obtain a list of available input or output midi devices. +If list is NULL, the function will only return the number of devices +(isOutput=1 for out devices, 0 for in devices). + +**Kind**: static method of [CsoundObj](#CsoundObj) + +| Param | Type | +| --- | --- | +| isOutput | number | + + + +### CsoundObj.getRtMidiName() ⇒ Promise.<string> +This function can be called to obtain a list of available input or output midi devices. +If list is NULL, the function will only return the number of devices +(isOutput=1 for out devices, 0 for in devices). + +**Kind**: static method of [CsoundObj](#CsoundObj) + + +### CsoundObj.midiMessage(midi, midi, midi) ⇒ Promise.<void> +Emit a midi message with a given triplet of values +in the range of 0 to 127. + +**Kind**: static method of [CsoundObj](#CsoundObj) + +| Param | Type | Description | +| --- | --- | --- | +| midi | number | status value | +| midi | number | data1 | +| midi | number | data2 | + + + +### CsoundObj.isScorePending() ⇒ Promise.<number> +Sees whether Csound score events are performed or not, +independently of real-time MIDI events + +**Kind**: static method of [CsoundObj](#CsoundObj) + + +### CsoundObj.setScorePending(pending) ⇒ Promise.<undefined> +Sets whether Csound score events are performed or not +(real-time events will continue to be performed). +Can be used by external software, such as a VST host, +to turn off performance of score events (while continuing to perform real-time events), +for example to mute a Csound score while working on other tracks of a piece, +or to play the Csound instruments live. + +**Kind**: static method of [CsoundObj](#CsoundObj) + +| Param | Type | +| --- | --- | +| pending | number | + + + +### CsoundObj.readScore(score) ⇒ Promise.<undefined> +Read, preprocess, and load a score from an ASCII string It can be called repeatedly, +with the new score events being added to the currently scheduled ones. + +**Kind**: static method of [CsoundObj](#CsoundObj) + +| Param | Type | +| --- | --- | +| score | string | + + + +### CsoundObj.getScoreTime() ⇒ Promise.<number> +Returns the current score time in seconds since the beginning of performance. + +**Kind**: static method of [CsoundObj](#CsoundObj) + + +### CsoundObj.getScoreOffsetSeconds() ⇒ Promise.<number> +Returns the score time beginning at which score events will actually immediately be performed + +**Kind**: static method of [CsoundObj](#CsoundObj) + + +### CsoundObj.setScoreOffsetSeconds(time) ⇒ Promise.<number> +Csound score events prior to the specified time are not performed, +and performance begins immediately at the specified time +(real-time events will continue to be performed as they are received). +Can be used by external software, such as a VST host, to begin +score performance midway through a Csound score, +for example to repeat a loop in a sequencer, +or to synchronize other events with the Csound score. + +**Kind**: static method of [CsoundObj](#CsoundObj) + +| Param | Type | +| --- | --- | +| time | number | + + + +### CsoundObj.rewindScore() ⇒ Promise.<number> +Rewinds a compiled Csound score to the time specified with csoundObj.setScoreOffsetSeconds(). + +**Kind**: static method of [CsoundObj](#CsoundObj) + + +### CsoundObj.tableLength(tableNum) ⇒ Promise.<number> +Returns the length of a function table +(not including the guard point), +or -1 if the table does not exist. + +**Kind**: static method of [CsoundObj](#CsoundObj) + +| Param | Type | +| --- | --- | +| tableNum | string | + + + +### CsoundObj.tableGet(tableNum, tableIndex) ⇒ Promise.<number> +Returns the value of a slot in a function table. +The table number and index are assumed to be valid. + +**Kind**: static method of [CsoundObj](#CsoundObj) + +| Param | Type | +| --- | --- | +| tableNum | string | +| tableIndex | string | + + + +### CsoundObj.tableSet(tableNum, tableIndex, value) ⇒ Promise.<undefined> +Sets the value of a slot in a function table. +The table number and index are assumed to be valid. + +**Kind**: static method of [CsoundObj](#CsoundObj) + +| Param | Type | +| --- | --- | +| tableNum | string | +| tableIndex | string | +| value | string | + + + +### CsoundObj.tableCopyIn(tableNum, tableIndex, array) ⇒ Promise.<undefined> +Copy the contents of an Array or TypedArray from javascript into a given csound function table. +The table number is assumed to be valid, and the table needs to have sufficient space +to receive all the array contents. +The table number and index are assumed to be valid. + +**Kind**: static method of [CsoundObj](#CsoundObj) + +| Param | Type | +| --- | --- | +| tableNum | string | +| tableIndex | string | +| array | Array.<number> \| ArrayLike.<number> | + + + +### CsoundObj.tableCopyOut(tableNum) ⇒ Promise.<(Float64Array\|undefined)> +Copies the contents of a function table from csound into Float64Array. +The function returns a Float64Array if the table exists, otherwise +it returns undefined. + +**Kind**: static method of [CsoundObj](#CsoundObj) + +| Param | Type | +| --- | --- | +| tableNum | string | + + + +### CsoundObj.getTableArgs(tableNum) ⇒ Promise.<(Float64Array\|undefined)> +Copies the contents of a function table from csound into Float64Array. +The function returns a Float64Array if the table exists, otherwise +it returns undefined. + +**Kind**: static method of [CsoundObj](#CsoundObj) + +| Param | Type | +| --- | --- | +| tableNum | string | + + + +### CsoundObj.isNamedGEN(tableNum) ⇒ Promise.<number> +Checks if a given GEN number num is a named GEN if so, +it returns the string length (excluding terminating NULL char). +Otherwise it returns 0. + +**Kind**: static method of [CsoundObj](#CsoundObj) + +| Param | Type | +| --- | --- | +| tableNum | string | + + + +### CsoundObj.getNamedGEN(tableNum) ⇒ Promise.<(string\|undefined)> +Gets the GEN name from a number num, if this is a named GEN. +If the table number doesn't represent a named GEN, it will +return undefined. + +**Kind**: static method of [CsoundObj](#CsoundObj) + +| Param | Type | +| --- | --- | +| tableNum | string | + + + +## PublicEvents : enum +**Kind**: global enum +**Read only**: true +**Properties** + +| Name | Type | Description | +| --- | --- | --- | +| "play" | string | called anytime performance goes from pause/stop to a running state. | +| "pause" | string | called after any successful csound.pause() calls. | +| "stop" | string | called after end of performance or after a successful csound.stop(). | +| "realtimePerformanceStarted" | string | called at the start of realtime performance but not on resume or render. | +| "realtimePerformancePaused" | string | only called if csound.pause() was successfully called during performance. | +| "realtimePerformanceResumed" | string | only called if csound.resume() was successfully called after a pause. | +| "realtimePerformanceEnded" | string | called after end of performance or after a successful csound.stop(). | +| "renderStarted" | string | called at the start of offline/non-realtime render to disk. | +| "renderEnded" | string | called at the end of offline/non-realtime render to disk. | +| "onAudioNodeCreated" | string | called when an audioNode is created from the AudioContext before realtime performance. the event callback will include the audioNode itself, which is needed if autoConnect is set to false. | +| "message" | string | the main entrypoint to csound's messaging (-m) system, a default event listener will print the message to the browser console, this default listener can be removed by the user. | + + + +## Csound([params]) ⇒ Promise.<(CsoundObj\|undefined)> +The default entry for @csound/wasm/browser module. +If loaded successfully, it returns CsoundObj, +otherwise undefined. + +**Kind**: global function + +| Param | Type | Default | Description | +| --- | --- | --- | --- | +| [params] | Object | | Initialization parameters | +| [params.audioContext] | AudioContext | | Optional AudioContext to use; if none given, an AudioContext will be created. | +| [params.inputChannelCount] | Number | | Optional input channel count for AudioNode used with WebAudio graph. Defaults to the value of nchnls_i in useWorker but 2 otherwise. | +| [params.outputChannelCount] | Number | | Optional output channel count AudioNode used with WebAudio graph. Defaults to the value of nchnls in useWorker but 2 otherwise. | +| [params.autoConnect] | Boolean | true | Set to configure Csound to automatically connect to the audioContext.destination output. | +| [params.withPlugins] | Array.<Object> | | Array of WebAssembly Csound plugin libraries to use with Csound. | +| [params.useWorker] | Boolean | false | Configure to use backend using Web Workers to run Csound in a thread separate from audio callback. | +| [params.useSAB] | Boolean | true | Configure to use SharedArrayBuffers for WebWorker communications if platform supports it. | +| [params.useSPN] | Boolean | false | Configure to use explicitly request ScriptProcessorNode rather than AudioWorklet. Recommended only for debug testing purposes. | + + + +## CSOUND\_PARAMS +**Kind**: global typedef +**Properties** + +| Name | Type | +| --- | --- | +| debug_mode | number | +| buffer_frames | number | +| hardware_buffer_frames | number | +| displays | number | +| ascii_graphs | number | +| postscript_graphs | number | +| message_level | number | +| tempo | number | +| ring_bell | number | +| use_cscore | number | +| terminate_on_midi | number | +| heartbeat | number | +| defer_gen01_load | number | +| midi_key | number | +| midi_key_cps | number | +| midi_key_oct | number | +| midi_key_pch | number | +| midi_velocity | number | + + + +## CS\_MIDIDEVICE +**Kind**: global typedef +**Properties** + +| Name | Type | +| --- | --- | +| device_name | string | +| interface_name | string | +| device_id | string | +| midi_module | string | +| isOutput | number | + + + diff --git a/src/App.svelte b/src/App.svelte index 67a8f20..a1dba28 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -1,9 +1,15 @@ + + +
+

{message}

+
+ + +
+
+
+ + diff --git a/src/lib/Editor.svelte b/src/lib/Editor.svelte index 3ff73ae..ffa4ae5 100644 --- a/src/lib/Editor.svelte +++ b/src/lib/Editor.svelte @@ -14,6 +14,7 @@ import { oneDark } from '@codemirror/theme-one-dark'; import { vim } from '@replit/codemirror-vim'; import { editorSettings } from './stores/editorSettings'; + import { csound } from './csound'; interface Props { initialValue?: string; @@ -40,6 +41,19 @@ const lineWrappingCompartment = new Compartment(); const vimCompartment = new Compartment(); + const evaluateKeymap = keymap.of([ + { + key: 'Mod-e', + run: (view) => { + const code = view.state.doc.toString(); + csound.evaluate(code).catch(err => { + console.error('Evaluation error:', err); + }); + return true; + } + } + ]); + onMount(() => { const settings = $editorSettings; @@ -75,6 +89,7 @@ ...baseExtensions, languageExtensions[language], oneDark, + evaluateKeymap, lineNumbersCompartment.of(settings.showLineNumbers ? lineNumbers() : []), lineWrappingCompartment.of(settings.enableLineWrapping ? EditorView.lineWrapping : []), vimCompartment.of(settings.vimMode ? vim() : []), diff --git a/src/lib/EditorWithLogs.svelte b/src/lib/EditorWithLogs.svelte index 967e3b2..d095eaf 100644 --- a/src/lib/EditorWithLogs.svelte +++ b/src/lib/EditorWithLogs.svelte @@ -67,14 +67,6 @@ export function setValue(value: string): void { editorRef?.setValue(value); } - - export function addLog(message: string): void { - logPanelRef?.addLog(message); - } - - export function clearLogs(): void { - logPanelRef?.clearLogs(); - }
diff --git a/src/lib/FileBrowser.svelte b/src/lib/FileBrowser.svelte new file mode 100644 index 0000000..52a1757 --- /dev/null +++ b/src/lib/FileBrowser.svelte @@ -0,0 +1,347 @@ + + +
+
+ Files + +
+ +
+ {#if loading} +
Loading...
+ {:else} +
+ {#each projects as project} +
selectProject(project)} + role="button" + tabindex="0" + > +
+ +
+
+
{project.title}
+
+ +
+ {/each} +
+ {/if} +
+ + {#if selectedProject} + + {/if} +
+ + + + diff --git a/src/lib/InputDialog.svelte b/src/lib/InputDialog.svelte new file mode 100644 index 0000000..b75f800 --- /dev/null +++ b/src/lib/InputDialog.svelte @@ -0,0 +1,121 @@ + + + +
+ + +
+ + +
+
+
+ + diff --git a/src/lib/LogPanel.svelte b/src/lib/LogPanel.svelte index 7ead1ce..e75220a 100644 --- a/src/lib/LogPanel.svelte +++ b/src/lib/LogPanel.svelte @@ -1,28 +1,73 @@
+
+ Output +
+ + +
+
{#if logs.length === 0}
No output yet...
{:else} {#each logs as log, i} -
- {i + 1} - {log} +
+ {formatTime(log.timestamp)} + {log.message}
{/each} {/if} @@ -37,6 +82,50 @@ background-color: #1a1a1a; } + .log-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 0.5rem 0.75rem; + background-color: #2a2a2a; + border-bottom: 1px solid #3a3a3a; + } + + .log-title { + font-size: 0.75rem; + font-weight: 600; + color: rgba(255, 255, 255, 0.6); + text-transform: uppercase; + letter-spacing: 0.05em; + } + + .log-actions { + display: flex; + gap: 0.25rem; + } + + .action-button { + padding: 0.25rem; + background-color: transparent; + color: rgba(255, 255, 255, 0.6); + border: none; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + transition: all 0.2s; + } + + .action-button:hover:not(:disabled) { + color: rgba(255, 255, 255, 0.9); + background-color: rgba(255, 255, 255, 0.1); + } + + .action-button:disabled { + opacity: 0.3; + cursor: not-allowed; + } + .log-content { flex: 1; overflow-y: auto; @@ -63,10 +152,19 @@ background-color: #252525; } - .log-index { + .log-entry.error { + background-color: rgba(255, 0, 0, 0.1); + border-left: 3px solid rgba(255, 0, 0, 0.6); + } + + .log-entry.error .log-message { + color: rgba(255, 100, 100, 0.95); + } + + .log-timestamp { color: rgba(255, 255, 255, 0.4); - min-width: 2rem; - text-align: right; + min-width: 6rem; + font-size: 0.75rem; } .log-message { diff --git a/src/lib/Modal.svelte b/src/lib/Modal.svelte new file mode 100644 index 0000000..d04eb27 --- /dev/null +++ b/src/lib/Modal.svelte @@ -0,0 +1,78 @@ + + +{#if visible} + +{/if} + + diff --git a/src/lib/csound/engine.ts b/src/lib/csound/engine.ts new file mode 100644 index 0000000..97e9083 --- /dev/null +++ b/src/lib/csound/engine.ts @@ -0,0 +1,125 @@ +import { Csound } from '@csound/browser'; + +export interface CsoundEngineOptions { + onMessage?: (message: string) => void; + onError?: (error: string) => void; +} + +export class CsoundEngine { + private csound: Csound | null = null; + private initialized = false; + private running = false; + private options: CsoundEngineOptions; + + constructor(options: CsoundEngineOptions = {}) { + this.options = options; + } + + async init(): Promise { + if (this.initialized) return; + + try { + this.csound = await Csound(); + + this.csound.on('message', (message: string) => { + this.options.onMessage?.(message); + }); + + await this.csound.setOption('-odac'); + + this.initialized = true; + this.log('Csound initialized successfully'); + } catch (error) { + const errorMsg = error instanceof Error ? error.message : 'Failed to initialize Csound'; + this.error(errorMsg); + throw error; + } + } + + async evaluateCode(code: string): Promise { + if (!this.initialized || !this.csound) { + throw new Error('Csound not initialized. Call init() first.'); + } + + try { + if (this.running) { + await this.stop(); + } + + this.log('Resetting Csound...'); + await this.csound.reset(); + + this.log('Setting audio output...'); + await this.csound.setOption('-odac'); + + const orcMatch = code.match(/([\s\S]*?)<\/CsInstruments>/); + const scoMatch = code.match(/([\s\S]*?)<\/CsScore>/); + + if (!orcMatch || !scoMatch) { + throw new Error('Invalid CSD format. Must contain and sections.'); + } + + const orc = orcMatch[1].trim(); + const sco = scoMatch[1].trim(); + + this.log('Compiling orchestra...'); + await this.csound.compileOrc(orc); + + this.log('Reading score...'); + await this.csound.readScore(sco); + + this.log('Starting...'); + await this.csound.start(); + + this.log('Performing...'); + this.running = true; + await this.csound.perform(); + + this.log('Performance complete'); + this.running = false; + } catch (error) { + this.running = false; + const errorMsg = error instanceof Error ? error.message : 'Evaluation failed'; + this.error(errorMsg); + throw error; + } + } + + async stop(): Promise { + if (!this.csound || !this.running) return; + + try { + await this.csound.stop(); + await this.csound.reset(); + this.running = false; + this.log('Stopped'); + } catch (error) { + const errorMsg = error instanceof Error ? error.message : 'Failed to stop'; + this.error(errorMsg); + } + } + + isRunning(): boolean { + return this.running; + } + + isInitialized(): boolean { + return this.initialized; + } + + private log(message: string): void { + this.options.onMessage?.(message); + } + + private error(message: string): void { + this.options.onError?.(message); + } + + async destroy(): Promise { + if (this.running) { + await this.stop(); + } + this.csound = null; + this.initialized = false; + } +} diff --git a/src/lib/csound/index.ts b/src/lib/csound/index.ts new file mode 100644 index 0000000..bb46668 --- /dev/null +++ b/src/lib/csound/index.ts @@ -0,0 +1,4 @@ +export { CsoundEngine } from './engine'; +export type { CsoundEngineOptions } from './engine'; +export { csound, csoundLogs, csoundInitialized, csoundRunning } from './store'; +export type { LogEntry } from './store'; diff --git a/src/lib/csound/store.ts b/src/lib/csound/store.ts new file mode 100644 index 0000000..1437c3c --- /dev/null +++ b/src/lib/csound/store.ts @@ -0,0 +1,109 @@ +import { writable, derived, get } from 'svelte/store'; +import { CsoundEngine } from './engine'; + +export interface LogEntry { + timestamp: Date; + message: string; + type: 'info' | 'error'; +} + +interface CsoundState { + initialized: boolean; + running: boolean; + logs: LogEntry[]; +} + +function createCsoundStore() { + const initialState: CsoundState = { + initialized: false, + running: false, + logs: [] + }; + + const { subscribe, set, update } = writable(initialState); + + let engine: CsoundEngine | null = null; + + function addLog(message: string, type: 'info' | 'error' = 'info') { + update(state => ({ + ...state, + logs: [...state.logs, { timestamp: new Date(), message, type }] + })); + } + + return { + subscribe, + + async init() { + if (engine) return; + + try { + engine = new CsoundEngine({ + onMessage: (msg) => addLog(msg, 'info'), + onError: (err) => addLog(err, 'error') + }); + + await engine.init(); + + update(state => ({ + ...state, + initialized: true + })); + } catch (error) { + const errorMsg = error instanceof Error ? error.message : 'Failed to initialize'; + addLog(errorMsg, 'error'); + throw error; + } + }, + + async evaluate(code: string) { + if (!engine) { + throw new Error('CSound engine not initialized'); + } + + try { + update(state => ({ ...state, running: true })); + await engine.evaluateCode(code); + } catch (error) { + const errorMsg = error instanceof Error ? error.message : 'Evaluation failed'; + addLog(errorMsg, 'error'); + throw error; + } finally { + update(state => ({ ...state, running: false })); + } + }, + + async stop() { + if (!engine) return; + + try { + await engine.stop(); + update(state => ({ ...state, running: false })); + } catch (error) { + const errorMsg = error instanceof Error ? error.message : 'Stop failed'; + addLog(errorMsg, 'error'); + } + }, + + clearLogs() { + update(state => ({ + ...state, + logs: [] + })); + }, + + async destroy() { + if (engine) { + await engine.destroy(); + engine = null; + } + set(initialState); + } + }; +} + +export const csound = createCsoundStore(); + +export const csoundLogs = derived(csound, $csound => $csound.logs); +export const csoundInitialized = derived(csound, $csound => $csound.initialized); +export const csoundRunning = derived(csound, $csound => $csound.running);