Compare commits

...

364 Commits

Author SHA1 Message Date
Felix Roos 18d7222804 Publish
- @strudel.cycles/core@0.7.2
 - @strudel.cycles/csound@0.7.1
 - @strudel.cycles/eval@0.7.1
 - @strudel.cycles/midi@0.7.1
 - @strudel.cycles/mini@0.7.2
 - @strudel.cycles/osc@0.7.1
 - @strudel.cycles/react@0.7.1
 - @strudel.cycles/serial@0.7.1
 - @strudel.cycles/soundfonts@0.7.1
 - @strudel.cycles/tonal@0.7.1
 - @strudel.cycles/tone@0.7.1
 - @strudel.cycles/transpiler@0.7.1
 - @strudel.cycles/webaudio@0.7.1
 - @strudel.cycles/webdirt@0.7.1
 - @strudel.cycles/xen@0.7.1
2023-03-23 11:34:07 +01:00
Felix Roos ff1c37d361 bump core + mini to 0.7.1 2023-03-23 11:26:23 +01:00
Felix Roos 3abe6db3de Publish
- @strudel.cycles/core@0.7.0
 - @strudel.cycles/csound@0.7.0
 - @strudel.cycles/eval@0.7.0
 - @strudel.cycles/midi@0.7.0
 - @strudel.cycles/mini@0.7.0
 - @strudel.cycles/osc@0.7.0
 - @strudel.cycles/react@0.7.0
 - @strudel.cycles/serial@0.7.0
 - @strudel.cycles/soundfonts@0.7.0
 - @strudel.cycles/tonal@0.7.0
 - @strudel.cycles/tone@0.7.0
 - @strudel.cycles/transpiler@0.7.0
 - @strudel.cycles/webaudio@0.7.0
 - @strudel.cycles/webdirt@0.7.0
 - @strudel.cycles/xen@0.7.0
2023-03-23 11:21:02 +01:00
Felix Roos b9ca9d3b06 update lerna 2023-03-23 11:17:21 +01:00
Felix Roos 9bbc04843d Merge pull request #534 from tidalcycles/soundfont-freq-support
feat: add freq support to gm soundfonts
2023-03-23 10:21:55 +01:00
Felix Roos ddf61a6438 Merge pull request #531 from ijc8/main
Maintain random seed state in parser, not globally
2023-03-23 10:18:58 +01:00
Felix Roos ba35a81e9b - feat: add freq support to gm soundfonts
- refactor: toMidi -> noteToMidi
- refactor: fromMidi -> midiToFreq
2023-03-23 10:18:24 +01:00
Felix Roos 13133583ca Merge pull request #527 from bwagner/main
FIXES: alias pm for polymeter
2023-03-21 22:35:17 +01:00
Felix Roos e91b981740 Merge pull request #529 from revolunet/patch-1
fix(footer): fix link to tidalcycles
2023-03-21 22:29:06 +01:00
Bernhard Wagner 1458ee64ec FIXES: alias pm for polymeter 2023-03-21 21:20:57 +01:00
Ian Clester 4e2a5e1663 Add pairwise independence test, tweak randOffset 2023-03-20 22:27:35 -04:00
Ian Clester c98b0e9687 Format code with prettier 2023-03-20 19:19:42 -04:00
Ian Clester 4e25990de9 Uncomment test for random choice operator (|) 2023-03-20 17:46:57 -04:00
Ian Clester 4b51f1a3fc Sync up ? with degrade() 2023-03-20 15:26:27 -04:00
Ian Clester 5490b01004 Maintain random seed state in parser, not globally 2023-03-20 14:59:43 -04:00
Julien Bouquillon 43176e1f1d fix(footer): fix link to tidalcycles 2023-03-20 01:38:21 +01:00
Bernhard Wagner fffd3c07ca FIXES: alias pm for polymeter
(return statement was missing)
2023-03-18 22:12:12 +01:00
Felix Roos 952bd38e70 Merge pull request #525 from bwagner/patch-4
Update intro.mdx
2023-03-18 20:25:21 +01:00
Felix Roos 8589f5cc89 Merge pull request #524 from bwagner/patch-3
Update samples.mdx
2023-03-18 20:24:38 +01:00
Bernhard Wagner ec594d52f8 Update intro.mdx
shortly -> concisely
2023-03-18 19:31:57 +01:00
Bernhard Wagner 848cbf9c60 Update samples.mdx
fixes few typos and the former `samples` tab is now `sounds`
2023-03-18 15:37:59 +01:00
Felix Roos e02be7dd9a Merge pull request #521 from tidalcycles/fix-envelope
fix: envelopes in chrome
2023-03-17 09:03:10 +01:00
Felix Roos 33d35e47a6 fix: envelope in chrome 2023-03-17 08:56:05 +01:00
Felix Roos 18d62b32c2 Merge pull request #516 from tidalcycles/source-nodes
registerSound API + improved sounds tab + regroup soundfonts
2023-03-16 16:13:30 +01:00
Felix Roos 58ed51b49c snapshot 2023-03-16 15:49:56 +01:00
Felix Roos 9f9fe529e4 fix only tune that uses soundfonts 2023-03-16 15:49:08 +01:00
Felix Roos aad721890a pass duration to source callback 2023-03-16 15:48:59 +01:00
Felix Roos e63a5d10c8 regroup sounds
- extra tab for drum-machines
- combine synths and soundfonts
2023-03-16 15:21:16 +01:00
Felix Roos 15681262de use only _ for easier copying via double click 2023-03-16 15:19:38 +01:00
Felix Roos b5d3aa81f9 format 2023-03-16 12:25:08 +01:00
Felix Roos a688061616 group soundfonts into readable names 2023-03-16 12:23:58 +01:00
Felix Roos f788d347ec add envelope to soundfonts 2023-03-15 23:02:05 +01:00
Felix Roos a33ce67dd4 fix: endless preview 2023-03-15 23:01:56 +01:00
Felix Roos 8a22c5627a fix: cut glitches due to envelope release trouble 2023-03-15 22:42:03 +01:00
Felix Roos 7b529a30f4 style sounds tab 2023-03-14 22:54:12 +01:00
Felix Roos 4910b34f26 Merge pull request #518 from tidalcycles/fonts
add 2 illegible fonts
2023-03-14 21:54:52 +01:00
Felix Roos 6a73b43e3a add 2 illegible fonts 2023-03-12 21:16:46 +01:00
Felix Roos b59b26460d soundfont list curation part 2 2023-03-12 20:48:29 +01:00
Felix Roos aa324ae25d start curating the soundfont list 2023-03-10 11:33:55 +01:00
Felix Roos ff412f6396 level down soundfonts
+ do not load in prebake (breaks static rendering)
+ instead load on import
2023-03-09 23:19:19 +01:00
Felix Roos ac148b2f32 register soundfonts as sounds too 2023-03-09 22:48:40 +01:00
Felix Roos 6f5d096e6d rename setSound to registerSound 2023-03-09 21:32:40 +01:00
Felix Roos ff1e6714c6 document setSound api 2023-03-09 21:27:24 +01:00
Felix Roos 41d82eaadf add cleanup to sound tab 2023-03-09 10:53:35 +01:00
Felix Roos be6b011204 proper audio node cleanup
+ cleanup onTriggerSample
2023-03-09 10:46:45 +01:00
Felix Roos 65e48c05f0 sounds onset and offset can be triggered independently
+ sounds tab now supports mousedown / mouseup to listen
2023-03-09 08:35:20 +01:00
Felix Roos cee08ea67d sounds list can now filter out defaults 2023-03-08 00:33:21 +01:00
Felix Roos 35ef26c013 can now pass meta data to setSound
+ added types for synth and sample
+ list sample count for samples in sounds tab (as before)
2023-03-07 21:43:01 +01:00
Felix Roos b08a0b8102 use nanostore for soundmap
+ rename tab samples to sounds
+ listed sounds are now reactive
2023-03-06 23:11:09 +01:00
Felix Roos 220342154b Merge pull request #474 from bwagner/main
Update README.md
2023-03-06 22:55:00 +01:00
Bernhard Wagner ef7182cd76 Merge branch 'tidalcycles:main' into main 2023-03-06 20:15:16 +01:00
Felix Roos 6059c69995 first draft of soundMap to register keys for s
+ refactor sampler to use it
+ refactor synth to use it
+ add 'source' control
+ wip: samples tab
+ wip: webadirt ?
+ wip: soundfonts
2023-03-05 23:27:37 +01:00
Felix Roos 892d8ba152 Merge pull request #508 from tidalcycles/arrange
add arrange function
2023-03-05 14:52:30 +01:00
Felix Roos da11069f75 Merge pull request #514 from tidalcycles/react-update
update react to 18
2023-03-05 14:29:08 +01:00
Alex McLean bf72908dc9 Support list syntax in mininotation (#512)
fixes #504

* support list syntax in mininotation
* support compound controls
* remove redundant : splitting of s/n and note/n from webaudio
* patternable scale names
2023-03-04 18:06:18 +00:00
Felix Roos 81ab5c78e2 update react to 18 2023-03-03 20:38:20 +01:00
Felix Roos de503c7d63 add snapshot 2023-03-03 20:30:35 +01:00
Felix Roos 487ccb90a1 add arrange function 2023-03-03 20:17:23 +01:00
Felix Roos 4bfeaa47bf Merge pull request #502 from tidalcycles/scale-space-alternative
can now use : as a replacement for space in scales
2023-03-03 12:40:23 +01:00
Felix Roos 9a4d8036a6 add test 2023-03-03 12:38:11 +01:00
Felix Roos a50bd6375d use : instead of _ 2023-03-03 12:37:25 +01:00
Felix Roos 14508e91a9 can now use _ as a replacement for space in scales 2023-03-02 20:39:06 +01:00
Alex McLean 2d5064bd5b Reinstate slice and splice (#500)
Reinstate slice/splice, fixing up "unit" param using new global cps
2023-03-02 14:44:41 +00:00
Felix Roos fdc4819e39 Merge pull request #501 from tidalcycles/nano-repl-highlighting-fix
fix: nano-repl highlighting
2023-03-02 14:49:30 +01:00
Felix Roos efddbb616b fix: nano-repl highlighting 2023-03-02 14:47:15 +01:00
Alex McLean db4d0f26a8 Add control aliases (#497)
* add and simplify control aliases, fixes #496
* roomsize now alias of room
* simplify webaudio param handling
---------

Co-authored-by: Felix Roos <flix91@gmail.com>
2023-03-02 13:17:12 +00:00
Felix Roos 06cffbc31b add readme to nano repl 2023-03-01 12:57:33 +01:00
Felix Roos 3cfb0f4c76 Merge pull request #493 from tidalcycles/cps
implement cps in scheduler
2023-03-01 09:27:26 +01:00
Felix Roos 436ba067bc reset cps before eval 2023-03-01 09:23:01 +01:00
Felix Roos f8724bf47e comment out cps ui 2023-03-01 00:05:23 +01:00
Felix Roos 1f7e293204 add setcps to global scope 2023-02-28 23:56:11 +01:00
Felix Roos 86d2652258 pass cps to hap trigger 2023-02-28 23:54:56 +01:00
Felix Roos f5c0cc2e2d no need to warn if evalScope is called more often 2023-02-28 23:54:40 +01:00
Felix Roos 4463d4a417 Update README.md 2023-02-28 23:25:40 +01:00
Felix Roos df35a5c9b1 Merge pull request #491 from tidalcycles/refactor-react
react style fixes
2023-02-28 23:06:29 +01:00
Felix Roos dda93b3c13 Update @strudel.cycles/react to 0.6.4 2023-02-28 23:02:52 +01:00
Felix Roos 5de40b6bf5 fix disabled button take 2 2023-02-28 23:02:28 +01:00
Felix Roos dcbe799d25 Merge pull request #490 from tidalcycles/refactor-react
refactor react package
2023-02-28 22:57:19 +01:00
Felix Roos a5641aef03 Update @strudel.cycles/react to 0.6.3 2023-02-28 22:52:57 +01:00
Felix Roos 668f97a4c8 refactor react package for publishing 2023-02-28 22:48:50 +01:00
Felix Roos e9d577496b fix disabled button 2023-02-28 22:48:06 +01:00
Felix Roos 66fba947ac Update @strudel.cycles/react to 0.6.2 2023-02-28 22:40:52 +01:00
Felix Roos 18c7e02609 fix: minirepl styles for package 2023-02-28 22:40:18 +01:00
Felix Roos 7e1351507a add variables to react tailwind config 2023-02-28 22:17:24 +01:00
Felix Roos 032acbd27c comment out autocomplete 2023-02-28 22:11:17 +01:00
Felix Roos 8b51f0fbd2 Update @strudel.cycles/react to 0.6.1 2023-02-28 21:57:03 +01:00
Felix Roos ead5942ef0 implement cps + add baasic tempo control ui 2023-02-28 13:00:35 +01:00
Felix Roos cfe369d9fe Merge pull request #488 from tidalcycles/algolia
add algolia creds + optimize sidebar for crawling
2023-02-27 23:47:34 +01:00
Felix Roos 2750a3c130 fix pseudo relative links 2023-02-27 22:59:50 +01:00
Felix Roos a7f6d6ecac add algolia config 2023-02-27 22:59:38 +01:00
Felix Roos 3680282450 Merge pull request #485 from tidalcycles/app-height-fix
fix app height
2023-02-27 19:28:09 +01:00
Felix Roos 3b3435c999 change 1px 2023-02-27 19:24:49 +01:00
Felix Roos e2ae6278c2 Merge pull request #484 from tidalcycles/revert-wip-390
Revert "Another attempt at composable functions - WIP (#390)"
2023-02-27 16:04:20 +01:00
Alex McLean 37d6723e3c Update mini-notation.mdx (#365)
`x!n` is no longer an alias for `[x*n]@n` and the behaviour is a bit different. Probably best to remove this line in case it confuses someone?
2023-02-27 14:45:19 +00:00
Felix Roos 961bbf6f55 Revert "Another attempt at composable functions - WIP (#390)"
This reverts commit cbae355896.
2023-02-27 15:20:49 +01:00
Felix Roos fc71d349ed Merge pull request #482 from tidalcycles/docs
docs: packages + offline
2023-02-27 12:52:09 +01:00
Felix Roos 97c8ab0f71 fix format 2023-02-27 12:49:23 +01:00
Felix Roos 28534ea547 add screenshots 2023-02-27 12:47:38 +01:00
Felix Roos 1a75b79a33 better offline docs
fixes https://github.com/tidalcycles/strudel/issues/424
2023-02-27 12:34:17 +01:00
Felix Roos 75dfb0af35 add dependencygraph 2023-02-27 11:12:21 +01:00
Felix Roos 073fa09648 packages doc 2023-02-27 11:10:47 +01:00
Felix Roos 3015e7b203 Merge pull request #480 from tidalcycles/fix-array-args
Fix array args
2023-02-25 14:26:49 +01:00
Felix Roos 7ac0cdc0f9 fix: lint error 2023-02-25 14:18:23 +01:00
Felix Roos 49234183a3 Merge remote-tracking branch 'origin/main' into fix-array-args 2023-02-25 14:13:55 +01:00
Felix Roos ed7763df92 fix: reify after sequence to preserve array
ref https://github.com/tidalcycles/strudel/issues/383#issuecomment-1399514090
2023-02-25 14:13:39 +01:00
Felix Roos b818a02f76 Merge pull request #478 from tidalcycles/midi-fleshout
midi cc support
2023-02-25 12:33:22 +01:00
Felix Roos 886f8449fd add midichan + docs 2023-02-25 12:23:01 +01:00
Felix Roos 5de6643604 midi: support ccn and ccv 2023-02-24 10:15:21 +01:00
Felix Roos 6283273d81 add helper hap.ensureObjectValue 2023-02-24 10:15:06 +01:00
Bernhard Wagner 5506d18bc5 Update README.md
npm -> pnpm
2023-02-23 18:28:11 +01:00
Felix Roos c8f8f02a46 Merge pull request #473 from tidalcycles/fix-hashes
fix: hash links
2023-02-23 00:11:04 +01:00
Felix Roos 4c5d6f1d6c fix: hash links 2023-02-23 00:04:53 +01:00
Felix Roos f349e36345 Merge pull request #467 from tidalcycles/vim-mode
settings tab with vim / emacs modes + additional themes and fonts
2023-02-22 22:54:39 +01:00
Felix Roos 1574c4ab13 Merge pull request #471 from bwagner/patch-1
Update input-output.mdx
2023-02-22 22:53:22 +01:00
Felix Roos 0dc9bc3ab6 Merge pull request #464 from bwagner/main
FIXES: freqs instead of pitches
2023-02-22 22:52:49 +01:00
Felix Roos 0006d57a4a move isZen to nanostore
+ correctly parse non strings in store
2023-02-22 22:47:33 +01:00
Felix Roos 70f7e73b9a store footer tab + latest code in nanostore 2023-02-22 22:23:54 +01:00
Felix Roos b67b049802 refactor settings to nanostores 2023-02-22 22:04:39 +01:00
Felix Roos ff99dbcd22 customizable font family 2023-02-22 20:28:22 +01:00
Felix Roos 0198697737 Merge pull request #472 from tidalcycles/osc-fix
fix: osc should not return a promise
2023-02-22 20:01:00 +01:00
Felix Roos a742a71f67 fix: osc should not return a promise 2023-02-22 19:50:37 +01:00
Felix Roos 8f6c1531fa more contrast for blackscreen theme 2023-02-22 19:45:06 +01:00
Bernhard Wagner d9f56f11cb Merge pull request #1 from tidalcycles/main
a
2023-02-22 17:15:00 +01:00
Bernhard Wagner 7ff877464f Update input-output.mdx
npm -> pnpm
2023-02-22 17:06:15 +01:00
Alex McLean 7c367eb1e8 slice and splice (#466)
Implements `slice` and `splice` from tidal, intended for beat slicing
2023-02-22 11:51:31 +00:00
Felix Roos 20848aac09 fix store import 2023-02-19 23:28:46 +01:00
Felix Roos 7716574076 use terminal font for console + samples 2023-02-19 23:28:33 +01:00
Felix Roos 89cd0c769b fix bluescreen line bg 2023-02-19 23:27:56 +01:00
Felix Roos a21b3d788f add settings reset button 2023-02-19 22:32:28 +01:00
Felix Roos 7994ba8b38 get rid of all hard coded colors 2023-02-19 22:18:32 +01:00
Felix Roos 81b4cb2f16 more themes + fonts 2023-02-19 22:18:12 +01:00
Felix Roos 14af1df6a3 define font + fix app height 2023-02-19 20:04:30 +01:00
Felix Roos ac3bd7d7db rename algoboy theme + add font 2023-02-19 20:04:19 +01:00
Felix Roos 632e8e9634 use css var for highlighting color 2023-02-19 20:03:12 +01:00
Felix Roos 1e3f09f69b add some custom themes 2023-02-19 18:53:35 +01:00
Felix Roos f3f18ffca7 fix app height for ios 2023-02-19 13:50:08 +01:00
Felix Roos ceb3aa0627 add emacs mode + fontSize slider 2023-02-19 13:49:55 +01:00
Felix Roos 014555fe5d add vim toggle to settings
+ added persistent global state store
+ refactored themes to use the new store
2023-02-19 01:51:31 +01:00
Alex McLean fdb76867a7 weave and weaveWith (#465)
Added weave and weaveWith from tidal
2023-02-18 00:00:18 +00:00
Felix Roos 57157fde9c Merge branch 'main' into docs 2023-02-17 21:53:02 +01:00
Felix Roos 4a3540cf2b encapsulate footer components 2023-02-17 21:48:45 +01:00
Felix Roos 540bd938f2 remove unused Footer
+ fix AvatarList (still unused)
2023-02-17 21:48:34 +01:00
Bernhard Wagner c6fbebd996 FIXES: freqs instead of pitches 2023-02-17 19:05:09 +01:00
Felix Roos 18d3a7e23a add vim extension (hard coded) 2023-02-17 10:41:49 +01:00
Alex McLean cbae355896 Another attempt at composable functions - WIP (#390)
Summary of changes:

- Made unary functions composable, including controls. So e.g. s("bd sd").every(3,fast(2).iter(4).n(4)) works the same as s("bd sd").every(3,x => x.fast(2).iter(4).n(4))
- Made operators/alignments composable too, so s("bd sd").every(3, set.squeeze.n(3, 4)) works
- Patterns are not treated as functions, so s("bd sd").every(3, n(5)) is an annoying runtime error. s("bd sd").every(3, set.n(5)) does work though.

Other minor changes:

- standardised alignment 'squeezeOut' as lowercase 'squeezeout'
- made firstCycleValues turn haps sorted in order of 'part'
2023-02-16 23:15:21 +00:00
Felix Roos 924e8a764a Merge pull request #461 from bwagner/main
FIXES: Warning about jsxBracketSameLine deprecation
2023-02-16 20:38:45 +01:00
Bernhard Wagner 40e629cb0e ADDS: Continuation to Testing 2023-02-16 15:24:44 +01:00
Bernhard Wagner 99ec261f79 ADDS: Continuation to Docs 2023-02-16 15:23:34 +01:00
Bernhard Wagner 9daea5bbc2 ADDS: Continuation to REPL 2023-02-16 15:22:35 +01:00
Bernhard Wagner 4704c03d48 ADDS: Continuation to Strudel vs Tidal 2023-02-16 15:21:25 +01:00
Bernhard Wagner 9dbf52bedb ADDS: Continuation to Pattern Alignment 2023-02-16 15:20:26 +01:00
Bernhard Wagner cfd04ab7fa ADDS: Continuation to Patterns 2023-02-16 15:19:14 +01:00
Bernhard Wagner 33dbf75e3b ADDS: Continuation to Offline 2023-02-16 15:18:32 +01:00
Bernhard Wagner 8ffaeeb171 ADDS: Continuation to MIDI and OSC 2023-02-16 15:16:51 +01:00
Bernhard Wagner 451828e5d4 ADDS: Continuation to Tonal Modifiers 2023-02-16 15:15:54 +01:00
Bernhard Wagner 176877f2b8 ADDS: Continuation to Accumulation Modifiers 2023-02-16 15:14:38 +01:00
Bernhard Wagner 61b0d08b5f ADDS: Continuation to Conditional Modifiers 2023-02-16 15:12:37 +01:00
Bernhard Wagner f50a6ce1ae ADDS: Continuation to Signals 2023-02-16 15:11:28 +01:00
Bernhard Wagner 8549d6ea8b ADDS: Continuation to Control Parameters 2023-02-16 15:09:37 +01:00
Bernhard Wagner 90a90a87a5 ADDS: Continuation to Time Modifiers 2023-02-16 15:08:45 +01:00
Bernhard Wagner 760bdea521 ADDS: Continuation to Pattern Constructors 2023-02-16 15:07:03 +01:00
Bernhard Wagner 0fd783e83f ADDS: Continuation to Functional JavaScript API 2023-02-16 15:03:58 +01:00
Bernhard Wagner 42caa9058b ADDS: Continuation to Synths 2023-02-16 15:00:17 +01:00
Bernhard Wagner 9ab315ac2c ADDS: Continuation to Samples 2023-02-16 14:58:02 +01:00
Bernhard Wagner a08ad07537 ADDS: Continuation to Csound 2023-02-16 14:55:01 +01:00
Bernhard Wagner c6ce763f7c FIXES: Warning about jsxBracketSameLine deprecation 2023-02-16 14:46:33 +01:00
Felix Roos 70d1eb17a7 Merge branch 'main' into vim-mode 2023-02-16 11:41:01 +01:00
Felix Roos 0393205eda Merge pull request #438 from bwagner/patch-8
Update synths.mdx
2023-02-14 22:11:02 +01:00
Bernhard Wagner 5c05a5a63e Update synths.mdx
in future -> in the future
ADDS: Continuation to Audio Effects.
2023-02-14 22:08:40 +01:00
Felix Roos b01d9f849b Merge pull request #437 from bwagner/patch-5
Update mini-notation.mdx
2023-02-14 19:54:41 +01:00
Felix Roos 0ab904275c Merge pull request #436 from bwagner/patch-4
Update code.mdx
2023-02-14 19:54:24 +01:00
Bernhard Wagner 5f82a86801 Update mini-notation.mdx
descreases -> decreases
twice a cycle -> twice per cycle
2023-02-14 19:40:03 +01:00
Bernhard Wagner 13a5d8de86 Update code.mdx
The code example says "sine", but the description says "sawtooth".
2023-02-14 19:08:47 +01:00
Felix Roos f024b71808 Update CONTRIBUTING.md
add more infos on code style, eslint and tests
2023-02-14 18:16:39 +01:00
Felix Roos cd35a17904 Merge pull request #433 from tidalcycles/fix-anchors
Fix anchors
2023-02-13 00:43:29 +01:00
Felix Roos be3f68ed06 fix comment 2023-02-13 00:42:16 +01:00
Felix Roos 64b8fd9584 fix: right sidebar anchors earlier 2023-02-13 00:41:42 +01:00
Felix Roos b001d1d0f1 update lockfile 2023-02-13 00:37:20 +01:00
Felix Roos 64ad75c79e fix: anchor links
fixes https://github.com/tidalcycles/strudel/issues/432
+ fix style regression in mini repl
2023-02-13 00:35:40 +01:00
Felix Roos 12eb361d69 remove slash before anchor link 2023-02-12 20:13:59 +01:00
Felix Roos dd51bd0a2f Merge pull request #427 from tidalcycles/docs
autocomplete preparations
2023-02-11 21:02:11 +01:00
Felix Roos 0fb478a7c8 no autocomplete until upstream merge 2023-02-11 20:55:35 +01:00
Felix Roos 9694ecf55b autocomplete dark / light 2023-02-11 13:02:04 +01:00
Felix Roos fe80478b8a Merge remote-tracking branch 'origin/main' into docs 2023-02-10 23:18:04 +01:00
Felix Roos a253c26bee Merge pull request #431 from tidalcycles/themes
Themes
2023-02-10 23:14:47 +01:00
Felix Roos a500f983f5 Merge branch 'main' into themes 2023-02-10 22:59:52 +01:00
Felix Roos 3579b6f8f3 dynamic highlight color
+ refactor hooks
2023-02-10 22:52:34 +01:00
Felix Roos 14cb954213 themed minirepl
+ add strudelTheme
+ add lineBackground with opacity
+ add some missing light flags
+ add fallback css variables for non js context
2023-02-10 21:42:27 +01:00
Felix Roos 6170eef441 format 2023-02-10 20:09:23 +01:00
Felix Roos ddb9af4d57 themify rest of layout (except minirepls) 2023-02-10 20:07:36 +01:00
Felix Roos 6db2c0bf93 only invert prose for dark themes 2023-02-10 18:34:02 +01:00
Felix Roos 46f70e6a5e persisted theme + add all settings 2023-02-10 12:54:15 +01:00
Felix Roos 0b994a1b0f switchable css variables for theming 2023-02-10 11:32:36 +01:00
Felix Roos 0326244956 update themes 2023-02-10 09:25:58 +01:00
Felix Roos ea1b2252fc remove unused descriptions
+ fix some titles
+ comment out unfinished packages page
+ start offline page
2023-02-09 21:42:48 +01:00
Felix Roos 0da25ea7cc Merge pull request #429 from tidalcycles/minirepl-keyboard-support
minirepl: add keyboard shortcuts
2023-02-09 19:22:56 +01:00
Felix Roos 3209afa4ff minirepl: add keyboard shortcuts 2023-02-09 19:20:44 +01:00
Felix Roos 33e2631e5d update snapshot 2023-02-09 18:48:34 +01:00
Felix Roos f02a41b675 fix param description 2023-02-09 18:44:41 +01:00
Felix Roos f497d7b212 reactify autocomplete info 2023-02-09 18:37:29 +01:00
Felix Roos 0f18f5eb81 remove logs 2023-02-09 14:16:37 +01:00
Felix Roos 8da81104aa doc: note 2023-02-09 12:42:37 +01:00
Felix Roos 97cbdc6b0f use jsdoc in autocomplete, still not context aware
+ add noAutocomplete flags
+ add superdirtOnly flags
2023-02-09 12:19:38 +01:00
Felix Roos 7f53f4e36b Merge branch 'main' into docs 2023-02-09 08:57:20 +01:00
Felix Roos c350f262e6 Merge pull request #425 from tidalcycles/pwa
add cdn.freesound to cache list
2023-02-09 08:56:42 +01:00
Felix Roos 3e727893bf basic theme picker 2023-02-08 23:43:02 +01:00
Felix Roos 2a2c7437e4 add cdn.freesound to cache list 2023-02-08 21:18:03 +01:00
Felix Roos 4f0885d3af Merge branch 'main' into themes 2023-02-08 20:49:20 +01:00
Felix Roos 5656cd2fc6 Merge pull request #421 from tidalcycles/pwa
add more offline caching
2023-02-08 20:36:00 +01:00
Felix Roos b6e42876ad fix cache rule + disable service worker in dev 2023-02-08 20:32:03 +01:00
Felix Roos 3671344867 add freesound + shabda to offline cache rule 2023-02-08 20:26:44 +01:00
Felix Roos 30b149053c basic theme switcher 2023-02-08 18:54:34 +01:00
Felix Roos cdd40df9d3 disable sw in dev 2023-02-08 18:54:19 +01:00
Felix Roos 9d661808ba Merge pull request #419 from tidalcycles/pwa
add caching strategy for missing file types + cache all samples loaded from github
2023-02-07 22:08:01 +01:00
Felix Roos 6a5d849b3c add caching strategy for missing file types
+ cache all samples loaded from github
2023-02-07 21:50:08 +01:00
Felix Roos 25bbd73a90 Merge pull request #417 from tidalcycles/pwa
PWA with offline support
2023-02-06 23:29:56 +01:00
Felix Roos 3d27a5f8c7 format 2023-02-06 21:06:33 +01:00
Felix Roos 44ef372399 update og meta data 2023-02-06 21:05:28 +01:00
Felix Roos fd09dff381 basic pwa setup 2023-02-06 20:57:34 +01:00
Felix Roos 10580a0bec change rocket to strudel 2023-02-06 20:21:09 +01:00
Felix Roos f3f87b0a46 dont lint json 2023-02-06 20:20:45 +01:00
Felix Roos ac606bcf71 Update README.md 2023-02-05 23:16:38 +01:00
alex bb50df07e7 iclc reviews 2023-02-05 21:01:02 +00:00
Felix Roos f7ac033e1c Merge pull request #411 from tidalcycles/docs
improve samples doc
2023-02-05 17:27:32 +01:00
Felix Roos a1043352c1 Merge pull request #413 from tidalcycles/remove-google
google gtfo
2023-02-05 17:27:09 +01:00
Felix Roos e15fecf6cf google gtfo 2023-02-05 17:19:32 +01:00
Felix Roos 3e99f345ef better headings 2023-02-05 16:40:29 +01:00
Felix Roos 0a77770ef3 remove double directive 2023-02-05 16:37:00 +01:00
Felix Roos 379b8392dd add snapshot 2023-02-05 16:32:51 +01:00
Felix Roos 0fdae70461 improve samples doc 2023-02-05 16:28:43 +01:00
Felix Roos 087423c754 Merge pull request #409 from tidalcycles/docs
improve effects doc
2023-02-05 16:27:59 +01:00
Felix Roos ce64c98fb7 add adsr image link 2023-02-05 15:08:17 +01:00
Felix Roos e11a8a9dbb Merge pull request #410 from bwagner/patch-3
Update effects.mdx
2023-02-05 14:56:03 +01:00
Bernhard Wagner ef4a68a24d Update effects.mdx
Wether -> Whether
2023-02-05 14:54:27 +01:00
Felix Roos 0ec65c85d3 fix examples + snapshots 2023-02-05 14:52:01 +01:00
Felix Roos 74326f226d format 2023-02-05 14:42:08 +01:00
Felix Roos 554e0d7a39 remove envelope section from synths page 2023-02-05 14:38:47 +01:00
Felix Roos 1b99ac92dd categorize effects + add envelope doc
+ use short, more logical filter params as default
2023-02-05 14:34:59 +01:00
Felix Roos ff0b77908a Merge branch 'main' into docs 2023-02-05 14:34:16 +01:00
Felix Roos 83d19638ba Merge pull request #407 from tidalcycles/shabda-doc
add shabda doc
2023-02-03 19:54:47 +01:00
Felix Roos a7d484b1a8 add shabda doc 2023-02-03 19:53:01 +01:00
Felix Roos d87beb37b8 Merge pull request #405 from tidalcycles/fix-share-on-subpath
fix: share url on subpath
2023-02-02 21:45:56 +01:00
Felix Roos dcdb383960 fix: share url on subpath
fixes https://github.com/tidalcycles/strudel/issues/389
2023-02-02 21:43:24 +01:00
Felix Roos 478a7e95ea Merge pull request #404 from tidalcycles/csound-update
update csound + fix sound output
2023-02-02 21:35:45 +01:00
Felix Roos f7b3b582cc update csound + fix sound output
see https://github.com/csound/csound/issues/1690#issuecomment-1414301145
2023-02-02 21:31:44 +01:00
Felix Roos d1719dc277 Merge pull request #403 from tidalcycles/downgrade-csound
pin @csound/browser to 6.18.3 + bump
2023-02-02 19:53:36 +01:00
Felix Roos 9d8c812aa3 pin @csound/browser to 6.18.3 + bump 2023-02-02 19:51:16 +01:00
Felix Roos 2e390acce9 hotfix: deploy workflow install command 2023-02-01 23:04:13 +01:00
Felix Roos e04797d891 Merge pull request #400 from tidalcycles/awaitable-audio-init
release webaudio
2023-02-01 22:47:26 +01:00
Felix Roos fb6195b081 bump webaudio 2023-02-01 22:45:53 +01:00
Felix Roos c6491a3ccd Merge pull request #399 from tidalcycles/awaitable-audio-init
can now await initAudio + initAudioOnFirstClick
2023-02-01 22:45:16 +01:00
Felix Roos 014ae2107e can now await initAudio + initAudioOnFirstClick 2023-02-01 22:42:25 +01:00
Felix Roos 1457eb9d19 Merge pull request #398 from tidalcycles/fix-minirepl-styles
fix: minirepl styles
2023-02-01 22:32:18 +01:00
Felix Roos 6f5f8cc7d8 fix: minirepl styles 2023-02-01 22:30:24 +01:00
Felix Roos 9ec3fee455 Merge pull request #396 from tidalcycles/pnpm
proper builds + use pnpm workspaces
2023-02-01 22:17:18 +01:00
Felix Roos 2d52168c26 trash obsolete package-lock files 2023-02-01 20:43:16 +01:00
Felix Roos aa5dd7920f organize tasks 2023-02-01 20:38:56 +01:00
Felix Roos d60956e82a update contribution guide to pnpm
+ update root tasks
2023-02-01 20:33:13 +01:00
Alex McLean 91fd3fb5ca add missing snapshots 2023-02-01 15:51:42 +00:00
Alex McLean 46f3b662a7 add pattern methods hurry, press and pressBy (#397) 2023-02-01 15:49:55 +00:00
Felix Roos 54a61dcdae overhaul the remaining readmes 2023-02-01 16:29:18 +01:00
Felix Roos b58f5232c7 add nano-repl as workspace 2023-02-01 16:09:50 +01:00
Felix Roos 9a5bda862c update examples 2023-02-01 16:09:33 +01:00
Felix Roos 7f67adb639 bump eval 2023-02-01 15:49:05 +01:00
Felix Roos 48fd943622 fix: eval dependency + add deprecation note 2023-02-01 15:40:57 +01:00
Felix Roos 08350d00be remove sc prefix to fix build 2023-02-01 15:35:21 +01:00
Felix Roos 84bf4a2686 add pretest 2023-01-30 22:53:53 +01:00
Felix Roos 2823456e96 update lockfile 2023-01-30 22:50:44 +01:00
Felix Roos a373629fc2 fix: gh actions use pnpm 2023-01-30 22:48:24 +01:00
Felix Roos 0aa2f72a3a update core examples 2023-01-29 23:27:35 +01:00
Felix Roos c032f6dde1 add prepublishOnly everywhere 2023-01-29 00:14:07 +01:00
Felix Roos f448d8f334 rename website package 2023-01-29 00:08:29 +01:00
Felix Roos a5d4a43753 xen build 2023-01-28 23:19:33 +01:00
Felix Roos 52e01321ec webdirt build 2023-01-28 23:18:25 +01:00
Felix Roos 19692e0122 webaudio build 2023-01-28 23:14:56 +01:00
Felix Roos cd1da3cb81 transpiler build 2023-01-28 23:12:00 +01:00
Felix Roos ffaa8f4ab3 tone build 2023-01-28 23:09:25 +01:00
Felix Roos f3da9d680d bump 2023-01-28 23:07:55 +01:00
Felix Roos 6118bd24f2 tonal build 2023-01-28 23:06:59 +01:00
Felix Roos fddbb2e2ac soundfonts build 2023-01-28 23:04:30 +01:00
Felix Roos cb7156b091 serial build 2023-01-28 23:01:20 +01:00
Felix Roos b5914a3f12 react build 2023-01-28 22:58:40 +01:00
Felix Roos 348d576ff3 remove dist folder 2023-01-28 22:49:13 +01:00
Felix Roos 7507654749 osc build 2023-01-28 22:40:10 +01:00
Felix Roos c25aad0c4a midi build 2023-01-28 22:35:08 +01:00
Felix Roos 4aa32415cd eval build 2023-01-28 22:32:48 +01:00
Felix Roos cde23bbf98 csound build 2023-01-28 22:27:18 +01:00
Felix Roos 555fe0b629 bump mini 2023-01-28 22:16:33 +01:00
Felix Roos be44c28fd3 add mini build 2023-01-28 22:16:04 +01:00
Felix Roos 657091307e remove unused deps 2023-01-28 22:12:11 +01:00
Felix Roos 458dc6e07d add core build 2023-01-28 22:05:11 +01:00
Felix Roos cfaecf9fcb migrate lerna to pnpm
https://lerna.js.org/docs/recipes/using-pnpm-with-lerna
2023-01-28 22:01:35 +01:00
Felix Roos 902f722d4c add import linting + fix checks 2023-01-28 21:00:42 +01:00
Felix Roos ea2d8b2773 use workspace protocol everywhere 2023-01-28 20:37:56 +01:00
Felix Roos b319754dc3 fix website build 2023-01-28 20:31:23 +01:00
Felix Roos 35f2040717 use pnpm workspaces 2023-01-28 20:14:26 +01:00
Felix Roos 4121fc926e write more about packages / monorepo setup 2023-01-28 17:59:59 +01:00
Felix Roos fd9ab987e6 Merge branch 'main' into docs 2023-01-28 14:23:01 +01:00
Felix Roos 6c3a235b08 Merge pull request #379 from urswilke/add-tidal-drum-patterns-to-examples
Add tidal-drum-patterns to examples
2023-01-27 14:40:40 +01:00
Felix Roos 07cb861385 Merge pull request #388 from tidalcycles/ribbon
add ribbon + test + docs
2023-01-27 14:39:56 +01:00
Alex McLean 217e348fc3 Notes are not essential :) (#393)
* Notes are not essential :)

* Update notes.mdx
2023-01-27 11:10:37 +00:00
Felix Roos ad917afba4 Merge pull request #391 from tidalcycles/docs
document csound
2023-01-21 17:18:12 +01:00
Felix Roos 9539c6d317 fix: left sidebar scroll 2023-01-21 17:14:18 +01:00
Felix Roos e946e1e01e add csound docs 2023-01-21 17:05:57 +01:00
Felix Roos fea1e71a99 Merge remote-tracking branch 'origin/main' into docs 2023-01-21 15:57:54 +01:00
Felix Roos 9731eb8c5b add ribbon + test + docs 2023-01-19 12:25:12 +01:00
Felix Roos e058ae54ef Merge pull request #387 from tidalcycles/rename-a-to-angle
Rename a to angle
2023-01-19 12:04:50 +01:00
Felix Roos ba6714f384 add draw examples 2023-01-19 12:00:00 +01:00
Felix Roos 0a0a761e23 rename a to angle 2023-01-19 11:59:43 +01:00
Felix Roos 88e8368022 Merge pull request #386 from tidalcycles/run
add run + test + docs
2023-01-19 11:49:38 +01:00
Felix Roos 468ccc6288 add run + test + docs 2023-01-19 11:47:11 +01:00
Felix Roos 77e0237148 Merge pull request #385 from tidalcycles/docs-use-note-instead-of-n
docs: use note instead of n to mitigate confusion
2023-01-19 11:32:55 +01:00
Felix Roos ffdfb07f72 docs: use note instead of n to mitigate confusion 2023-01-19 11:27:54 +01:00
Felix Roos 203ed88024 Merge pull request #384 from tidalcycles/update-my-patterns-readme
update my-patterns instructions
2023-01-18 17:10:08 +01:00
Felix Roos 9923bcd16f update my-patterns instructions 2023-01-18 17:01:27 +01:00
urswilke 0179ec051f Revert "run: npm test"
This reverts commit 00709539e9bdfbc1404e984f28453f0a54b5638e.
2023-01-16 23:19:46 +01:00
urswilke 05b6353355 move drum patterns in a new file 2023-01-16 23:11:17 +01:00
urswilke 47c6ed8829 Revert "import patterns created by new method in:"
This reverts commit b266e975755e035b85c0869a30219c80a84db856.
2023-01-16 23:06:43 +01:00
urswilke ff9ebd64e3 run: npm test 2023-01-16 22:41:12 +01:00
urswilke cd2b098b81 import patterns created by new method in:
https://github.com/urswilke/read_beats/commit/ecd8c03b5e29544819831459f4b4ac48d954640f
2023-01-16 22:41:12 +01:00
Felix Roos 7000a4cead Merge pull request #377 from tidalcycles/draw-fixes
Draw fixes
2023-01-15 23:19:42 +01:00
Felix Roos 5aa983b45b do less work when not drawing 2023-01-15 23:11:49 +01:00
Felix Roos 800989419b catch all query errors by default 2023-01-15 22:50:56 +01:00
Felix Roos 362679954a Merge pull request #372 from tidalcycles/local-draw
improve new draw logic
2023-01-14 11:07:07 +01:00
Felix Roos 2732d64beb fix: another dependency array 2023-01-14 10:58:30 +01:00
Felix Roos 88684d6d9e fix dependency array 2023-01-14 10:48:52 +01:00
Felix Roos 9afddf0850 fix sentence 2023-01-14 01:30:56 +01:00
Felix Roos a2d7d6e81f fix: mini repl could eval before modules are ready 2023-01-14 01:27:22 +01:00
Felix Roos 49bcb5e19f fix minor bugs 2023-01-13 16:35:24 +01:00
Felix Roos 3744ea6218 fix: warnings 2023-01-13 15:17:23 +01:00
Felix Roos b2c6d87633 MiniRepl: add punchcard flag for implicit vis
+ reintroduce editPattern
+ add punchcards to mini-notation.mdx
2023-01-13 15:13:39 +01:00
Felix Roos e4f538b674 fix option handling 2023-01-13 13:00:11 +01:00
Felix Roos ea0e0b4396 reset canvas when pattern changes
+ rename noteroll -> punchcard
2023-01-13 12:57:35 +01:00
Felix Roos 1ac784dc7a localize draw logic 2023-01-13 12:03:34 +01:00
Felix Roos d980c9ca4c Merge branch 'main' into docs 2023-01-13 01:52:36 +01:00
Felix Roos 6a2a60df0d hotfix: tests 2023-01-12 17:43:20 +01:00
Felix Roos ee9b89b6d5 add more synonyms 2023-01-12 17:40:58 +01:00
Felix Roos 68cc109933 burn down that undocumented list 2023-01-12 17:25:01 +01:00
Felix Roos 1015bb0fdc Merge pull request #369 from tidalcycles/docs
document more functions + change arp join
2023-01-12 14:40:58 +01:00
Felix Roos 7da94dd109 reformat headings
+ add another note about single quotes
2023-01-12 14:22:41 +01:00
Felix Roos 171808cf2c add note about single quote strings 2023-01-12 14:19:18 +01:00
Felix Roos 78d14cdfea automatically print synonyms in doc 2023-01-12 13:10:22 +01:00
Felix Roos 19982a2ffb remove unused package 2023-01-12 13:00:22 +01:00
Felix Roos 79904560fc fix: snapshot 2023-01-12 12:48:05 +01:00
Felix Roos 28aee1c58d fix polymeter doc 2023-01-12 12:43:41 +01:00
Felix Roos d4512f360b fix: double slash 2023-01-12 12:40:20 +01:00
Felix Roos 48d0ffe868 add script that finds all undocumented exports 2023-01-12 12:35:04 +01:00
Felix Roos 62af12f3b9 remove experimental class properties 2023-01-12 12:32:27 +01:00
Felix Roos efa4bfef2f fix snapshot 2023-01-12 02:23:06 +01:00
Felix Roos fc3ead4c01 Merge branch 'main' into docs 2023-01-12 02:20:16 +01:00
Felix Roos ca9c813b40 format 2023-01-12 02:18:09 +01:00
Felix Roos e3b0f47c40 hide internals for now 2023-01-12 02:15:42 +01:00
Felix Roos bf13e42092 update snap 2023-01-12 01:57:14 +01:00
Felix Roos 4345fda9bc more doc 2023-01-12 01:56:46 +01:00
Felix Roos 4059e9faa9 more doc 2023-01-12 00:10:58 +01:00
Felix Roos 95fcce1bcf fix: snapshot 2023-01-11 23:38:40 +01:00
Felix Roos 86cde9716a arpWith example 2023-01-11 23:37:59 +01:00
Felix Roos 6e315a50ed breaking: arp use innerJoin instead of squeezeJoin 2023-01-11 23:28:17 +01:00
Felix Roos 500c0b6534 doc: arp + arpWith 2023-01-11 23:26:03 +01:00
Felix Roos db75dca118 doc: split accumulation and conditions 2023-01-11 23:05:17 +01:00
Felix Roos d5f37e2cbe add internals doc 2023-01-11 22:48:47 +01:00
Felix Roos a120a9895b fix: right sidebar width 2023-01-11 22:48:33 +01:00
Felix Roos e9c1db020f hotfix: remove mocked .out 2023-01-11 22:11:22 +01:00
Felix Roos 0974540e5f fix: typo 2023-01-10 00:21:54 +01:00
246 changed files with 30071 additions and 52777 deletions
+3
View File
@@ -15,3 +15,6 @@ vite.config.js
!**/*.mjs
**/*.tsx
**/*.ts
**/*.json
**/dev-dist
**/dist
+3 -2
View File
@@ -9,8 +9,9 @@
"ecmaVersion": "latest",
"sourceType": "module"
},
"plugins": [],
"plugins": ["import"],
"rules": {
"no-unused-vars": ["warn", { "destructuredArrayIgnorePattern": ".", "ignoreRestSiblings": false }]
"no-unused-vars": ["warn", { "destructuredArrayIgnorePattern": ".", "ignoreRestSiblings": false }],
"import/no-extraneous-dependencies": ["error", {"devDependencies": true}]
}
}
+6 -3
View File
@@ -22,15 +22,18 @@ jobs:
url: ${{ steps.deployment.outputs.page_url }}
steps:
- uses: actions/checkout@v2
- uses: pnpm/action-setup@v2
with:
version: 7
- uses: actions/setup-node@v3
with:
node-version: 18
cache: "npm"
cache: "pnpm"
- name: Install Dependencies
run: npm ci && cd website && npm ci
run: pnpm install
- name: Build
run: npm run build
run: pnpm build
- name: Setup Pages
uses: actions/configure-pages@v2
+8 -5
View File
@@ -11,11 +11,14 @@ jobs:
steps:
- uses: actions/checkout@v2
- uses: pnpm/action-setup@v2
with:
version: 7
- uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: "npm"
- run: npm install
- run: npm run format-check
- run: npm run lint
- run: npm test
cache: 'pnpm'
- run: pnpm install
- run: pnpm run format-check
- run: pnpm run lint
- run: pnpm test
+3 -1
View File
@@ -37,4 +37,6 @@ talk/public/EmuSP12
talk/public/samples
server/samples/old
repl/stats.html
coverage
coverage
public/icons/apple-splash-*
dev-dist
+4 -1
View File
@@ -6,4 +6,7 @@
**/dist
packages/mini/krill-parser.js
packages/xen/tunejs.js
paper
paper
pnpm-lock.yaml
pnpm-workspace.yaml
**/dev-dist
+1 -1
View File
@@ -7,7 +7,7 @@
"jsxSingleQuote": false,
"trailingComma": "all",
"bracketSpacing": true,
"jsxBracketSameLine": false,
"bracketSameLine": false,
"arrowParens": "always",
"proseWrap": "preserve",
"htmlWhitespaceSensitivity": "css",
+34 -11
View File
@@ -58,23 +58,22 @@ To fix a bug that has been reported,
## Write Tests
There are still many tests that have not been written yet! Reading and writing tests is a great opportunity to get familiar with the codebase.
You can find the tests in each package in the `test` folder. To run all tests, run `npm test` from the root folder.
You can find the tests in each package in the `test` folder. To run all tests, run `pnpm test` from the root folder.
## Project Setup
To get the project up and running for development, make sure you have installed:
- git
- node, preferably v16
- [git](https://git-scm.com/)
- [node](https://nodejs.org/en/) >= 18
- [pnpm](https://pnpm.io/) (`npm i pnpm -g`)
then, do the following:
```sh
git clone https://github.com/tidalcycles/strudel.git && cd strudel
npm i # install at root to symlink packages
npx lerna bootstrap # install all dependencies in packages
cd repl && npm i # install repl dependencies
npm run start # start repl
pnpm i # install at root to symlink packages
pnpm start # start repl
```
Those commands might look slightly different for your OS.
@@ -83,6 +82,10 @@ Please report any problems you've had with the setup instructions!
## Code Style
To make sure the code changes only where it should, we are using prettier to unify the code style.
- You can format all files at once by running `pnpm prettier` from the project root
- Run `pnpm format-check` from the project root to check if all files are well formatted
If you use VSCode, you can
1. install [the prettier extension](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode)
@@ -90,12 +93,29 @@ If you use VSCode, you can
3. Choose "Configure Default Formatter..."
4. Select prettier
## ESLint
To prevent unwanted runtime errors, this project uses [eslint](https://eslint.org/).
- You can check for lint errors by running `pnpm lint`
There are also eslint extensions / plugins for most editors.
## Running Tests
- Run all tests with `pnpm test`
- Run all tests with UI using `pnpm test-ui`
## Running all CI Checks
When opening a PR, the CI runner will automatically check the code style and eslint, as well as run all tests.
You can run the same check with `pnpm check`
## Package Workflow
The project is split into multiple [packages](https://github.com/tidalcycles/strudel/tree/main/packages) with independent versioning.
When you run `npm i` on the root folder, [npm workspaces](https://docs.npmjs.com/cli/v7/using-npm/workspaces) will symlink all packages
in the `node_modules` folder. This will allow any js file to import `@strudel.cycles/<package-name>` to get the local version,
which allows developing multiple packages at the same time
When you run `pnpm i` on the root folder, [pnpm workspaces](https://pnpm.io/workspaces) will install all dependencies of all subpackages. This will allow any js file to import `@strudel.cycles/<package-name>` to get the local version,
allowing to develop multiple packages at the same time.
## Package Publishing
@@ -106,12 +126,15 @@ npm login
npx lerna publish
```
To manually publish a single package, increase the version in the `package.json`, then run `pnpm publish`.
Important: Always publish with `pnpm`, as `npm` does not support overriding main files in `publishConfig`, which is done in all the packages.
### New Packages
To add a new package, you have to publish it manually the first time, using:
```sh
cd packages/<package-name> && npm publish --access public
cd packages/<package-name> && pnpm publish --access public
```
## Have Fun
+3 -2
View File
@@ -7,14 +7,15 @@ An experiment in making a [Tidal](https://github.com/tidalcycles/tidal/) using w
- Try it here: <https://strudel.tidalcycles.org/>
- Docs: <https://strudel.tidalcycles.org/learn/>
- Technical Blog Post: <https://loophole-letters.vercel.app/strudel>
- 1 Year of Strudel Blog Post: <https://loophole-letters.vercel.app/strudel1year>
## Running Locally
After cloning the project, you can run the REPL locally:
```bash
npm run setup
npm run repl
pnpm run setup
pnpm run repl
```
## Using Strudel In Your Project
+17
View File
@@ -0,0 +1,17 @@
// this barrel export is currently only used to find undocumented exports
export * from './packages/core/index.mjs';
export * from './packages/csound/index.mjs';
export * from './packages/embed/index.mjs';
export * from './packages/eval/index.mjs';
export * from './packages/midi/index.mjs';
export * from './packages/mini/index.mjs';
export * from './packages/osc/index.mjs';
export * from './packages/react/index.mjs';
export * from './packages/serial/index.mjs';
export * from './packages/soundfonts/index.mjs';
export * from './packages/tonal/index.mjs';
export * from './packages/tone/index.mjs';
export * from './packages/transpiler/index.mjs';
export * from './packages/webaudio/index.mjs';
export * from './packages/webdirt/index.mjs';
export * from './packages/xen/index.mjs';
+3 -1
View File
@@ -2,5 +2,7 @@
"packages": [
"packages/*"
],
"version": "independent"
"version": "independent",
"npmClient": "pnpm",
"useWorkspaces": true
}
+49 -10
View File
@@ -3,18 +3,57 @@
This directory can be used to save your own patterns, which then get
made into a pattern swatch.
0. fork and clone the strudel repository
1. run `npm run setup` in the strudel folder
1. Save one or more .txt files in this folder
2. run `npm run repl` in the top-level strudel folder
3. open `http://localhost:3000/swatch/` !
Example: <https://felixroos.github.io/strudel/swatch/>
## deploy
1. in your fork, go to settings -> pages and select "Github Actions" as source
2. edit `website/public/CNAME` to contain `<your-username>.github.io/strudel`
3. edit `website/astro.config.mjs` to use site: `https://<your-username>.github.io` and base `/strudel`
4. go to Actions -> `Build and Deploy` and click `Run workflow`
5. view your patterns at `<your-username>.github.io/strudel/swatch/`
### 1. fork the [strudel repo on github](https://github.com/tidalcycles/strudel.git)
### 2. clone your fork to your machine `git clone https://github.com/<your-username>/strudel.git strudel && cd strudel`
### 3. create a separate branch like `git branch patternuary && git checkout patternuary`
### 4. save one or more .txt files in the my-patterns folder
### 5. edit `website/public/CNAME` to contain `<your-username>.github.io/strudel`
### 6. edit `website/astro.config.mjs` to use site: `https://<your-username>.github.io` and base `/strudel`, like this
```js
const site = 'https://<your-username>.github.io';
const base = '/strudel';
```
### 7. commit & push the changes
```sh
git add . && git commit -m "site config" && git push --set-upstream origin
```
### 8. deploy to github pages
- go to settings -> pages and select "Github Actions" as source
- go to settings -> environments -> github-pages and press the edit button next to `main` and type in `patternuary` (under "Deployment branches")
- go to Actions -> `Build and Deploy` and click `Run workflow` with branch `patternuary`
### 9. view your patterns at `<your-username>.github.io/strudel/swatch/`
Alternatively, github pages allows you to use a custom domain, like https://mycooldomain.org/swatch/. [See their documentation for details](https://docs.github.com/en/pages/configuring-a-custom-domain-for-your-github-pages-site).
### 10. optional: automatic deployment
If you want to automatically deploy your site on push, go to `deploy.yml` and change `workflow_dispatch` to `push`.
## running locally
- install dependencies with `npm run setup`
- run dev server with `npm run repl` and open `http://localhost:3000/strudel/swatch/`
## tests fail?
Your tests might fail if the code does not follow prettiers format.
In that case, run `npm run codeformat`. To disable that, remove `npm run format-check` from `test.yml`
## updating your fork
To update your fork, you can pull the main branch and merge it into your `patternuary` branch.
-20806
View File
File diff suppressed because it is too large Load Diff
+26 -15
View File
@@ -4,30 +4,30 @@
"private": true,
"description": "Port of tidalcycles to javascript",
"scripts": {
"setup": "pnpm i",
"pretest": "npm run jsdoc-json",
"prebuild": "npm run jsdoc-json",
"test": "vitest run --version",
"test-ui": "vitest --ui",
"test-coverage": "vitest --coverage",
"bootstrap": "lerna bootstrap",
"setup": "npm i && npm run jsdoc-json && npm run bootstrap && cd website && npm i",
"snapshot": "vitest run -u --silent",
"repl": "cd website && npm run dev",
"osc": "cd packages/osc && npm run server",
"build": "cd website && npm run build",
"prestart": "npm run jsdoc-json",
"test": "npm run pretest && vitest run --version",
"test-ui": "npm run pretest && vitest --ui",
"test-coverage": "npm run pretest && vitest --coverage",
"snapshot": "npm run pretest && vitest run -u --silent",
"repl": "npm run prestart && cd website && npm run dev",
"start": "npm run prestart && cd website && npm run dev",
"dev": "npm run prestart && cd website && npm run dev",
"build": "npm run prebuild && cd website && npm run build",
"preview": "cd website && npm run preview",
"osc": "cd packages/osc && npm run server",
"deploy": "NODE_DEBUG=gh-pages gh-pages -d out",
"jsdoc": "jsdoc packages/ -c jsdoc.config.json",
"jsdoc-json": "jsdoc packages/ --template ./node_modules/jsdoc-json --destination doc.json -c jsdoc.config.json",
"lint": "eslint . --ext mjs,js --quiet",
"codeformat": "prettier --write .",
"format-check": "prettier --check .",
"report-undocumented": "npm run jsdoc-json && node undocumented.mjs > undocumented.json",
"check": "npm run format-check && npm run lint && npm run test",
"iclc": "cd paper && pandoc --template=pandoc/iclc.html --citeproc --number-sections iclc2023.md -o iclc2023.html && pandoc --template=pandoc/iclc.latex --citeproc --number-sections iclc2023.md -o iclc2023.pdf"
},
"workspaces": [
"packages/*"
],
"repository": {
"type": "git",
"url": "git+https://github.com/tidalcycles/strudel.git"
@@ -45,19 +45,30 @@
"url": "https://github.com/tidalcycles/strudel/issues"
},
"homepage": "https://strudel.tidalcycles.org",
"dependencies": {
"@strudel.cycles/core": "workspace:*",
"@strudel.cycles/mini": "workspace:*",
"@strudel.cycles/tonal": "workspace:*",
"@strudel.cycles/transpiler": "workspace:*",
"@strudel.cycles/webaudio": "workspace:*",
"@strudel.cycles/xen": "workspace:*",
"acorn": "^8.8.1",
"dependency-tree": "^9.0.0",
"vitest": "^0.25.7"
},
"devDependencies": {
"@vitest/ui": "^0.25.7",
"c8": "^7.12.0",
"canvas": "^2.11.0",
"eslint": "^8.28.0",
"eslint-plugin-import": "^2.27.5",
"events": "^3.3.0",
"gh-pages": "^4.0.0",
"jsdoc": "^3.6.10",
"jsdoc-json": "^2.0.2",
"jsdoc-to-markdown": "^7.1.1",
"lerna": "^4.0.0",
"lerna": "^6.5.1",
"prettier": "^2.8.1",
"rollup-plugin-visualizer": "^5.8.1",
"vitest": "^0.25.7"
"rollup-plugin-visualizer": "^5.8.1"
}
}
+4 -3
View File
@@ -33,6 +33,7 @@ b: 3/2 - 7/4
c: 7/4 - 2
```
- [play with @strudel.cycles/core on codesandbox](https://codesandbox.io/s/strudel-core-test-qmz6qr?file=/src/index.js).
- [open color pattern example](https://raw.githack.com/tidalcycles/strudel/package-examples/packages/core/examples/canvas.html)
- [open minimal repl example](https://raw.githack.com/tidalcycles/strudel/package-examples/packages/core/examples/metro.html)
- [play with @strudel.cycles/core on codesandbox](https://codesandbox.io/s/strudel-core-test-forked-9ywhv7?file=/src/index.js).
- [open color pattern example](https://raw.githack.com/tidalcycles/strudel/main/packages/core/examples/canvas.html)
- [open minimal repl example](https://raw.githack.com/tidalcycles/strudel/main/packages/core/examples/vanilla.html)
- [open minimal vite example](./examples/vite-vanilla-repl/)
+4 -4
View File
@@ -22,11 +22,11 @@ Pattern.prototype.animate = function ({ callback, sync = false, smear = 0.5 } =
ctx.fillStyle = clearColor;
ctx.fillRect(0, 0, ww, wh);
frame.forEach((f) => {
let { x, y, w, h, s, r, a = 0, fill = 'darkseagreen' } = f.value;
let { x, y, w, h, s, r, angle = 0, fill = 'darkseagreen' } = f.value;
w *= ww;
h *= wh;
if (r !== undefined && a !== undefined) {
const radians = a * 2 * Math.PI;
if (r !== undefined && angle !== undefined) {
const radians = angle * 2 * Math.PI;
const [cx, cy] = [(ww - w) / 2, (wh - h) / 2];
x = cx + Math.cos(radians) * r * cx;
y = cy + Math.sin(radians) * r * cy;
@@ -51,7 +51,7 @@ Pattern.prototype.animate = function ({ callback, sync = false, smear = 0.5 } =
return silence;
};
export const { x, y, w, h, a, r, fill, smear } = createParams('x', 'y', 'w', 'h', 'a', 'r', 'fill', 'smear');
export const { x, y, w, h, angle, r, fill, smear } = createParams('x', 'y', 'w', 'h', 'angle', 'r', 'fill', 'smear');
export const rescale = register('rescale', function (f, pat) {
return pat.mul(x(f).w(f).y(f).h(f));
+320 -343
View File
File diff suppressed because it is too large Load Diff
+23 -21
View File
@@ -8,49 +8,51 @@ import createClock from './zyklus.mjs';
import { logger } from './logger.mjs';
export class Cyclist {
worker;
pattern;
started = false;
cps = 1; // TODO
getTime;
phase = 0;
constructor({ interval, onTrigger, onToggle, onError, getTime, latency = 0.1 }) {
this.getTime = getTime;
this.started = false;
this.cps = 1;
this.lastTick = 0; // absolute time when last tick (clock callback) happened
this.lastBegin = 0; // query begin of last tick
this.lastEnd = 0; // query end of last tick
this.getTime = getTime; // get absolute time
this.onToggle = onToggle;
this.latency = latency;
this.latency = latency; // fixed trigger time offset
const round = (x) => Math.round(x * 1000) / 1000;
this.clock = createClock(
getTime,
// called slightly before each cycle
(phase, duration, tick) => {
if (tick === 0) {
this.origin = phase;
}
const begin = round(phase - this.origin);
this.phase = begin - latency;
const end = round(begin + duration);
const time = getTime();
try {
const haps = this.pattern.queryArc(begin, end); // get Haps
const time = getTime();
const begin = this.lastEnd;
this.lastBegin = begin;
const end = round(begin + duration * this.cps);
this.lastEnd = end;
const haps = this.pattern.queryArc(begin, end);
const tickdeadline = phase - time; // time left till phase begins
this.lastTick = time + tickdeadline;
haps.forEach((hap) => {
if (hap.part.begin.equals(hap.whole.begin)) {
const deadline = hap.whole.begin + this.origin - time + latency;
const duration = hap.duration * 1;
onTrigger?.(hap, deadline, duration);
const deadline = (hap.whole.begin - begin) / this.cps + tickdeadline + latency;
const duration = hap.duration / this.cps;
onTrigger?.(hap, deadline, duration, this.cps);
}
});
} catch (e) {
logger(`[cyclist] error: ${e.message}`);
onError?.(e);
}
}, // called slightly before each cycle
},
interval, // duration of each cycle
);
}
getPhase() {
return this.getTime() - this.origin - this.latency;
}
now() {
return this.getTime() - this.origin + this.clock.minLatency;
const secondsSinceLastTick = this.getTime() - this.lastTick - this.clock.duration;
return this.lastBegin + secondsSinceLastTick * this.cps; // + this.clock.minLatency;
}
setStarted(v) {
this.started = v;
+6
View File
@@ -59,3 +59,9 @@ export const cleanupDraw = (clearScreen = true) => {
clearInterval(window.strudelScheduler);
}
};
Pattern.prototype.onPaint = function (onPaint) {
// this is evil! TODO: add pattern.context
this.context = { onPaint };
return this;
};
+2 -8
View File
@@ -11,15 +11,9 @@ This program is free software: you can redistribute it and/or modify it under th
*/
import { Pattern, timeCat, register, silence } from './pattern.mjs';
import { rotate, flatten } from './util.mjs';
import { rotate, flatten, splitAt, zipWith } from './util.mjs';
import Fraction from './fraction.mjs';
const splitAt = function (index, value) {
return [value.slice(0, index), value.slice(index)];
};
const zipWith = (f, xs, ys) => xs.map((n, i) => f(n, ys[i]));
const left = function (n, x) {
const [ons, offs] = n;
const [xs, ys] = x;
@@ -46,7 +40,7 @@ const _bjork = function (n, x) {
return Math.min(ons, offs) <= 1 ? [n, x] : _bjork(...(ons > offs ? left(n, x) : right(n, x)));
};
const bjork = function (ons, steps) {
export const bjork = function (ons, steps) {
const offs = steps - ons;
const x = Array(ons).fill([1]);
const y = Array(offs).fill([0]);
-8
View File
@@ -6,12 +6,7 @@ This program is free software: you can redistribute it and/or modify it under th
import { isPattern } from './index.mjs';
let scoped = false;
export const evalScope = async (...args) => {
if (scoped) {
console.warn('evalScope was called more than once.');
}
scoped = true;
const results = await Promise.allSettled(args);
const modules = results.filter((result) => result.status === 'fulfilled').map((r) => r.value);
results.forEach((result, i) => {
@@ -42,9 +37,6 @@ function safeEval(str, options = {}) {
}
export const evaluate = async (code, transpiler) => {
if (!scoped) {
await evalScope(); // at least scope Pattern.prototype.boostrap
}
if (transpiler) {
code = transpiler(code); // transform syntactically correct js code to semantically usable code
}
+2 -2
View File
@@ -1,13 +1,13 @@
<input
type="text"
id="text"
value="cat('a', 'b')"
value="seq('a', ['b', 'c'])"
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');
const strudel = await import('https://cdn.skypack.dev/@strudel.cycles/core@0.6.8');
Object.assign(window, strudel); // assign all strudel functions to global scope to use with eval
const input = document.getElementById('text');
const getEvents = () => {
+2 -2
View File
@@ -2,13 +2,13 @@
<input
type="text"
id="text"
value="cat('orange', 'indigo')"
value="seq('tomato', 'indigo', ['white', 'steelblue']).fast(4)"
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');
const strudel = await import('https://cdn.skypack.dev/@strudel.cycles/core@0.6.8');
// this adds all strudel functions to the global scope, to be used by eval
Object.assign(window, strudel);
// setup elements
-90
View File
@@ -1,90 +0,0 @@
<div style="position: absolute; bottom: 0; right: 0; padding: 4px; width: 100vw; display: flex">
<button id="start" style="font-size: 2em">start</button>
<button id="stop" style="font-size: 2em">stop</button>
<button id="slower" style="font-size: 2em">slower</button>
<button id="faster" style="font-size: 2em">faster</button>
</div>
<textarea
style="font-size: 2em; background: #052b49; color: #fff; height: 100%; width: 100%; outline: none; border: 0"
id="text"
spellcheck="false"
>
Loading...</textarea
>
<script type="module">
document.body.style = 'margin: 0';
import * as strudel from '@strudel.cycles/core';
const { cat, State, TimeSpan, Scheduler, getPlayableNoteValue, getFreq } = strudel;
Object.assign(window, strudel); // add strudel to eval scope
const ctx = new AudioContext();
const scheduler = new Scheduler({
// audioContext: getAudioContext(),
interval: 0.1,
onTrigger: (hap, time, duration) => {
const freq = getFrequency(hap);
const osc = ctx.createOscillator();
const gain = 0.2;
osc.frequency.value = freq;
osc.type = 'triangle';
const onset = ctx.currentTime + time;
const offset = onset + duration;
const attack = 0.05;
const release = 0.05;
osc.start(onset);
osc.stop(offset + release);
const g = ctx.createGain();
g.gain.setValueAtTime(gain, onset);
g.gain.linearRampToValueAtTime(gain, onset + attack);
g.gain.setValueAtTime(gain, offset - release);
g.gain.linearRampToValueAtTime(0, offset);
osc.connect(g);
g.connect(ctx.destination);
},
});
let initialCode = `stack('c4','e4',cat('g4','a4','b4','a4'))
.add(cat(0,1,2,3,4,3,2,1).slow(8))
.fast(2)
.cps(tri.range(1,8).slow(32))`;
try {
const base64 = decodeURIComponent(window.location.href.split('#')[1]);
initialCode = atob(base64);
} catch (err) {
console.warn('failed to decode', err);
}
const input = document.getElementById('text');
input.value = initialCode;
const evaluate = () => {
try {
const pattern = eval(input.value);
scheduler.setPattern(pattern);
window.location.hash = '#' + encodeURIComponent(btoa(input.value)); // update url hash
} catch (err) {
console.warn(err);
}
};
evaluate();
input.addEventListener('input', () => evaluate());
document.getElementById('start').addEventListener('click', async () => {
await ctx.resume();
scheduler.start();
});
document.getElementById('stop').addEventListener('click', () => scheduler.stop());
document.getElementById('slower').addEventListener('click', () => scheduler.setCps(scheduler.cps - 0.1));
document.getElementById('faster').addEventListener('click', () => scheduler.setCps(scheduler.cps + 0.1));
</script>
<!--
sequence(1,2).mul(55/2) // frequencies
.mul(slowcat(1,2))
.mul(slowcat(1,3/2,4/3,5/3).slow(8))
.fast(3)
.freq()
.velocity(.5)
.s('sawtooth')
.cutoff(800)
.out()
-->
-44
View File
@@ -1,44 +0,0 @@
<input
type="text"
id="text"
value="seq('c3','eb3','g3').note().s('sawtooth').out()"
style="width: 100%; font-size: 2em; outline: none; margin-bottom: 10px"
spellcheck="false"
/>
<button id="start">play</button>
<div id="output"></div>
<script type="module">
const strudel = await import('https://cdn.skypack.dev/@strudel.cycles/core@latest');
const controls = await import('https://cdn.skypack.dev/@strudel.cycles/core@latest/controls.mjs');
const { getAudioContext, Scheduler } = await import('https://cdn.skypack.dev/@strudel.cycles/webaudio@latest');
let scheduler;
const audioContext = getAudioContext();
const latency = 0.2;
Object.assign(window, strudel);
Object.assign(window, controls.default);
scheduler = new Scheduler({
audioContext,
interval: 0.1,
latency,
onEvent: (hap) => {
if (!hap.context.onTrigger) {
console.warn('no output chosen. use one of .out() .webdirt() .osc()');
}
},
});
let started;
document.getElementById('start').addEventListener('click', async () => {
const code = document.getElementById('text').value;
const pattern = eval(code);
const events = pattern._firstCycleValues;
console.log(code, '->', events);
scheduler.setPattern(pattern);
if (!started) {
scheduler.start();
started = true;
}
});
</script>
+16 -29
View File
@@ -6,46 +6,36 @@
<title>Buildless Vanilla Strudel REPL</title>
</head>
<body style="margin: 0; background: #222">
<div style="display: grid; height: 100vh">
<div style="display: grid; height: 100vh; grid-template-rows: 32px auto">
<button id="start" style="width: 100vw; height: 32px">evaluate</button>
<textarea
id="text"
style="font-size: 2em; border: 0; color: white; background: transparent; outline: none; padding: 20px"
spellcheck="false"
></textarea>
</div>
<button
id="start"
style="
position: absolute;
border-radius: 10px;
top: 20px;
right: 20px;
padding: 20px;
border: 2px solid white;
background: transparent;
color: white;
cursor: pointer;
"
>
evaluate
</button>
<div id="output"></div>
<script type="module">
import { controls, repl, evalScope } from 'https://cdn.skypack.dev/@strudel.cycles/core@0.3.2';
import { mini } from 'https://cdn.skypack.dev/@strudel.cycles/mini@0.3.2';
import { transpiler } from 'https://cdn.skypack.dev/@strudel.cycles/transpiler@0.3.2';
import { getAudioContext, webaudioOutput } from 'https://cdn.skypack.dev/@strudel.cycles/webaudio@0.3.3';
import { controls, repl, evalScope } from 'https://cdn.skypack.dev/@strudel.cycles/core@0.6.8';
import { mini } from 'https://cdn.skypack.dev/@strudel.cycles/mini@0.6.0';
import { transpiler } from 'https://cdn.skypack.dev/@strudel.cycles/transpiler@0.6.0';
import {
getAudioContext,
webaudioOutput,
initAudioOnFirstClick,
} from 'https://cdn.skypack.dev/@strudel.cycles/webaudio@0.6.0';
initAudioOnFirstClick();
const ctx = getAudioContext();
const input = document.getElementById('text');
input.innerHTML = getTune();
evalScope(
controls,
import('https://cdn.skypack.dev/@strudel.cycles/core@0.3.2'),
import('https://cdn.skypack.dev/@strudel.cycles/mini@0.3.2'),
import('https://cdn.skypack.dev/@strudel.cycles/tonal@0.3.3'),
import('https://cdn.skypack.dev/@strudel.cycles/webaudio@0.3.3'),
import('https://cdn.skypack.dev/@strudel.cycles/core@0.6.8'),
import('https://cdn.skypack.dev/@strudel.cycles/mini@0.6.0'),
import('https://cdn.skypack.dev/@strudel.cycles/tonal@0.6.0'),
import('https://cdn.skypack.dev/@strudel.cycles/webaudio@0.6.0'),
);
const { evaluate } = repl({
@@ -53,10 +43,7 @@
getTime: () => ctx.currentTime,
transpiler,
});
document.getElementById('start').addEventListener('click', () => {
ctx.resume();
evaluate(input.value);
});
document.getElementById('start').addEventListener('click', () => evaluate(input.value));
function getTune() {
return `await samples('github:tidalcycles/Dirt-Samples/master')
@@ -1,12 +1,12 @@
import { controls, repl, evalScope, setStringParser } from '@strudel.cycles/core';
import { mini } from '@strudel.cycles/mini';
import { getAudioContext, webaudioOutput } from '@strudel.cycles/webaudio';
// import { transpiler } from '@strudel.cycles/transpiler';
import { controls, repl, evalScope } from '@strudel.cycles/core';
import { getAudioContext, webaudioOutput, initAudioOnFirstClick } from '@strudel.cycles/webaudio';
import { transpiler } from '@strudel.cycles/transpiler';
import tune from './tune.mjs';
const ctx = getAudioContext();
const input = document.getElementById('text');
input.innerHTML = tune;
initAudioOnFirstClick();
evalScope(
controls,
@@ -16,14 +16,13 @@ evalScope(
import('@strudel.cycles/tonal'),
);
setStringParser(mini);
const { evaluate } = repl({
defaultOutput: webaudioOutput,
getTime: () => ctx.currentTime,
// transpiler,
transpiler,
});
document.getElementById('start').addEventListener('click', () => {
ctx.resume();
console.log('eval', input.value);
evaluate(input.value);
});
-885
View File
@@ -1,885 +0,0 @@
{
"name": "vite-vanilla-repl",
"version": "0.0.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "vite-vanilla-repl",
"version": "0.0.0",
"devDependencies": {
"vite": "^3.2.0"
}
},
"node_modules/@esbuild/android-arm": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.15.13.tgz",
"integrity": "sha512-RY2fVI8O0iFUNvZirXaQ1vMvK0xhCcl0gqRj74Z6yEiO1zAUa7hbsdwZM1kzqbxHK7LFyMizipfXT3JME+12Hw==",
"cpu": [
"arm"
],
"dev": true,
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-loong64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.13.tgz",
"integrity": "sha512-+BoyIm4I8uJmH/QDIH0fu7MG0AEx9OXEDXnqptXCwKOlOqZiS4iraH1Nr7/ObLMokW3sOCeBNyD68ATcV9b9Ag==",
"cpu": [
"loong64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.13.tgz",
"integrity": "sha512-Cu3SC84oyzzhrK/YyN4iEVy2jZu5t2fz66HEOShHURcjSkOSAVL8C/gfUT+lDJxkVHpg8GZ10DD0rMHRPqMFaQ==",
"dev": true,
"hasInstallScript": true,
"bin": {
"esbuild": "bin/esbuild"
},
"engines": {
"node": ">=12"
},
"optionalDependencies": {
"@esbuild/android-arm": "0.15.13",
"@esbuild/linux-loong64": "0.15.13",
"esbuild-android-64": "0.15.13",
"esbuild-android-arm64": "0.15.13",
"esbuild-darwin-64": "0.15.13",
"esbuild-darwin-arm64": "0.15.13",
"esbuild-freebsd-64": "0.15.13",
"esbuild-freebsd-arm64": "0.15.13",
"esbuild-linux-32": "0.15.13",
"esbuild-linux-64": "0.15.13",
"esbuild-linux-arm": "0.15.13",
"esbuild-linux-arm64": "0.15.13",
"esbuild-linux-mips64le": "0.15.13",
"esbuild-linux-ppc64le": "0.15.13",
"esbuild-linux-riscv64": "0.15.13",
"esbuild-linux-s390x": "0.15.13",
"esbuild-netbsd-64": "0.15.13",
"esbuild-openbsd-64": "0.15.13",
"esbuild-sunos-64": "0.15.13",
"esbuild-windows-32": "0.15.13",
"esbuild-windows-64": "0.15.13",
"esbuild-windows-arm64": "0.15.13"
}
},
"node_modules/esbuild-android-64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.15.13.tgz",
"integrity": "sha512-yRorukXBlokwTip+Sy4MYskLhJsO0Kn0/Fj43s1krVblfwP+hMD37a4Wmg139GEsMLl+vh8WXp2mq/cTA9J97g==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-android-arm64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.15.13.tgz",
"integrity": "sha512-TKzyymLD6PiVeyYa4c5wdPw87BeAiTXNtK6amWUcXZxkV51gOk5u5qzmDaYSwiWeecSNHamFsaFjLoi32QR5/w==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-darwin-64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.15.13.tgz",
"integrity": "sha512-WAx7c2DaOS6CrRcoYCgXgkXDliLnFv3pQLV6GeW1YcGEZq2Gnl8s9Pg7ahValZkpOa0iE/ojRVQ87sbUhF1Cbg==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-darwin-arm64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.13.tgz",
"integrity": "sha512-U6jFsPfSSxC3V1CLiQqwvDuj3GGrtQNB3P3nNC3+q99EKf94UGpsG9l4CQ83zBs1NHrk1rtCSYT0+KfK5LsD8A==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-freebsd-64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.13.tgz",
"integrity": "sha512-whItJgDiOXaDG/idy75qqevIpZjnReZkMGCgQaBWZuKHoElDJC1rh7MpoUgupMcdfOd+PgdEwNQW9DAE6i8wyA==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-freebsd-arm64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.13.tgz",
"integrity": "sha512-6pCSWt8mLUbPtygv7cufV0sZLeylaMwS5Fznj6Rsx9G2AJJsAjQ9ifA+0rQEIg7DwJmi9it+WjzNTEAzzdoM3Q==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-linux-32": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.15.13.tgz",
"integrity": "sha512-VbZdWOEdrJiYApm2kkxoTOgsoCO1krBZ3quHdYk3g3ivWaMwNIVPIfEE0f0XQQ0u5pJtBsnk2/7OPiCFIPOe/w==",
"cpu": [
"ia32"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-linux-64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.15.13.tgz",
"integrity": "sha512-rXmnArVNio6yANSqDQlIO4WiP+Cv7+9EuAHNnag7rByAqFVuRusLbGi2697A5dFPNXoO//IiogVwi3AdcfPC6A==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-linux-arm": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.15.13.tgz",
"integrity": "sha512-Ac6LpfmJO8WhCMQmO253xX2IU2B3wPDbl4IvR0hnqcPrdfCaUa2j/lLMGTjmQ4W5JsJIdHEdW12dG8lFS0MbxQ==",
"cpu": [
"arm"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-linux-arm64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.13.tgz",
"integrity": "sha512-alEMGU4Z+d17U7KQQw2IV8tQycO6T+rOrgW8OS22Ua25x6kHxoG6Ngry6Aq6uranC+pNWNMB6aHFPh7aTQdORQ==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-linux-mips64le": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.13.tgz",
"integrity": "sha512-47PgmyYEu+yN5rD/MbwS6DxP2FSGPo4Uxg5LwIdxTiyGC2XKwHhHyW7YYEDlSuXLQXEdTO7mYe8zQ74czP7W8A==",
"cpu": [
"mips64el"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-linux-ppc64le": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.13.tgz",
"integrity": "sha512-z6n28h2+PC1Ayle9DjKoBRcx/4cxHoOa2e689e2aDJSaKug3jXcQw7mM+GLg+9ydYoNzj8QxNL8ihOv/OnezhA==",
"cpu": [
"ppc64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-linux-riscv64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.13.tgz",
"integrity": "sha512-+Lu4zuuXuQhgLUGyZloWCqTslcCAjMZH1k3Xc9MSEJEpEFdpsSU0sRDXAnk18FKOfEjhu4YMGaykx9xjtpA6ow==",
"cpu": [
"riscv64"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-linux-s390x": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.13.tgz",
"integrity": "sha512-BMeXRljruf7J0TMxD5CIXS65y7puiZkAh+s4XFV9qy16SxOuMhxhVIXYLnbdfLrsYGFzx7U9mcdpFWkkvy/Uag==",
"cpu": [
"s390x"
],
"dev": true,
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-netbsd-64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.13.tgz",
"integrity": "sha512-EHj9QZOTel581JPj7UO3xYbltFTYnHy+SIqJVq6yd3KkCrsHRbapiPb0Lx3EOOtybBEE9EyqbmfW1NlSDsSzvQ==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-openbsd-64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.13.tgz",
"integrity": "sha512-nkuDlIjF/sfUhfx8SKq0+U+Fgx5K9JcPq1mUodnxI0x4kBdCv46rOGWbuJ6eof2n3wdoCLccOoJAbg9ba/bT2w==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-sunos-64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.15.13.tgz",
"integrity": "sha512-jVeu2GfxZQ++6lRdY43CS0Tm/r4WuQQ0Pdsrxbw+aOrHQPHV0+LNOLnvbN28M7BSUGnJnHkHm2HozGgNGyeIRw==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"sunos"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-windows-32": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.15.13.tgz",
"integrity": "sha512-XoF2iBf0wnqo16SDq+aDGi/+QbaLFpkiRarPVssMh9KYbFNCqPLlGAWwDvxEVz+ywX6Si37J2AKm+AXq1kC0JA==",
"cpu": [
"ia32"
],
"dev": true,
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-windows-64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.15.13.tgz",
"integrity": "sha512-Et6htEfGycjDrtqb2ng6nT+baesZPYQIW+HUEHK4D1ncggNrDNk3yoboYQ5KtiVrw/JaDMNttz8rrPubV/fvPQ==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=12"
}
},
"node_modules/esbuild-windows-arm64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.13.tgz",
"integrity": "sha512-3bv7tqntThQC9SWLRouMDmZnlOukBhOCTlkzNqzGCmrkCJI7io5LLjwJBOVY6kOUlIvdxbooNZwjtBvj+7uuVg==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=12"
}
},
"node_modules/fsevents": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
"dev": true,
"hasInstallScript": true,
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/function-bind": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
"dev": true
},
"node_modules/has": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
"dev": true,
"dependencies": {
"function-bind": "^1.1.1"
},
"engines": {
"node": ">= 0.4.0"
}
},
"node_modules/is-core-module": {
"version": "2.11.0",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz",
"integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==",
"dev": true,
"dependencies": {
"has": "^1.0.3"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/nanoid": {
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
"integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==",
"dev": true,
"bin": {
"nanoid": "bin/nanoid.cjs"
},
"engines": {
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
}
},
"node_modules/path-parse": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
"dev": true
},
"node_modules/picocolors": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
"dev": true
},
"node_modules/postcss": {
"version": "8.4.18",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.18.tgz",
"integrity": "sha512-Wi8mWhncLJm11GATDaQKobXSNEYGUHeQLiQqDFG1qQ5UTDPTEvKw0Xt5NsTpktGTwLps3ByrWsBrG0rB8YQ9oA==",
"dev": true,
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/postcss"
}
],
"dependencies": {
"nanoid": "^3.3.4",
"picocolors": "^1.0.0",
"source-map-js": "^1.0.2"
},
"engines": {
"node": "^10 || ^12 || >=14"
}
},
"node_modules/resolve": {
"version": "1.22.1",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
"integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==",
"dev": true,
"dependencies": {
"is-core-module": "^2.9.0",
"path-parse": "^1.0.7",
"supports-preserve-symlinks-flag": "^1.0.0"
},
"bin": {
"resolve": "bin/resolve"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/rollup": {
"version": "2.79.1",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz",
"integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==",
"dev": true,
"bin": {
"rollup": "dist/bin/rollup"
},
"engines": {
"node": ">=10.0.0"
},
"optionalDependencies": {
"fsevents": "~2.3.2"
}
},
"node_modules/source-map-js": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/supports-preserve-symlinks-flag": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
"dev": true,
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/vite": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/vite/-/vite-3.2.2.tgz",
"integrity": "sha512-pLrhatFFOWO9kS19bQ658CnRYzv0WLbsPih6R+iFeEEhDOuYgYCX2rztUViMz/uy/V8cLCJvLFeiOK7RJEzHcw==",
"dev": true,
"dependencies": {
"esbuild": "^0.15.9",
"postcss": "^8.4.18",
"resolve": "^1.22.1",
"rollup": "^2.79.1"
},
"bin": {
"vite": "bin/vite.js"
},
"engines": {
"node": "^14.18.0 || >=16.0.0"
},
"optionalDependencies": {
"fsevents": "~2.3.2"
},
"peerDependencies": {
"less": "*",
"sass": "*",
"stylus": "*",
"sugarss": "*",
"terser": "^5.4.0"
},
"peerDependenciesMeta": {
"less": {
"optional": true
},
"sass": {
"optional": true
},
"stylus": {
"optional": true
},
"sugarss": {
"optional": true
},
"terser": {
"optional": true
}
}
}
},
"dependencies": {
"@esbuild/android-arm": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.15.13.tgz",
"integrity": "sha512-RY2fVI8O0iFUNvZirXaQ1vMvK0xhCcl0gqRj74Z6yEiO1zAUa7hbsdwZM1kzqbxHK7LFyMizipfXT3JME+12Hw==",
"dev": true,
"optional": true
},
"@esbuild/linux-loong64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.13.tgz",
"integrity": "sha512-+BoyIm4I8uJmH/QDIH0fu7MG0AEx9OXEDXnqptXCwKOlOqZiS4iraH1Nr7/ObLMokW3sOCeBNyD68ATcV9b9Ag==",
"dev": true,
"optional": true
},
"esbuild": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.13.tgz",
"integrity": "sha512-Cu3SC84oyzzhrK/YyN4iEVy2jZu5t2fz66HEOShHURcjSkOSAVL8C/gfUT+lDJxkVHpg8GZ10DD0rMHRPqMFaQ==",
"dev": true,
"requires": {
"@esbuild/android-arm": "0.15.13",
"@esbuild/linux-loong64": "0.15.13",
"esbuild-android-64": "0.15.13",
"esbuild-android-arm64": "0.15.13",
"esbuild-darwin-64": "0.15.13",
"esbuild-darwin-arm64": "0.15.13",
"esbuild-freebsd-64": "0.15.13",
"esbuild-freebsd-arm64": "0.15.13",
"esbuild-linux-32": "0.15.13",
"esbuild-linux-64": "0.15.13",
"esbuild-linux-arm": "0.15.13",
"esbuild-linux-arm64": "0.15.13",
"esbuild-linux-mips64le": "0.15.13",
"esbuild-linux-ppc64le": "0.15.13",
"esbuild-linux-riscv64": "0.15.13",
"esbuild-linux-s390x": "0.15.13",
"esbuild-netbsd-64": "0.15.13",
"esbuild-openbsd-64": "0.15.13",
"esbuild-sunos-64": "0.15.13",
"esbuild-windows-32": "0.15.13",
"esbuild-windows-64": "0.15.13",
"esbuild-windows-arm64": "0.15.13"
}
},
"esbuild-android-64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.15.13.tgz",
"integrity": "sha512-yRorukXBlokwTip+Sy4MYskLhJsO0Kn0/Fj43s1krVblfwP+hMD37a4Wmg139GEsMLl+vh8WXp2mq/cTA9J97g==",
"dev": true,
"optional": true
},
"esbuild-android-arm64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.15.13.tgz",
"integrity": "sha512-TKzyymLD6PiVeyYa4c5wdPw87BeAiTXNtK6amWUcXZxkV51gOk5u5qzmDaYSwiWeecSNHamFsaFjLoi32QR5/w==",
"dev": true,
"optional": true
},
"esbuild-darwin-64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.15.13.tgz",
"integrity": "sha512-WAx7c2DaOS6CrRcoYCgXgkXDliLnFv3pQLV6GeW1YcGEZq2Gnl8s9Pg7ahValZkpOa0iE/ojRVQ87sbUhF1Cbg==",
"dev": true,
"optional": true
},
"esbuild-darwin-arm64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.13.tgz",
"integrity": "sha512-U6jFsPfSSxC3V1CLiQqwvDuj3GGrtQNB3P3nNC3+q99EKf94UGpsG9l4CQ83zBs1NHrk1rtCSYT0+KfK5LsD8A==",
"dev": true,
"optional": true
},
"esbuild-freebsd-64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.13.tgz",
"integrity": "sha512-whItJgDiOXaDG/idy75qqevIpZjnReZkMGCgQaBWZuKHoElDJC1rh7MpoUgupMcdfOd+PgdEwNQW9DAE6i8wyA==",
"dev": true,
"optional": true
},
"esbuild-freebsd-arm64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.13.tgz",
"integrity": "sha512-6pCSWt8mLUbPtygv7cufV0sZLeylaMwS5Fznj6Rsx9G2AJJsAjQ9ifA+0rQEIg7DwJmi9it+WjzNTEAzzdoM3Q==",
"dev": true,
"optional": true
},
"esbuild-linux-32": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.15.13.tgz",
"integrity": "sha512-VbZdWOEdrJiYApm2kkxoTOgsoCO1krBZ3quHdYk3g3ivWaMwNIVPIfEE0f0XQQ0u5pJtBsnk2/7OPiCFIPOe/w==",
"dev": true,
"optional": true
},
"esbuild-linux-64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.15.13.tgz",
"integrity": "sha512-rXmnArVNio6yANSqDQlIO4WiP+Cv7+9EuAHNnag7rByAqFVuRusLbGi2697A5dFPNXoO//IiogVwi3AdcfPC6A==",
"dev": true,
"optional": true
},
"esbuild-linux-arm": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.15.13.tgz",
"integrity": "sha512-Ac6LpfmJO8WhCMQmO253xX2IU2B3wPDbl4IvR0hnqcPrdfCaUa2j/lLMGTjmQ4W5JsJIdHEdW12dG8lFS0MbxQ==",
"dev": true,
"optional": true
},
"esbuild-linux-arm64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.13.tgz",
"integrity": "sha512-alEMGU4Z+d17U7KQQw2IV8tQycO6T+rOrgW8OS22Ua25x6kHxoG6Ngry6Aq6uranC+pNWNMB6aHFPh7aTQdORQ==",
"dev": true,
"optional": true
},
"esbuild-linux-mips64le": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.13.tgz",
"integrity": "sha512-47PgmyYEu+yN5rD/MbwS6DxP2FSGPo4Uxg5LwIdxTiyGC2XKwHhHyW7YYEDlSuXLQXEdTO7mYe8zQ74czP7W8A==",
"dev": true,
"optional": true
},
"esbuild-linux-ppc64le": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.13.tgz",
"integrity": "sha512-z6n28h2+PC1Ayle9DjKoBRcx/4cxHoOa2e689e2aDJSaKug3jXcQw7mM+GLg+9ydYoNzj8QxNL8ihOv/OnezhA==",
"dev": true,
"optional": true
},
"esbuild-linux-riscv64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.13.tgz",
"integrity": "sha512-+Lu4zuuXuQhgLUGyZloWCqTslcCAjMZH1k3Xc9MSEJEpEFdpsSU0sRDXAnk18FKOfEjhu4YMGaykx9xjtpA6ow==",
"dev": true,
"optional": true
},
"esbuild-linux-s390x": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.13.tgz",
"integrity": "sha512-BMeXRljruf7J0TMxD5CIXS65y7puiZkAh+s4XFV9qy16SxOuMhxhVIXYLnbdfLrsYGFzx7U9mcdpFWkkvy/Uag==",
"dev": true,
"optional": true
},
"esbuild-netbsd-64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.13.tgz",
"integrity": "sha512-EHj9QZOTel581JPj7UO3xYbltFTYnHy+SIqJVq6yd3KkCrsHRbapiPb0Lx3EOOtybBEE9EyqbmfW1NlSDsSzvQ==",
"dev": true,
"optional": true
},
"esbuild-openbsd-64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.13.tgz",
"integrity": "sha512-nkuDlIjF/sfUhfx8SKq0+U+Fgx5K9JcPq1mUodnxI0x4kBdCv46rOGWbuJ6eof2n3wdoCLccOoJAbg9ba/bT2w==",
"dev": true,
"optional": true
},
"esbuild-sunos-64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.15.13.tgz",
"integrity": "sha512-jVeu2GfxZQ++6lRdY43CS0Tm/r4WuQQ0Pdsrxbw+aOrHQPHV0+LNOLnvbN28M7BSUGnJnHkHm2HozGgNGyeIRw==",
"dev": true,
"optional": true
},
"esbuild-windows-32": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.15.13.tgz",
"integrity": "sha512-XoF2iBf0wnqo16SDq+aDGi/+QbaLFpkiRarPVssMh9KYbFNCqPLlGAWwDvxEVz+ywX6Si37J2AKm+AXq1kC0JA==",
"dev": true,
"optional": true
},
"esbuild-windows-64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.15.13.tgz",
"integrity": "sha512-Et6htEfGycjDrtqb2ng6nT+baesZPYQIW+HUEHK4D1ncggNrDNk3yoboYQ5KtiVrw/JaDMNttz8rrPubV/fvPQ==",
"dev": true,
"optional": true
},
"esbuild-windows-arm64": {
"version": "0.15.13",
"resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.13.tgz",
"integrity": "sha512-3bv7tqntThQC9SWLRouMDmZnlOukBhOCTlkzNqzGCmrkCJI7io5LLjwJBOVY6kOUlIvdxbooNZwjtBvj+7uuVg==",
"dev": true,
"optional": true
},
"fsevents": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
"dev": true,
"optional": true
},
"function-bind": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
"dev": true
},
"has": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
"dev": true,
"requires": {
"function-bind": "^1.1.1"
}
},
"is-core-module": {
"version": "2.11.0",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz",
"integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==",
"dev": true,
"requires": {
"has": "^1.0.3"
}
},
"nanoid": {
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
"integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==",
"dev": true
},
"path-parse": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
"dev": true
},
"picocolors": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
"dev": true
},
"postcss": {
"version": "8.4.18",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.18.tgz",
"integrity": "sha512-Wi8mWhncLJm11GATDaQKobXSNEYGUHeQLiQqDFG1qQ5UTDPTEvKw0Xt5NsTpktGTwLps3ByrWsBrG0rB8YQ9oA==",
"dev": true,
"requires": {
"nanoid": "^3.3.4",
"picocolors": "^1.0.0",
"source-map-js": "^1.0.2"
}
},
"resolve": {
"version": "1.22.1",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
"integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==",
"dev": true,
"requires": {
"is-core-module": "^2.9.0",
"path-parse": "^1.0.7",
"supports-preserve-symlinks-flag": "^1.0.0"
}
},
"rollup": {
"version": "2.79.1",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz",
"integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==",
"dev": true,
"requires": {
"fsevents": "~2.3.2"
}
},
"source-map-js": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
"dev": true
},
"supports-preserve-symlinks-flag": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
"dev": true
},
"vite": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/vite/-/vite-3.2.2.tgz",
"integrity": "sha512-pLrhatFFOWO9kS19bQ658CnRYzv0WLbsPih6R+iFeEEhDOuYgYCX2rztUViMz/uy/V8cLCJvLFeiOK7RJEzHcw==",
"dev": true,
"requires": {
"esbuild": "^0.15.9",
"fsevents": "~2.3.2",
"postcss": "^8.4.18",
"resolve": "^1.22.1",
"rollup": "^2.79.1"
}
}
}
}
@@ -11,5 +11,12 @@
},
"devDependencies": {
"vite": "^3.2.0"
},
"dependencies": {
"@strudel.cycles/core": "workspace:*",
"@strudel.cycles/mini": "workspace:*",
"@strudel.cycles/transpiler": "workspace:*",
"@strudel.cycles/webaudio": "workspace:*",
"@strudel.cycles/tonal": "workspace:*"
}
}
@@ -1,4 +1,4 @@
/* export default `await samples('github:tidalcycles/Dirt-Samples/master')
export default `await samples('github:tidalcycles/Dirt-Samples/master')
stack(
// amen
@@ -29,11 +29,3 @@ stack(
,
n("0 1").s("east").delay(.5).degradeBy(.8).speed(rand.range(.5,1.5))
).reset("<x@7 x(5,8)>")`;
*/
export default `stack(
n("c3 [eb3,g3]")
.delay("<0 .5>")
.delaytime(".16 | .33")
.delayfeedback(".6 | .8")
).sometimes(x=>x.speed("-1"))`;
-65
View File
@@ -1,65 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Buildless Vanilla Strudel REPL</title>
</head>
<body style="margin: 0; background: #222">
<div style="display: grid; height: 100vh">
<textarea
id="text"
style="font-size: 2em; border: 0; color: white; background: transparent; outline: none; padding: 20px"
spellcheck="false"
>
// LOADING</textarea
>
</div>
<button
id="start"
style="
position: absolute;
border-radius: 10px;
top: 20px;
right: 20px;
padding: 20px;
border: 2px solid white;
background: transparent;
color: white;
cursor: pointer;
"
>
evaluate
</button>
<div id="output"></div>
<script type="module">
import { controls, repl, evalScope } from 'https://cdn.skypack.dev/@strudel.cycles/core@0.4.1';
import { transpiler } from 'https://cdn.skypack.dev/@strudel.cycles/transpiler@0.4.1';
const modules = [
import('https://cdn.skypack.dev/@strudel.cycles/core@0.4.1'),
import('https://cdn.skypack.dev/@strudel.cycles/mini@0.4.1'),
];
const input = document.getElementById('text');
Promise.all(modules).then(() => {
input.innerHTML = `note("<c3 [d3 e3]>").cutoff(1000)`;
document.getElementById('start').addEventListener('click', () => {
evaluate(input.value);
});
});
evalScope(controls, ...modules);
const { evaluate } = repl({
defaultOutput: (hap, deadline, duration) => {
console.log(deadline, duration, hap.value);
},
getTime: () => Date.now() / 1000,
transpiler,
beforeEval: (code) => console.log('evaluate', code),
afterEval: (code) => {},
});
</script>
</body>
</html>
+13
View File
@@ -126,6 +126,19 @@ export class Hap {
setContext(context) {
return new Hap(this.whole, this.part, this.value, context);
}
ensureObjectValue() {
/* if (isNote(hap.value)) {
// supports primitive hap values that look like notes
hap.value = { note: hap.value };
} */
if (typeof this.value !== 'object') {
throw new Error(
`expected hap.value to be an object, but got "${this.value}". Hint: append .note() or .s() to the end`,
'error',
);
}
}
}
export default Hap;
-1639
View File
File diff suppressed because it is too large Load Diff
+13 -3
View File
@@ -1,11 +1,17 @@
{
"name": "@strudel.cycles/core",
"version": "0.5.0",
"version": "0.7.2",
"description": "Port of Tidal Cycles to JavaScript",
"main": "index.mjs",
"type": "module",
"publishConfig": {
"main": "dist/index.js",
"module": "dist/index.mjs"
},
"scripts": {
"test": "vitest run"
"test": "vitest run",
"build": "vite build",
"prepublishOnly": "pnpm build"
},
"repository": {
"type": "git",
@@ -27,5 +33,9 @@
"dependencies": {
"fraction.js": "^4.2.0"
},
"gitHead": "0e26d4e741500f5bae35b023608f062a794905c2"
"gitHead": "0e26d4e741500f5bae35b023608f062a794905c2",
"devDependencies": {
"vite": "^3.2.2",
"vitest": "^0.25.7"
}
}
+417 -91
View File
File diff suppressed because it is too large Load Diff
+14 -2
View File
@@ -4,7 +4,7 @@ Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/st
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { Pattern, toMidi, getDrawContext, freqToMidi, isNote } from './index.mjs';
import { Pattern, noteToMidi, getDrawContext, freqToMidi, isNote } from './index.mjs';
const scale = (normalized, min, max) => normalized * (max - min) + min;
const getValue = (e) => {
@@ -18,7 +18,7 @@ const getValue = (e) => {
}
note = note ?? n;
if (typeof note === 'string') {
return toMidi(note);
return noteToMidi(note);
}
if (typeof note === 'number') {
return note;
@@ -283,3 +283,15 @@ export function pianoroll({
ctx.stroke();
return this;
}
function getOptions(drawTime, options = {}) {
let [lookbehind, lookahead] = drawTime;
lookbehind = Math.abs(lookbehind);
const cycles = lookahead + lookbehind;
const playhead = lookbehind / cycles;
return { fold: 1, ...options, cycles, playhead };
}
Pattern.prototype.punchcard = function (options) {
return this.onPaint((ctx, time, haps, drawTime) => pianoroll({ ctx, time, haps, ...getOptions(drawTime, options) }));
};
+22 -6
View File
@@ -2,6 +2,7 @@ import { Cyclist } from './cyclist.mjs';
import { evaluate as _evaluate } from './evaluate.mjs';
import { logger } from './logger.mjs';
import { setTime } from './time.mjs';
import { evalScope } from './evaluate.mjs';
export function repl({
interval,
@@ -12,18 +13,17 @@ export function repl({
afterEval,
getTime,
transpiler,
editPattern,
onToggle,
editPattern,
}) {
const scheduler = new Cyclist({
interval,
onTrigger: async (hap, deadline, duration) => {
onTrigger: async (hap, deadline, duration, cps) => {
try {
if (!hap.context.onTrigger || !hap.context.dominantTrigger) {
await defaultOutput(hap, deadline, duration);
await defaultOutput(hap, deadline, duration, cps);
}
if (hap.context.onTrigger) {
const cps = 1;
// call signature of output / onTrigger is different...
await hap.context.onTrigger(getTime() + deadline, hap, getTime(), cps);
}
@@ -35,13 +35,24 @@ export function repl({
getTime,
onToggle,
});
setTime(() => scheduler.getPhase()); // TODO: refactor?
setTime(() => scheduler.now()); // TODO: refactor?
const evaluate = async (code, autostart = true) => {
if (!code) {
throw new Error('no code to evaluate');
}
try {
beforeEval?.({ code });
scheduler.setCps(1); // reset cps in case the code does not contain a setCps call
// problem: when the code does contain a setCps after an awaited promise,
// the cps will be 1 until the promise resolves
// example:
/*
await new Promise(resolve => setTimeout(resolve,1000))
setCps(.5)
note("c a f e")
*/
// to make sure the setCps inside the code is called immediately,
// it has to be placed first
let { pattern } = await _evaluate(code, transpiler);
logger(`[eval] code updated`);
@@ -58,5 +69,10 @@ export function repl({
const stop = () => scheduler.stop();
const start = () => scheduler.start();
const pause = () => scheduler.pause();
return { scheduler, evaluate, start, stop, pause };
const setCps = (cps) => scheduler.setCps(cps);
evalScope({
setCps,
setcps: setCps,
});
return { scheduler, evaluate, start, stop, pause, setCps };
}
+8
View File
@@ -114,6 +114,14 @@ const timeToRands = (t, n) => timeToRandsPrime(timeToIntSeed(t), n);
*
*/
/**
* A discrete pattern of numbers from 0 to n-1
* @example
* run(4).scale('C4 major').note()
* // "0 1 2 3".scale('C4 major').note()
*/
export const run = (n) => saw.range(0, n).floor().segment(n);
/**
* A continuous pattern of random numbers, between 0 and 1.
*
+28
View File
@@ -0,0 +1,28 @@
/*
controls.test.mjs - <short description TODO>
Copyright (C) 2023 Strudel contributors - see <https://github.com/tidalcycles/strudel/blob/main/packages/core/test/controls.test.mjs>
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import controls from '../controls.mjs';
import { mini } from '../../mini/mini.mjs';
import { describe, it, expect } from 'vitest';
describe('controls', () => {
it('should support controls', () => {
expect(controls.s('bd').firstCycleValues).toEqual([{ s: 'bd' }]);
});
it('should support compound controls', () => {
expect(controls.s(mini('bd:3')).firstCycleValues).toEqual([{ s: 'bd', n: 3 }]);
expect(controls.s(mini('bd:3 sd:4:1.4')).firstCycleValues).toEqual([
{ s: 'bd', n: 3 },
{ s: 'sd', n: 4, gain: 1.4 },
]);
});
it('should support ignore extra elements in compound controls', () => {
expect(controls.s(mini('bd:3:0.4 sd:4:0.5:3:17')).firstCycleValues).toEqual([
{ s: 'bd', n: 3, gain: 0.4 },
{ s: 'sd', n: 4, gain: 0.5 },
]);
});
});
+67
View File
@@ -44,6 +44,7 @@ import {
ply,
rev,
time,
run,
} from '../index.mjs';
import { steady } from '../signal.mjs';
@@ -908,6 +909,18 @@ describe('Pattern', () => {
);
});
});
describe('run', () => {
it('Can run', () => {
expect(run(4).firstCycle()).toStrictEqual(sequence(0, 1, 2, 3).firstCycle());
});
});
describe('ribbon', () => {
it('Can ribbon', () => {
expect(cat(0, 1, 2, 3, 4, 5, 6, 7).ribbon(2, 4).fast(4).firstCycle()).toStrictEqual(
sequence(2, 3, 4, 5).firstCycle(),
);
});
});
describe('linger', () => {
it('Can linger on the first quarter of a cycle', () => {
expect(sequence(0, 1, 2, 3, 4, 5, 6, 7).linger(0.25).firstCycle()).toStrictEqual(
@@ -936,4 +949,58 @@ describe('Pattern', () => {
expect(stack(sequence('a', silence), pure('a').mask(0, 1)).defragmentHaps().firstCycle().length).toStrictEqual(2);
});
});
describe('press', () => {
it('Can syncopate events', () => {
sameFirst(sequence('a', 'b', 'c', 'd').press(), sequence(silence, 'a', silence, 'b', silence, 'c', silence, 'd'));
});
});
describe('hurry', () => {
it('Can speed up patterns and sounds', () => {
sameFirst(s('a', 'b').hurry(2), s('a', 'b').fast(2).speed(2));
});
});
/*describe('composable functions', () => {
it('Can compose functions', () => {
sameFirst(sequence(3, 4).fast(2).rev().fast(2), fast(2).rev().fast(2)(sequence(3, 4)));
});
it('Can compose by method chaining operators with controls', () => {
sameFirst(s('bd').apply(set.n(3).fast(2)), s('bd').set.n(3).fast(2));
});
it('Can compose by method chaining operators and alignments with controls', () => {
sameFirst(s('bd').apply(set.in.n(3).fast(2)), s('bd').set.n(3).fast(2));
// sameFirst(s('bd').apply(set.squeeze.n(3).fast(2)), s('bd').set.squeeze.n(3).fast(2));
});
});
describe('weave', () => {
it('Can distribute patterns along a pattern', () => {
sameFirst(n(0, 1).weave(2, s('bd', silence), s(silence, 'sd')), sequence(s('bd').n(0), s('sd').n(1)));
});
});
*/
describe('slice', () => {
it('Can slice a sample', () => {
sameFirst(
s('break').slice(4, sequence(0, 1, 2, 3)),
sequence(
{ begin: 0, end: 0.25, s: 'break', _slices: 4 },
{ begin: 0.25, end: 0.5, s: 'break', _slices: 4 },
{ begin: 0.5, end: 0.75, s: 'break', _slices: 4 },
{ begin: 0.75, end: 1, s: 'break', _slices: 4 },
),
);
});
});
describe('splice', () => {
it('Can splice a sample', () => {
sameFirst(
s('break').splice(4, sequence(0, 1, 2, 3)),
sequence(
{ begin: 0, end: 0.25, s: 'break', _slices: 4, unit: 'c', speed: 1 },
{ begin: 0.25, end: 0.5, s: 'break', _slices: 4, unit: 'c', speed: 1 },
{ begin: 0.5, end: 0.75, s: 'break', _slices: 4, unit: 'c', speed: 1 },
{ begin: 0.75, end: 1, s: 'break', _slices: 4, unit: 'c', speed: 1 },
),
);
});
});
});
+17 -17
View File
@@ -8,8 +8,8 @@ import { pure } from '../pattern.mjs';
import {
isNote,
tokenizeNote,
toMidi,
fromMidi,
noteToMidi,
midiToFreq,
freqToMidi,
_mod,
compose,
@@ -75,27 +75,27 @@ describe('isNote', () => {
expect(tokenizeNote(123)).toStrictEqual([]);
});
});
describe('toMidi', () => {
describe('noteToMidi', () => {
it('should turn notes into midi', () => {
expect(toMidi('A4')).toEqual(69);
expect(toMidi('C4')).toEqual(60);
expect(toMidi('Db4')).toEqual(61);
expect(toMidi('C3')).toEqual(48);
expect(toMidi('Cb3')).toEqual(47);
expect(toMidi('Cbb3')).toEqual(46);
expect(toMidi('C#3')).toEqual(49);
expect(toMidi('C#3')).toEqual(49);
expect(toMidi('C##3')).toEqual(50);
expect(noteToMidi('A4')).toEqual(69);
expect(noteToMidi('C4')).toEqual(60);
expect(noteToMidi('Db4')).toEqual(61);
expect(noteToMidi('C3')).toEqual(48);
expect(noteToMidi('Cb3')).toEqual(47);
expect(noteToMidi('Cbb3')).toEqual(46);
expect(noteToMidi('C#3')).toEqual(49);
expect(noteToMidi('C#3')).toEqual(49);
expect(noteToMidi('C##3')).toEqual(50);
});
it('should throw an error when given a non-note', () => {
expect(() => toMidi('Q')).toThrowError(`not a note: "Q"`);
expect(() => toMidi('Z')).toThrowError(`not a note: "Z"`);
expect(() => noteToMidi('Q')).toThrowError(`not a note: "Q"`);
expect(() => noteToMidi('Z')).toThrowError(`not a note: "Z"`);
});
});
describe('fromMidi', () => {
describe('midiToFreq', () => {
it('should turn midi into frequency', () => {
expect(fromMidi(69)).toEqual(440);
expect(fromMidi(57)).toEqual(220);
expect(midiToFreq(69)).toEqual(440);
expect(midiToFreq(57)).toEqual(220);
});
});
describe('freqToMidi', () => {
+1 -1
View File
@@ -26,7 +26,7 @@ export const backgroundImage = function (src, animateOptions = {}) {
({
style: () => (container.style = bg + ';' + value),
className: () => (container.className = value + ' ' + initialClassName),
}[option]());
})[option]();
};
const funcOptions = Object.entries(animateOptions).filter(([_, v]) => typeof v === 'function');
const stringOptions = Object.entries(animateOptions).filter(([_, v]) => typeof v === 'string');
+17 -9
View File
@@ -19,7 +19,7 @@ export const tokenizeNote = (note) => {
};
// turns the given note into its midi number representation
export const toMidi = (note) => {
export const noteToMidi = (note) => {
const [pc, acc, oct = 3] = tokenizeNote(note);
if (!pc) {
throw new Error('not a note: "' + note + '"');
@@ -28,7 +28,7 @@ export const toMidi = (note) => {
const offset = acc?.split('').reduce((o, char) => o + { '#': 1, b: -1, s: 1 }[char], 0) || 0;
return (Number(oct) + 1) * 12 + chroma + offset;
};
export const fromMidi = (n) => {
export const midiToFreq = (n) => {
return Math.pow(2, (n - 69) / 12) * 440;
};
@@ -45,7 +45,7 @@ export const valueToMidi = (value, fallbackValue) => {
return freqToMidi(freq);
}
if (typeof note === 'string') {
return toMidi(note);
return noteToMidi(note);
}
if (typeof note === 'number') {
return note;
@@ -58,16 +58,18 @@ export const valueToMidi = (value, fallbackValue) => {
/**
* @deprecated does not appear to be referenced or invoked anywhere in the codebase
* @noAutocomplete
*/
export const getFreq = (noteOrMidi) => {
if (typeof noteOrMidi === 'number') {
return fromMidi(noteOrMidi);
return midiToFreq(noteOrMidi);
}
return fromMidi(toMidi(noteOrMidi));
return midiToFreq(noteToMidi(noteOrMidi));
};
/**
* @deprecated does not appear to be referenced or invoked anywhere in the codebase
* @noAutocomplete
*/
export const midi2note = (n) => {
const oct = Math.floor(n / 12) - 1;
@@ -89,7 +91,7 @@ export const getPlayableNoteValue = (hap) => {
}
// if value is number => interpret as midi number as long as its not marked as frequency
if (typeof note === 'number' && context.type !== 'frequency') {
note = fromMidi(hap.value);
note = midiToFreq(hap.value);
} else if (typeof note === 'number' && context.type === 'frequency') {
note = hap.value; // legacy workaround.. will be removed in the future
} else if (typeof note !== 'string' || !isNote(note)) {
@@ -108,9 +110,9 @@ export const getFrequency = (hap) => {
return getFreq(value.note || value.n || value.value);
}
if (typeof value === 'number' && context.type !== 'frequency') {
value = fromMidi(hap.value);
value = midiToFreq(hap.value);
} else if (typeof value === 'string' && isNote(value)) {
value = fromMidi(toMidi(hap.value));
value = midiToFreq(noteToMidi(hap.value));
} else if (typeof value !== 'number') {
throw new Error('not a note or frequency: ' + value);
}
@@ -168,7 +170,7 @@ export function parseNumeral(numOrString) {
return asNumber;
}
if (isNote(numOrString)) {
return toMidi(numOrString);
return noteToMidi(numOrString);
}
throw new Error(`cannot parse as numeral: "${numOrString}"`);
}
@@ -204,3 +206,9 @@ export function parseFractional(numOrString) {
}
export const fractionalArgs = (fn) => mapArgs(fn, parseFractional);
export const splitAt = function (index, value) {
return [value.slice(0, index), value.slice(index)];
};
export const zipWith = (f, xs, ys) => xs.map((n, i) => f(n, ys[i]));
+19
View File
@@ -0,0 +1,19 @@
import { defineConfig } from 'vite';
import { dependencies } from './package.json';
import { resolve } from 'path';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [],
build: {
lib: {
entry: resolve(__dirname, 'index.mjs'),
formats: ['es', 'cjs'],
fileName: (ext) => ({ es: 'index.mjs', cjs: 'index.js' }[ext]),
},
rollupOptions: {
external: [...Object.keys(dependencies)],
},
target: 'esnext',
},
});
+1 -1
View File
@@ -44,6 +44,6 @@ function createClock(
};
const getPhase = () => phase;
// setCallback
return { setDuration, start, stop, pause, duration, getPhase, minLatency };
return { setDuration, start, stop, pause, duration, interval, getPhase, minLatency };
}
export default createClock;
@@ -28,9 +28,7 @@ export const csound = register('csound', (instrument, pat) => {
logger('[csound] not loaded yet', 'warning');
return;
}
if (typeof hap.value !== 'object') {
throw new Error('csound only support objects as hap values');
}
hap.ensureObjectValue();
let { gain = 0.8 } = hap.value;
gain *= 0.2;
@@ -92,6 +90,7 @@ async function load() {
['message'].forEach((k) => _csound.on(k, (...args) => eventLogger(k, args)));
await _csound.setOption('-m0d'); // see -m flag https://csound.com/docs/manual/CommandFlags.html
await _csound.setOption('--sample-accurate');
await _csound.setOption('-odac');
await _csound.compileCsdText(csd);
// await _csound.compileOrc(livecodeOrc);
await _csound.compileOrc(presetsOrc);
+14 -4
View File
@@ -1,10 +1,15 @@
{
"name": "@strudel.cycles/csound",
"version": "0.5.1",
"version": "0.7.1",
"description": "csound bindings for strudel",
"main": "csound.mjs",
"main": "index.mjs",
"publishConfig": {
"main": "dist/index.js",
"module": "dist/index.mjs"
},
"scripts": {
"test": "echo \"No tests present.\" && exit 0"
"build": "vite build",
"prepublishOnly": "npm run build"
},
"repository": {
"type": "git",
@@ -27,6 +32,11 @@
},
"homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": {
"@csound/browser": "^6.18.3"
"@csound/browser": "6.18.5",
"@strudel.cycles/core": "workspace:*",
"@strudel.cycles/webaudio": "workspace:*"
},
"devDependencies": {
"vite": "^3.2.2"
}
}
+19
View File
@@ -0,0 +1,19 @@
import { defineConfig } from 'vite';
import { dependencies } from './package.json';
import { resolve } from 'path';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [],
build: {
lib: {
entry: resolve(__dirname, 'index.mjs'),
formats: ['es', 'cjs'],
fileName: (ext) => ({ es: 'index.mjs', cjs: 'index.js' }[ext]),
},
rollupOptions: {
external: [...Object.keys(dependencies)],
},
target: 'esnext',
},
});
+4
View File
@@ -3,6 +3,10 @@
This package contains the strudel code transformer and evaluator.
It allows creating strudel patterns from input code that is optimized for minimal keystrokes and human readability.
## Deprecation Note
This package will not be developed further. Consider using `@strudel.cycles/transpiler` as a replacement.
## Install
```sh
-297
View File
@@ -1,297 +0,0 @@
{
"name": "@strudel.cycles/eval",
"version": "0.5.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@strudel.cycles/eval",
"version": "0.1.1",
"license": "AGPL-3.0-or-later",
"dependencies": {
"estraverse": "^5.3.0",
"shift-ast": "^6.1.0",
"shift-codegen": "^7.0.3",
"shift-parser": "^7.0.3",
"shift-spec": "^2018.0.2",
"shift-traverser": "^1.0.0"
}
},
"node_modules/estraverse": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
"integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
"engines": {
"node": ">=4.0"
}
},
"node_modules/esutils": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
"integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/multimap": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/multimap/-/multimap-1.1.0.tgz",
"integrity": "sha512-0ZIR9PasPxGXmRsEF8jsDzndzHDj7tIav+JUmvIFB/WHswliFnquxECT/De7GR4yg99ky/NlRKJT82G1y271bw=="
},
"node_modules/object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/shift-ast": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/shift-ast/-/shift-ast-6.1.0.tgz",
"integrity": "sha512-Vj4XUIJIFPIh6VcBGJ1hjH/kM88XGer94Pr7Rvxa+idEylDsrwtLw268HoxGo5xReL6T3DdRl/9/Pr1XihZ/8Q=="
},
"node_modules/shift-codegen": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/shift-codegen/-/shift-codegen-7.0.3.tgz",
"integrity": "sha512-dfCVVdBF0qZ6pkajQ3bjxRdNEltyxEITVe7tBJkQt2eCI3znUkSxq0VSe/tTWq1LKHeAS4HuOiqYEuHMFkSq9w==",
"dependencies": {
"esutils": "^2.0.2",
"object-assign": "^4.1.0",
"shift-reducer": "6.0.0"
}
},
"node_modules/shift-parser": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/shift-parser/-/shift-parser-7.0.3.tgz",
"integrity": "sha512-uYX2ORyZfKZrUc4iKKkO9KOhzUSxCrSBk7QK6ZmShId+BOo1gh1IwecVy97ynyOTpmhPWUttjC8BzsnQl65Zew==",
"dependencies": {
"multimap": "^1.0.2",
"shift-ast": "6.0.0",
"shift-reducer": "6.0.0",
"shift-regexp-acceptor": "2.0.3"
}
},
"node_modules/shift-parser/node_modules/shift-ast": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/shift-ast/-/shift-ast-6.0.0.tgz",
"integrity": "sha512-XXxDcEBWVBzqWXfNYJlLyJ1/9kMvOXVRXiqPjkOrTCC5qRsBvEMJMRLLFhU3tn8ue56Y7IZyBE6bexFum5QLUw=="
},
"node_modules/shift-reducer": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/shift-reducer/-/shift-reducer-6.0.0.tgz",
"integrity": "sha512-2rJraRP8drIOjvaE/sALa+0tGJmMVUzlmS3wIJerJbaYuCjpFAiF0WjkTOFVtz1144Nm/ECmqeG+7yRhuMVsMg==",
"dependencies": {
"shift-ast": "6.0.0"
}
},
"node_modules/shift-reducer/node_modules/shift-ast": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/shift-ast/-/shift-ast-6.0.0.tgz",
"integrity": "sha512-XXxDcEBWVBzqWXfNYJlLyJ1/9kMvOXVRXiqPjkOrTCC5qRsBvEMJMRLLFhU3tn8ue56Y7IZyBE6bexFum5QLUw=="
},
"node_modules/shift-regexp-acceptor": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/shift-regexp-acceptor/-/shift-regexp-acceptor-2.0.3.tgz",
"integrity": "sha512-sxL7e5JNUFxm+gutFRXktX2D6KVgDAHNuDsk5XHB9Z+N5yXooZG6pdZ1GEbo3Jz6lF7ETYLBC4WAjIFm2RKTmA==",
"dependencies": {
"unicode-match-property-ecmascript": "1.0.4",
"unicode-match-property-value-ecmascript": "1.0.2",
"unicode-property-aliases-ecmascript": "1.0.4"
}
},
"node_modules/shift-spec": {
"version": "2018.0.2",
"resolved": "https://registry.npmjs.org/shift-spec/-/shift-spec-2018.0.2.tgz",
"integrity": "sha512-5CP/cKDEim4rZ6ViCSipTLY2U7HJr8q/kpDuCBmebFqbx/0DeozWO+9ienHmYjgGLDfHrqj+LBAN67FRK2vE6w=="
},
"node_modules/shift-traverser": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/shift-traverser/-/shift-traverser-1.0.0.tgz",
"integrity": "sha512-DMY3512wJbdC+IC+nhLH3/Stgr2BbxbNcg7qyZ6+e5qNnNs8TBQJWdMsRgHlX1JXwF4C0ONKS8VUxsPT0Tf7aw==",
"dependencies": {
"estraverse": "4.2.0",
"shift-spec": "2018.0.0"
}
},
"node_modules/shift-traverser/node_modules/estraverse": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz",
"integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/shift-traverser/node_modules/shift-spec": {
"version": "2018.0.0",
"resolved": "https://registry.npmjs.org/shift-spec/-/shift-spec-2018.0.0.tgz",
"integrity": "sha512-/aiPOkj7dbe+CV2VZhIMTHQToZmgniofpRG7Yr7x2/0sO6CSVC++py1Wzf+s+rWSTDHKcLvziVAxjRRV4i4EoQ=="
},
"node_modules/unicode-canonical-property-names-ecmascript": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz",
"integrity": "sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ==",
"engines": {
"node": ">=4"
}
},
"node_modules/unicode-match-property-ecmascript": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz",
"integrity": "sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==",
"dependencies": {
"unicode-canonical-property-names-ecmascript": "^1.0.4",
"unicode-property-aliases-ecmascript": "^1.0.4"
},
"engines": {
"node": ">=4"
}
},
"node_modules/unicode-match-property-value-ecmascript": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.0.2.tgz",
"integrity": "sha512-Rx7yODZC1L/T8XKo/2kNzVAQaRE88AaMvI1EF/Xnj3GW2wzN6fop9DDWuFAKUVFH7vozkz26DzP0qyWLKLIVPQ==",
"engines": {
"node": ">=4"
}
},
"node_modules/unicode-property-aliases-ecmascript": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.0.4.tgz",
"integrity": "sha512-2WSLa6OdYd2ng8oqiGIWnJqyFArvhn+5vgx5GTxMbUYjCYKUcuKS62YLFF0R/BDGlB1yzXjQOLtPAfHsgirEpg==",
"engines": {
"node": ">=4"
}
}
},
"dependencies": {
"estraverse": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
"integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="
},
"esutils": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
"integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="
},
"multimap": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/multimap/-/multimap-1.1.0.tgz",
"integrity": "sha512-0ZIR9PasPxGXmRsEF8jsDzndzHDj7tIav+JUmvIFB/WHswliFnquxECT/De7GR4yg99ky/NlRKJT82G1y271bw=="
},
"object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
},
"shift-ast": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/shift-ast/-/shift-ast-6.1.0.tgz",
"integrity": "sha512-Vj4XUIJIFPIh6VcBGJ1hjH/kM88XGer94Pr7Rvxa+idEylDsrwtLw268HoxGo5xReL6T3DdRl/9/Pr1XihZ/8Q=="
},
"shift-codegen": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/shift-codegen/-/shift-codegen-7.0.3.tgz",
"integrity": "sha512-dfCVVdBF0qZ6pkajQ3bjxRdNEltyxEITVe7tBJkQt2eCI3znUkSxq0VSe/tTWq1LKHeAS4HuOiqYEuHMFkSq9w==",
"requires": {
"esutils": "^2.0.2",
"object-assign": "^4.1.0",
"shift-reducer": "6.0.0"
}
},
"shift-parser": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/shift-parser/-/shift-parser-7.0.3.tgz",
"integrity": "sha512-uYX2ORyZfKZrUc4iKKkO9KOhzUSxCrSBk7QK6ZmShId+BOo1gh1IwecVy97ynyOTpmhPWUttjC8BzsnQl65Zew==",
"requires": {
"multimap": "^1.0.2",
"shift-ast": "6.0.0",
"shift-reducer": "6.0.0",
"shift-regexp-acceptor": "2.0.3"
},
"dependencies": {
"shift-ast": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/shift-ast/-/shift-ast-6.0.0.tgz",
"integrity": "sha512-XXxDcEBWVBzqWXfNYJlLyJ1/9kMvOXVRXiqPjkOrTCC5qRsBvEMJMRLLFhU3tn8ue56Y7IZyBE6bexFum5QLUw=="
}
}
},
"shift-reducer": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/shift-reducer/-/shift-reducer-6.0.0.tgz",
"integrity": "sha512-2rJraRP8drIOjvaE/sALa+0tGJmMVUzlmS3wIJerJbaYuCjpFAiF0WjkTOFVtz1144Nm/ECmqeG+7yRhuMVsMg==",
"requires": {
"shift-ast": "6.0.0"
},
"dependencies": {
"shift-ast": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/shift-ast/-/shift-ast-6.0.0.tgz",
"integrity": "sha512-XXxDcEBWVBzqWXfNYJlLyJ1/9kMvOXVRXiqPjkOrTCC5qRsBvEMJMRLLFhU3tn8ue56Y7IZyBE6bexFum5QLUw=="
}
}
},
"shift-regexp-acceptor": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/shift-regexp-acceptor/-/shift-regexp-acceptor-2.0.3.tgz",
"integrity": "sha512-sxL7e5JNUFxm+gutFRXktX2D6KVgDAHNuDsk5XHB9Z+N5yXooZG6pdZ1GEbo3Jz6lF7ETYLBC4WAjIFm2RKTmA==",
"requires": {
"unicode-match-property-ecmascript": "1.0.4",
"unicode-match-property-value-ecmascript": "1.0.2",
"unicode-property-aliases-ecmascript": "1.0.4"
}
},
"shift-spec": {
"version": "2018.0.2",
"resolved": "https://registry.npmjs.org/shift-spec/-/shift-spec-2018.0.2.tgz",
"integrity": "sha512-5CP/cKDEim4rZ6ViCSipTLY2U7HJr8q/kpDuCBmebFqbx/0DeozWO+9ienHmYjgGLDfHrqj+LBAN67FRK2vE6w=="
},
"shift-traverser": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/shift-traverser/-/shift-traverser-1.0.0.tgz",
"integrity": "sha512-DMY3512wJbdC+IC+nhLH3/Stgr2BbxbNcg7qyZ6+e5qNnNs8TBQJWdMsRgHlX1JXwF4C0ONKS8VUxsPT0Tf7aw==",
"requires": {
"estraverse": "4.2.0",
"shift-spec": "2018.0.0"
},
"dependencies": {
"estraverse": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz",
"integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM="
},
"shift-spec": {
"version": "2018.0.0",
"resolved": "https://registry.npmjs.org/shift-spec/-/shift-spec-2018.0.0.tgz",
"integrity": "sha512-/aiPOkj7dbe+CV2VZhIMTHQToZmgniofpRG7Yr7x2/0sO6CSVC++py1Wzf+s+rWSTDHKcLvziVAxjRRV4i4EoQ=="
}
}
},
"unicode-canonical-property-names-ecmascript": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz",
"integrity": "sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ=="
},
"unicode-match-property-ecmascript": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz",
"integrity": "sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==",
"requires": {
"unicode-canonical-property-names-ecmascript": "^1.0.4",
"unicode-property-aliases-ecmascript": "^1.0.4"
}
},
"unicode-match-property-value-ecmascript": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.0.2.tgz",
"integrity": "sha512-Rx7yODZC1L/T8XKo/2kNzVAQaRE88AaMvI1EF/Xnj3GW2wzN6fop9DDWuFAKUVFH7vozkz26DzP0qyWLKLIVPQ=="
},
"unicode-property-aliases-ecmascript": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.0.4.tgz",
"integrity": "sha512-2WSLa6OdYd2ng8oqiGIWnJqyFArvhn+5vgx5GTxMbUYjCYKUcuKS62YLFF0R/BDGlB1yzXjQOLtPAfHsgirEpg=="
}
}
}
+16 -5
View File
@@ -1,15 +1,21 @@
{
"name": "@strudel.cycles/eval",
"version": "0.5.0",
"version": "0.7.1",
"description": "Code evaluator for strudel",
"main": "index.mjs",
"publishConfig": {
"main": "dist/index.js",
"module": "dist/index.mjs"
},
"scripts": {
"build": "vite build",
"test": "vitest run",
"prepublishOnly": "npm run build"
},
"type": "module",
"directories": {
"test": "test"
},
"scripts": {
"test": "vitest run"
},
"repository": {
"type": "git",
"url": "git+https://github.com/tidalcycles/strudel.git"
@@ -28,12 +34,17 @@
},
"homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": {
"@strudel.cycles/core": "^0.5.0",
"@strudel.cycles/core": "workspace:*",
"estraverse": "^5.3.0",
"shift-ast": "^7.0.0",
"shift-codegen": "^8.1.0",
"shift-parser": "^8.0.0",
"shift-spec": "^2019.0.0",
"shift-traverser": "^1.0.0"
},
"devDependencies": {
"@strudel.cycles/mini": "workspace:*",
"vite": "^3.2.2",
"vitest": "^0.25.7"
}
}
+19
View File
@@ -0,0 +1,19 @@
import { defineConfig } from 'vite';
import { dependencies } from './package.json';
import { resolve } from 'path';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [],
build: {
lib: {
entry: resolve(__dirname, 'index.mjs'),
formats: ['es', 'cjs'],
fileName: (ext) => ({ es: 'index.mjs', cjs: 'index.js' }[ext]),
},
rollupOptions: {
external: [...Object.keys(dependencies)],
},
target: 'esnext',
},
});
-4
View File
@@ -7,7 +7,3 @@ This package adds midi functionality to strudel Patterns.
```sh
npm i @strudel.cycles/midi --save
```
## Dev Notes
- is this package really necessary? currently, /tone also depends on webmidi through @tonejs/piano. Either move piano out of /tone or merge /midi into /tone...
+30 -15
View File
@@ -5,8 +5,9 @@ This program is free software: you can redistribute it and/or modify it under th
*/
import * as _WebMidi from 'webmidi';
import { Pattern, isPattern, isNote, getPlayableNoteValue, logger } from '@strudel.cycles/core';
import { Pattern, isPattern, logger } from '@strudel.cycles/core';
import { getAudioContext } from '@strudel.cycles/webaudio';
import { noteToMidi } from '@strudel.cycles/core';
// if you use WebMidi from outside of this package, make sure to import that instance:
export const { WebMidi } = _WebMidi;
@@ -63,7 +64,7 @@ function getDevice(output, outputs) {
}
// Pattern.prototype.midi = function (output: string | number, channel = 1) {
Pattern.prototype.midi = function (output, channel = 1) {
Pattern.prototype.midi = function (output) {
if (!supportsMidi()) {
throw new Error(`🎹 WebMidi is not enabled. Supported Browsers: https://caniuse.com/?search=webmidi`);
}
@@ -90,11 +91,6 @@ Pattern.prototype.midi = function (output, channel = 1) {
);
}
return this.onTrigger((time, hap) => {
let note = getPlayableNoteValue(hap);
const velocity = hap.context?.velocity ?? 0.9;
if (!isNote(note)) {
throw new Error('not a note: ' + note);
}
if (!midiReady) {
return;
}
@@ -106,15 +102,34 @@ Pattern.prototype.midi = function (output, channel = 1) {
.join(' | ')}`,
);
}
// console.log('midi', value, output);
hap.ensureObjectValue();
// calculate time
const timingOffset = WebMidi.time - getAudioContext().currentTime * 1000;
time = time * 1000 + timingOffset;
// const inMs = '+' + (time - Tone.getContext().currentTime) * 1000;
// await enableWebMidi()
device.playNote(note, channel, {
time,
duration: hap.duration.valueOf() * 1000 - 5,
attack: velocity,
});
// destructure value
const { note, nrpnn, nrpv, ccn, ccv, midichan = 1 } = hap.value;
const velocity = hap.context?.velocity ?? 0.9; // TODO: refactor velocity
const duration = hap.duration.valueOf() * 1000 - 5;
if (note) {
const midiNumber = noteToMidi(note);
device.playNote(midiNumber, midichan, {
time,
duration,
attack: velocity,
});
}
if (ccv && ccn) {
if (typeof ccv !== 'number' || ccv < 0 || ccv > 1) {
throw new Error('expected ccv to be a number between 0 and 1');
}
if (!['string', 'number'].includes(typeof ccn)) {
throw new Error('expected ccn to be a number or a string');
}
const scaled = Math.round(ccv * 127);
device.sendControlChange(ccn, scaled, midichan, { time });
}
});
};
-130
View File
@@ -1,130 +0,0 @@
{
"name": "@strudel.cycles/midi",
"version": "0.5.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@strudel.cycles/midi",
"version": "0.1.1",
"license": "AGPL-3.0-or-later",
"dependencies": {
"tone": "^14.7.77",
"webmidi": "^2.5.2"
}
},
"node_modules/@babel/runtime": {
"version": "7.17.8",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.8.tgz",
"integrity": "sha512-dQpEpK0O9o6lj6oPu0gRDbbnk+4LeHlNcBpspf6Olzt3GIX4P1lWF1gS+pHLDFlaJvbR6q7jCfQ08zA4QJBnmA==",
"dependencies": {
"regenerator-runtime": "^0.13.4"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/automation-events": {
"version": "4.0.14",
"resolved": "https://registry.npmjs.org/automation-events/-/automation-events-4.0.14.tgz",
"integrity": "sha512-CB2Me0yW8sz7gSGwMiSfgfs1Oqlgs53k+eVESN6axvRyMAD3zlSp2nqndD2TQAtW3yOtSEJWNGsw0r48+f1wtw==",
"dependencies": {
"@babel/runtime": "^7.17.2",
"tslib": "^2.3.1"
},
"engines": {
"node": ">=12.20.1"
}
},
"node_modules/regenerator-runtime": {
"version": "0.13.9",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz",
"integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA=="
},
"node_modules/standardized-audio-context": {
"version": "25.3.21",
"resolved": "https://registry.npmjs.org/standardized-audio-context/-/standardized-audio-context-25.3.21.tgz",
"integrity": "sha512-CZEnayJJjNefeU+z1QGDhaid1LAAYxWYa2ipNk75ropwec9rq6fmclyhXb1wGtDsZ402irX3HLt1U/PwP9+1fA==",
"dependencies": {
"@babel/runtime": "^7.17.2",
"automation-events": "^4.0.14",
"tslib": "^2.3.1"
}
},
"node_modules/tone": {
"version": "14.7.77",
"resolved": "https://registry.npmjs.org/tone/-/tone-14.7.77.tgz",
"integrity": "sha512-tCfK73IkLHyzoKUvGq47gyDyxiKLFvKiVCOobynGgBB9Dl0NkxTM2p+eRJXyCYrjJwy9Y0XCMqD3uOYsYt2Fdg==",
"dependencies": {
"standardized-audio-context": "^25.1.8",
"tslib": "^2.0.1"
}
},
"node_modules/tslib": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
},
"node_modules/webmidi": {
"version": "2.5.3",
"resolved": "https://registry.npmjs.org/webmidi/-/webmidi-2.5.3.tgz",
"integrity": "sha512-PyMGvKcDGpvbQUfnmBORQJciyG3VAZ4aHlGy1iRZ3uEs4kG4HCvI7KRthUpM1vuHDPL98lidRIUaoRomkJtWtg==",
"engines": {
"node": ">0.6.x"
}
}
},
"dependencies": {
"@babel/runtime": {
"version": "7.17.8",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.8.tgz",
"integrity": "sha512-dQpEpK0O9o6lj6oPu0gRDbbnk+4LeHlNcBpspf6Olzt3GIX4P1lWF1gS+pHLDFlaJvbR6q7jCfQ08zA4QJBnmA==",
"requires": {
"regenerator-runtime": "^0.13.4"
}
},
"automation-events": {
"version": "4.0.14",
"resolved": "https://registry.npmjs.org/automation-events/-/automation-events-4.0.14.tgz",
"integrity": "sha512-CB2Me0yW8sz7gSGwMiSfgfs1Oqlgs53k+eVESN6axvRyMAD3zlSp2nqndD2TQAtW3yOtSEJWNGsw0r48+f1wtw==",
"requires": {
"@babel/runtime": "^7.17.2",
"tslib": "^2.3.1"
}
},
"regenerator-runtime": {
"version": "0.13.9",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz",
"integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA=="
},
"standardized-audio-context": {
"version": "25.3.21",
"resolved": "https://registry.npmjs.org/standardized-audio-context/-/standardized-audio-context-25.3.21.tgz",
"integrity": "sha512-CZEnayJJjNefeU+z1QGDhaid1LAAYxWYa2ipNk75ropwec9rq6fmclyhXb1wGtDsZ402irX3HLt1U/PwP9+1fA==",
"requires": {
"@babel/runtime": "^7.17.2",
"automation-events": "^4.0.14",
"tslib": "^2.3.1"
}
},
"tone": {
"version": "14.7.77",
"resolved": "https://registry.npmjs.org/tone/-/tone-14.7.77.tgz",
"integrity": "sha512-tCfK73IkLHyzoKUvGq47gyDyxiKLFvKiVCOobynGgBB9Dl0NkxTM2p+eRJXyCYrjJwy9Y0XCMqD3uOYsYt2Fdg==",
"requires": {
"standardized-audio-context": "^25.1.8",
"tslib": "^2.0.1"
}
},
"tslib": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
},
"webmidi": {
"version": "2.5.3",
"resolved": "https://registry.npmjs.org/webmidi/-/webmidi-2.5.3.tgz",
"integrity": "sha512-PyMGvKcDGpvbQUfnmBORQJciyG3VAZ4aHlGy1iRZ3uEs4kG4HCvI7KRthUpM1vuHDPL98lidRIUaoRomkJtWtg=="
}
}
}
+14 -3
View File
@@ -1,8 +1,16 @@
{
"name": "@strudel.cycles/midi",
"version": "0.5.0",
"version": "0.7.1",
"description": "Midi API for strudel",
"main": "index.mjs",
"publishConfig": {
"main": "dist/index.js",
"module": "dist/index.mjs"
},
"scripts": {
"build": "vite build",
"prepublishOnly": "npm run build"
},
"repository": {
"type": "git",
"url": "git+https://github.com/tidalcycles/strudel.git"
@@ -21,8 +29,11 @@
},
"homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": {
"@strudel.cycles/tone": "^0.5.0",
"tone": "^14.7.77",
"@strudel.cycles/core": "workspace:*",
"@strudel.cycles/webaudio": "workspace:*",
"webmidi": "^3.0.21"
},
"devDependencies": {
"vite": "^3.2.2"
}
}
+19
View File
@@ -0,0 +1,19 @@
import { defineConfig } from 'vite';
import { dependencies } from './package.json';
import { resolve } from 'path';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [],
build: {
lib: {
entry: resolve(__dirname, 'index.mjs'),
formats: ['es', 'cjs'],
fileName: (ext) => ({ es: 'index.mjs', cjs: 'index.js' }[ext]),
},
rollupOptions: {
external: [...Object.keys(dependencies)],
},
target: 'esnext',
},
});
+1 -1
View File
@@ -40,5 +40,5 @@ The parser [krill-parser.js] is generated from [krill.pegjs](./krill.pegjs) usin
To generate the parser, run
```js
npm run build:parser
npm build:parser
```
+150 -123
View File
@@ -182,21 +182,21 @@ function peg$parse(input, options) {
var peg$c8 = "#";
var peg$c9 = "^";
var peg$c10 = "_";
var peg$c11 = ":";
var peg$c12 = "[";
var peg$c13 = "]";
var peg$c14 = "{";
var peg$c15 = "}";
var peg$c16 = "%";
var peg$c17 = "<";
var peg$c18 = ">";
var peg$c19 = "@";
var peg$c20 = "!";
var peg$c21 = "(";
var peg$c22 = ")";
var peg$c23 = "/";
var peg$c24 = "*";
var peg$c25 = "?";
var peg$c11 = "[";
var peg$c12 = "]";
var peg$c13 = "{";
var peg$c14 = "}";
var peg$c15 = "%";
var peg$c16 = "<";
var peg$c17 = ">";
var peg$c18 = "@";
var peg$c19 = "!";
var peg$c20 = "(";
var peg$c21 = ")";
var peg$c22 = "/";
var peg$c23 = "*";
var peg$c24 = "?";
var peg$c25 = ":";
var peg$c26 = "struct";
var peg$c27 = "target";
var peg$c28 = "euclid";
@@ -237,21 +237,21 @@ function peg$parse(input, options) {
var peg$e15 = peg$literalExpectation("#", false);
var peg$e16 = peg$literalExpectation("^", false);
var peg$e17 = peg$literalExpectation("_", false);
var peg$e18 = peg$literalExpectation(":", false);
var peg$e19 = peg$literalExpectation("[", false);
var peg$e20 = peg$literalExpectation("]", false);
var peg$e21 = peg$literalExpectation("{", false);
var peg$e22 = peg$literalExpectation("}", false);
var peg$e23 = peg$literalExpectation("%", false);
var peg$e24 = peg$literalExpectation("<", false);
var peg$e25 = peg$literalExpectation(">", false);
var peg$e26 = peg$literalExpectation("@", false);
var peg$e27 = peg$literalExpectation("!", false);
var peg$e28 = peg$literalExpectation("(", false);
var peg$e29 = peg$literalExpectation(")", false);
var peg$e30 = peg$literalExpectation("/", false);
var peg$e31 = peg$literalExpectation("*", false);
var peg$e32 = peg$literalExpectation("?", false);
var peg$e18 = peg$literalExpectation("[", false);
var peg$e19 = peg$literalExpectation("]", false);
var peg$e20 = peg$literalExpectation("{", false);
var peg$e21 = peg$literalExpectation("}", false);
var peg$e22 = peg$literalExpectation("%", false);
var peg$e23 = peg$literalExpectation("<", false);
var peg$e24 = peg$literalExpectation(">", false);
var peg$e25 = peg$literalExpectation("@", false);
var peg$e26 = peg$literalExpectation("!", false);
var peg$e27 = peg$literalExpectation("(", false);
var peg$e28 = peg$literalExpectation(")", false);
var peg$e29 = peg$literalExpectation("/", false);
var peg$e30 = peg$literalExpectation("*", false);
var peg$e31 = peg$literalExpectation("?", false);
var peg$e32 = peg$literalExpectation(":", false);
var peg$e33 = peg$literalExpectation("struct", false);
var peg$e34 = peg$literalExpectation("target", false);
var peg$e35 = peg$literalExpectation("euclid", false);
@@ -279,36 +279,37 @@ function peg$parse(input, options) {
var peg$f8 = function(p, s, r) { return x => x.options_['ops'].push({ type_: "bjorklund", arguments_ :{ pulse: p, step:s, rotation:r }}) };
var peg$f9 = function(a) { return x => x.options_['ops'].push({ type_: "stretch", arguments_ :{ amount:a, type: 'slow' }}) };
var peg$f10 = function(a) { return x => x.options_['ops'].push({ type_: "stretch", arguments_ :{ amount:a, type: 'fast' }}) };
var peg$f11 = function(a) { return x => x.options_['ops'].push({ type_: "degradeBy", arguments_ :{ amount:a } }) };
var peg$f12 = function(s, ops) { const result = new ElementStub(s, {ops: [], weight: 1, reps: 1});
var peg$f11 = function(a) { return x => x.options_['ops'].push({ type_: "degradeBy", arguments_ :{ amount:a, seed: seed++ } }) };
var peg$f12 = function(s) { return x => x.options_['ops'].push({ type_: "tail", arguments_ :{ element:s } }) };
var peg$f13 = function(s, ops) { const result = new ElementStub(s, {ops: [], weight: 1, reps: 1});
for (const op of ops) {
op(result);
}
return result;
};
var peg$f13 = function(s) { return new PatternStub(s, 'fastcat'); };
var peg$f14 = function(tail) { return { alignment: 'stack', list: tail }; };
var peg$f15 = function(tail) { return { alignment: 'rand', list: tail }; };
var peg$f16 = function(head, tail) { if (tail && tail.list.length > 0) { return new PatternStub([head, ...tail.list], tail.alignment); } else { return head; } };
var peg$f17 = function(head, tail) { return new PatternStub(tail ? [head, ...tail.list] : [head], 'polymeter'); };
var peg$f18 = function(sc) { return sc; };
var peg$f19 = function(s) { return { name: "struct", args: { mini:s }}};
var peg$f20 = function(s) { return { name: "target", args : { name:s}}};
var peg$f21 = function(p, s, r) { return { name: "bjorklund", args :{ pulse: p, step:parseInt(s) }}};
var peg$f22 = function(a) { return { name: "stretch", args :{ amount: a}}};
var peg$f23 = function(a) { return { name: "shift", args :{ amount: "-"+a}}};
var peg$f24 = function(a) { return { name: "shift", args :{ amount: a}}};
var peg$f25 = function(a) { return { name: "stretch", args :{ amount: "1/"+a}}};
var peg$f26 = function(s) { return { name: "scale", args :{ scale: s.join("")}}};
var peg$f27 = function(s, v) { return v};
var peg$f28 = function(s, ss) { ss.unshift(s); return new PatternStub(ss, 'slowcat'); };
var peg$f29 = function(sg) {return sg};
var peg$f30 = function(o, soc) { return new OperatorStub(o.name,o.args,soc)};
var peg$f31 = function(sc) { return sc };
var peg$f32 = function(c) { return c };
var peg$f33 = function(v) { return new CommandStub("setcps", { value: v})};
var peg$f34 = function(v) { return new CommandStub("setcps", { value: (v/120/2)})};
var peg$f35 = function() { return new CommandStub("hush")};
var peg$f14 = function(s) { return new PatternStub(s, 'fastcat'); };
var peg$f15 = function(tail) { return { alignment: 'stack', list: tail }; };
var peg$f16 = function(tail) { return { alignment: 'rand', list: tail, seed: seed++ }; };
var peg$f17 = function(head, tail) { if (tail && tail.list.length > 0) { return new PatternStub([head, ...tail.list], tail.alignment, tail.seed); } else { return head; } };
var peg$f18 = function(head, tail) { return new PatternStub(tail ? [head, ...tail.list] : [head], 'polymeter'); };
var peg$f19 = function(sc) { return sc; };
var peg$f20 = function(s) { return { name: "struct", args: { mini:s }}};
var peg$f21 = function(s) { return { name: "target", args : { name:s}}};
var peg$f22 = function(p, s, r) { return { name: "bjorklund", args :{ pulse: p, step:parseInt(s) }}};
var peg$f23 = function(a) { return { name: "stretch", args :{ amount: a}}};
var peg$f24 = function(a) { return { name: "shift", args :{ amount: "-"+a}}};
var peg$f25 = function(a) { return { name: "shift", args :{ amount: a}}};
var peg$f26 = function(a) { return { name: "stretch", args :{ amount: "1/"+a}}};
var peg$f27 = function(s) { return { name: "scale", args :{ scale: s.join("")}}};
var peg$f28 = function(s, v) { return v};
var peg$f29 = function(s, ss) { ss.unshift(s); return new PatternStub(ss, 'slowcat'); };
var peg$f30 = function(sg) {return sg};
var peg$f31 = function(o, soc) { return new OperatorStub(o.name,o.args,soc)};
var peg$f32 = function(sc) { return sc };
var peg$f33 = function(c) { return c };
var peg$f34 = function(v) { return new CommandStub("setcps", { value: v})};
var peg$f35 = function(v) { return new CommandStub("setcps", { value: (v/120/2)})};
var peg$f36 = function() { return new CommandStub("hush")};
var peg$currPos = 0;
var peg$savedPos = 0;
var peg$posDetailsCache = [{ line: 1, column: 1 }];
@@ -848,15 +849,6 @@ function peg$parse(input, options) {
s0 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$e17); }
}
if (s0 === peg$FAILED) {
if (input.charCodeAt(peg$currPos) === 58) {
s0 = peg$c11;
peg$currPos++;
} else {
s0 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$e18); }
}
}
}
}
}
@@ -899,11 +891,11 @@ function peg$parse(input, options) {
s0 = peg$currPos;
s1 = peg$parsews();
if (input.charCodeAt(peg$currPos) === 91) {
s2 = peg$c12;
s2 = peg$c11;
peg$currPos++;
} else {
s2 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$e19); }
if (peg$silentFails === 0) { peg$fail(peg$e18); }
}
if (s2 !== peg$FAILED) {
s3 = peg$parsews();
@@ -911,11 +903,11 @@ function peg$parse(input, options) {
if (s4 !== peg$FAILED) {
s5 = peg$parsews();
if (input.charCodeAt(peg$currPos) === 93) {
s6 = peg$c13;
s6 = peg$c12;
peg$currPos++;
} else {
s6 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$e20); }
if (peg$silentFails === 0) { peg$fail(peg$e19); }
}
if (s6 !== peg$FAILED) {
s7 = peg$parsews();
@@ -943,11 +935,11 @@ function peg$parse(input, options) {
s0 = peg$currPos;
s1 = peg$parsews();
if (input.charCodeAt(peg$currPos) === 123) {
s2 = peg$c14;
s2 = peg$c13;
peg$currPos++;
} else {
s2 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$e21); }
if (peg$silentFails === 0) { peg$fail(peg$e20); }
}
if (s2 !== peg$FAILED) {
s3 = peg$parsews();
@@ -955,11 +947,11 @@ function peg$parse(input, options) {
if (s4 !== peg$FAILED) {
s5 = peg$parsews();
if (input.charCodeAt(peg$currPos) === 125) {
s6 = peg$c15;
s6 = peg$c14;
peg$currPos++;
} else {
s6 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$e22); }
if (peg$silentFails === 0) { peg$fail(peg$e21); }
}
if (s6 !== peg$FAILED) {
s7 = peg$parsepolymeter_steps();
@@ -990,11 +982,11 @@ function peg$parse(input, options) {
s0 = peg$currPos;
if (input.charCodeAt(peg$currPos) === 37) {
s1 = peg$c16;
s1 = peg$c15;
peg$currPos++;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$e23); }
if (peg$silentFails === 0) { peg$fail(peg$e22); }
}
if (s1 !== peg$FAILED) {
s2 = peg$parseslice();
@@ -1019,11 +1011,11 @@ function peg$parse(input, options) {
s0 = peg$currPos;
s1 = peg$parsews();
if (input.charCodeAt(peg$currPos) === 60) {
s2 = peg$c17;
s2 = peg$c16;
peg$currPos++;
} else {
s2 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$e24); }
if (peg$silentFails === 0) { peg$fail(peg$e23); }
}
if (s2 !== peg$FAILED) {
s3 = peg$parsews();
@@ -1031,11 +1023,11 @@ function peg$parse(input, options) {
if (s4 !== peg$FAILED) {
s5 = peg$parsews();
if (input.charCodeAt(peg$currPos) === 62) {
s6 = peg$c18;
s6 = peg$c17;
peg$currPos++;
} else {
s6 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$e25); }
if (peg$silentFails === 0) { peg$fail(peg$e24); }
}
if (s6 !== peg$FAILED) {
s7 = peg$parsews();
@@ -1088,6 +1080,9 @@ function peg$parse(input, options) {
s0 = peg$parseop_replicate();
if (s0 === peg$FAILED) {
s0 = peg$parseop_degrade();
if (s0 === peg$FAILED) {
s0 = peg$parseop_tail();
}
}
}
}
@@ -1102,11 +1097,11 @@ function peg$parse(input, options) {
s0 = peg$currPos;
if (input.charCodeAt(peg$currPos) === 64) {
s1 = peg$c19;
s1 = peg$c18;
peg$currPos++;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$e26); }
if (peg$silentFails === 0) { peg$fail(peg$e25); }
}
if (s1 !== peg$FAILED) {
s2 = peg$parsenumber();
@@ -1130,11 +1125,11 @@ function peg$parse(input, options) {
s0 = peg$currPos;
if (input.charCodeAt(peg$currPos) === 33) {
s1 = peg$c20;
s1 = peg$c19;
peg$currPos++;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$e27); }
if (peg$silentFails === 0) { peg$fail(peg$e26); }
}
if (s1 !== peg$FAILED) {
s2 = peg$parsenumber();
@@ -1158,11 +1153,11 @@ function peg$parse(input, options) {
s0 = peg$currPos;
if (input.charCodeAt(peg$currPos) === 40) {
s1 = peg$c21;
s1 = peg$c20;
peg$currPos++;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$e28); }
if (peg$silentFails === 0) { peg$fail(peg$e27); }
}
if (s1 !== peg$FAILED) {
s2 = peg$parsews();
@@ -1186,11 +1181,11 @@ function peg$parse(input, options) {
}
s12 = peg$parsews();
if (input.charCodeAt(peg$currPos) === 41) {
s13 = peg$c22;
s13 = peg$c21;
peg$currPos++;
} else {
s13 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$e29); }
if (peg$silentFails === 0) { peg$fail(peg$e28); }
}
if (s13 !== peg$FAILED) {
peg$savedPos = s0;
@@ -1224,11 +1219,11 @@ function peg$parse(input, options) {
s0 = peg$currPos;
if (input.charCodeAt(peg$currPos) === 47) {
s1 = peg$c23;
s1 = peg$c22;
peg$currPos++;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$e30); }
if (peg$silentFails === 0) { peg$fail(peg$e29); }
}
if (s1 !== peg$FAILED) {
s2 = peg$parseslice();
@@ -1252,11 +1247,11 @@ function peg$parse(input, options) {
s0 = peg$currPos;
if (input.charCodeAt(peg$currPos) === 42) {
s1 = peg$c24;
s1 = peg$c23;
peg$currPos++;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$e31); }
if (peg$silentFails === 0) { peg$fail(peg$e30); }
}
if (s1 !== peg$FAILED) {
s2 = peg$parseslice();
@@ -1280,11 +1275,11 @@ function peg$parse(input, options) {
s0 = peg$currPos;
if (input.charCodeAt(peg$currPos) === 63) {
s1 = peg$c25;
s1 = peg$c24;
peg$currPos++;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$e32); }
if (peg$silentFails === 0) { peg$fail(peg$e31); }
}
if (s1 !== peg$FAILED) {
s2 = peg$parsenumber();
@@ -1301,6 +1296,34 @@ function peg$parse(input, options) {
return s0;
}
function peg$parseop_tail() {
var s0, s1, s2;
s0 = peg$currPos;
if (input.charCodeAt(peg$currPos) === 58) {
s1 = peg$c25;
peg$currPos++;
} else {
s1 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$e32); }
}
if (s1 !== peg$FAILED) {
s2 = peg$parseslice();
if (s2 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f12(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
return s0;
}
function peg$parseslice_with_ops() {
var s0, s1, s2, s3;
@@ -1314,7 +1337,7 @@ function peg$parse(input, options) {
s3 = peg$parseslice_op();
}
peg$savedPos = s0;
s0 = peg$f12(s1, s2);
s0 = peg$f13(s1, s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -1339,7 +1362,7 @@ function peg$parse(input, options) {
}
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
s1 = peg$f13(s1);
s1 = peg$f14(s1);
}
s0 = s1;
@@ -1388,7 +1411,7 @@ function peg$parse(input, options) {
}
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
s1 = peg$f14(s1);
s1 = peg$f15(s1);
}
s0 = s1;
@@ -1437,7 +1460,7 @@ function peg$parse(input, options) {
}
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
s1 = peg$f15(s1);
s1 = peg$f16(s1);
}
s0 = s1;
@@ -1458,7 +1481,7 @@ function peg$parse(input, options) {
s2 = null;
}
peg$savedPos = s0;
s0 = peg$f16(s1, s2);
s0 = peg$f17(s1, s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -1478,7 +1501,7 @@ function peg$parse(input, options) {
s2 = null;
}
peg$savedPos = s0;
s0 = peg$f17(s1, s2);
s0 = peg$f18(s1, s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -1499,7 +1522,7 @@ function peg$parse(input, options) {
s4 = peg$parsequote();
if (s4 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f18(s3);
s0 = peg$f19(s3);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -1561,7 +1584,7 @@ function peg$parse(input, options) {
s3 = peg$parsemini_or_operator();
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f19(s3);
s0 = peg$f20(s3);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -1594,7 +1617,7 @@ function peg$parse(input, options) {
s5 = peg$parsequote();
if (s5 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f20(s4);
s0 = peg$f21(s4);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -1639,7 +1662,7 @@ function peg$parse(input, options) {
s7 = null;
}
peg$savedPos = s0;
s0 = peg$f21(s3, s5, s7);
s0 = peg$f22(s3, s5, s7);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -1672,7 +1695,7 @@ function peg$parse(input, options) {
s3 = peg$parsenumber();
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f22(s3);
s0 = peg$f23(s3);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -1701,7 +1724,7 @@ function peg$parse(input, options) {
s3 = peg$parsenumber();
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f23(s3);
s0 = peg$f24(s3);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -1730,7 +1753,7 @@ function peg$parse(input, options) {
s3 = peg$parsenumber();
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f24(s3);
s0 = peg$f25(s3);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -1759,7 +1782,7 @@ function peg$parse(input, options) {
s3 = peg$parsenumber();
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f25(s3);
s0 = peg$f26(s3);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -1801,7 +1824,7 @@ function peg$parse(input, options) {
s5 = peg$parsequote();
if (s5 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f26(s4);
s0 = peg$f27(s4);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -1876,11 +1899,11 @@ function peg$parse(input, options) {
if (s1 !== peg$FAILED) {
s2 = peg$parsews();
if (input.charCodeAt(peg$currPos) === 91) {
s3 = peg$c12;
s3 = peg$c11;
peg$currPos++;
} else {
s3 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$e19); }
if (peg$silentFails === 0) { peg$fail(peg$e18); }
}
if (s3 !== peg$FAILED) {
s4 = peg$parsews();
@@ -1893,7 +1916,7 @@ function peg$parse(input, options) {
s9 = peg$parsemini_or_operator();
if (s9 !== peg$FAILED) {
peg$savedPos = s7;
s7 = peg$f27(s5, s9);
s7 = peg$f28(s5, s9);
} else {
peg$currPos = s7;
s7 = peg$FAILED;
@@ -1910,7 +1933,7 @@ function peg$parse(input, options) {
s9 = peg$parsemini_or_operator();
if (s9 !== peg$FAILED) {
peg$savedPos = s7;
s7 = peg$f27(s5, s9);
s7 = peg$f28(s5, s9);
} else {
peg$currPos = s7;
s7 = peg$FAILED;
@@ -1922,15 +1945,15 @@ function peg$parse(input, options) {
}
s7 = peg$parsews();
if (input.charCodeAt(peg$currPos) === 93) {
s8 = peg$c13;
s8 = peg$c12;
peg$currPos++;
} else {
s8 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$e20); }
if (peg$silentFails === 0) { peg$fail(peg$e19); }
}
if (s8 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f28(s5, s6);
s0 = peg$f29(s5, s6);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -1976,7 +1999,7 @@ function peg$parse(input, options) {
s4 = peg$parsecomment();
}
peg$savedPos = s0;
s0 = peg$f29(s1);
s0 = peg$f30(s1);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -1998,7 +2021,7 @@ function peg$parse(input, options) {
s5 = peg$parsemini_or_operator();
if (s5 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f30(s1, s5);
s0 = peg$f31(s1, s5);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -2023,7 +2046,7 @@ function peg$parse(input, options) {
s1 = peg$parsemini_or_operator();
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
s1 = peg$f31(s1);
s1 = peg$f32(s1);
}
s0 = s1;
if (s0 === peg$FAILED) {
@@ -2056,7 +2079,7 @@ function peg$parse(input, options) {
if (s2 !== peg$FAILED) {
s3 = peg$parsews();
peg$savedPos = s0;
s0 = peg$f32(s2);
s0 = peg$f33(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -2081,7 +2104,7 @@ function peg$parse(input, options) {
s3 = peg$parsenumber();
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f33(s3);
s0 = peg$f34(s3);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -2110,7 +2133,7 @@ function peg$parse(input, options) {
s3 = peg$parsenumber();
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f34(s3);
s0 = peg$f35(s3);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -2136,7 +2159,7 @@ function peg$parse(input, options) {
}
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
s1 = peg$f35();
s1 = peg$f36();
}
s0 = s1;
@@ -2162,10 +2185,13 @@ function peg$parse(input, options) {
this.location_ = location();
}
var PatternStub = function(source, alignment)
var PatternStub = function(source, alignment, seed)
{
this.type_ = "pattern";
this.arguments_ = { alignment : alignment};
this.arguments_ = { alignment: alignment };
if (seed !== undefined) {
this.arguments_.seed = seed;
}
this.source_ = source;
}
@@ -2191,6 +2217,7 @@ function peg$parse(input, options) {
this.options_ = options;
}
var seed = 0;
peg$result = peg$startRuleFunction();
+14 -7
View File
@@ -19,10 +19,13 @@ This program is free software: you can redistribute it and/or modify it under th
this.location_ = location();
}
var PatternStub = function(source, alignment)
var PatternStub = function(source, alignment, seed)
{
this.type_ = "pattern";
this.arguments_ = { alignment : alignment};
this.arguments_ = { alignment: alignment };
if (seed !== undefined) {
this.arguments_.seed = seed;
}
this.source_ = source;
}
@@ -48,6 +51,7 @@ This program is free software: you can redistribute it and/or modify it under th
this.options_ = options;
}
var seed = 0;
}
start = statement
@@ -96,7 +100,7 @@ quote = '"' / "'"
// ------------------ steps and cycles ---------------------------
// single step definition (e.g bd)
step_char = [0-9a-zA-Z~] / "-" / "#" / "." / "^" / "_" / ":"
step_char = [0-9a-zA-Z~] / "-" / "#" / "." / "^" / "_"
step = ws chars:step_char+ ws { return new AtomStub(chars.join("")) }
// define a sub cycle e.g. [1 2, 3 [4]]
@@ -119,7 +123,7 @@ slice = step / sub_cycle / polymeter / slow_sequence
// slice modifier affects the timing/size of a slice (e.g. [a b c]@3)
// at this point, we assume we can represent them as regular sequence operators
slice_op = op_weight / op_bjorklund / op_slow / op_fast / op_replicate / op_degrade
slice_op = op_weight / op_bjorklund / op_slow / op_fast / op_replicate / op_degrade / op_tail
op_weight = "@" a:number
{ return x => x.options_['weight'] = a }
@@ -137,7 +141,10 @@ op_fast = "*"a:slice
{ return x => x.options_['ops'].push({ type_: "stretch", arguments_ :{ amount:a, type: 'fast' }}) }
op_degrade = "?"a:number?
{ return x => x.options_['ops'].push({ type_: "degradeBy", arguments_ :{ amount:a } }) }
{ return x => x.options_['ops'].push({ type_: "degradeBy", arguments_ :{ amount:a, seed: seed++ } }) }
op_tail = ":" s:slice
{ return x => x.options_['ops'].push({ type_: "tail", arguments_ :{ element:s } }) }
// a slice with an modifier applied i.e [bd@4 sd@3]@2 hh]
slice_with_ops = s:slice ops:slice_op*
@@ -159,12 +166,12 @@ stack_tail = tail:(comma @sequence)+
// a choose is a series of pipe-separated sequence, one of which is
// chosen at random, each cycle
choose_tail = tail:(pipe @sequence)+
{ return { alignment: 'rand', list: tail }; }
{ return { alignment: 'rand', list: tail, seed: seed++ }; }
// if the stack contains only one element, we don't create a stack but return the
// underlying element
stack_or_choose = head:sequence tail:(stack_tail / choose_tail)?
{ if (tail && tail.list.length > 0) { return new PatternStub([head, ...tail.list], tail.alignment); } else { return head; } }
{ if (tail && tail.list.length > 0) { return new PatternStub([head, ...tail.list], tail.alignment, tail.seed); } else { return head; } }
polymeter_stack = head:sequence tail:stack_tail?
{ return new PatternStub(tail ? [head, ...tail.list] : [head], 'polymeter'); }
+11 -30
View File
@@ -7,17 +7,13 @@ This program is free software: you can redistribute it and/or modify it under th
import * as krill from './krill-parser.js';
import * as strudel from '@strudel.cycles/core';
/* var _seedState = 0;
const randOffset = 0.0002;
function _nextSeed() {
return _seedState++;
} */
const randOffset = 0.0003;
const applyOptions = (parent, code) => (pat, i) => {
const ast = parent.source_[i];
const options = ast.options_;
const ops = options?.ops;
if (ops) {
for (const op of ops) {
switch (op.type_) {
@@ -43,27 +39,14 @@ const applyOptions = (parent, code) => (pat, i) => {
break;
}
case 'degradeBy': {
// TODO: find out what is right here
// example:
/*
stack(
s("hh*8").degrade(),
s("[ht*8]?")
)
*/
// above example will only be in sync when _degradeBy is used...
// it also seems that the nextSeed will create undeterministic behaviour
// as it uses a global _seedState. This is probably the reason for
// https://github.com/tidalcycles/strudel/issues/245
// this is how it was:
/*
return strudel.reify(pat)._degradeByWith(
strudel.rand.early(randOffset * _nextSeed()).segment(1),
op.arguments_.amount ?? 0.5,
);
*/
pat = strudel.reify(pat).degradeBy(op.arguments_.amount === null ? 0.5 : op.arguments_.amount);
pat = strudel
.reify(pat)
._degradeByWith(strudel.rand.early(randOffset * op.arguments_.seed), op.arguments_.amount ?? 0.5);
break;
}
case 'tail': {
const friend = patternifyAST(op.arguments_.element, code);
pat = pat.fmap((a) => (b) => Array.isArray(a) ? [...a, b] : [a, b]).appLeft(friend);
break;
}
default: {
@@ -108,9 +91,7 @@ export function patternifyAST(ast, code) {
return strudel.stack(...aligned);
}
if (alignment === 'rand') {
// https://github.com/tidalcycles/strudel/issues/245#issuecomment-1345406422
// return strudel.chooseInWith(strudel.rand.early(randOffset * _nextSeed()).segment(1), children);
return strudel.chooseCycles(...children);
return strudel.chooseInWith(strudel.rand.early(randOffset * ast.arguments_.seed).segment(1), children);
}
const weightedChildren = ast.source_.some((child) => !!child.options_?.weight);
if (!weightedChildren && alignment === 'slowcat') {
-1031
View File
File diff suppressed because it is too large Load Diff
+12 -6
View File
@@ -1,12 +1,18 @@
{
"name": "@strudel.cycles/mini",
"version": "0.5.0",
"version": "0.7.2",
"description": "Mini notation for strudel",
"main": "index.mjs",
"type": "module",
"publishConfig": {
"main": "dist/index.js",
"module": "dist/index.mjs"
},
"scripts": {
"test": "vitest run",
"build:parser": "peggy -o krill-parser.js --format es ./krill.pegjs"
"build:parser": "peggy -o krill-parser.js --format es ./krill.pegjs",
"build": "vite build",
"prepublishOnly": "npm run build"
},
"repository": {
"type": "git",
@@ -26,11 +32,11 @@
},
"homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": {
"@strudel.cycles/core": "^0.5.0",
"@strudel.cycles/eval": "^0.5.0",
"@strudel.cycles/tone": "^0.5.0"
"@strudel.cycles/core": "workspace:*"
},
"devDependencies": {
"peggy": "^2.0.1"
"peggy": "^2.0.1",
"vite": "^3.2.2",
"vitest": "^0.25.7"
}
}
+20 -3
View File
@@ -140,7 +140,22 @@ describe('mini', () => {
expect(haps.length < 230).toBe(true);
// 'Had too many cycles remaining after degradeBy 0.8');
});
/*it('supports the random choice operator ("|") with nesting', () => {
it('supports multiple independent uses of the random choice operator ("|")', () => {
const numCycles = 1000;
const values = mini('[a|b] [a|b]')
.queryArc(0, numCycles)
.map((e) => e.value);
const observed = { aa: 0, ab: 0, ba: 0, bb: 0 };
for (let i = 0; i < values.length; i += 2) {
const chunk = values.slice(i, i + 2);
observed[chunk.join('')]++;
}
for (const count of Object.values(observed)) {
// Should fall within 99% confidence interval for binomial with p=0.25.
expect(215 <= count && count <= 286).toBe(true);
}
});
it('supports the random choice operator ("|") with nesting', () => {
const numCycles = 900;
const haps = mini('a | [b | c] | [d | e | f]').queryArc(0, numCycles);
// Should have about 1/3 a, 1/6 each of b | c, and 1/9 each of d | e | f.
@@ -165,6 +180,8 @@ describe('mini', () => {
// 15.086 is the chisq for 5 degrees of freedom at 99%, so for 99% of uniformly-distributed
// PRNG, this test should succeed
expect(chisq <= 15.086).toBe(true);
// assert(chisq <= 15.086, chisq + ' was expected to be less than 15.086 under chi-squared test');
});*/
});
it('supports lists', () => {
expect(minV('a:b c:d:[e:f] g')).toEqual([['a', 'b'], ['c', 'd', ['e', 'f']], 'g']);
});
});
+19
View File
@@ -0,0 +1,19 @@
import { defineConfig } from 'vite';
import { dependencies } from './package.json';
import { resolve } from 'path';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [],
build: {
lib: {
entry: resolve(__dirname, 'index.mjs'),
formats: ['es', 'cjs'],
fileName: (ext) => ({ es: 'index.mjs', cjs: 'index.js' }[ext]),
},
rollupOptions: {
external: [...Object.keys(dependencies)],
},
target: 'esnext',
},
});
+2 -2
View File
@@ -34,6 +34,6 @@ Now open the REPL and type:
s("<bd sd> hh").osc()
```
or just [click here](http://localhost:3000/#cygiPGJkIHNkPiBoaCIpLm9zYygp)...
or just [click here](https://strudel.tidalcycles.org/#cygiPGJkIHNkPiBoaCIpLm9zYygp)...
You can read more about [how to use Superdirt with Strudel the Tutorial](https://strudel.tidalcycles.org/learn/outputs#superdirt-api)
You can read more about [how to use Superdirt with Strudel the Tutorial](https://strudel.tidalcycles.org/learn/input-output/#superdirt-api)
+5 -3
View File
@@ -39,14 +39,16 @@ let startedAt = -1;
/**
*
* Sends each hap as an OSC message, which can be picked up by SuperCollider or any other OSC-enabled software.
* For more info, read [MIDI & OSC in the docs](https://strudel.tidalcycles.org/learn/input-output)
*
* @name osc
* @memberof Pattern
* @returns Pattern
*/
Pattern.prototype.osc = async function () {
const osc = await connect();
return this.onTrigger((time, hap, currentTime, cps = 1) => {
Pattern.prototype.osc = function () {
return this.onTrigger(async (time, hap, currentTime, cps = 1) => {
hap.ensureObjectValue();
const osc = await connect();
const cycle = hap.wholeOrPart().begin.valueOf();
const delta = hap.duration.valueOf();
// time should be audio time of onset
-60
View File
@@ -1,60 +0,0 @@
{
"name": "@strudel.cycles/osc",
"version": "0.4.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@strudel.cycles/osc",
"version": "0.1.0",
"license": "AGPL-3.0-or-later",
"dependencies": {
"osc-js": "^2.3.2"
}
},
"node_modules/osc-js": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/osc-js/-/osc-js-2.3.2.tgz",
"integrity": "sha512-9i7J4u1hH+glooGMh+ki1ni0JGqKmylT8r0nXKugHbRK63rR+kl4O+5tGW6+/EszjbCju3KV+eXQQzFDdGrmhg==",
"dependencies": {
"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": {
"osc-js": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/osc-js/-/osc-js-2.3.2.tgz",
"integrity": "sha512-9i7J4u1hH+glooGMh+ki1ni0JGqKmylT8r0nXKugHbRK63rR+kl4O+5tGW6+/EszjbCju3KV+eXQQzFDdGrmhg==",
"requires": {
"ws": "^8.5.0"
}
},
"ws": {
"version": "8.5.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz",
"integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==",
"requires": {}
}
}
}
+11 -4
View File
@@ -1,14 +1,19 @@
{
"name": "@strudel.cycles/osc",
"version": "0.4.0",
"version": "0.7.1",
"description": "OSC messaging for strudel",
"main": "osc.mjs",
"publishConfig": {
"main": "dist/index.js",
"module": "dist/index.mjs"
},
"scripts": {
"test": "echo \"No tests present.\" && exit 0",
"server": "node server.js",
"tidal-sniffer": "node tidal-sniffer.js",
"client": "npx serve -p 4321",
"build": "npx pkg server.js --targets node16-macos-x64,node16-win-x64,node16-linux-x64 --out-path bin"
"build-bin": "npx pkg server.js --targets node16-macos-x64,node16-win-x64,node16-linux-x64 --out-path bin",
"build": "vite build",
"prepublishOnly": "npm run build"
},
"repository": {
"type": "git",
@@ -31,9 +36,11 @@
},
"homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": {
"@strudel.cycles/core": "workspace:*",
"osc-js": "^2.4.0"
},
"devDependencies": {
"pkg": "^5.7.0"
"pkg": "^5.7.0",
"vite": "^3.2.2"
}
}
+19
View File
@@ -0,0 +1,19 @@
import { defineConfig } from 'vite';
import { dependencies } from './package.json';
import { resolve } from 'path';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [],
build: {
lib: {
entry: resolve(__dirname, 'osc.mjs'),
formats: ['es', 'cjs'],
fileName: (ext) => ({ es: 'index.mjs', cjs: 'index.js' }[ext]),
},
rollupOptions: {
external: [...Object.keys(dependencies)],
},
target: 'esnext',
},
});
-2
View File
@@ -11,8 +11,6 @@ node_modules
dist-ssr
*.local
!dist
# Editor directories and files
.vscode/*
!.vscode/extensions.json
+33 -21
View File
@@ -13,31 +13,43 @@ npm i @strudel.cycles/react
Here is a minimal example of how to set up a MiniRepl:
```jsx
import { evalScope, controls } from '@strudel.cycles/core';
import * as React from 'react';
import '@strudel.cycles/react/dist/style.css';
import { MiniRepl } from '@strudel.cycles/react';
import { prebake } from '../repl/src/prebake.mjs';
import { evalScope, controls } from '@strudel.cycles/core';
import { samples, initAudioOnFirstClick } from '@strudel.cycles/webaudio';
evalScope(
controls,
import('@strudel.cycles/core'),
import('@strudel.cycles/tonal'),
import('@strudel.cycles/mini'),
import('@strudel.cycles/webaudio'),
/* probably import other strudel packages */
);
async function prebake() {
await samples(
'https://strudel.tidalcycles.org/tidal-drum-machines.json',
'github:ritchse/tidal-drum-machines/main/machines/'
);
await samples(
'https://strudel.tidalcycles.org/EmuSP12.json',
'https://strudel.tidalcycles.org/EmuSP12/'
);
}
prebake();
async function init() {
await evalScope(
controls,
import('@strudel.cycles/core'),
import('@strudel.cycles/mini'),
import('@strudel.cycles/webaudio'),
import('@strudel.cycles/tonal')
);
await prebake();
initAudioOnFirstClick();
}
export function Repl({ tune }) {
return <MiniRepl tune={tune} hideOutsideView={true} />;
if (typeof window !== 'undefined') {
init();
}
export default function App() {
return <MiniRepl tune={`s("bd sd,hh*4")`} />;
}
```
## Development
If you change something in here and want to see the changes in the repl, make sure to run `npm run build` inside this folder!
```js
npm run dev # dev server
npm run build # build package
```
- Open [example on stackblitz](https://stackblitz.com/edit/react-ts-saaair?file=tune.tsx,App.tsx)
- Also check out the [nano-repl](./examples/nano-repl/) for a more sophisticated example
File diff suppressed because one or more lines are too long
-401
View File
@@ -1,401 +0,0 @@
import l, { useCallback as N, useRef as k, useEffect as _, useMemo as K, useState as w, useLayoutEffect as G } from "react";
import Z from "@uiw/react-codemirror";
import { Decoration as y, EditorView as J } from "@codemirror/view";
import { StateEffect as Q, StateField as X } from "@codemirror/state";
import { javascript as ee } from "@codemirror/lang-javascript";
import { tags as s } from "@lezer/highlight";
import { createTheme as te } from "@uiw/codemirror-themes";
import { repl as re, logger as ne, pianoroll as oe } from "@strudel.cycles/core";
import { webaudioOutput as ae, getAudioContext as ce } from "@strudel.cycles/webaudio";
import { useInView as se } from "react-hook-inview";
import { transpiler as ie } from "@strudel.cycles/transpiler";
const le = te({
theme: "dark",
settings: {
background: "#222",
foreground: "#75baff",
caret: "#ffcc00",
selection: "rgba(128, 203, 196, 0.5)",
selectionMatch: "#036dd626",
lineHighlight: "#00000050",
gutterBackground: "transparent",
gutterForeground: "#8a919966"
},
styles: [
{ tag: s.keyword, color: "#c792ea" },
{ tag: s.operator, color: "#89ddff" },
{ tag: s.special(s.variableName), color: "#eeffff" },
{ tag: s.typeName, color: "#c3e88d" },
{ tag: s.atom, color: "#f78c6c" },
{ tag: s.number, color: "#c3e88d" },
{ tag: s.definition(s.variableName), color: "#82aaff" },
{ tag: s.string, color: "#c3e88d" },
{ tag: s.special(s.string), color: "#c3e88d" },
{ tag: s.comment, color: "#7d8799" },
{ tag: s.variableName, color: "#c792ea" },
{ tag: s.tagName, color: "#c3e88d" },
{ tag: s.bracket, color: "#525154" },
{ tag: s.meta, color: "#ffcb6b" },
{ tag: s.attributeName, color: "#c792ea" },
{ tag: s.propertyName, color: "#c792ea" },
{ tag: s.className, color: "#decb6b" },
{ tag: s.invalid, color: "#ffffff" }
]
});
const j = Q.define(), ue = X.define({
create() {
return y.none;
},
update(e, r) {
try {
for (let t of r.effects)
if (t.is(j))
if (t.value) {
const a = y.mark({ attributes: { style: "background-color: #FFCA2880" } });
e = y.set([a.range(0, r.newDoc.length)]);
} else
e = y.set([]);
return e;
} catch (t) {
return console.warn("flash error", t), e;
}
},
provide: (e) => J.decorations.from(e)
}), de = (e) => {
e.dispatch({ effects: j.of(!0) }), setTimeout(() => {
e.dispatch({ effects: j.of(!1) });
}, 200);
}, z = Q.define(), fe = X.define({
create() {
return y.none;
},
update(e, r) {
try {
for (let t of r.effects)
if (t.is(z)) {
const a = t.value.map(
(o) => (o.context.locations || []).map(({ start: i, end: u }) => {
const m = o.context.color || "#FFCA28";
let n = r.newDoc.line(i.line).from + i.column, d = r.newDoc.line(u.line).from + u.column;
const v = r.newDoc.length;
return n > v || d > v ? void 0 : y.mark({ attributes: { style: `outline: 1.5px solid ${m};` } }).range(n, d);
})
).flat().filter(Boolean) || [];
e = y.set(a, !0);
}
return e;
} catch {
return y.set([]);
}
},
provide: (e) => J.decorations.from(e)
}), me = [ee(), le, fe, ue];
function ge({ value: e, onChange: r, onViewChanged: t, onSelectionChange: a, options: o, editorDidMount: i }) {
const u = N(
(d) => {
r?.(d);
},
[r]
), m = N(
(d) => {
t?.(d);
},
[t]
), n = N(
(d) => {
d.selectionSet && a && a?.(d.state.selection);
},
[a]
);
return /* @__PURE__ */ l.createElement(l.Fragment, null, /* @__PURE__ */ l.createElement(Z, {
value: e,
onChange: u,
onCreateEditor: m,
onUpdate: n,
extensions: me
}));
}
function W(...e) {
return e.filter(Boolean).join(" ");
}
function pe({ view: e, pattern: r, active: t, getTime: a }) {
const o = k([]), i = k();
_(() => {
if (e)
if (r && t) {
let u = requestAnimationFrame(function m() {
try {
const n = a(), v = [Math.max(i.current || n, n - 1 / 10, 0), n + 1 / 60];
i.current = v[1], o.current = o.current.filter((p) => p.whole.end > n);
const h = r.queryArc(...v).filter((p) => p.hasOnset());
o.current = o.current.concat(h), e.dispatch({ effects: z.of(o.current) });
} catch {
e.dispatch({ effects: z.of([]) });
}
u = requestAnimationFrame(m);
});
return () => {
cancelAnimationFrame(u);
};
} else
o.current = [], e.dispatch({ effects: z.of([]) });
}, [r, t, e]);
}
function he(e, r = !1) {
const t = k(), a = k(), o = (m) => {
if (a.current !== void 0) {
const n = m - a.current;
e(m, n);
}
a.current = m, t.current = requestAnimationFrame(o);
}, i = () => {
t.current = requestAnimationFrame(o);
}, u = () => {
t.current && cancelAnimationFrame(t.current), delete t.current;
};
return _(() => {
t.current && (u(), i());
}, [e]), _(() => (r && i(), u), []), {
start: i,
stop: u
};
}
function ve({ pattern: e, started: r, getTime: t, onDraw: a }) {
let o = k([]), i = k(null);
const { start: u, stop: m } = he(
N(() => {
const n = t();
if (i.current === null) {
i.current = n;
return;
}
const d = e.queryArc(Math.max(i.current, n - 1 / 10), n), v = 4;
i.current = n, o.current = (o.current || []).filter((h) => h.whole.end > n - v).concat(d.filter((h) => h.hasOnset())), a(n, o.current);
}, [e])
);
_(() => {
r ? u() : (o.current = [], m());
}, [r]);
}
function be(e) {
return _(() => (window.addEventListener("message", e), () => window.removeEventListener("message", e)), [e]), N((r) => window.postMessage(r, "*"), []);
}
function Ee({
defaultOutput: e,
interval: r,
getTime: t,
evalOnMount: a = !1,
initialCode: o = "",
autolink: i = !1,
beforeEval: u,
afterEval: m,
editPattern: n,
onEvalError: d,
onToggle: v,
canvasId: h
}) {
const p = K(() => we(), []);
h = h || `canvas-${p}`;
const [F, A] = w(), [P, D] = w(), [b, q] = w(o), [x, V] = w(), [I, R] = w(), [L, B] = w(!1), H = b !== x, { scheduler: M, evaluate: c, start: f, stop: C, pause: O } = K(
() => re({
interval: r,
defaultOutput: e,
onSchedulerError: A,
onEvalError: (g) => {
D(g), d?.(g);
},
getTime: t,
transpiler: ie,
beforeEval: ({ code: g }) => {
q(g), u?.();
},
editPattern: n ? (g) => n(g, p) : void 0,
afterEval: ({ pattern: g, code: T }) => {
V(T), R(g), D(), A(), i && (window.location.hash = "#" + encodeURIComponent(btoa(T))), m?.();
},
onToggle: (g) => {
B(g), v?.(g);
}
}),
[e, r, t]
), Y = be(({ data: { from: g, type: T } }) => {
T === "start" && g !== p && C();
}), S = N(
async (g = !0) => {
await c(b, g), Y({ type: "start", from: p });
},
[c, b]
), U = k();
return _(() => {
!U.current && a && b && (U.current = !0, S());
}, [S, a, b]), _(() => () => {
M.stop();
}, [M]), {
id: p,
canvasId: h,
code: b,
setCode: q,
error: F || P,
schedulerError: F,
scheduler: M,
evalError: P,
evaluate: c,
activateCode: S,
activeCode: x,
isDirty: H,
pattern: I,
started: L,
start: f,
stop: C,
pause: O,
togglePlay: async () => {
L ? M.pause() : await S();
}
};
}
function we() {
return Math.floor((1 + Math.random()) * 65536).toString(16).substring(1);
}
function $({ type: e }) {
return /* @__PURE__ */ l.createElement("svg", {
xmlns: "http://www.w3.org/2000/svg",
className: "sc-h-5 sc-w-5",
viewBox: "0 0 20 20",
fill: "currentColor"
}, {
refresh: /* @__PURE__ */ l.createElement("path", {
fillRule: "evenodd",
d: "M4 2a1 1 0 011 1v2.101a7.002 7.002 0 0111.601 2.566 1 1 0 11-1.885.666A5.002 5.002 0 005.999 7H9a1 1 0 010 2H4a1 1 0 01-1-1V3a1 1 0 011-1zm.008 9.057a1 1 0 011.276.61A5.002 5.002 0 0014.001 13H11a1 1 0 110-2h5a1 1 0 011 1v5a1 1 0 11-2 0v-2.101a7.002 7.002 0 01-11.601-2.566 1 1 0 01.61-1.276z",
clipRule: "evenodd"
}),
play: /* @__PURE__ */ l.createElement("path", {
fillRule: "evenodd",
d: "M10 18a8 8 0 100-16 8 8 0 000 16zM9.555 7.168A1 1 0 008 8v4a1 1 0 001.555.832l3-2a1 1 0 000-1.664l-3-2z",
clipRule: "evenodd"
}),
pause: /* @__PURE__ */ l.createElement("path", {
fillRule: "evenodd",
d: "M18 10a8 8 0 11-16 0 8 8 0 0116 0zM7 8a1 1 0 012 0v4a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v4a1 1 0 102 0V8a1 1 0 00-1-1z",
clipRule: "evenodd"
})
}[e]);
}
const ye = "_container_3i85k_1", ke = "_header_3i85k_5", _e = "_buttons_3i85k_9", Fe = "_button_3i85k_9", Ce = "_buttonDisabled_3i85k_17", Ne = "_error_3i85k_21", xe = "_body_3i85k_25", E = {
container: ye,
header: ke,
buttons: _e,
button: Fe,
buttonDisabled: Ce,
error: Ne,
body: xe
}, Me = () => ce().currentTime;
function je({ tune: e, hideOutsideView: r = !1, enableKeyboard: t, withCanvas: a = !1, canvasHeight: o = 200 }) {
const {
code: i,
setCode: u,
evaluate: m,
activateCode: n,
error: d,
isDirty: v,
activeCode: h,
pattern: p,
started: F,
scheduler: A,
togglePlay: P,
stop: D,
canvasId: b,
id: q
} = Ee({
initialCode: e,
defaultOutput: ae,
getTime: Me,
editPattern: (c, f) => c.withContext((C) => ({ ...C, id: f }))
});
ve({
pattern: p,
started: a && F,
getTime: () => A.now(),
onDraw: (c, f) => {
const C = document.querySelector("#" + b).getContext("2d");
oe({ ctx: C, time: c, haps: f, autorange: 1, fold: 1, playhead: 1 });
}
});
const [x, V] = w(), [I, R] = se({
threshold: 0.01
}), L = k(), B = K(() => ((R || !r) && (L.current = !0), R || L.current), [R, r]);
pe({
view: x,
pattern: p,
active: F && !h?.includes("strudel disable-highlighting"),
getTime: () => A.getPhase()
}), G(() => {
if (t) {
const c = async (f) => {
(f.ctrlKey || f.altKey) && (f.code === "Enter" ? (f.preventDefault(), de(x), await n()) : f.code === "Period" && (D(), f.preventDefault()));
};
return window.addEventListener("keydown", c, !0), () => window.removeEventListener("keydown", c, !0);
}
}, [t, p, i, m, D, x]);
const [H, M] = w([]);
return Ae(
N((c) => {
const { data: f } = c.detail;
f?.hap?.context?.id === q && M((O) => O.concat([c.detail]).slice(-10));
}, [])
), /* @__PURE__ */ l.createElement("div", {
className: E.container,
ref: I
}, /* @__PURE__ */ l.createElement("div", {
className: E.header
}, /* @__PURE__ */ l.createElement("div", {
className: E.buttons
}, /* @__PURE__ */ l.createElement("button", {
className: W(E.button, F ? "sc-animate-pulse" : ""),
onClick: () => P()
}, /* @__PURE__ */ l.createElement($, {
type: F ? "pause" : "play"
})), /* @__PURE__ */ l.createElement("button", {
className: W(v ? E.button : E.buttonDisabled),
onClick: () => n()
}, /* @__PURE__ */ l.createElement($, {
type: "refresh"
}))), d && /* @__PURE__ */ l.createElement("div", {
className: E.error
}, d.message)), /* @__PURE__ */ l.createElement("div", {
className: E.body
}, B && /* @__PURE__ */ l.createElement(ge, {
value: i,
onChange: u,
onViewChanged: V
})), a && /* @__PURE__ */ l.createElement("canvas", {
id: b,
className: "w-full pointer-events-none",
height: o,
ref: (c) => {
c && c.width !== c.clientWidth && (c.width = c.clientWidth);
}
}), !!H.length && /* @__PURE__ */ l.createElement("div", {
className: "sc-bg-gray-800 sc-rounded-md sc-p-2"
}, H.map(({ message: c }, f) => /* @__PURE__ */ l.createElement("div", {
key: f
}, c))));
}
function Ae(e) {
De(ne.key, e);
}
function De(e, r, t = !1) {
_(() => (document.addEventListener(e, r, t), () => {
document.removeEventListener(e, r, t);
}), [r]);
}
const Ue = (e) => G(() => (window.addEventListener("keydown", e, !0), () => window.removeEventListener("keydown", e, !0)), [e]);
export {
ge as CodeMirror,
je as MiniRepl,
W as cx,
de as flash,
pe as useHighlighting,
Ue as useKeydown,
be as usePostMessage,
Ee as useStrudel
};
-1
View File
@@ -1 +0,0 @@
.cm-editor{background-color:transparent!important;height:100%;z-index:11;font-size:18px}.cm-theme-light{width:100%}.cm-line>*{background:#00000095}*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.sc-h-5{height:1.25rem}.sc-w-5{width:1.25rem}@keyframes sc-pulse{50%{opacity:.5}}.sc-animate-pulse{animation:sc-pulse 2s cubic-bezier(.4,0,.6,1) infinite}.sc-rounded-md{border-radius:.375rem}.sc-bg-gray-800{--tw-bg-opacity: 1;background-color:rgb(31 41 55 / var(--tw-bg-opacity))}.sc-p-2{padding:.5rem}._container_3i85k_1{overflow:hidden;border-radius:.375rem;--tw-bg-opacity: 1;background-color:rgb(34 34 34 / var(--tw-bg-opacity))}._header_3i85k_5{display:flex;justify-content:space-between;border-top-width:1px;--tw-border-opacity: 1;border-color:rgb(100 116 139 / var(--tw-border-opacity));--tw-bg-opacity: 1;background-color:rgb(51 65 85 / var(--tw-bg-opacity))}._buttons_3i85k_9{display:flex}._button_3i85k_9{display:flex;width:4rem;cursor:pointer;align-items:center;justify-content:center;border-right-width:1px;--tw-border-opacity: 1;border-color:rgb(100 116 139 / var(--tw-border-opacity));--tw-bg-opacity: 1;background-color:rgb(51 65 85 / var(--tw-bg-opacity));padding:.25rem;--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}._button_3i85k_9:hover{--tw-bg-opacity: 1;background-color:rgb(71 85 105 / var(--tw-bg-opacity))}._buttonDisabled_3i85k_17{display:flex;width:4rem;cursor:pointer;cursor:not-allowed;align-items:center;justify-content:center;--tw-bg-opacity: 1;background-color:rgb(71 85 105 / var(--tw-bg-opacity));padding:.25rem;--tw-text-opacity: 1;color:rgb(148 163 184 / var(--tw-text-opacity))}._error_3i85k_21{padding:.25rem;text-align:right;font-size:.875rem;line-height:1.25rem;--tw-text-opacity: 1;color:rgb(254 202 202 / var(--tw-text-opacity))}._body_3i85k_25{position:relative;overflow:auto}
+1 -1
View File
@@ -8,7 +8,7 @@ pnpm-debug.log*
lerna-debug.log*
node_modules
!dist
dist
dist-ssr
*.local
@@ -0,0 +1,15 @@
# nano-repl
this is an example of how to create a repl with strudel and react.
## Usage
after cloning the strudel repo:
```sh
pnpm i
cd packages/react/examples/nano-repl
pnpm dev
```
you should now have a repl running at `http://localhost:5173/`
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
.cm-editor{background-color:transparent!important;height:100%;z-index:11;font-size:16px}.cm-theme-light{width:100%}.cm-line>*{background:#00000095}*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji"}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input:-ms-input-placeholder,textarea:-ms-input-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}*,:before,:after{--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.fixed{position:fixed}.absolute{position:absolute}.bottom-0{bottom:0px}.z-\[12\]{z-index:12}.flex{display:flex}.w-full{width:100%}.justify-center{justify-content:center}.space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse: 0;margin-right:calc(.5rem * var(--tw-space-x-reverse));margin-left:calc(.5rem * calc(1 - var(--tw-space-x-reverse)))}.rounded-t-md{border-top-left-radius:.375rem;border-top-right-radius:.375rem}.bg-slate-500{--tw-bg-opacity: 1;background-color:rgb(100 116 139 / var(--tw-bg-opacity))}.px-2{padding-left:.5rem;padding-right:.5rem}body{background:#123}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
import{f as s,i as t,h as o,j as d,q as f,l as p,k as u,p as i,o as l,n as r,w as g}from"./index.ec9f9930.js";export{s as getAudioContext,t as getCachedBuffer,o as getDestination,d as getLoadedBuffer,f as getLoadedSamples,p as loadBuffer,u as loadGithubSamples,i as panic,l as resetLoadedSamples,r as samples,g as webaudioOutput};
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
import{P as w}from"./index.ec9f9930.js";var i,f=!1;async function b(a=38400){if(!f){if(f=!0,i)return i;if("serial"in navigator){const r=await navigator.serial.requestPort();await r.open({baudRate:a});const o=new TextEncoderStream;o.readable.pipeTo(r.writable);const s=o.writable.getWriter();i=function(e){s.write(e)}}else throw"Webserial is not available in this browser."}}const g=.1;w.prototype.serial=function(...a){return this._withHap(r=>{i||b(...a);const o=(s,e,u)=>{var t="";if(typeof e.value=="object")if("action"in e.value){t+=e.value.action+"(";var c=!0;for(const[n,l]of Object.entries(e.value))n!=="action"&&(c?c=!1:t+=",",t+=`${n}:${l}`);t+=")"}else for(const[n,l]of Object.entries(e.value))t+=`${n}:${l};`;else t=e.value;const v=(s-u+g)*1e3;window.setTimeout(i,v,t)};return r.setContext({...r.context,onTrigger:o})})};export{b as getWriter};
-14
View File
@@ -1,14 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Strudel Nano REPL</title>
<script type="module" crossorigin src="./assets/index.ec9f9930.js"></script>
<link rel="stylesheet" href="./assets/index.75f8960b.css">
</head>
<body>
<div id="root"></div>
</body>
</html>
File diff suppressed because it is too large Load Diff
+10 -3
View File
@@ -1,7 +1,7 @@
{
"name": "nano-repl",
"name": "@strudel.cycles/nano-repl",
"private": true,
"version": "0.0.0",
"version": "0.6.0",
"type": "module",
"scripts": {
"dev": "vite",
@@ -10,7 +10,14 @@
},
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0"
"react-dom": "^18.2.0",
"@strudel.cycles/core": "workspace:*",
"@strudel.cycles/osc": "workspace:*",
"@strudel.cycles/mini": "workspace:*",
"@strudel.cycles/transpiler": "workspace:*",
"@strudel.cycles/webaudio": "workspace:*",
"@strudel.cycles/tonal": "workspace:*",
"@strudel.cycles/react": "workspace:*"
},
"devDependencies": {
"@types/react": "^18.0.17",
+12 -20
View File
@@ -1,27 +1,21 @@
import { evalScope, controls } from '@strudel.cycles/core';
import { getAudioContext, panic, webaudioOutput } from '@strudel.cycles/webaudio';
import { controls, evalScope } from '@strudel.cycles/core';
import { CodeMirror, useHighlighting, useKeydown, useStrudel, flash } from '@strudel.cycles/react';
import { getAudioContext, initAudioOnFirstClick, panic, webaudioOutput } from '@strudel.cycles/webaudio';
import { useCallback, useState } from 'react';
import CodeMirror, { flash } from '../../../src/components/CodeMirror6';
import useKeydown from '../../../src/hooks/useKeydown.mjs';
import useStrudel from '../../../src/hooks/useStrudel';
import useHighlighting from '../../../src/hooks/useHighlighting';
import './style.css';
// import { prebake } from '../../../../../repl/src/prebake.mjs';
initAudioOnFirstClick();
// TODO: only import stuff when play is pressed?
evalScope(
controls,
import('@strudel.cycles/core'),
// import('@strudel.cycles/tone'),
// import('@strudel.cycles/midi'), // TODO: find out why midi loads tone.js
import('@strudel.cycles/tonal'),
import('@strudel.cycles/mini'),
import('@strudel.cycles/xen'),
import('@strudel.cycles/webaudio'),
import('@strudel.cycles/osc'),
import('@strudel.cycles/webdirt'),
import('@strudel.cycles/serial'),
import('@strudel.cycles/soundfonts'),
);
const defaultTune = `samples({
@@ -30,7 +24,7 @@ const defaultTune = `samples({
hh: ['hh27/000_hh27closedhh.wav','hh/000_hh3closedhh.wav'],
}, 'github:tidalcycles/Dirt-Samples/master/');
stack(
s("bd,[~ <sd!3 sd(3,4,2)>],hh(3,4)") // drums
s("bd,[~ <sd!3 sd(3,4,2)>],hh*8") // drums
.speed(perlin.range(.7,.9)) // random sample speed variation
//.hush()
,"<a1 b1*2 a1(3,8) e2>" // bassline
@@ -43,7 +37,7 @@ stack(
.gain(.4) // turn down
.cutoff(sine.slow(7).range(300,5000)) // automate cutoff
//.hush()
,"<Am7!3 <Em7 E7b13 Em7 Ebm7b5>>".voicings() // chords
,"<Am7!3 <Em7 E7b13 Em7 Ebm7b5>>".voicings('lefthand') // chords
.superimpose(x=>x.add(.04)) // add second, slightly detuned voice
.add(perlin.range(0,.5)) // random pitch variation
.n() // wrap in "n"
@@ -72,7 +66,7 @@ function App() {
const [code, setCode] = useState(defaultTune);
const [view, setView] = useState();
// const [code, setCode] = useState(`"c3".note().slow(2)`);
const { scheduler, evaluate, schedulerError, evalError, isDirty, activeCode, pattern } = useStrudel({
const { scheduler, evaluate, schedulerError, evalError, isDirty, activeCode, pattern, started } = useStrudel({
code,
defaultOutput: webaudioOutput,
getTime,
@@ -81,8 +75,8 @@ function App() {
useHighlighting({
view,
pattern,
active: !activeCode?.includes('strudel disable-highlighting'),
getTime: () => scheduler.getPhase(),
active: started && !activeCode?.includes('strudel disable-highlighting'),
getTime: () => scheduler.now(),
});
const error = evalError || schedulerError;
@@ -103,7 +97,7 @@ function App() {
scheduler.start();
}
} else if (e.code === 'Period') {
scheduler.pause();
scheduler.stop();
panic();
e.preventDefault();
}
@@ -114,13 +108,11 @@ function App() {
);
return (
<div>
{/* <textarea value={code} onChange={(e) => setCode(e.target.value)} cols="64" rows="30" /> */}
<nav className="z-[12] w-full flex justify-center absolute bottom-0">
<nav className="z-[12] w-full flex justify-center fixed bottom-0">
<div className="bg-slate-500 space-x-2 px-2 rounded-t-md">
<button
onClick={async () => {
await evaluate(code);
await getAudioContext().resume();
scheduler.start();
}}
>
@@ -2,6 +2,18 @@
@tailwind components;
@tailwind utilities;
:root {
--background: #222;
--lineBackground: #22222250;
--foreground: #fff;
--caret: #ffcc00;
--selection: rgba(128, 203, 196, 0.5);
--selectionMatch: #036dd626;
--lineHighlight: #00000050;
--gutterBackground: transparent;
--gutterForeground: #8a919966;
}
body {
background: #123;
}
@@ -6,9 +6,23 @@ This program is free software: you can redistribute it and/or modify it under th
module.exports = {
// TODO: find out if leaving out tutorial path works now
content: ['./src/**/*.{js,jsx,ts,tsx}'],
content: ['./src/**/*.{js,jsx,ts,tsx}', '../../src/**/*.{html,js,jsx,md,mdx,ts,tsx}'],
theme: {
extend: {},
extend: {
colors: {
// codemirror-theme settings
background: 'var(--background)',
lineBackground: 'var(--lineBackground)',
foreground: 'var(--foreground)',
caret: 'var(--caret)',
selection: 'var(--selection)',
selectionMatch: 'var(--selectionMatch)',
gutterBackground: 'var(--gutterBackground)',
gutterForeground: 'var(--gutterForeground)',
gutterBorder: 'var(--gutterBorder)',
lineHighlight: 'var(--lineHighlight)',
},
},
},
plugins: [],
};
+22 -23
View File
@@ -1,24 +1,18 @@
{
"name": "@strudel.cycles/react",
"version": "0.5.0",
"version": "0.7.1",
"description": "React components for strudel",
"main": "dist/index.cjs.js",
"module": "dist/index.es.js",
"exports": {
".": {
"require": "./dist/index.cjs.js",
"import": "./dist/index.es.js"
},
"./dist/style.css": "./dist/style.css"
"main": "src/index.js",
"publishConfig": {
"main": "dist/index.js",
"module": "dist/index.mjs"
},
"files": [
"dist"
],
"scripts": {
"dev": "vite",
"build": "vite build",
"watch": "vite build --watch",
"preview": "vite preview"
"preview": "vite preview",
"prepublishOnly": "npm run build"
},
"repository": {
"type": "git",
@@ -38,27 +32,32 @@
},
"homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": {
"@codemirror/autocomplete": "^6.4.0",
"@codemirror/lang-javascript": "^6.1.1",
"@strudel.cycles/core": "^0.5.0",
"@strudel.cycles/tone": "^0.5.0",
"@strudel.cycles/transpiler": "^0.5.0",
"@strudel.cycles/webaudio": "^0.5.0",
"@codemirror/state": "^6.2.0",
"@codemirror/view": "^6.7.3",
"@lezer/highlight": "^1.1.3",
"@replit/codemirror-emacs": "^6.0.0",
"@replit/codemirror-vim": "^6.0.6",
"@strudel.cycles/core": "workspace:*",
"@strudel.cycles/transpiler": "workspace:*",
"@strudel.cycles/webaudio": "workspace:*",
"@uiw/codemirror-themes": "^4.12.4",
"@uiw/react-codemirror": "^4.12.4",
"react-hook-inview": "^4.5.0"
},
"peerDependencies": {
"react": "^17.0.2",
"react-dom": "^17.0.2"
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"@types/react": "^17.0.2",
"@types/react-dom": "^17.0.2",
"@types/react": "^18.0.28",
"@types/react-dom": "^18.0.11",
"@vitejs/plugin-react": "^2.2.0",
"autoprefixer": "^10.4.7",
"postcss": "^8.4.18",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"tailwindcss": "^3.0.24",
"vite": "^3.2.2"
}
-3
View File
@@ -6,11 +6,8 @@ import { controls, evalScope } from '@strudel.cycles/core';
evalScope(
controls,
import('@strudel.cycles/core'),
// import('@strudel.cycles/tone'),
import('@strudel.cycles/tonal'),
import('@strudel.cycles/mini'),
import('@strudel.cycles/midi'),
import('@strudel.cycles/xen'),
import('@strudel.cycles/webaudio'),
);
@@ -0,0 +1,77 @@
import { createRoot } from 'react-dom/client';
import jsdoc from '../../../../doc.json';
const getDocLabel = (doc) => doc.name || doc.longname;
const getInnerText = (html) => {
var div = document.createElement('div');
div.innerHTML = html;
return div.textContent || div.innerText || '';
};
export function Autocomplete({ doc }) {
return (
<div className="prose dark:prose-invert max-h-[400px] overflow-auto">
<h3 className="pt-0 mt-0">{getDocLabel(doc)}</h3>
<div dangerouslySetInnerHTML={{ __html: doc.description }} />
<ul>
{doc.params?.map(({ name, type, description }, i) => (
<li key={i}>
{name} : {type.names?.join(' | ')} {description ? <> - {getInnerText(description)}</> : ''}
</li>
))}
</ul>
<div>
{doc.examples?.map((example, i) => (
<div key={i}>
<pre
className="cursor-pointer"
onMouseDown={(e) => {
console.log('ola!');
navigator.clipboard.writeText(example);
e.stopPropagation();
}}
>
{example}
</pre>
</div>
))}
</div>
</div>
);
}
const jsdocCompletions = jsdoc.docs
.filter(
(doc) =>
getDocLabel(doc) &&
!getDocLabel(doc).startsWith('_') &&
!['package'].includes(doc.kind) &&
!['superdirtOnly', 'noAutocomplete'].some((tag) => doc.tags?.find((t) => t.originalTitle === tag)),
)
// https://codemirror.net/docs/ref/#autocomplete.Completion
.map((doc) /*: Completion */ => ({
label: getDocLabel(doc),
// detail: 'xxx', // An optional short piece of information to show (with a different style) after the label.
info: () => {
const node = document.createElement('div');
// if Autocomplete is non-interactive, it could also be rendered at build time..
// .. using renderToStaticMarkup
createRoot(node).render(<Autocomplete doc={doc} />);
return node;
},
type: 'function', // https://codemirror.net/docs/ref/#autocomplete.Completion.type
}));
export const strudelAutocomplete = (context /* : CompletionContext */) => {
let word = context.matchBefore(/\w*/);
if (word.from == word.to && !context.explicit) return null;
return {
from: word.from,
options: jsdocCompletions,
/* options: [
{ label: 'match', type: 'keyword' },
{ label: 'hello', type: 'variable', info: '(World)' },
{ label: 'magic', type: 'text', apply: '⠁⭒*.✩.*⭒⠁', detail: 'macro' },
], */
};
};
+43 -9
View File
@@ -1,4 +1,4 @@
import React from 'react';
import React, { useMemo } from 'react';
import _CodeMirror from '@uiw/react-codemirror';
import { EditorView, Decoration } from '@codemirror/view';
import { StateField, StateEffect } from '@codemirror/state';
@@ -6,6 +6,10 @@ import { javascript } from '@codemirror/lang-javascript';
import strudelTheme from '../themes/strudel-theme';
import './style.css';
import { useCallback } from 'react';
import { autocompletion } from '@codemirror/autocomplete';
//import { strudelAutocomplete } from './Autocomplete';
import { vim } from '@replit/codemirror-vim';
import { emacs } from '@replit/codemirror-emacs';
export const setFlash = StateEffect.define();
const flashField = StateField.define({
@@ -49,19 +53,20 @@ const highlightField = StateField.define({
try {
for (let e of tr.effects) {
if (e.is(setHighlights)) {
const { haps } = e.value;
const marks =
e.value
haps
.map((hap) =>
(hap.context.locations || []).map(({ start, end }) => {
const color = hap.context.color || '#FFCA28';
// const color = hap.context.color || e.value.color || '#FFCA28';
let from = tr.newDoc.line(start.line).from + start.column;
let to = tr.newDoc.line(end.line).from + end.column;
const l = tr.newDoc.length;
if (from > l || to > l) {
return; // dont mark outside of range, as it will throw an error
}
// const mark = Decoration.mark({ attributes: { style: `outline: 1px solid ${color}` } });
const mark = Decoration.mark({ attributes: { style: `outline: 1.5px solid ${color};` } });
//const mark = Decoration.mark({ attributes: { style: `outline: 2px solid ${color};` } });
const mark = Decoration.mark({ attributes: { class: `outline outline-2 outline-foreground` } });
return mark.range(from, to);
}),
)
@@ -79,9 +84,27 @@ const highlightField = StateField.define({
provide: (f) => EditorView.decorations.from(f),
});
const extensions = [javascript(), strudelTheme, highlightField, flashField];
const staticExtensions = [
javascript(),
highlightField,
flashField,
// javascriptLanguage.data.of({ autocomplete: strudelAutocomplete }),
// autocompletion({ override: [strudelAutocomplete] }),
autocompletion({ override: [] }), // wait for https://github.com/uiwjs/react-codemirror/pull/458
];
export default function CodeMirror({ value, onChange, onViewChanged, onSelectionChange, options, editorDidMount }) {
export default function CodeMirror({
value,
onChange,
onViewChanged,
onSelectionChange,
theme,
keybindings,
fontSize = 18,
fontFamily = 'monospace',
options,
editorDidMount,
}) {
const handleOnChange = useCallback(
(value) => {
onChange?.(value);
@@ -102,16 +125,27 @@ export default function CodeMirror({ value, onChange, onViewChanged, onSelection
},
[onSelectionChange],
);
const extensions = useMemo(() => {
let bindings = {
vim,
emacs,
};
if (bindings[keybindings]) {
return [...staticExtensions, bindings[keybindings]()];
}
return staticExtensions;
}, [keybindings]);
return (
<>
<div style={{ fontSize, fontFamily }} className="w-full">
<_CodeMirror
value={value}
theme={theme || strudelTheme}
onChange={handleOnChange}
onCreateEditor={handleOnCreateEditor}
onUpdate={handleOnUpdate}
extensions={extensions}
/>
</>
</div>
);
}
+8 -1
View File
@@ -2,7 +2,7 @@ import React from 'react';
export function Icon({ type }) {
return (
<svg xmlns="http://www.w3.org/2000/svg" className="sc-h-5 sc-w-5" viewBox="0 0 20 20" fill="currentColor">
<svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
{
{
refresh: (
@@ -26,6 +26,13 @@ export function Icon({ type }) {
clipRule="evenodd"
/>
),
stop: (
<path
fillRule="evenodd"
d="M2 10a8 8 0 1116 0 8 8 0 01-16 0zm5-2.25A.75.75 0 017.75 7h4.5a.75.75 0 01.75.75v4.5a.75.75 0 01-.75.75h-4.5a.75.75 0 01-.75-.75v-4.5z"
clipRule="evenodd"
/>
),
}[type]
}
</svg>
+66 -42
View File
@@ -1,21 +1,34 @@
import { pianoroll } from '@strudel.cycles/core';
import { getAudioContext, webaudioOutput } from '@strudel.cycles/webaudio';
import React, { useLayoutEffect, useMemo, useRef, useState, useCallback, useEffect } from 'react';
import { useInView } from 'react-hook-inview';
import 'tailwindcss/tailwind.css';
import cx from '../cx';
import useHighlighting from '../hooks/useHighlighting.mjs';
import usePatternFrame from '../hooks/usePatternFrame.mjs';
import useStrudel from '../hooks/useStrudel.mjs';
import CodeMirror6, { flash } from './CodeMirror6';
import { Icon } from './Icon';
import styles from './MiniRepl.module.css';
import './style.css';
import { logger } from '@strudel.cycles/core';
import useEvent from '../hooks/useEvent.mjs';
import useKeydown from '../hooks/useKeydown.mjs';
const getTime = () => getAudioContext().currentTime;
export function MiniRepl({ tune, hideOutsideView = false, enableKeyboard, withCanvas = false, canvasHeight = 200 }) {
export function MiniRepl({
tune,
hideOutsideView = false,
enableKeyboard,
drawTime,
punchcard,
canvasHeight = 200,
theme,
}) {
drawTime = drawTime || (punchcard ? [0, 4] : undefined);
const evalOnMount = !!drawTime;
const drawContext = useCallback(
!!drawTime ? (canvasId) => document.querySelector('#' + canvasId)?.getContext('2d') : null,
[drawTime],
);
const {
code,
setCode,
@@ -34,25 +47,13 @@ export function MiniRepl({ tune, hideOutsideView = false, enableKeyboard, withCa
} = useStrudel({
initialCode: tune,
defaultOutput: webaudioOutput,
editPattern: (pat) => (punchcard ? pat.punchcard() : pat),
getTime,
editPattern: (pat, id) => {
return pat.withContext((ctx) => ({ ...ctx, id }));
},
evalOnMount,
drawContext,
drawTime,
});
usePatternFrame({
pattern,
started: withCanvas && started,
getTime: () => scheduler.now(),
onDraw: (time, haps) => {
const ctx = document.querySelector('#' + canvasId).getContext('2d');
pianoroll({ ctx, time, haps, autorange: 1, fold: 1, playhead: 1 });
},
});
/* useEffect(() => {
init && activateCode();
}, [init, activateCode]); */
const [view, setView] = useState();
const [ref, isVisible] = useInView({
threshold: 0.01,
@@ -68,9 +69,30 @@ export function MiniRepl({ tune, hideOutsideView = false, enableKeyboard, withCa
view,
pattern,
active: started && !activeCode?.includes('strudel disable-highlighting'),
getTime: () => scheduler.getPhase(),
getTime: () => scheduler.now(),
});
// keyboard shortcuts
useKeydown(
useCallback(
async (e) => {
if (view?.hasFocus) {
if (e.ctrlKey || e.altKey) {
if (e.code === 'Enter') {
e.preventDefault();
flash(view);
await activateCode();
} else if (e.code === 'Period') {
stop();
e.preventDefault();
}
}
}
},
[activateCode, stop, view],
),
);
// set active pattern on ctrl+enter
useLayoutEffect(() => {
if (enableKeyboard) {
@@ -106,22 +128,34 @@ export function MiniRepl({ tune, hideOutsideView = false, enableKeyboard, withCa
);
return (
<div className={styles.container} ref={ref}>
<div className={styles.header}>
<div className={styles.buttons}>
<button className={cx(styles.button, started ? 'sc-animate-pulse' : '')} onClick={() => togglePlay()}>
<Icon type={started ? 'pause' : 'play'} />
<div className="overflow-hidden rounded-t-md bg-background border border-lineHighlight" ref={ref}>
<div className="flex justify-between bg-lineHighlight">
<div className="flex">
<button
className={cx(
'cursor-pointer w-16 flex items-center justify-center p-1 border-r border-lineHighlight text-foreground bg-lineHighlight hover:bg-background',
started ? 'animate-pulse' : '',
)}
onClick={() => togglePlay()}
>
<Icon type={started ? 'stop' : 'play'} />
</button>
<button className={cx(isDirty ? styles.button : styles.buttonDisabled)} onClick={() => activateCode()}>
<button
className={cx(
'w-16 flex items-center justify-center p-1 text-foreground border-lineHighlight bg-lineHighlight',
isDirty ? 'text-foreground hover:bg-background cursor-pointer' : 'opacity-50 cursor-not-allowed',
)}
onClick={() => activateCode()}
>
<Icon type="refresh" />
</button>
</div>
{error && <div className={styles.error}>{error.message}</div>}
{error && <div className="text-right p-1 text-sm text-red-200">{error.message}</div>}
</div>
<div className={styles.body}>
{show && <CodeMirror6 value={code} onChange={setCode} onViewChanged={setView} />}
<div className="overflow-auto relative">
{show && <CodeMirror6 value={code} onChange={setCode} onViewChanged={setView} theme={theme} />}
</div>
{withCanvas && (
{drawTime && (
<canvas
id={canvasId}
className="w-full pointer-events-none"
@@ -134,7 +168,7 @@ export function MiniRepl({ tune, hideOutsideView = false, enableKeyboard, withCa
></canvas>
)}
{!!log.length && (
<div className="sc-bg-gray-800 sc-rounded-md sc-p-2">
<div className="bg-gray-800 rounded-md p-2">
{log.map(({ message }, i) => (
<div key={i}>{message}</div>
))}
@@ -148,13 +182,3 @@ export function MiniRepl({ tune, hideOutsideView = false, enableKeyboard, withCa
function useLogger(onTrigger) {
useEvent(logger.key, onTrigger);
}
// TODO: dedupe
function useEvent(name, onTrigger, useCapture = false) {
useEffect(() => {
document.addEventListener(name, onTrigger, useCapture);
return () => {
document.removeEventListener(name, onTrigger, useCapture);
};
}, [onTrigger]);
}
@@ -1,27 +0,0 @@
.container {
@apply sc-rounded-md sc-overflow-hidden sc-bg-[#222222];
}
.header {
@apply sc-flex sc-justify-between sc-bg-slate-700 sc-border-t sc-border-slate-500;
}
.buttons {
@apply sc-flex;
}
.button {
@apply sc-cursor-pointer sc-w-16 sc-flex sc-items-center sc-justify-center sc-p-1 sc-bg-slate-700 sc-border-r sc-border-slate-500 sc-text-white hover:sc-bg-slate-600;
}
.buttonDisabled {
@apply sc-cursor-pointer sc-w-16 sc-flex sc-items-center sc-justify-center sc-p-1 sc-bg-slate-600 sc-text-slate-400 sc-cursor-not-allowed;
}
.error {
@apply sc-text-right sc-p-1 sc-text-sm sc-text-red-200;
}
.body {
@apply sc-overflow-auto sc-relative;
}

Some files were not shown because too many files have changed in this diff Show More