Source: CsoundNode.js

  1. /*
  2. * C S O U N D
  3. *
  4. * L I C E N S E
  5. *
  6. * This software is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 2.1 of the License, or (at your option) any later version.
  10. *
  11. * This software is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. */
  15. // Setup a single global AudioContext object if not already defined
  16. var CSOUND_AUDIO_CONTEXT = CSOUND_AUDIO_CONTEXT ||
  17. (function() {
  18. try {
  19. var AudioContext = window.AudioContext || window.webkitAudioContext;
  20. return new AudioContext();
  21. }
  22. catch(error) {
  23. console.log('Web Audio API is not supported in this browser');
  24. }
  25. return null;
  26. }());
  27. /** This ES6 Class defines a Custom Node as an AudioWorkletNode
  28. * that holds a Csound engine.
  29. */
  30. class CsoundNode extends AudioWorkletNode {
  31. /**
  32. *
  33. * @constructor
  34. * @param {AudioContext} context AudioContext in which this node will run
  35. * @param {object} options Configuration options, holding numberOfInputs,
  36. * numberOfOutputs
  37. * @returns {object} A new CsoundNode
  38. */
  39. constructor(context, options) {
  40. options = options || {};
  41. options.numberOfInputs = 1;
  42. options.numberOfOutputs = 2;
  43. options.channelCount = 2;
  44. options.sampleRate = context.sampleRate;
  45. super(context, 'Csound', options);
  46. this.msgCallback = (msg) => { console.log(msg); }
  47. this.port.start();
  48. this.port.onmessage = (event) => {
  49. let data = event.data;
  50. switch(data[0]) {
  51. case "log":
  52. this.msgCallback(data[1]);
  53. break;
  54. default:
  55. console.log('[CsoundNode] Invalid Message: "' + event.data);
  56. }
  57. };
  58. }
  59. /** Writes data to a file in the WASM filesystem for
  60. * use with csound.
  61. *
  62. * @param {string} filePath A string containing the path to write to.
  63. * @param {blob} blobData The data to write to file.
  64. */
  65. writeToFS(filePath, blobData) {
  66. this.port.postMessage(["writeToFS", filePath, blobData]);
  67. }
  68. /** Compiles a CSD, which may be given as a filename in the
  69. * WASM filesystem or a string containing the code
  70. *
  71. * @param {string} csd A string containing the CSD filename or the CSD code.
  72. */
  73. compileCSD(filePath) {
  74. this.port.postMessage(["compileCSD", filePath]);
  75. }
  76. /** Compiles Csound orchestra code.
  77. *
  78. * @param {string} orcString A string containing the orchestra code.
  79. */
  80. compileOrc(orcString) {
  81. this.port.postMessage(["compileOrc", orcString]);
  82. }
  83. /** Sets a Csound engine option (flag)
  84. *
  85. *
  86. * @param {string} option The Csound engine option to set. This should
  87. * not contain any whitespace.
  88. */
  89. setOption(option) {
  90. this.port.postMessage(["setOption", option]);
  91. }
  92. render(filePath) {
  93. }
  94. /** Evaluates Csound orchestra code.
  95. *
  96. * @param {string} codeString A string containing the orchestra code.
  97. */
  98. evaluateCode(codeString) {
  99. this.port.postMessage(["evalCode", codeString]);
  100. }
  101. /** Reads a numeric score string.
  102. *
  103. * @param {string} scoreString A string containing a numeric score.
  104. */
  105. readScore(scoreString) {
  106. this.port.postMessage(["readScore", scoreString]);
  107. }
  108. /** Sets the value of a control channel in the software bus
  109. *
  110. * @param {string} channelName A string containing the channel name.
  111. * @param {number} value The value to be set.
  112. */
  113. setControlChannel(channelName, value) {
  114. this.port.postMessage(["setControlChannel",
  115. channelName, value]);
  116. }
  117. /** Sets the value of a string channel in the software bus
  118. *
  119. * @param {string} channelName A string containing the channel name.
  120. * @param {string} stringValue The string to be set.
  121. */
  122. setStringChannel(channelName, value) {
  123. this.port.postMessage(["setStringChannel",
  124. channelName, value]);
  125. }
  126. /** Starts processing in this node
  127. */
  128. start() {
  129. this.port.postMessage(["start"]);
  130. }
  131. /** Resets the Csound engine.
  132. */
  133. reset() {
  134. this.port.postMessage(["reset"]);
  135. }
  136. destroy() {
  137. }
  138. /** Starts performance, same as start()
  139. */
  140. play() {
  141. this.port.postMessage(["play"]);
  142. }
  143. /** Stops (pauses) performance
  144. */
  145. stop() {
  146. this.port.postMessage(["stop"]);
  147. }
  148. /** Sets a callback to process Csound console messages.
  149. *
  150. * @param {function} msgCallback A callback to process messages
  151. * with signature function(message), where message is a string
  152. * from Csound.
  153. */
  154. setMessageCallback(msgCallback) {
  155. this.msgCallback = msgCallback;
  156. }
  157. /** Sends a MIDI channel message to Csound
  158. *
  159. * @param {number} byte1 MIDI status byte
  160. * @param {number} byte2 MIDI data byte 1
  161. * @param {number} byte1 MIDI data byte 2
  162. *
  163. */
  164. midiMessage(byte1, byte2, byte3) {
  165. this.port.postMessage(["midiMessage", byte1, byte2, byte3]);
  166. }
  167. }
  168. /** This E6 class is used to setup scripts and
  169. allow the creation of new CsoundNode objects
  170. @hideconstructor
  171. */
  172. class CsoundNodeFactory {
  173. /**
  174. * This static method is used to asynchronously setup scripts for AudioWorklet Csound
  175. *
  176. * @param {string} script_base A string containing the base path to scripts
  177. */
  178. static importScripts(script_base='./') {
  179. let actx = CSOUND_AUDIO_CONTEXT;
  180. return new Promise( (resolve) => {
  181. actx.audioWorklet.addModule(script_base + 'libcsound-worklet.wasm.js').then(() => {
  182. actx.audioWorklet.addModule(script_base + 'libcsound-worklet.js').then(() => {
  183. actx.audioWorklet.addModule(script_base + 'CsoundProcessor.js').then(() => {
  184. resolve();
  185. }) }) })
  186. })
  187. }
  188. /**
  189. * This static method creates a new CsoundNode.
  190. * @param {number} InputChannelCount number of input channels
  191. * @param {number} OutputChannelCount number of output channels
  192. * @returns {object}
  193. */
  194. static createNode(inputChannelCount=1, outputChannelCount=2) {
  195. var options = {};
  196. options.numberOfInputs = inputChannelCount;
  197. options.numberOfOutputs = outputChannelCount;
  198. return new CsoundNode(CSOUND_AUDIO_CONTEXT, options);
  199. }
  200. }