mirror of
https://github.com/eliasstepanik/strudel-docker.git
synced 2026-01-11 21:58:31 +00:00
commit
5aa01ce91c
288
packages/core/controls.mjs
Normal file
288
packages/core/controls.mjs
Normal file
@ -0,0 +1,288 @@
|
||||
import { Pattern, sequence } from '@strudel.cycles/core/strudel.mjs';
|
||||
|
||||
const controls = {};
|
||||
const generic_params = [
|
||||
['s', 's', 'sound'],
|
||||
//['s', 'toArg', 'for internal sound routing'],
|
||||
// ["f", "from", "for internal sound routing"),
|
||||
//['f', 'to', 'for internal sound routing'],
|
||||
['f', 'accelerate', 'a pattern of numbers that speed up (or slow down) samples while they play.'],
|
||||
['f', 'amp', 'like @gain@, but linear.'],
|
||||
[
|
||||
'f',
|
||||
'attack',
|
||||
'a pattern of numbers to specify the attack time (in seconds) of an envelope applied to each sample.',
|
||||
],
|
||||
['f', 'bandf', 'a pattern of numbers from 0 to 1. Sets the center frequency of the band-pass filter.'],
|
||||
['f', 'bandq', 'a pattern of anumbers from 0 to 1. Sets the q-factor of the band-pass filter.'],
|
||||
[
|
||||
'f',
|
||||
'begin',
|
||||
'a pattern of numbers from 0 to 1. Skips the beginning of each sample, e.g. `0.25` to cut off the first quarter from each sample.',
|
||||
],
|
||||
['f', 'legato', 'controls the amount of overlap between two adjacent sounds'],
|
||||
// ['f', 'clhatdecay', ''],
|
||||
[
|
||||
'f',
|
||||
'crush',
|
||||
'bit crushing, a pattern of numbers from 1 (for drastic reduction in bit-depth) to 16 (for barely no reduction).',
|
||||
],
|
||||
[
|
||||
'f',
|
||||
'coarse',
|
||||
'fake-resampling, a pattern of numbers for lowering the sample rate, i.e. 1 for original 2 for half, 3 for a third and so on.',
|
||||
],
|
||||
['i', 'channel', 'choose the channel the pattern is sent to in superdirt'],
|
||||
[
|
||||
'i',
|
||||
'cut',
|
||||
'In the style of classic drum-machines, `cut` will stop a playing sample as soon as another samples with in same cutgroup is to be played. An example would be an open hi-hat followed by a closed one, essentially muting the open.',
|
||||
],
|
||||
['f', 'cutoff', 'a pattern of numbers from 0 to 1. Applies the cutoff frequency of the low-pass filter.'],
|
||||
// ['f', 'cutoffegint', ''],
|
||||
['f', 'decay', ''],
|
||||
['f', 'delay', 'a pattern of numbers from 0 to 1. Sets the level of the delay signal.'],
|
||||
['f', 'delayfeedback', 'a pattern of numbers from 0 to 1. Sets the amount of delay feedback.'],
|
||||
['f', 'delaytime', 'a pattern of numbers from 0 to 1. Sets the length of the delay.'],
|
||||
['f', 'detune', ''],
|
||||
['f', 'djf', 'DJ filter, below 0.5 is low pass filter, above is high pass filter.'],
|
||||
[
|
||||
'f',
|
||||
'dry',
|
||||
'when set to `1` will disable all reverb for this pattern. See `room` and `size` for more information about reverb.',
|
||||
],
|
||||
[
|
||||
'f',
|
||||
'end',
|
||||
'the same as `begin`, but cuts the end off samples, shortening them; e.g. `0.75` to cut off the last quarter of each sample.',
|
||||
],
|
||||
[
|
||||
'f',
|
||||
'fadeTime',
|
||||
"Used when using begin/end or chop/striate and friends, to change the fade out time of the 'grain' envelope.",
|
||||
],
|
||||
[
|
||||
'f',
|
||||
'fadeInTime',
|
||||
'As with fadeTime, but controls the fade in time of the grain envelope. Not used if the grain begins at position 0 in the sample.',
|
||||
],
|
||||
['f', 'freq', ''],
|
||||
[
|
||||
'f',
|
||||
'gain',
|
||||
'a pattern of numbers that specify volume. Values less than 1 make the sound quieter. Values greater than 1 make the sound louder. For the linear equivalent, see @amp@.',
|
||||
],
|
||||
['f', 'gate', ''],
|
||||
// ['f', 'hatgrain', ''],
|
||||
[
|
||||
'f',
|
||||
'hcutoff',
|
||||
'a pattern of numbers from 0 to 1. Applies the cutoff frequency of the high-pass filter. Also has alias @hpf@',
|
||||
],
|
||||
[
|
||||
'f',
|
||||
'hold',
|
||||
'a pattern of numbers to specify the hold time (in seconds) of an envelope applied to each sample. Only takes effect if `attack` and `release` are also specified.',
|
||||
],
|
||||
[
|
||||
'f',
|
||||
'hresonance',
|
||||
'a pattern of numbers from 0 to 1. Applies the resonance of the high-pass filter. Has alias @hpq@',
|
||||
],
|
||||
// ['f', 'lagogo', ''],
|
||||
// ['f', 'lclap', ''],
|
||||
// ['f', 'lclaves', ''],
|
||||
// ['f', 'lclhat', ''],
|
||||
// ['f', 'lcrash', ''],
|
||||
['f', 'leslie', ''],
|
||||
['f', 'lrate', ''],
|
||||
['f', 'lsize', ''],
|
||||
// ['f', 'lfo', ''],
|
||||
// ['f', 'lfocutoffint', ''],
|
||||
// ['f', 'lfodelay', ''],
|
||||
// ['f', 'lfoint', ''],
|
||||
// ['f', 'lfopitchint', ''],
|
||||
// ['f', 'lfoshape', ''],
|
||||
// ['f', 'lfosync', ''],
|
||||
// ['f', 'lhitom', ''],
|
||||
// ['f', 'lkick', ''],
|
||||
// ['f', 'llotom', ''],
|
||||
[
|
||||
'f',
|
||||
'lock',
|
||||
'A pattern of numbers. Specifies whether delaytime is calculated relative to cps. When set to 1, delaytime is a direct multiple of a cycle.',
|
||||
],
|
||||
['f', 'loop', 'loops the sample (from `begin` to `end`) the specified number of times.'],
|
||||
// ['f', 'lophat', ''],
|
||||
// ['f', 'lsnare', ''],
|
||||
['f', 'n', 'The note or sample number to choose for a synth or sampleset'],
|
||||
['f', 'note', 'The note or pitch to play a sound or synth with'],
|
||||
['f', 'degree', ''],
|
||||
['f', 'mtranspose', ''],
|
||||
['f', 'ctranspose', ''],
|
||||
['f', 'harmonic', ''],
|
||||
['f', 'stepsPerOctave', ''],
|
||||
['f', 'octaveR', ''],
|
||||
[
|
||||
'f',
|
||||
'nudge',
|
||||
'Nudges events into the future by the specified number of seconds. Negative numbers work up to a point as well (due to internal latency)',
|
||||
],
|
||||
['i', 'octave', ''],
|
||||
['f', 'offset', ''],
|
||||
// ['f', 'ophatdecay', ''],
|
||||
[
|
||||
'i',
|
||||
'orbit',
|
||||
'a pattern of numbers. An `orbit` is a global parameter context for patterns. Patterns with the same orbit will share hardware output bus offset and global effects, e.g. reverb and delay. The maximum number of orbits is specified in the superdirt startup, numbers higher than maximum will wrap around.',
|
||||
],
|
||||
['f', 'overgain', ''],
|
||||
['f', 'overshape', ''],
|
||||
[
|
||||
'f',
|
||||
'pan',
|
||||
'a pattern of numbers between 0 and 1, from left to right (assuming stereo), once round a circle (assuming multichannel)',
|
||||
],
|
||||
[
|
||||
'f',
|
||||
'panspan',
|
||||
'a pattern of numbers between -inf and inf, which controls how much multichannel output is fanned out (negative is backwards ordering)',
|
||||
],
|
||||
[
|
||||
'f',
|
||||
'pansplay',
|
||||
'a pattern of numbers between 0.0 and 1.0, which controls the multichannel spread range (multichannel only)',
|
||||
],
|
||||
[
|
||||
'f',
|
||||
'panwidth',
|
||||
'a pattern of numbers between 0.0 and inf, which controls how much each channel is distributed over neighbours (multichannel only)',
|
||||
],
|
||||
[
|
||||
'f',
|
||||
'panorient',
|
||||
'a pattern of numbers between -1.0 and 1.0, which controls the relative position of the centre pan in a pair of adjacent speakers (multichannel only)',
|
||||
],
|
||||
// ['f', 'pitch1', ''],
|
||||
// ['f', 'pitch2', ''],
|
||||
// ['f', 'pitch3', ''],
|
||||
// ['f', 'portamento', ''],
|
||||
['f', 'rate', "used in SuperDirt softsynths as a control rate or 'speed'"],
|
||||
[
|
||||
'f',
|
||||
'release',
|
||||
'a pattern of numbers to specify the release time (in seconds) of an envelope applied to each sample.',
|
||||
],
|
||||
['f', 'resonance', 'a pattern of numbers from 0 to 1. Specifies the resonance of the low-pass filter.'],
|
||||
['f', 'room', 'a pattern of numbers from 0 to 1. Sets the level of reverb.'],
|
||||
// ['f', 'sagogo', ''],
|
||||
// ['f', 'sclap', ''],
|
||||
// ['f', 'sclaves', ''],
|
||||
// ['f', 'scrash', ''],
|
||||
['f', 'semitone', ''],
|
||||
[
|
||||
'f',
|
||||
'shape',
|
||||
'wave shaping distortion, a pattern of numbers from 0 for no distortion up to 1 for loads of distortion.',
|
||||
],
|
||||
[
|
||||
'f',
|
||||
'size',
|
||||
'a pattern of numbers from 0 to 1. Sets the perceptual size (reverb time) of the `room` to be used in reverb.',
|
||||
],
|
||||
['f', 'slide', ''],
|
||||
[
|
||||
'f',
|
||||
'speed',
|
||||
'a pattern of numbers which changes the speed of sample playback, i.e. a cheap way of changing pitch. Negative values will play the sample backwards!',
|
||||
],
|
||||
['f', 'squiz', ''],
|
||||
['f', 'stutterdepth', ''],
|
||||
['f', 'stuttertime', ''],
|
||||
['f', 'sustain', ''],
|
||||
['f', 'timescale', ''],
|
||||
['f', 'timescalewin', ''],
|
||||
// ['f', 'tomdecay', ''],
|
||||
[
|
||||
's',
|
||||
'unit',
|
||||
'used in conjunction with `speed`, accepts values of "r" (rate, default behavior), "c" (cycles), or "s" (seconds). Using `unit "c"` means `speed` will be interpreted in units of cycles, e.g. `speed "1"` means samples will be stretched to fill a cycle. Using `unit "s"` means the playback speed will be adjusted so that the duration is the number of seconds specified by `speed`.',
|
||||
],
|
||||
['f', 'velocity', ''],
|
||||
// ['f', 'vcfegint', ''],
|
||||
// ['f', 'vcoegint', ''],
|
||||
['f', 'voice', ''],
|
||||
[
|
||||
's',
|
||||
'vowel',
|
||||
'formant filter to make things sound like vowels, a pattern of either `a`, `e`, `i`, `o` or `u`. Use a rest (`~`) for no effect.',
|
||||
],
|
||||
['f', 'waveloss', ''],
|
||||
['f', 'dur', ''],
|
||||
// ['f', 'modwheel', ''],
|
||||
['f', 'expression', ''],
|
||||
['f', 'sustainpedal', ''],
|
||||
['f', 'tremolodepth', "Tremolo Audio DSP effect | params are 'tremolorate' and 'tremolodepth'"],
|
||||
['f', 'tremolorate', "Tremolo Audio DSP effect | params are 'tremolorate' and 'tremolodepth'"],
|
||||
['f', 'phaserdepth', "Phaser Audio DSP effect | params are 'phaserrate' and 'phaserdepth'"],
|
||||
['f', 'phaserrate', "Phaser Audio DSP effect | params are 'phaserrate' and 'phaserdepth'"],
|
||||
['f', 'fshift', 'frequency shifter'],
|
||||
['f', 'fshiftnote', 'frequency shifter'],
|
||||
['f', 'fshiftphase', 'frequency shifter'],
|
||||
['f', 'triode', 'tube distortion'],
|
||||
['f', 'krush', 'shape/bass enhancer'],
|
||||
['f', 'kcutoff', ''],
|
||||
['f', 'octer', 'octaver effect'],
|
||||
['f', 'octersub', 'octaver effect'],
|
||||
['f', 'octersubsub', 'octaver effect'],
|
||||
['f', 'ring', 'ring modulation'],
|
||||
['f', 'ringf', 'ring modulation'],
|
||||
['f', 'ringdf', 'ring modulation'],
|
||||
['f', 'distort', 'noisy fuzzy distortion'],
|
||||
['f', 'freeze', 'Spectral freeze'],
|
||||
['f', 'xsdelay', ''],
|
||||
['f', 'tsdelay', ''],
|
||||
['f', 'real', 'Spectral conform'],
|
||||
['f', 'imag', ''],
|
||||
['f', 'enhance', 'Spectral enhance'],
|
||||
['f', 'partials', ''],
|
||||
['f', 'comb', 'Spectral comb'],
|
||||
['f', 'smear', 'Spectral smear'],
|
||||
['f', 'scram', 'Spectral scramble'],
|
||||
['f', 'binshift', 'Spectral binshift'],
|
||||
['f', 'hbrick', 'High pass sort of spectral filter'],
|
||||
['f', 'lbrick', 'Low pass sort of spectral filter'],
|
||||
['f', 'midichan', ''],
|
||||
['f', 'control', ''],
|
||||
['f', 'ccn', ''],
|
||||
['f', 'ccv', ''],
|
||||
['f', 'polyTouch', ''],
|
||||
['f', 'midibend', ''],
|
||||
['f', 'miditouch', ''],
|
||||
['f', 'ctlNum', ''],
|
||||
['f', 'frameRate', ''],
|
||||
['f', 'frames', ''],
|
||||
['f', 'hours', ''],
|
||||
['s', 'midicmd', ''],
|
||||
['f', 'minutes', ''],
|
||||
['f', 'progNum', ''],
|
||||
['f', 'seconds', ''],
|
||||
['f', 'songPtr', ''],
|
||||
['f', 'uid', ''],
|
||||
['f', 'val', ''],
|
||||
['f', 'cps', ''],
|
||||
];
|
||||
|
||||
const _name = (name, ...pats) => sequence(...pats).withValue((x) => ({ [name]: x }));
|
||||
|
||||
const _unionise = (func) =>
|
||||
function (...pats) {
|
||||
return this.union(func(...pats));
|
||||
};
|
||||
|
||||
generic_params.forEach(([type, name, description]) => {
|
||||
controls[name] = (...pats) => _name(name, ...pats);
|
||||
Pattern.prototype[name] = _unionise(controls[name]);
|
||||
});
|
||||
|
||||
export default controls;
|
||||
26
packages/core/examples/basic.html
Normal file
26
packages/core/examples/basic.html
Normal file
@ -0,0 +1,26 @@
|
||||
<input
|
||||
type="text"
|
||||
id="text"
|
||||
value="cat('a', 'b')"
|
||||
style="width: 100%; font-size: 2em; outline: none; margin-bottom: 10px"
|
||||
spellcheck="false"
|
||||
/>
|
||||
<div id="output"></div>
|
||||
<script type="module">
|
||||
const strudel = await import('https://cdn.skypack.dev/@strudel.cycles/core@0.0.2');
|
||||
Object.assign(window, strudel); // assign all strudel functions to global scope to use with eval
|
||||
const input = document.getElementById('text');
|
||||
const getEvents = () => {
|
||||
const code = document.getElementById('text').value;
|
||||
const pattern = eval(code);
|
||||
const events = pattern.firstCycle();
|
||||
console.log(code, '->', events);
|
||||
document.getElementById('output').innerHTML = events.map((e) => e.show()).join('<br/>');
|
||||
};
|
||||
getEvents();
|
||||
input.addEventListener('input', () => getEvents());
|
||||
</script>
|
||||
<p>
|
||||
This page shows how skypack can be used to import strudel core directly into a simple html page. <br />
|
||||
No server, no bundler and no build setup is needed to run this!
|
||||
</p>
|
||||
34
packages/core/examples/canvas.html
Normal file
34
packages/core/examples/canvas.html
Normal file
@ -0,0 +1,34 @@
|
||||
<body style="margin: 0">
|
||||
<input
|
||||
type="text"
|
||||
id="text"
|
||||
value="cat('orange', 'indigo')"
|
||||
style="width: 100%; font-size: 2em; background: black; color: white; outline: none; position: absolute; top: 0"
|
||||
spellcheck="false"
|
||||
/>
|
||||
<canvas id="canvas"></canvas>
|
||||
<script type="module">
|
||||
const strudel = await import('https://cdn.skypack.dev/@strudel.cycles/core@0.0.2');
|
||||
// this adds all strudel functions to the global scope, to be used by eval
|
||||
Object.assign(window, strudel);
|
||||
// setup elements
|
||||
const input = document.getElementById('text');
|
||||
const canvas = document.getElementById('canvas');
|
||||
canvas.width = window.innerWidth;
|
||||
canvas.height = window.innerHeight;
|
||||
const ctx = canvas.getContext('2d');
|
||||
input.focus(); // autofocus
|
||||
input.setSelectionRange(input.value.length, input.value.length); // move cursor to end
|
||||
paint(input.value); // initial paint
|
||||
input.addEventListener('input', (e) => paint(e.target.value)); // repaint on input
|
||||
|
||||
function paint(code) {
|
||||
const pattern = eval(code); // run code
|
||||
const events = pattern.firstCycle(); // query first cycle
|
||||
events.forEach((event) => {
|
||||
ctx.fillStyle = event.value;
|
||||
ctx.fillRect(event.whole.begin * canvas.width, 0, event.duration * canvas.width, canvas.height);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
33
packages/osc/README.md
Normal file
33
packages/osc/README.md
Normal file
@ -0,0 +1,33 @@
|
||||
# @strudel.cycles/osc
|
||||
|
||||
OSC messaging between strudel and super collider?
|
||||
|
||||
## Sniffing Tidal Messages
|
||||
|
||||
```sh
|
||||
npm run tidal-sniffer
|
||||
```
|
||||
|
||||
Now open a .tidal file and play something. There should be logs like:
|
||||
|
||||
```log
|
||||
received: /dirt/play [
|
||||
'_id_', '1',
|
||||
'cps', 0.5625,
|
||||
'cutoff', 100,
|
||||
'cycle', 724.1875,
|
||||
'delta', 0.11111068725585938,
|
||||
'orbit', 0,
|
||||
's', 'arpy'
|
||||
]
|
||||
```
|
||||
|
||||
## Web Client + Server (WIP)
|
||||
|
||||
```sh
|
||||
npm run client
|
||||
npm run server # another terminal
|
||||
```
|
||||
|
||||
Then go to [http://localhost:4321](localhost:4321) and push the button.
|
||||
In your server terminal, there should be a log.
|
||||
22
packages/osc/index.html
Normal file
22
packages/osc/index.html
Normal file
@ -0,0 +1,22 @@
|
||||
<button id="send" style="font-size: 2em">Thank you @ojack</button>
|
||||
<script type="text/javascript" src="./node_modules/osc-js/lib/osc.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
const comm = new OSC();
|
||||
comm.open(); // connect by default to ws://localhost:8080
|
||||
document.getElementById('send').addEventListener('click', function () {
|
||||
// this will send a message via websocket to the node server
|
||||
|
||||
// the great question: how do i know which time super collider is at????????
|
||||
const message = new OSC.Message(
|
||||
'/dirt/play',
|
||||
...['_id_', '1', 'cps', 0.5625, 'cycle', 412.3333435058594, 'delta', 0.592592716217041, 'orbit', 0, 's', 'hh'],
|
||||
);
|
||||
|
||||
comm.send(message);
|
||||
console.log('sent:', message.address, message.args);
|
||||
});
|
||||
/*
|
||||
comm.on('*', (m) => {
|
||||
console.log('received:', m.address, m.args);
|
||||
}); */
|
||||
</script>
|
||||
23
packages/osc/osc.mjs
Normal file
23
packages/osc/osc.mjs
Normal file
@ -0,0 +1,23 @@
|
||||
import OSC from './node_modules/osc-js/lib/osc.js';
|
||||
import { Pattern } from '@strudel.cycles/core/strudel.mjs';
|
||||
|
||||
const comm = new OSC();
|
||||
comm.open();
|
||||
const latency = 0.1;
|
||||
|
||||
Pattern.prototype.osc = function () {
|
||||
return this._withEvent((event) => {
|
||||
const onTrigger = (time, event, currentTime) => {
|
||||
// time should be audio time of onset
|
||||
// currentTime should be current time of audio context (slightly before time)
|
||||
const keyvals = Object.entries(event.value).flat();
|
||||
const offset = (time - currentTime + latency) * 1000;
|
||||
const ts = Math.floor(Date.now() + offset);
|
||||
const message = new OSC.Message('/dirt/play', ...keyvals);
|
||||
const bundle = new OSC.Bundle([message], ts);
|
||||
bundle.timestamp(ts); // workaround for https://github.com/adzialocha/osc-js/issues/60
|
||||
comm.send(bundle);
|
||||
};
|
||||
return event.setContext({ ...event.context, onTrigger });
|
||||
});
|
||||
};
|
||||
76
packages/osc/package-lock.json
generated
Normal file
76
packages/osc/package-lock.json
generated
Normal file
@ -0,0 +1,76 @@
|
||||
{
|
||||
"name": "@strudel.cycles/osc",
|
||||
"version": "0.0.1",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@strudel.cycles/osc",
|
||||
"version": "0.0.1",
|
||||
"license": "GPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"osc-js": "^2.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/isomorphic-ws": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz",
|
||||
"integrity": "sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==",
|
||||
"peerDependencies": {
|
||||
"ws": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/osc-js": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/osc-js/-/osc-js-2.3.0.tgz",
|
||||
"integrity": "sha512-P2Oy9tf8Z9lQw8JZeR62HNqbKdxj7Kqbsag+ImiJvyxPDReGMVt5LtZbMh/7Ve/wbYEGODkQdFAaLHFVkIlHPw==",
|
||||
"dependencies": {
|
||||
"isomorphic-ws": "4.0.1",
|
||||
"ws": "8.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "8.5.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz",
|
||||
"integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"bufferutil": "^4.0.1",
|
||||
"utf-8-validate": "^5.0.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"bufferutil": {
|
||||
"optional": true
|
||||
},
|
||||
"utf-8-validate": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"isomorphic-ws": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz",
|
||||
"integrity": "sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==",
|
||||
"requires": {}
|
||||
},
|
||||
"osc-js": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/osc-js/-/osc-js-2.3.0.tgz",
|
||||
"integrity": "sha512-P2Oy9tf8Z9lQw8JZeR62HNqbKdxj7Kqbsag+ImiJvyxPDReGMVt5LtZbMh/7Ve/wbYEGODkQdFAaLHFVkIlHPw==",
|
||||
"requires": {
|
||||
"isomorphic-ws": "4.0.1",
|
||||
"ws": "8.5.0"
|
||||
}
|
||||
},
|
||||
"ws": {
|
||||
"version": "8.5.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz",
|
||||
"integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==",
|
||||
"requires": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
32
packages/osc/package.json
Normal file
32
packages/osc/package.json
Normal file
@ -0,0 +1,32 @@
|
||||
{
|
||||
"name": "@strudel.cycles/osc",
|
||||
"version": "0.0.1",
|
||||
"description": "OSC messaging for strudel",
|
||||
"main": "osc.mjs",
|
||||
"scripts": {
|
||||
"test": "echo \"No tests present.\" && exit 0",
|
||||
"server": "node server.js",
|
||||
"tidal-sniffer": "node tidal-sniffer.js",
|
||||
"client": "npx serve -p 4321"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/tidalcycles/strudel.git"
|
||||
},
|
||||
"keywords": [
|
||||
"tidalcycles",
|
||||
"strudel",
|
||||
"pattern",
|
||||
"livecoding",
|
||||
"algorave"
|
||||
],
|
||||
"author": "Felix Roos <flix91@gmail.com>",
|
||||
"license": "GPL-3.0-or-later",
|
||||
"bugs": {
|
||||
"url": "https://github.com/tidalcycles/strudel/issues"
|
||||
},
|
||||
"homepage": "https://github.com/tidalcycles/strudel#readme",
|
||||
"dependencies": {
|
||||
"osc-js": "^2.3.0"
|
||||
}
|
||||
}
|
||||
30
packages/osc/server.js
Normal file
30
packages/osc/server.js
Normal file
@ -0,0 +1,30 @@
|
||||
const OSC = require('osc-js');
|
||||
|
||||
const config = {
|
||||
receiver: 'ws', // @param {string} Where messages sent via 'send' method will be delivered to, 'ws' for Websocket clients, 'udp' for udp client
|
||||
udpServer: {
|
||||
host: 'localhost', // @param {string} Hostname of udp server to bind to
|
||||
port: 57121, // @param {number} Port of udp client for messaging
|
||||
// enabling the following line will receive tidal messages:
|
||||
// port: 57120, // @param {number} Port of udp client for messaging
|
||||
exclusive: false, // @param {boolean} Exclusive flag
|
||||
},
|
||||
udpClient: {
|
||||
host: 'localhost', // @param {string} Hostname of udp client for messaging
|
||||
port: 57120, // @param {number} Port of udp client for messaging
|
||||
},
|
||||
wsServer: {
|
||||
host: 'localhost', // @param {string} Hostname of WebSocket server
|
||||
port: 8080, // @param {number} Port of WebSocket server
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
const osc = new OSC({ plugin: new OSC.BridgePlugin(config) });
|
||||
|
||||
osc.open(); // start a WebSocket server on port 8080
|
||||
|
||||
console.log('osc client running on port', config.udpClient.port);
|
||||
console.log('osc server running on port', config.udpServer.port);
|
||||
console.log('websocket server running on port', config.wsServer.port);
|
||||
125
packages/osc/tidal-sniffer.js
Normal file
125
packages/osc/tidal-sniffer.js
Normal file
@ -0,0 +1,125 @@
|
||||
const OSC = require('osc-js');
|
||||
|
||||
const config = {
|
||||
receiver: 'ws', // @param {string} Where messages sent via 'send' method will be delivered to, 'ws' for Websocket clients, 'udp' for udp client
|
||||
udpServer: {
|
||||
host: 'localhost', // @param {string} Hostname of udp server to bind to
|
||||
port: 57120, // @param {number} Port of udp client for messaging
|
||||
exclusive: false, // @param {boolean} Exclusive flag
|
||||
},
|
||||
udpClient: {
|
||||
host: 'localhost', // @param {string} Hostname of udp client for messaging
|
||||
// port: 57120, // @param {number} Port of udp client for messaging
|
||||
port: 41235, // @param {number} Port of udp client for messaging
|
||||
},
|
||||
wsServer: {
|
||||
host: 'localhost', // @param {string} Hostname of WebSocket server
|
||||
port: 8080, // @param {number} Port of WebSocket server
|
||||
},
|
||||
};
|
||||
const osc = new OSC({ plugin: new OSC.BridgePlugin(config) });
|
||||
|
||||
osc.open(); // start a WebSocket server on port 8080
|
||||
|
||||
console.log('osc client running on port', config.udpClient.port);
|
||||
console.log('osc server running on port', config.udpServer.port);
|
||||
console.log('websocket server running on port', config.wsServer.port);
|
||||
|
||||
// listen for messages from the client
|
||||
osc.on('*', (m) => {
|
||||
console.log('received:', m.address, m.args);
|
||||
});
|
||||
|
||||
/*
|
||||
example tidal messages:
|
||||
|
||||
/*
|
||||
|
||||
received: /dirt/play [
|
||||
'_id_',
|
||||
'1',
|
||||
'cps',
|
||||
0.5625,
|
||||
'cycle',
|
||||
503.5,
|
||||
'delta',
|
||||
0.8888888359069824,
|
||||
'orbit',
|
||||
0,
|
||||
's',
|
||||
'bd'
|
||||
]
|
||||
received: /dirt/play [
|
||||
'_id_', '1',
|
||||
'cps', 0.5625,
|
||||
'cycle', 503.6666564941406,
|
||||
'delta', 0.592592716217041,
|
||||
'orbit', 0,
|
||||
's', 'hh'
|
||||
]
|
||||
received: /dirt/play [
|
||||
'_id_',
|
||||
'1',
|
||||
'cps',
|
||||
0.5625,
|
||||
'cycle',
|
||||
504,
|
||||
'delta',
|
||||
0.8888888359069824,
|
||||
'orbit',
|
||||
0,
|
||||
's',
|
||||
'bd'
|
||||
]
|
||||
received: /dirt/play [
|
||||
'_id_',
|
||||
'1',
|
||||
'cps',
|
||||
0.5625,
|
||||
'cycle',
|
||||
504,
|
||||
'delta',
|
||||
0.592592716217041,
|
||||
'orbit',
|
||||
0,
|
||||
's',
|
||||
'hh'
|
||||
]
|
||||
received: /dirt/play [
|
||||
'_id_',
|
||||
'1',
|
||||
'cps',
|
||||
0.5625,
|
||||
'cycle',
|
||||
504.3333435058594,
|
||||
'delta',
|
||||
0.5925922393798828,
|
||||
'orbit',
|
||||
0,
|
||||
's',
|
||||
'hh'
|
||||
]
|
||||
received: /dirt/play [
|
||||
'_id_',
|
||||
'1',
|
||||
'cps',
|
||||
0.5625,
|
||||
'cycle',
|
||||
504.5,
|
||||
'delta',
|
||||
0.8888888359069824,
|
||||
'orbit',
|
||||
0,
|
||||
's',
|
||||
'bd'
|
||||
]
|
||||
received: /dirt/play [
|
||||
'_id_', '1',
|
||||
'cps', 0.5625,
|
||||
'cycle', 504.6666564941406,
|
||||
'delta', 0.592592716217041,
|
||||
'orbit', 0,
|
||||
's', 'hh'
|
||||
]
|
||||
|
||||
*/
|
||||
@ -28,8 +28,10 @@ import '@strudel.cycles/xen/tune.mjs';
|
||||
import '@strudel.cycles/core/euclid.mjs';
|
||||
import '@strudel.cycles/tone/pianoroll.mjs';
|
||||
import '@strudel.cycles/tone/draw.mjs';
|
||||
import '@strudel.cycles/osc/osc.mjs';
|
||||
import controls from '@strudel.cycles/core/controls.mjs';
|
||||
|
||||
extend(Tone, strudel, strudel.Pattern.prototype.bootstrap(), toneHelpers, voicingHelpers, drawHelpers, uiHelpers, {
|
||||
extend(Tone, strudel, strudel.Pattern.prototype.bootstrap(), controls, toneHelpers, voicingHelpers, drawHelpers, uiHelpers, {
|
||||
gist,
|
||||
euclid,
|
||||
mini,
|
||||
@ -76,8 +78,10 @@ function App() {
|
||||
if (e.ctrlKey || e.altKey) {
|
||||
if (e.code === 'Enter') {
|
||||
await activateCode();
|
||||
e.preventDefault();
|
||||
} else if (e.code === 'Period') {
|
||||
cycle.stop();
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -42,7 +42,7 @@ function useCycle(props) {
|
||||
?.filter((event) => event.part.begin.equals(event.whole.begin))
|
||||
.forEach((event) => {
|
||||
Tone.getTransport().schedule((time) => {
|
||||
onEvent(time, event);
|
||||
onEvent(time, event, Tone.getContext().currentTime);
|
||||
Tone.Draw.schedule(() => {
|
||||
// do drawing or DOM manipulation here
|
||||
onDraw?.(time, event);
|
||||
|
||||
@ -27,7 +27,7 @@ function useRepl({ tune, defaultSynth, autolink = true, onEvent, onDraw }) {
|
||||
const cycle = useCycle({
|
||||
onDraw,
|
||||
onEvent: useCallback(
|
||||
(time, event) => {
|
||||
(time, event, currentTime) => {
|
||||
try {
|
||||
onEvent?.(event);
|
||||
const { onTrigger, velocity } = event.context;
|
||||
@ -41,7 +41,7 @@ function useRepl({ tune, defaultSynth, autolink = true, onEvent, onDraw }) {
|
||||
/* console.warn('no instrument chosen', event);
|
||||
throw new Error(`no instrument chosen for ${JSON.stringify(event)}`); */
|
||||
} else {
|
||||
onTrigger(time, event);
|
||||
onTrigger(time, event, currentTime);
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn(err);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user