plugins/synths.cracked.js

  1. /**
  2. * gang_of_oscillators
  3. *
  4. * Just a bunch of oscillators
  5. *
  6. * [See more synth examples](examples/synth.html)
  7. *
  8. * @plugin
  9. * @category Synth
  10. * @function
  11. * @memberof cracked
  12. * @name cracked#gang_of_oscillators
  13. * @public
  14. * @param {Object} [params] map of optional values
  15. */
  16. cracked.gang_of_oscillators = function(params) {
  17. /*
  18. //expected format
  19. {
  20. size:16,
  21. lp_freq:20000,
  22. type:sine
  23. }
  24. //or just type as a string
  25. */
  26. var type = __.isStr(params) ? params : __.isObj(params) && params.type ? params.type : "sine";
  27. var lp_freq = __.isObj(params) && params.lp_freq ? params.lp_freq : 20000;
  28. var size = __.isObj(params) && params.size ? params.size : 16;
  29. __().begin("gang_of_oscillators",params);
  30. __().gain();
  31. for(var i=0;i<size;i++) {
  32. __().begin("v"+(i+1)).osc({type:type}).lowpass(lp_freq).gain(0).end("v"+(i+1)).connect("gain");
  33. }
  34. __.end("gang_of_oscillators");
  35. return cracked;
  36. };
  37. /**
  38. * monosynth
  39. *
  40. * Simple monophonic synth
  41. *
  42. * [See more synth examples](examples/synth.html)
  43. *
  44. * @plugin
  45. * @category Synth
  46. * @function
  47. * @memberof cracked
  48. * @name cracked#monosynth
  49. * @public
  50. * @param {Object} [params] map of optional values
  51. */
  52. cracked.monosynth = function (params) {
  53. var methods = {
  54. init: function (options) {
  55. var opts = options || {};
  56. /*
  57. expected format
  58. {
  59. lfo_type:"sawtooth",
  60. lfo_intensity:0,
  61. lfo_speed:5
  62. osc_type:"sine",
  63. osc_frequency:440,
  64. osc_detune:0
  65. lp_q:0,
  66. lp_frequency:440
  67. adsr_envelope:0.5
  68. gain_volume:1
  69. }
  70. */
  71. //set up a basic synth: lfo, sine, lowpass, envelope
  72. //options:
  73. //lfo- type, intensity, speed
  74. //osc- type, frequency, detune
  75. //lowpass- q, frequency
  76. //adsr- envelope
  77. //gain- volume
  78. var lfo_type = opts.lfo_type || "sawtooth",
  79. lfo_intensity = opts.lfo_intensity || 0,
  80. lfo_speed = opts.lfo_speed || 5,
  81. osc_type = opts.osc_type || "sine",
  82. osc_frequency = opts.osc_frequency || 440,
  83. osc_detune = opts.osc_detune || 0,
  84. lp_q = opts.lp_q || 0,
  85. lp_frequency = opts.lp_frequency || 440,
  86. adsr_envelope = opts.adsr_envelope || 0.5,
  87. gain_volume = opts.gain_volume || 1;
  88. __().begin("monosynth", params).
  89. lfo({
  90. gain:lfo_intensity,
  91. frequency:lfo_speed,
  92. type:lfo_type
  93. }).
  94. osc({
  95. detune:osc_detune,
  96. frequency:osc_frequency,
  97. type:osc_type
  98. }).
  99. lowpass({
  100. q: lp_q,
  101. frequency:lp_frequency
  102. }).
  103. adsr({
  104. envelope:adsr_envelope
  105. }).
  106. gain({
  107. gain:gain_volume
  108. }).
  109. end("monosynth");
  110. },
  111. noteOn: function (params) {
  112. //process incoming arguments for this note
  113. var args = params || {};
  114. var freq = __.isNum(params) ? __.pitch2freq(params) : __.pitch2freq(args.pitch);
  115. var vel = __.isNum(args.velocity) ? args.velocity/127 : 0.5;
  116. var env = args.envelope || [0.01,0.1,0.5];
  117. //loop thru selected nodes
  118. cracked.each("monosynth", function (el, index, arr) {
  119. //select any internal oscillator nodes the monosynth contains (using "el.search(osc)")
  120. //and then call frequency() passing in the pitch argument we got w noteOn.
  121. cracked.exec("frequency", [freq], el.search("osc"));
  122. //apply the velocity to the output gain
  123. cracked.exec("volume", [vel], el.search("gain"));
  124. //grab internal adsr and call trigger, pass the envelope parameter we received
  125. cracked.exec("adsr", ["trigger", env], el.search("adsr"));
  126. });
  127. },
  128. noteOff: function (params) {
  129. cracked.each("monosynth", function (el, index, arr) {
  130. params = __.ifUndef(params,0.1);
  131. var p = __.isNum(params) ? params : __.ifUndef(params.envelope,0.1);
  132. //call the adsr release
  133. cracked.exec("adsr", ["release",p], el.search("adsr"));
  134. });
  135. }
  136. };
  137. if (methods[params]) {
  138. methods[params].apply(this, Array.prototype.slice.call(arguments, 1));
  139. } else {
  140. methods.init(params);
  141. }
  142. return cracked;
  143. };
  144. /**
  145. * polysynth
  146. *
  147. * Simple polyphonic synth
  148. *
  149. * [See more synth examples](examples/synth.html)
  150. *
  151. * @plugin
  152. * @category Synth
  153. * @function
  154. * @memberof cracked
  155. * @name cracked#polysynth
  156. * @public
  157. * @param {Object} [params] map of optional values
  158. */
  159. cracked.polysynth = function (params) {
  160. var methods = {
  161. init: function (options) {
  162. var opts = options || {};
  163. /*
  164. expected format
  165. {
  166. lfo_type:"sawtooth",
  167. lfo_intensity:0,
  168. lfo_speed:5
  169. osc_type:"sine",
  170. osc_frequency:440,
  171. osc_detune:0
  172. lp_q:0,
  173. lp_frequency:440
  174. adsr_envelope:0.5
  175. gain_volume:1
  176. }
  177. */
  178. //set up a basic synth: lfo, sine, lowpass, envelope
  179. //options:
  180. //lfo- type, intensity, speed
  181. //osc- type, frequency, detune
  182. //lowpass- q, frequency
  183. //adsr- envelope
  184. //gain- volume
  185. params = params || {};
  186. //defaults
  187. params.lfo_type = opts.lfo_type || "sawtooth";
  188. params.lfo_intensity = opts.lfo_intensity || 0;
  189. params.lfo_speed = opts.lfo_speed || 5;
  190. params.osc_type = opts.osc_type || "sine";
  191. params.osc_frequency = opts.osc_frequency || 440;
  192. params.osc_detune = opts.osc_detune || 0;
  193. params.lp_q = opts.lp_q || 0;
  194. params.lp_frequency = opts.lp_frequency || 440;
  195. params.adsr_envelope = opts.adsr_envelope || 0.5;
  196. params.gain_volume = opts.gain_volume || 1;
  197. //we'll add a map so we can track active voices
  198. params.active_voices = {};
  199. //just a stub we'll attach voices to in the noteon method
  200. __().begin("polysynth", params).
  201. gain({
  202. gain:params.gain_volume
  203. }).
  204. end("polysynth");
  205. },
  206. noteOn: function (params) {
  207. //process incoming arguments for this note
  208. var args = params || {};
  209. var note_number = __.isNum(params) ? params : args.pitch;
  210. var freq = __.pitch2freq(note_number);
  211. var vel = __.isNum(args.velocity) ? args.velocity/127 : 0.5;
  212. var env = args.envelope || [0.01,0.1,0.5];
  213. var instance_id = note_number+"_"+Date.now();
  214. //loop thru selected nodes, filtering on the type polysynth
  215. cracked.each("polysynth", function (el, index, arr) {
  216. //get the settings that were stored when the object was created
  217. var voices = el.getParams().settings.active_voices;
  218. var settings = el.getParams().settings;
  219. //if not currently active
  220. if(!voices[note_number]) {
  221. //ignore the grid while we're creating the voice
  222. __.loop("toggle_grid");
  223. //create a new voice
  224. __().lfo({
  225. type:settings.lfo_type,
  226. gain:settings.lfo_intensity,
  227. frequency:settings.lfo_speed,
  228. id:instance_id+"_lfo",
  229. class:instance_id+"_class",
  230. modulates:"frequency"
  231. }).osc({
  232. id:instance_id+"_osc",
  233. class:instance_id+"_class",
  234. frequency:freq,
  235. type:settings.osc_type,
  236. detune: settings.osc_detune
  237. }).adsr({
  238. envelope:env,
  239. id:instance_id+"_adsr",
  240. class:instance_id+"_class"
  241. }).lowpass({
  242. id:instance_id+"_lp",
  243. class:instance_id+"_class",
  244. frequency:settings.lp_frequency,
  245. q:settings.lp_q
  246. }).gain({
  247. id:instance_id+"_gain",
  248. class:instance_id+"_class",
  249. gain:vel
  250. }).connect(el);
  251. //flip it back before we start it up
  252. __.loop("toggle_grid");
  253. //start it up
  254. cracked.exec("start", [], __.find("."+instance_id+"_class"));
  255. //trigger the envelope
  256. cracked.exec("adsr", ["trigger", env], __.find("#"+instance_id+"_adsr"));
  257. voices[note_number]=instance_id;
  258. }
  259. });
  260. },
  261. noteOff: function (params) {
  262. cracked.each("polysynth", function (el, index, arr) {
  263. //the only params should be the pitch and the (optional) envelope release time
  264. var note_number = __.isNum(params) ? params : params.pitch;
  265. var release = __.ifUndef(params.envelope,0.1);
  266. //get the active voices map
  267. var voices = el.getParams().settings.active_voices;
  268. //and the instance id
  269. var instance_id = note_number ? voices[note_number] : false;
  270. //if its active
  271. if(instance_id) {
  272. //call the adsr release
  273. cracked.exec("adsr", ["release", release], __.find("#"+instance_id+"_adsr"));
  274. //schedule the removal of the voice after it's done playing
  275. cracked.exec("remove", [((release*1000)+250)], __.find("."+instance_id+"_class"));
  276. //clear the active status so it can be run again
  277. delete voices[note_number];
  278. }
  279. });
  280. },
  281. update:function (params) {
  282. //update synth params from control
  283. cracked.each("polysynth", function (el, index, arr) {
  284. //get the active voices map
  285. var settings = el.getParams().settings;
  286. var voices = el.getParams().settings.active_voices;
  287. //iterate over the node's params and update with any new values
  288. //any new voices will created with these values
  289. Object.keys(params).map(function(setting,index,arr){
  290. settings[setting]=params[setting];
  291. });
  292. //iterate over the voices currently playing and update their values
  293. Object.keys(voices).map(function(pitch,index,arr){
  294. var instance_id = voices[pitch];
  295. Object.keys(params).map(function(param,index,arr){
  296. switch (param) {
  297. case "lfo_speed":
  298. cracked.exec("frequency", [params[param]], __.find("#"+instance_id+"_lfo"));
  299. break;
  300. case "lfo_intensity":
  301. cracked.exec("volume", [params[param]], __.find("#"+instance_id+"_lfo"));
  302. break;
  303. case "lp_frequency":
  304. cracked.exec("frequency", [params[param]], __.find("#"+instance_id+"_lp"));
  305. break;
  306. case "lp_q":
  307. cracked.exec("q", [params[param]], __.find("#"+instance_id+"_lp"));
  308. break;
  309. }
  310. });
  311. });
  312. });
  313. }
  314. };
  315. if (methods[params]) {
  316. methods[params].apply(this, Array.prototype.slice.call(arguments, 1));
  317. } else {
  318. methods.init(params);
  319. }
  320. return cracked;
  321. };