Compare commits

..

406 Commits

Author SHA1 Message Date
Felix Roos 0506e4a635 work on transpose:
- support  octaves
- still some bugs
- add experimental emitNotes flag to stepInNamedScale
2024-01-07 22:15:00 +01:00
Felix Roos b52cb198b5 Merge pull request #889 from tidalcycles/invisible-selection
fix: invisible selection on vim + emacs mode
2024-01-06 15:23:55 +01:00
Felix Roos bcee6632af fix: invisible selection on vim + emacs mode 2024-01-06 15:21:58 +01:00
Felix Roos 4c49b84213 Merge pull request #888 from tidalcycles/scale-anchors
scales can now be anchored
2024-01-05 22:29:20 +01:00
Felix Roos b27f177138 scales can now be anchored 2024-01-05 13:03:40 +01:00
Felix Roos f37652edb3 Merge pull request #887 from tidalcycles/root-voicings
add root mode for voicings
2024-01-04 11:21:42 +01:00
Felix Roos 27049eb706 add root mode for voicings
+ allow numbers as anchor
2024-01-04 11:02:13 +01:00
Felix Roos 4d25e3b90b hotfix: Zach B-B 2024-01-03 00:23:28 +01:00
Felix Roos 8d143f4bf1 Merge pull request #885 from tidalcycles/showcase
Showcase
2024-01-01 18:32:17 +01:00
Felix Roos 88fbd78492 fix: shuffle by default 2024-01-01 18:04:38 +01:00
Felix Roos 9a235a7c15 comment out chalmers vid 2024-01-01 17:59:54 +01:00
Felix Roos d99607ea14 add showcase 2024-01-01 17:56:29 +01:00
Felix Roos 5150ec06dc Merge pull request #882 from daslyfe/audio_device_selection
bugfix: suspend and close exisiting audio context when changing interface
2024-01-01 14:22:10 +01:00
Felix Roos b9f190562e Merge pull request #884 from tidalcycles/mastodon-links
add mastodon link
2024-01-01 14:21:52 +01:00
Felix Roos eb189d077d add mastodon link 2024-01-01 14:20:01 +01:00
Jade (Rose) Rowland 8f56ef6507 suspend and close exisiting audio context 2023-12-31 11:39:14 -05:00
Felix Roos 92e0d09c28 Merge pull request #881 from tidalcycles/error-tolerance
fix: make sure n is never undefined before nanFallback
2023-12-31 17:02:57 +01:00
Felix Roos 64d80f98db fix: make sure n is never undefined before nanFallback 2023-12-31 16:59:45 +01:00
Felix Roos 01391d316b Merge pull request #880 from tidalcycles/error-tolerance
Error tolerance
2023-12-31 16:47:22 +01:00
Felix Roos 1d32da075d mini: tolerate non-breaking space (alt+spacebar on macos) 2023-12-31 16:44:35 +01:00
Felix Roos 9b32900b96 revert changing default voicing dict for now... 2023-12-31 16:27:35 +01:00
Felix Roos ecf837636b show line where mini notation parse errors happen 2023-12-31 15:06:30 +01:00
Felix Roos 5a76bc93b8 rename default dict to legacy 2023-12-31 14:19:16 +01:00
Felix Roos b14e5577b4 breaking: use ireal as default voicing dictionary
+ this has the big benefit that all chord symbols work by default without the need to specify a dict
2023-12-31 12:19:05 +01:00
Felix Roos 4121ac91ee fix: graceful handling of invalid chord symbols 2023-12-31 12:11:08 +01:00
Felix Roos c44f202c44 fix: scale errors safety net 2023-12-31 12:07:08 +01:00
Felix Roos 9059b16c7c fix: editor focus on click below code 2023-12-31 11:20:35 +01:00
Felix Roos 3760f51c3c Merge pull request #871 from daslyfe/bugfix_sample_select
bugfix: sound select indexes out of bounds
2023-12-31 10:06:24 +01:00
Felix Roos aa18293f1d Merge pull request #854 from daslyfe/audio_device_selection
Audio device selection
2023-12-31 10:03:00 +01:00
Jade (Rose) Rowland 63066937e9 remove leftover setting 2023-12-30 22:51:30 -05:00
Jade (Rose) Rowland 34f0d631d9 remove console statement 2023-12-30 22:44:40 -05:00
Jade (Rose) Rowland b3522fea6a prevent selection while playing to avoid artifacts 2023-12-30 22:40:27 -05:00
Felix Roos 2ae3ce53f3 hotfix: zen mode was broken 2023-12-31 01:19:46 +01:00
Felix Roos 2adacbd27b Merge pull request #879 from tidalcycles/dependency-update
Dependency update
2023-12-31 00:59:49 +01:00
Felix Roos 45eed1b7e7 fix: for some reason, ci fails without it... 2023-12-31 00:58:09 +01:00
Felix Roos b36d334f78 fix: duplicate pretest 2023-12-31 00:56:38 +01:00
Felix Roos bd648185bc use node 20 for deployment too + bump node types 2023-12-31 00:54:39 +01:00
Felix Roos 67f1dc32a5 bump node version for test 2023-12-31 00:52:01 +01:00
Felix Roos d47d9dadf4 fix: build
+ seems fraction.js now works with ssr
2023-12-31 00:45:04 +01:00
Felix Roos b52f8c2925 update remaining packages 2023-12-31 00:41:06 +01:00
Felix Roos 59ce624521 update webmidi + codemirror 2023-12-31 00:34:55 +01:00
Felix Roos 70b9fbfcf6 updates..
+ codemirror-themes
+ acorn + escodegen
+ supabase
2023-12-31 00:13:05 +01:00
Felix Roos 74b9279416 update eslint 2023-12-31 00:03:45 +01:00
Felix Roos c3ba8ee0fa update fraction js 2023-12-30 23:59:00 +01:00
Felix Roos 4f5ca0767d updates... 2023-12-30 23:50:30 +01:00
Felix Roos d0a1374d2f more updates.. 2023-12-30 23:43:54 +01:00
Felix Roos a747c976fe update nanostores 2023-12-30 23:26:27 +01:00
Felix Roos 94fcea1040 codeformat 2023-12-30 23:21:34 +01:00
Felix Roos 7db684e308 update lerna 2023-12-30 23:19:22 +01:00
Felix Roos 1981e1c34c update dependency-tree + prettier
+ rerun undocumented script
2023-12-30 23:15:44 +01:00
Felix Roos 5274508665 fix: wrong parens.. 2023-12-30 23:09:31 +01:00
Felix Roos 6f68bdcf5b update vite + astro 2023-12-30 23:08:15 +01:00
Felix Roos cf20f1059f Merge branch 'main' into dependency-update 2023-12-30 22:26:05 +01:00
Felix Roos 307c69edb9 hotfix: another slight logic error 2023-12-30 10:36:50 +01:00
Felix Roos 9a07b15952 hotfix: update button behavior 2023-12-30 10:07:47 +01:00
Jade (Rose) Rowland a178d310fd Merge branch 'tidalcycles:main' into bugfix_sample_select 2023-12-29 23:51:09 -05:00
Jade (Rose) Rowland 69554c0762 merged with vanilla updates 2023-12-29 22:27:01 -05:00
Jade (Rose) Rowland 3152bc123a Merge branch 'main' into audio_device_selection 2023-12-29 20:37:29 -05:00
Felix Roos 4871abbd71 fix superdough example readme 2023-12-29 16:57:46 +01:00
Felix Roos bd2333377f Merge pull request #878 from tidalcycles/move-examples
move all examples to separate examples folder
2023-12-29 16:53:39 +01:00
Felix Roos 76aaedba66 lockfile 2023-12-29 16:50:58 +01:00
Felix Roos b0f316eecd add readme to examples folder 2023-12-29 16:49:05 +01:00
Felix Roos 49903abed7 better naming 2023-12-29 16:48:00 +01:00
Felix Roos d106875605 Merge branch 'main' into move-examples 2023-12-29 16:40:28 +01:00
Felix Roos c13feccdcd better names + more readme 2023-12-29 16:36:38 +01:00
Felix Roos 6aebf99ebc hotfix: panel style 2023-12-29 16:22:50 +01:00
Felix Roos 5d6ccf56d7 add some readme 2023-12-29 16:18:13 +01:00
Felix Roos 0323f75bae move all examples to separate examples folder 2023-12-29 16:05:10 +01:00
Felix Roos f31fdb0988 Merge pull request #876 from tidalcycles/vanilla-repl-4
final vanillification
2023-12-29 15:29:17 +01:00
Felix Roos 92024f68cf default panel position right 2023-12-29 15:26:47 +01:00
Felix Roos f9c1e46c01 delete outdated svg graphics 2023-12-29 15:17:00 +01:00
Felix Roos a5af6d5f4c remove gist function 2023-12-29 15:07:53 +01:00
Felix Roos b1d06c4eb7 remove nano-repl workspace 2023-12-29 15:04:12 +01:00
Felix Roos 89bf3b2817 fix: tooltip padding 2023-12-29 15:03:36 +01:00
Felix Roos ea8aabceed fix: loader 2023-12-29 15:03:26 +01:00
Felix Roos 9fd2d3d640 add missing flash checkbox 2023-12-29 13:58:06 +01:00
Felix Roos 967dea346f fix: lint error 2023-12-29 13:57:52 +01:00
Felix Roos 57bc0b7032 cleanup 2023-12-29 01:07:11 +01:00
Felix Roos 9974311344 better repl init + a bit of ssr for main repl 2023-12-29 00:55:14 +01:00
Felix Roos b0bdd09032 fix: dumb react ssr workaround 2023-12-29 00:30:46 +01:00
Felix Roos f477273d8a delete react package 2023-12-28 21:06:18 +01:00
Felix Roos 014303b0d5 remove dependencies to @strudel.cycles/react 2023-12-28 21:04:29 +01:00
Felix Roos 201fe726b6 replace main repl 2023-12-28 20:48:59 +01:00
Felix Roos 2ed3a5c582 fix: lint 2023-12-28 20:36:59 +01:00
Felix Roos 975a198ee9 whole docs now run new repl
- move MicroRepl code to MiniRepl
- fix a ssr bug
2023-12-28 20:36:23 +01:00
Felix Roos 837f8f318d microrepl claviature support 2023-12-28 19:40:01 +01:00
Felix Roos f96827d052 fix: initial highlighting 2023-12-28 18:03:50 +01:00
Felix Roos a9dc0912d0 fix: drawer performance issue 2023-12-28 17:54:51 +01:00
Jade (Rose) Rowland b5039ecce8 fix import 2023-12-28 10:19:20 -05:00
Felix Roos 624affe092 Merge pull request #874 from daslyfe/clock_drift
Bug Fix #119: Clock drift
2023-12-27 18:38:09 +01:00
Felix Roos 9b5842b20e hotfix: fix build error 2023-12-27 13:28:20 +01:00
Felix Roos edfa0c65f9 Merge pull request #873 from tidalcycles/vanilla-repl-3
main repl vanillification
2023-12-27 13:17:02 +01:00
Felix Roos 39e81aa77c reduce drawTime to fix perf (for now) 2023-12-27 12:46:10 +01:00
Felix Roos 8bde87246b load imported samples directly in prebake 2023-12-27 12:45:27 +01:00
Felix Roos e2a082f748 move repl2 to /next 2023-12-27 11:42:18 +01:00
Felix Roos d7c2cf562f fix: hot reloading 2023-12-27 11:40:07 +01:00
Felix Roos f44ceb46cd remove react-dom dependency 2023-12-26 18:26:51 +01:00
Felix Roos f524dde5ae tooltip without react + simplify autocomplete 2023-12-26 18:03:58 +01:00
Felix Roos 47aa4cf198 autocomplete without react 2023-12-26 17:56:07 +01:00
Felix Roos 1b55a5e3e0 add trailing slash 2023-12-26 17:55:08 +01:00
Jade (Rose) Rowland cb57d8f497 working 2023-12-25 20:42:20 -05:00
Jade (Rose) Rowland e12421cee8 still getting too late 2023-12-25 20:27:42 -05:00
Felix Roos 53484db768 style fixes + remove lastShared state 2023-12-26 00:29:43 +01:00
Felix Roos 4c337a5114 fix: lint errors 2023-12-26 00:06:59 +01:00
Felix Roos 63ae95186b repl2: share + clean up imports 2023-12-26 00:06:15 +01:00
Felix Roos 8c0065f563 pull out repl utility functions
+ repl2 initCode / switching patterns works now
2023-12-25 23:53:54 +01:00
Felix Roos 5c41d6789c checkbox for isPatternHighlightingEnabled 2023-12-25 21:11:28 +01:00
Felix Roos e19f799046 wire up settings and theming 2023-12-25 20:52:00 +01:00
Felix Roos 7dac1e275d repl2: working update + shuffle buttons + faster loading 2023-12-25 20:08:57 +01:00
Felix Roos d74d50c40a first steps of using StrudelMirror in main repl 2023-12-25 19:37:03 +01:00
Felix Roos 8b4be4d100 Merge pull request #866 from tidalcycles/vanilla-repl-2
more work on vanilla repl: repl web component + package
2023-12-25 17:47:25 +01:00
Felix Roos de4460a047 remove redundant test pages 2023-12-25 15:45:49 +01:00
Felix Roos 0ed615a312 fix: add .piano function 2023-12-25 15:32:12 +01:00
Felix Roos e8e8f888dd fix: stop other repls on start 2023-12-25 15:21:54 +01:00
Jade (Rose) Rowland 3776791239 working 2023-12-23 12:30:55 -05:00
Jade (Rose) Rowland 9979a08cb0 updated comment 2023-12-18 00:35:54 -05:00
Jade (Rose) Rowland 17dd48f2a4 prettier 2023-12-18 00:28:41 -05:00
Jade (Rose) Rowland 355ac2ea29 Merge branch 'main' into audio_device_selection 2023-12-18 00:22:33 -05:00
Jade Rowland 000e1293db initialize on first load 2023-12-18 00:09:30 -05:00
Jade Rowland 48fa04f164 system standard resets audio context 2023-12-17 23:15:38 -05:00
Felix Roos ed8f5bf24f fix: first frame 2023-12-17 22:51:43 +01:00
Felix Roos e17df79133 microrepl: ssr static code 2023-12-17 22:42:51 +01:00
Felix Roos 9519d286bd remove web component attribute support
+ settings are now controlled via nanostore
2023-12-17 21:48:53 +01:00
Felix Roos b462f19ef1 fix: boolean settings 2023-12-17 21:28:25 +01:00
Felix Roos 1cdb5964c6 settings sync 2023-12-17 21:16:29 +01:00
Felix Roos 592e54a53f delete strudelmirror example 2023-12-17 21:13:48 +01:00
Felix Roos 40f3212efa fix: first frame active state 2023-12-16 17:04:08 +01:00
Felix Roos 53d6ef04b8 fix: drawTime + canvasHeight 2023-12-16 16:22:28 +01:00
Felix Roos c29d032027 add autodraw flag 2023-12-16 16:19:18 +01:00
Felix Roos 7fcd9d8a83 approaching proper draw logic in microrepl 2023-12-16 16:14:40 +01:00
Felix Roos fc034830d0 use StrudelMirror directly in MicroRepl 2023-12-16 13:08:09 +01:00
Felix Roos 48e06bd213 fix: redundant style injections for multiple repls 2023-12-16 12:37:16 +01:00
Felix Roos 20e8a30583 copy recipes page to test new MicroRepl 2023-12-15 23:30:27 +01:00
Felix Roos 89c06046b5 fix: repl package import on server 2023-12-15 23:27:38 +01:00
Felix Roos 9c13f6bb53 fix: mini repl play toggle button 2023-12-15 23:20:21 +01:00
Felix Roos b5edcde638 cleanup 2023-12-15 23:18:58 +01:00
Felix Roos 93e8186388 MicroRepl poc 2023-12-15 22:23:20 +01:00
Felix Roos 8e35079fd1 more code in example 2023-12-15 21:01:45 +01:00
Felix Roos 372e93e8f8 repl now accepts code in innerHTML + bump 2023-12-15 11:50:49 +01:00
Felix Roos 9b58cf9890 bump repl to 0.9.3 2023-12-15 11:34:14 +01:00
Felix Roos 9fac2c3c33 band aid fix for process is not defined 2023-12-15 11:33:35 +01:00
Felix Roos 505ad17447 fix: process is not defined 2023-12-15 11:25:36 +01:00
Felix Roos 129327077a bump repl to 0.9.2 2023-12-15 11:24:38 +01:00
Felix Roos 034df01e1f better theme style handling 2023-12-15 11:23:10 +01:00
Felix Roos be1e5a7d0e remove onReady callback
+ use strudelTheme as default
2023-12-15 11:21:48 +01:00
Felix Roos 9872a89a29 bump repl to 0.9.1 2023-12-15 10:49:13 +01:00
Felix Roos cf445633c2 bundle as iife instead of cjs 2023-12-15 10:47:58 +01:00
Felix Roos 2cec7bc1a6 fix: rollup warning 2023-12-15 10:47:45 +01:00
Felix Roos 46bfd74e8d repl build config without externals 2023-12-15 10:37:24 +01:00
Felix Roos f6b1e13113 fix: lint errors + wrong color 2023-12-15 10:14:20 +01:00
Felix Roos f0c3d38ea7 move repl web component to new repl package
+ use it in /vanilla + /vanilla/mini
2023-12-15 10:12:47 +01:00
Felix Roos d4afbc63e2 strudel web component 2023-12-15 09:22:50 +01:00
Felix Roos 466574a856 Merge pull request #865 from tidalcycles/vanilla-repl-2
Vanilla repl 3
2023-12-15 00:11:35 +01:00
Felix Roos a8d0d39055 fix: improve last fix by using Prec.highest 2023-12-15 00:07:08 +01:00
Felix Roos 1b04243af6 fix: ctrl+enter on linux 2023-12-15 00:03:12 +01:00
Felix Roos 29082808cb fix: make canvas visible
- automate editor background handling
2023-12-14 23:00:18 +01:00
Felix Roos d23cac506c fix: fontFamily setting 2023-12-14 22:51:17 +01:00
Felix Roos 8959c630ae move css variable injection to themes
+ prepare new HeadCommon in extra file
2023-12-14 22:39:44 +01:00
Felix Roos 35a4f9808d Merge pull request #863 from tidalcycles/vanilla-repl-2
Vanilla repl 2
2023-12-14 21:28:48 +01:00
Felix Roos 49906e98b7 get rid of duplication 2023-12-14 14:50:26 +01:00
Felix Roos 7b6f63db05 comment out fontFamily for now 2023-12-14 14:50:17 +01:00
Felix Roos 37f97e1c17 remove package-lock 2023-12-14 14:48:39 +01:00
Felix Roos 60e559a910 add /vanilla route to website
+ disable autocomplete for now
2023-12-14 14:38:10 +01:00
Felix Roos f6f5ee0375 fix: linter error 2023-12-14 11:50:52 +01:00
Felix Roos 72d1f129cf add settings form to codemirror example 2023-12-14 11:29:33 +01:00
Felix Roos 9474ed7d85 dedicated strudelmirror example 2023-12-14 10:37:16 +01:00
Felix Roos 99194814a9 move code hashing helpers to core 2023-12-14 10:36:56 +01:00
Felix Roos 87feac447b - make codemirror package more capable
- add themes from react package
- add Autocomplete from react package
- handle dynamic extensions
- handle keyboard shortcuts
- make StrudelMirror as capable as Repl
2023-12-14 09:42:25 +01:00
Felix Roos 0a070a2084 superdough 0.9.12 2023-12-12 22:27:08 +01:00
Felix Roos 1d3f87a431 Merge pull request #861 from tidalcycles/envelope-fix
fix: finally repair envelopes
2023-12-12 21:49:22 +01:00
Felix Roos f38d1c272e cleanup 2023-12-12 21:46:52 +01:00
Felix Roos b0bbd58f66 fix: finally repair envelopes 2023-12-12 21:36:32 +01:00
Felix Roos 73fd801907 Merge pull request #860 from tidalcycles/fix-slashless-urls
add missing trailing slashes
2023-12-12 21:20:00 +01:00
Felix Roos bc59f20af4 add missing trailing slashes 2023-12-12 21:17:15 +01:00
Felix Roos daf9187572 Merge pull request #839 from daslyfe/file_import
Sound Import from local file system
2023-12-12 19:08:31 +01:00
Felix Roos 783c856fd7 Merge pull request #858 from tidalcycles/pattern-organization
Pattern organization
2023-12-11 22:48:35 +01:00
Felix Roos f939e00a84 cleanup 2023-12-11 22:46:30 +01:00
Felix Roos b31faa9bbc Merge branch 'main' into pattern-organization 2023-12-11 22:43:53 +01:00
Felix Roos 853a31b638 improve code init logic 2023-12-11 22:17:10 +01:00
Felix Roos faaed61384 fix: do not sync activePattern between tabs
+ only deselect activePattern if code in url differs
2023-12-11 22:01:08 +01:00
Felix Roos b50f686a96 move dirt sample selection to prebake 2023-12-11 11:04:49 +01:00
Jade Rowland f45a3933de update text 2023-12-10 23:08:48 -05:00
Felix Roos 98a1786c10 hotfix: deselect user pattern when opening links 2023-12-10 21:06:51 +01:00
Felix Roos 1f16ebc5b2 fix: deselect user pattern when opening links 2023-12-10 21:01:51 +01:00
Felix Roos 79e8e430f1 fix: style for small screen 2023-12-10 19:50:20 +01:00
Felix Roos 6440621d5d Merge branch 'main' into audio_device_selection 2023-12-10 19:32:29 +01:00
Jade Rowland 59a4cd9cf8 updating buton location 2023-12-10 11:37:18 -05:00
Jade (Rose) Rowland 6238be7f2c Merge branch 'tidalcycles:main' into file_import 2023-12-10 11:28:50 -05:00
Jade Rowland 4051276d74 fixing merge conflicts 2023-12-10 11:27:39 -05:00
Jade Rowland 145510266a fixing merge conflicts 2023-12-10 11:26:46 -05:00
Jade Rowland 4d361a2b3d fixing merge conflicts 2023-12-10 11:24:39 -05:00
Jade Rowland c4fe664215 seperating things out 2023-12-10 11:22:50 -05:00
Jade Rowland ace71693b7 db utils should be reusable 2023-12-10 10:19:57 -05:00
Felix Roos f19ac748e6 slim down template
- move import + export logic to settings.mjs
2023-12-10 14:28:27 +01:00
Jade Rowland a7713213f9 indexdb 2023-12-10 00:44:34 -05:00
Felix Roos 845571a716 hotfix: pattern save was broken :-/ 2023-12-09 23:54:49 +01:00
Felix Roos 8b1dfab077 Merge pull request #855 from tidalcycles/export-patterns
Export patterns + ui tweaks
2023-12-09 17:25:11 +01:00
Felix Roos 6969800192 fix: update button bug 2023-12-09 17:23:24 +01:00
Felix Roos 37a13d1a00 add tooltips 2023-12-08 23:09:24 +01:00
Felix Roos 8c89938eaf pattern json export + import 2023-12-08 23:07:30 +01:00
Felix Roos 5116bf74ad reorder pattern action buttons 2023-12-08 22:25:05 +01:00
Felix Roos a6f969614e fix: rename cancel 2023-12-08 22:24:49 +01:00
Felix Roos c1b7476f43 + show active pattern at the top
+ move active pattern functions to top
2023-12-08 22:22:07 +01:00
Felix Roos 0ca4c1de0f make sure new recent patterns are on top 2023-12-08 22:21:26 +01:00
Jade Rowland ec44103d20 comments 2023-12-08 12:17:01 -05:00
Jade Rowland 89fe0f11ae cleaning up 2023-12-08 12:12:58 -05:00
Felix Roos 63a01088a0 hotfix: dont overwrite user pattern on shuffle 2023-12-08 11:42:15 +01:00
Felix Roos c3e48f1b4d Merge pull request #852 from tidalcycles/import-export-patterns
patterns tab: import patterns + style
2023-12-08 09:41:10 +01:00
Felix Roos 31cd626820 style sounds and patterns tab more consistently 2023-12-08 09:30:06 +01:00
Felix Roos 8dd0e9600d add some padding 2023-12-08 09:20:42 +01:00
Felix Roos 10d86ff835 more style + set reset flag 2023-12-08 09:18:55 +01:00
Felix Roos cec8553fb5 clear sounds + cps on pattern change
+ similar to shuffle
2023-12-08 09:18:33 +01:00
Felix Roos b03bf9ec3d remove log 2023-12-08 09:17:43 +01:00
Felix Roos a750490eea begin pattern import button 2023-12-08 08:49:03 +01:00
Jade Rowland 5281c61f99 fixing async stuff 2023-12-08 01:01:06 -05:00
Jade Rowland 52628920d9 working 2023-12-07 22:17:50 -05:00
Felix Roos ac2e450e38 fix: duplicate example 2023-12-07 21:43:08 +01:00
Felix Roos f8d1e9e004 Merge pull request #769 from tidalcycles/patterns-tab
Patterns tab + Refactor Panel
2023-12-07 20:42:00 +01:00
Felix Roos e551dfeddb reorder tabs + remove duplicated examples 2023-12-07 20:30:13 +01:00
Felix Roos 2f9244e90d style 2023-12-07 20:28:04 +01:00
Felix Roos 056fae518b add checks for forbidden actions 2023-12-07 20:28:01 +01:00
Felix Roos a45d87667f fix: update / fork logic 2023-12-07 20:17:01 +01:00
Felix Roos 7225b0b18d merge changes 2023-12-07 17:33:23 +01:00
Felix Roos e72c9cbebf Merge branch 'main' into patterns-tab 2023-12-07 17:33:07 +01:00
Felix Roos c787bb26c7 Merge pull request #850 from bwagner/pnpm_8.11.0
CHANGES: pnpm 8.1.3 to 8.11.0
2023-12-07 10:25:15 +01:00
Bernhard Wagner 20f3ddd277 CHANGES: pnpm 8.1.3 to 8.11.0 2023-12-07 10:19:26 +01:00
Felix Roos 5cac182b26 Merge pull request #849 from shiyouganai/shiyouganai-slashocalypse
Fix edge case with rehype-urls and trailing slashes in image file paths
2023-12-07 09:35:13 +01:00
Priere 78b2b5ccc3 Don't trim serialized query part of URL 2023-12-06 17:54:29 -05:00
Priere 60cee7fd2f Fix to not add trailing slash to URL with filename 2023-12-06 17:43:15 -05:00
Felix Roos 2bd21df762 Merge pull request #848 from shiyouganai/shiyouganai-slashocalypse
Fix examples page, piano() and a few workshop imgs
2023-12-06 23:02:31 +01:00
Priere a32ed02745 Fix piano and two doc images 2023-12-06 16:57:43 -05:00
Priere 93c5eeb99e Fix examples page and de workshop img 2023-12-06 16:45:37 -05:00
Felix Roos ff45baba2c Merge pull request #846 from tidalcycles/slashocalypse
fix: swatch png src
2023-12-06 22:30:59 +01:00
Felix Roos cc4d38b4a5 fix: swatch png src 2023-12-06 22:28:14 +01:00
Felix Roos bf30587180 Merge pull request #845 from tidalcycles/slashocalypse
fix: missing hash for links starting with /
2023-12-06 22:19:30 +01:00
Felix Roos 1b372a9a7b fix: missing hash for links starting with / 2023-12-06 22:16:43 +01:00
Felix Roos d9f78e1283 Merge pull request #842 from tidalcycles/slashocalypse
improve slashing + base href behavior
2023-12-06 22:05:05 +01:00
Felix Roos 2ddd2d1f39 fix: search links with basehref 2023-12-06 21:46:13 +01:00
Felix Roos 6b31160bbd fix: astro warnings 2023-12-06 21:34:39 +01:00
Felix Roos 74aa85b38b Merge pull request #843 from shiyouganai/shiyouganai-slashocalypse
Add in fixes from my fork to slashocalypse branch
2023-12-06 21:26:48 +01:00
Priere 8e9b607fa1 Revert to simpler link parsing and run formatter 2023-12-06 03:57:30 -05:00
Priere 5332f99a5a Fix another comment 2023-12-06 03:43:38 -05:00
Priere e1a8bd333d Fix comment 2023-12-06 03:41:54 -05:00
Priere 018a45e2d4 Rework rehype plugin and clean up changes 2023-12-06 02:57:28 -05:00
Priere 2c43fb2512 Remove unnecessary change 2023-12-05 19:34:15 -05:00
Priere 3e5664a877 Add quick fix for non root bases 2023-12-05 19:34:15 -05:00
Felix Roos ac41426a20 improve slashing + base href behavior
+ prefix sidebar links & mdx links with base
+ add trailing slashes
2023-12-05 23:09:34 +01:00
Felix Roos 8a0cf14fd7 Merge pull request #838 from ilesinge/prevent_crawler_404
Prevent 404 on Algolia crawls
2023-12-05 18:43:57 +01:00
Felix Roos 362f0df117 Merge pull request #820 from daslyfe/multichannel_audio
Multichannel audio
2023-12-05 18:26:47 +01:00
Felix Roos 1190f6e091 fix weird rounding change... 2023-12-05 18:17:33 +01:00
Bernhard Wagner ebf93c06dd ADDS: JetBrains IDE files (#840)
Co-authored-by: Bernhard Wagner <gitcommit@nosuch.biz>
2023-12-05 11:19:57 +00:00
Bernhard Wagner fe8debe6e8 CHANGES: github action pnpm version from 7 to 8.3.1 (#835)
Co-authored-by: Bernhard Wagner <gitcommit@nosuch.biz>
2023-12-05 11:19:26 +00:00
Bernhard Wagner b8866cf575 CHANGES: pin pnpm to version 8.3.1 (#834)
Co-authored-by: Bernhard Wagner <gitcommit@nosuch.biz>
2023-12-05 11:19:17 +00:00
Bernhard Wagner 2ce8576ab2 CHANGES: github action checkout v2->v4 (#837)
Co-authored-by: Bernhard Wagner <gitcommit@nosuch.biz>
2023-12-05 11:19:06 +00:00
Bernhard Wagner 1bca05e349 FIXES: palindrome abc -> abccba (#831)
* FIXES: palindrome abc -> abccba

* FIXES: tests for palindrome

---------

Co-authored-by: Bernhard Wagner <gitcommit@nosuch.biz>
2023-12-05 10:15:13 +00:00
Jade Rowland 8146725f89 removed unessecary change 2023-12-04 19:20:28 -05:00
Jade Rowland 34c44a29c6 style input 2023-12-04 19:11:38 -05:00
Alexandre G.-Raymond 7c948dd812 Prevent 404 on Algolia crawls 2023-12-04 14:48:54 +01:00
Jade Rowland 61919a1b3e upload samples 2023-12-03 23:33:04 -05:00
Jade Rowland 9be10890e1 update tests 2023-12-03 18:31:01 -05:00
Jade Rowland f51109cb26 comment 2023-12-03 18:26:48 -05:00
Jade Rowland 9aec18b3a0 add 1 to channel selection 2023-12-03 18:25:30 -05:00
Felix Roos b374cdf47c Merge pull request #830 from drewgbarnes/patch-1
Fix a typo
2023-12-02 09:43:50 +01:00
Drew Barnes b16f235e1c Update first-sounds.mdx 2023-12-01 14:23:07 -08:00
Felix Roos c26e5bbf0d Merge pull request #827 from ilesinge/add_search
Add and style algolia search
2023-11-30 10:42:41 +01:00
Alexandre G.-Raymond 020c326458 Codeformat for search 2023-11-26 22:52:17 +01:00
Alexandre G.-Raymond dacd1e6146 Add and style algolia search 2023-11-26 22:50:17 +01:00
Felix Roos 25b2eb5783 snapshot 2023-11-25 16:20:12 +01:00
Felix Roos 7c3dbac083 Merge branch 'main' into multichannel_audio 2023-11-25 16:19:44 +01:00
Felix Roos 54b5d69c8d Merge pull request #818 from atfornes/main
Hydra fixes and improvements
2023-11-25 15:50:43 +01:00
Felix Roos 407510e265 document feedStrudel 2023-11-25 15:20:19 +01:00
Felix Roos f7a283f87d add feedStrudel option + smarter reinit logic 2023-11-24 12:49:05 +01:00
Ámbar Tenorio Fornés 508a6c92b2 default hydra config without audio detection 2023-11-24 12:21:04 +01:00
Felix Roos 9045ce05da comment 2023-11-24 11:28:20 +01:00
Felix Roos 456be920d3 else clause 2023-11-24 11:17:30 +01:00
Felix Roos 71b4e14dd3 Merge branch 'main' into atfornesmain 2023-11-24 11:10:15 +01:00
Felix Roos fb7a9e2083 format 2023-11-24 10:28:40 +01:00
Felix Roos 69e99fa902 Merge pull request #808 from kasparsj/hydra-options
add options param to initHydra
2023-11-24 10:07:17 +01:00
Felix Roos 5e7e1ff0e8 Merge pull request #800 from ilesinge/improve_doc
Improve documentation for synonym functions
2023-11-24 09:57:02 +01:00
Felix Roos 18e9ceb501 populate autocomplete with all synonyms
+ properly display synonyms based on label
2023-11-24 09:52:39 +01:00
Felix Roos 458c97b927 list synonyms in Autocomplete 2023-11-24 09:37:26 +01:00
Felix Roos 4fd3e6e99c fix: entry.name mutation bug 2023-11-24 09:37:11 +01:00
Felix Roos c606f86e9d Merge pull request #806 from Bubobubobubobubo/noises
New noise type: "crackle"
2023-11-24 09:00:53 +01:00
Jade Rowland e7376c4fc8 panic function update 2023-11-23 14:17:17 -05:00
Jade Rowland 886b16d6fd fixed panic function 2023-11-23 14:07:32 -05:00
Jade Rowland 64c2e7c1e8 cleaning up 2023-11-23 13:45:58 -05:00
Jade Rowland 88e933ee25 cleaning up 2023-11-23 13:31:00 -05:00
Jade Rowland 1f62d85d8b cleaning up 2023-11-23 13:29:22 -05:00
Jade Rowland 3fd5fdf1ee preview outputs should be stereo 2023-11-23 12:43:51 -05:00
Jade Rowland e419733716 channel splitting and channels parameter 2023-11-23 01:04:53 -05:00
Alexandre Gravel-Raymond ecc8e0a108 More code beauty please 2023-11-22 20:38:25 +01:00
Alexandre Gravel-Raymond 2868a5ff4f Update the undocumented functions file 2023-11-22 20:36:01 +01:00
Alexandre Gravel-Raymond d71877f915 Move jsdoc files to subfolder 2023-11-22 20:35:19 +01:00
Ámbar Tenorio Fornés e0e896d4b5 improve hydra audio detection initialization 2023-11-22 17:39:16 +01:00
Ámbar Tenorio Fornés 0a280e9399 prettier code formatting 2023-11-22 15:47:16 +01:00
Ámbar Tenorio Fornés a58ac967a6 add Hydra audio example to documentation 2023-11-22 13:51:42 +01:00
Ámbar Tenorio Fornés ebc6723f55 enable hydra options update 2023-11-22 13:51:01 +01:00
Ámbar Tenorio Fornés 66fd20ace5 add hydra audio capture option 2023-11-22 12:36:43 +01:00
Ámbar Tenorio Fornés de00212bb6 fix hydra performance issues 2023-11-22 12:27:18 +01:00
Jade Rowland f0b458cd17 need to figure out channels input + seperating left right channels from source 2023-11-22 00:03:34 -05:00
Jade Rowland 7aa217828b testing 2023-11-20 23:06:35 -05:00
Felix Roos b27f8e45dc hotfix: build 2023-11-18 21:48:50 +01:00
Felix Roos 3e02f0a0ba Merge pull request #815 from tidalcycles/color-hsl
Color hsl
2023-11-18 21:18:16 +01:00
Felix Roos f759999826 pianoroll smear as float 2023-11-18 21:16:03 +01:00
Felix Roos 049aa35286 hsl + hsla functions 2023-11-18 21:15:52 +01:00
Felix Roos dd8be657c2 Merge pull request #813 from tidalcycles/fix-multiple-repls
fix: multiple repls
2023-11-17 23:18:23 +01:00
Felix Roos 09752241ba fix: multiple repls 2023-11-17 23:16:09 +01:00
Felix Roos 83561a0ceb Update README.md 2023-11-17 21:46:34 +01:00
Felix Roos b8ca83ddd1 rename pattern 2023-11-17 21:29:50 +01:00
Felix Roos 4cae6c051b fix: save pattern 2023-11-17 20:55:23 +01:00
Felix Roos e5f9467329 fix: slight layout improvement 2023-11-17 20:55:04 +01:00
Felix Roos 34c1f3c7de fix: examples layout 2023-11-17 20:54:48 +01:00
Kaspars c0f56be2a9 add options param to initHydra 2023-11-17 20:41:37 +01:00
Felix Roos c6f89431d9 fix: merge error 2023-11-17 20:37:21 +01:00
Felix Roos 80601451b6 Merge branch 'main' into patterns-tab 2023-11-17 20:36:15 +01:00
Felix Roos f74d1beb62 Merge branch 'main' of https://github.com/tidalcycles/strudel 2023-11-17 20:29:53 +01:00
Felix Roos b73ea7f086 Merge pull request #801 from ilesinge/add_doc
Add doc for euclidLegatoRot, wordfall and slider
2023-11-17 16:04:10 +01:00
Alexandre Gravel-Raymond a3bf6975c4 Mark optional slider params 2023-11-17 15:33:53 +01:00
Felix Roos 08ebb52e9b Merge pull request #804 from kasparsj/highlight-active-line-opt
add option to disable active line highlighting in Code Settings
2023-11-17 14:42:58 +01:00
Felix Roos 47a6af2b25 Merge pull request #805 from tidalcycles/pq-functions
tidal style d1 ... d9 functions + more
2023-11-17 14:37:51 +01:00
Felix Roos a84cfb5677 bump superdough 2023-11-17 14:07:26 +01:00
Raphael Forment 6e92c4915a Run prettier 2023-11-17 13:37:16 +01:00
Raphael Forment 2a18df61cd Fix documentation 2023-11-17 13:23:54 +01:00
Raphael Forment fb28083ce6 Fixing density and adding documentation 2023-11-17 13:18:49 +01:00
Raphael Forment 476bda812e Initial work on crackle 2023-11-17 12:55:17 +01:00
Felix Roos 265f01eb06 add p1 .. p9 shortcuts as well 2023-11-17 10:50:45 +01:00
Felix Roos cb9ef3ee6a add q1 .. q9 shortcuts 2023-11-17 09:45:44 +01:00
Felix Roos a4487179a5 fix tests 2023-11-16 07:25:20 +01:00
Felix Roos f43f627037 + add hush
+ add the ability to evaluate without clearing
2023-11-16 07:17:59 +01:00
Felix Roos a7b799f154 p and q methods + all function 2023-11-16 06:52:47 +01:00
Felix Roos ee7cd8cd4c remove pseudo note variables 2023-11-16 06:51:51 +01:00
Kaspars 2d1fcf4a8d fix typo 2023-11-15 22:53:56 +01:00
Kaspars ef14074164 add option to disable active line highlighting in Code Settings 2023-11-15 22:38:00 +01:00
Felix Roos 49aa9cf6da Merge pull request #802 from kasparsj/fix-strudelTheme-outline
remove unwanted cm6 outline for strudelTheme
2023-11-15 20:12:23 +01:00
Kaspars 8d5ef45e2b fix codeformat 2023-11-15 19:22:52 +01:00
Kaspars 9ef177e735 Merge branch 'main' into fix-strudelTheme-outline 2023-11-14 16:33:06 +01:00
Kaspars 7f1919ebc6 move fix to Repl.css 2023-11-14 11:16:51 +01:00
Felix Roos 1aaaaba619 Merge pull request #798 from daslyfe/phaser
Create phaser effect
2023-11-13 23:30:14 +01:00
Felix Roos b7dd9b7d35 Merge remote-tracking branch 'origin/main' into phaser 2023-11-13 21:59:10 +01:00
Kaspars ff0af15ac7 remove unwanted cm6 outline for strudelTheme 2023-11-13 00:06:01 +01:00
Alexandre G.-Raymond 8a7ff9e01d Add snapshot for euclidLegatoRot 2023-11-11 17:24:18 +01:00
Alexandre G.-Raymond e52e257d9b Document euclidLegatoRot 2023-11-11 17:03:21 +01:00
Alexandre G.-Raymond 2d6654b234 Document slider 2023-11-11 16:24:09 +01:00
Alexandre G.-Raymond 1b0427e943 Document wordfall 2023-11-11 16:23:58 +01:00
Alexandre G.-Raymond 24e1881876 Ignore jsdoc-synonyms by eslint 2023-11-11 15:53:03 +01:00
Alexandre G.-Raymond bfff38a262 Fix code format of JSDoc synonym plugin 2023-11-11 15:44:15 +01:00
Alexandre G.-Raymond 2aff31fe06 Simplify JSDoc 2023-11-11 14:24:20 +01:00
Alexandre G.-Raymond 6ba2b767b0 Show synonyms in API reference 2023-11-11 14:23:55 +01:00
Alexandre G.-Raymond bbc292561f Use synonyms in Tooltip 2023-11-11 14:23:30 +01:00
Alexandre G.-Raymond e4b00117c3 Parse the synonyms tag in source documentation 2023-11-11 14:22:52 +01:00
Alex McLean d25851ac17 support multiple named serial connections, change default baudrate to 115200 (#551) 2023-11-10 11:17:35 +00:00
Felix Roos d02450c0d9 Merge pull request #662 from gogins/main
Fix for #1. Enables named instruments for csoundm.
2023-11-09 09:27:57 +01:00
Felix Roos 400b2109fb Merge pull request #706 from Bubobubobubobubo/global-vibrato
Adding vibrato to Superdough sampler
2023-11-09 09:26:45 +01:00
Felix Roos 2a09f9ef0b fix: sampler broke without vibrato 2023-11-09 09:21:40 +01:00
Felix Roos 0052d349d9 refactor: move vibrato up + cleanup oscillator 2023-11-09 09:06:50 +01:00
Felix Roos 7c8a8b8b70 simplify 2023-11-09 08:59:23 +01:00
Felix Roos 85835087ba format 2023-11-09 08:48:23 +01:00
Felix Roos 5aac042c80 Merge branch 'main' into global-vibrato 2023-11-09 08:47:37 +01:00
Felix Roos 9dbd073073 Merge pull request #784 from ilesinge/document_pianoroll
Document pianoroll
2023-11-09 08:46:03 +01:00
Felix Roos 035f6c8098 Merge pull request #795 from bwagner/patch-2
Update first-effects.mdx
2023-11-09 08:45:29 +01:00
Felix Roos d2a06654c3 Merge pull request #796 from bwagner/patch-3
Update pattern-effects.mdx
2023-11-09 08:44:43 +01:00
Felix Roos c028686298 Merge pull request #797 from bwagner/patch-4
Update recap.mdx
2023-11-09 08:43:50 +01:00
Felix Roos 858a02634e dedupe / move supertdirt phaser controls 2023-11-09 08:40:21 +01:00
Felix Roos 2682fd38f8 add to doc page 2023-11-09 08:29:21 +01:00
Felix Roos 3ecb701504 add params to superdough readme 2023-11-09 08:19:04 +01:00
Felix Roos ac359a0e9e doc: phaser synonyms 2023-11-09 08:17:16 +01:00
Felix Roos df636cf4f6 phaser shortcuts 2023-11-09 08:16:17 +01:00
Felix Roos 791a363694 snapshots 2023-11-08 23:33:46 +01:00
Jade Rowland 06eb8b5be8 fixed doc 2023-11-07 22:57:00 -05:00
Jade Rowland 4b4290e087 add more parameters 2023-11-07 22:52:39 -05:00
Jade Rowland c89f91215c Merge branch 'tidalcycles:main' into phaser 2023-11-07 22:11:55 -05:00
Jade Rowland 65d07dbff2 create free running lfos for different orbits 2023-11-07 22:09:53 -05:00
Jade Rowland 2e4d28b910 update doc 2023-11-07 21:49:56 -05:00
Jade Rowland 51c63fa996 turn off of depth is 0 2023-11-07 21:44:57 -05:00
Jade Rowland f0a582dc92 cleaning up 2023-11-07 21:10:12 -05:00
Jade Rowland bee36a7a4f cleaning up 2023-11-07 20:25:31 -05:00
Jade Rowland 0edd54dee3 cleaning up 2023-11-07 20:23:45 -05:00
Bernhard Wagner 4a16cd891f FIXES: table 2023-11-07 18:55:35 +01:00
Bernhard Wagner 23dd9d4c01 Update recap.mdx
Adds a few missing translations
2023-11-07 16:16:46 +01:00
Bernhard Wagner df277d6b7b Update pattern-effects.mdx
grammatical amendments
2023-11-07 16:10:57 +01:00
Bernhard Wagner eed2c304e4 Update first-effects.mdx
grammatical amendments
2023-11-07 15:57:31 +01:00
Felix Roos bd5953317e Merge pull request #794 from bwagner/patch-1
Update first-sounds.mdx
2023-11-07 11:53:48 +01:00
Bernhard Wagner b88f58cc21 Update first-sounds.mdx
in eine sogenannte Funktion -> in einer sogenannten Funktion
2023-11-07 11:38:13 +01:00
Jade Rowland 9cbf3079f3 change to bus effect experiment 2023-11-06 19:02:09 -05:00
Jade Rowland a9efda3bc3 tweaking to make it sound good 2023-11-05 15:40:02 -05:00
Alexandre G.-Raymond 8fe4dca4c2 Update pianoroll documentation 2023-11-05 18:07:42 +01:00
Alexandre G.-Raymond 8fa7bf795a Fix pianoroll documentation 2023-11-05 16:25:10 +01:00
Alexandre G.-Raymond f045fb44de Add color function documentation 2023-11-05 16:23:36 +01:00
Alexandre G.-Raymond 0d06840a1b Add label function documentation 2023-11-05 16:23:21 +01:00
Alexandre G.-Raymond 624e540cb4 Add pianoroll function documentation 2023-11-05 16:23:00 +01:00
Jade Rowland b3f8df1783 initial commit 2023-11-04 20:20:25 -04:00
Felix Roos 53160c638a half working user pattern management 2023-10-29 14:00:13 +01:00
Felix Roos a2077f7be1 fix logger import 2023-10-29 13:14:29 +01:00
Felix Roos b245d44220 hide patterns tab for now
+ add examples to welcome tab
2023-10-29 13:14:25 +01:00
Felix Roos b15f7bcf45 Merge branch 'main' into patterns-tab 2023-10-29 13:05:25 +01:00
Raphael Forment 08aab9d4d8 Adding vibrato to Superdough sampler
Modulating the `playbackRate` to simulate a vibrato!
2023-09-20 19:44:32 +02:00
Felix Roos dc254bd562 upstream changes 2023-09-17 17:28:25 +02:00
Felix Roos 6c3173f45b Merge branch 'main' into patterns-tab 2023-09-17 17:28:18 +02:00
Felix Roos c75c3f1ab1 basic pattern add function 2023-08-24 11:55:21 +02:00
Felix Roos 281ad5fac9 add nanFallback for better errors 2023-08-24 08:50:42 +02:00
Felix Roos eb0301d74e remove annoying logs 2023-08-24 08:50:18 +02:00
Felix Roos 4a76757dd2 Merge branch 'main' into patterns-tab 2023-08-23 22:56:12 +02:00
Felix Roos 1855dae35e add patterns tab with examples 2023-08-23 22:51:36 +02:00
Felix Roos 4e7e715f10 rename footer -> panel 2023-08-19 00:20:46 +02:00
Felix Roos 5be1004292 encapsulate panel tabs 2023-08-19 00:19:32 +02:00
gogins 1170ff58ee Fix for #1. 2023-08-10 20:26:37 -04:00
251 changed files with 8136 additions and 9870 deletions
+2 -1
View File
@@ -20,4 +20,5 @@ vite.config.js
**/dist
/src-tauri/target/**/*
reverbGen.mjs
hydra.mjs
hydra.mjs
jsdoc-synonyms.js
+3 -3
View File
@@ -21,13 +21,13 @@ jobs:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v2
with:
version: 7
version: 8.11.0
- uses: actions/setup-node@v3
with:
node-version: 18
node-version: 20
cache: "pnpm"
- name: Install Dependencies
run: pnpm install
+3 -3
View File
@@ -7,13 +7,13 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18]
node-version: [20]
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v2
with:
version: 7
version: 8.11.0
- uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
+84 -1
View File
@@ -43,4 +43,87 @@ dev-dist
Dirt-Samples
tidal-drum-machines
webaudiofontdata
src-tauri/target
src-tauri/target
# BEGIN JetBrains -> END JetBrains
# for JetBrains IDE users, e.g. WebStorm. Source: https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
# AWS User-specific
.idea/**/aws.xml
# Generated files
.idea/**/contentModel.xml
# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
# Gradle
.idea/**/gradle.xml
.idea/**/libraries
# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/artifacts
# .idea/compiler.xml
# .idea/jarRepositories.xml
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# *.iml
# *.ipr
# CMake
cmake-build-*/
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# File-based project format
*.iws
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# SonarLint plugin
.idea/sonarlint/
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Editor-based Rest Client
.idea/httpRequests
# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser
# END JetBrains -> BEGIN JetBrains
+1 -1
View File
@@ -9,4 +9,4 @@ packages/xen/tunejs.js
paper
pnpm-lock.yaml
pnpm-workspace.yaml
**/dev-dist
**/dev-dist
+1 -1
View File
@@ -66,7 +66,7 @@ To get the project up and running for development, make sure you have installed:
- [git](https://git-scm.com/)
- [node](https://nodejs.org/en/) >= 18
- [pnpm](https://pnpm.io/) (`npm i pnpm -g`)
- [pnpm](https://pnpm.io/) (`curl -fsSL https://get.pnpm.io/install.sh | env PNPM_VERSION=8.11.0 sh -`)
then, do the following:
-1
View File
@@ -30,7 +30,6 @@ There are multiple npm packages you can use to use strudel, or only parts of it,
- [`midi`](./packages/midi): webmidi bindings
- [`serial`](./packages/serial): webserial bindings
- [`tonal`](./packages/tonal): tonal functions
- [`xen`](./packages/xen): microtonal / xenharmonic functions
- ... [and there are more](./packages/)
Click on the package names to find out more about each one.
-1806
View File
File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 150 KiB

+3
View File
@@ -0,0 +1,3 @@
# examples
This folder contains usage examples for different scenarios.
+5
View File
@@ -0,0 +1,5 @@
# buildless examples
These examples show you how strudel can be used in a regular html file, without the need for a build tool.
Most examples are using [skypack](https://www.skypack.dev/)
@@ -1,4 +1,4 @@
<!DOCTYPE html>
<!doctype html>
<button id="play">play</button>
<button id="stop">stop</button>
<script type="module">
@@ -1,4 +1,4 @@
<!DOCTYPE html>
<!doctype html>
<button id="a">A</button>
<button id="b">B</button>
<button id="c">C</button>
@@ -1,4 +1,4 @@
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
@@ -0,0 +1,31 @@
<script src="https://unpkg.com/@strudel/repl@0.9.4"></script>
<strudel-editor>
<!--
// @date 23-08-15
// "golf rolf" @by froos @license CC BY-NC-SA 4.0
setcps(1)
stack(
s("bd*2, ~ rim*<1!3 2>, hh*4").bank('RolandTR909')
.off(-1/8, set(speed("1.5").gain(.25)))
.mask("<0!16 1!64>")
,
note("g1(3,8)")
.s("gm_synth_bass_2:<0 2>")
.delay(".8:.25:.25")
.clip("<.5!16 2!32>")
.off(1/8, add(note("12?0.7")))
.lpf(sine.range(500,2000).slow(32)).lpq(8)
.add(note("0,.05"))
.mask("<0!8 1!32>")
,
n("<0 1 2 3 4>*8").scale('G4 minor')
.s("gm_lead_6_voice")
.clip(sine.range(.2,.8).slow(8))
.jux(rev)
.room(2)
.sometimes(add(note("12")))
.lpf(perlin.range(200,20000).slow(4))
).reset("<x@15 x(5,8)>")
-->
</strudel-editor>
+8
View File
@@ -0,0 +1,8 @@
# codemirror-repl example
This folder demonstrates how to set up a full strudel repl with the `@strudel/codemirror` package. Run it using:
```sh
pnpm i
pnpm dev
```
@@ -1,4 +1,4 @@
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
@@ -9,7 +9,7 @@
"preview": "vite preview"
},
"devDependencies": {
"vite": "^4.3.2"
"vite": "^5.0.10"
},
"dependencies": {
"@strudel/codemirror": "workspace:*",
+13
View File
@@ -0,0 +1,13 @@
# headless-repl demo
This demo shows how to use strudel in "headless mode".
Buttons A / B / C will switch between different patterns.
It showcases the usage of the `@strudel/web` package, using [vite](https://vitejs.dev/) as the dev server.
## Running
```sh
pnpm i && cd examples/headless-repl
pnpm dev
# open http://localhost:5173/
```
@@ -1,4 +1,4 @@
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
@@ -10,7 +10,7 @@
"preview": "vite preview"
},
"devDependencies": {
"vite": "^4.3.2"
"vite": "^5.0.10"
},
"dependencies": {
"@strudel/web": "workspace:*"
+1
View File
@@ -0,0 +1 @@
dist
+10
View File
@@ -0,0 +1,10 @@
# minimal repl
This folder demonstrates how to set up a minimal strudel repl using vite and vanilla JS. Run it using:
```sh
npm i
npm run dev
```
If you're looking for a more feature rich alternative, have a look at the [../codemirror-repl](codemirror-repl example)
@@ -1,4 +1,4 @@
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
@@ -10,7 +10,7 @@
"preview": "vite preview"
},
"devDependencies": {
"vite": "^4.3.3"
"vite": "^5.0.10"
},
"dependencies": {
"@strudel.cycles/core": "workspace:*",
+11
View File
@@ -0,0 +1,11 @@
# superdough demo
This demo shows how to use [superdough](https://www.npmjs.com/package/superdough) with [vite](https://vitejs.dev/).
## Running
```sh
pnpm i && cd examples/headless-repl
pnpm dev
# open http://localhost:5173/
```
+38
View File
@@ -0,0 +1,38 @@
<!doctype html>
<html>
<head>
<title>Superdough Example</title>
<meta charset="UTF-8" />
</head>
<body>
<button id="play">PLAAAAAAAY</button>
<script type="module">
import { superdough, samples, initAudioOnFirstClick, registerSynthSounds } from 'superdough';
const init = Promise.all([
initAudioOnFirstClick(),
samples('github:tidalcycles/Dirt-Samples/master'),
registerSynthSounds(),
]);
const loop = (t = 0) => {
// superdough(value, time, duration)
superdough({ s: 'bd', delay: 0.5 }, t);
superdough({ note: 'g1', s: 'sawtooth', cutoff: 600, resonance: 8 }, t, 0.125);
superdough({ note: 'g2', s: 'sawtooth', cutoff: 600, resonance: 8 }, t + 0.25, 0.125);
superdough({ s: 'hh' }, t + 0.25);
superdough({ s: 'sd', room: 0.5 }, t + 0.5);
superdough({ s: 'hh' }, t + 0.75);
};
document.getElementById('play').addEventListener('click', async () => {
await init;
let t = 0.1;
while (t < 16) {
loop(t++);
}
});
</script>
</body>
</html>
@@ -12,6 +12,6 @@
"superdough": "workspace:*"
},
"devDependencies": {
"vite": "^4.4.5"
"vite": "^5.0.10"
}
}
@@ -1,12 +1,17 @@
/*
postcss.config.js - <short description TODO>
Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/strudel/blob/main/repl/postcss.config.js>
jsdoc-synonyms.js - Add support for @synonym tag
Copyright (C) 2023 Strudel contributors - see <https://github.com/tidalcycles/strudel/blob/main/packages/midi/midi.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/>.
*/
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};
function defineTags(dictionary) {
dictionary.defineTag('synonyms', {
mustHaveValue: true,
onTagged: function (doclet, tag) {
doclet.synonyms_text = tag.value;
doclet.synonyms = doclet.synonyms_text.split(/[ ,]+/);
},
});
}
module.exports = { defineTags: defineTags };
@@ -3,7 +3,7 @@
"includePattern": ".+\\.(js(doc|x)?|mjs)$",
"excludePattern": "node_modules|shift-parser|shift-reducer|shift-traverser|dist"
},
"plugins": ["plugins/markdown"],
"plugins": ["plugins/markdown", "jsdoc/jsdoc-synonyms"],
"opts": {
"destination": "./out/",
"recurse": true
+4 -2
View File
@@ -65,7 +65,7 @@ async function getUndocumented(path, docs) {
}
// read doc.json file
const { docs } = JSON.parse(await readFile(resolve(__dirname, 'doc.json'), 'utf8'));
const { docs } = JSON.parse(await readFile(resolve(__dirname, '..', 'doc.json'), 'utf8'));
const paths = dependencyTree.toList({
filename: 'index.mjs',
@@ -76,7 +76,9 @@ const paths = dependencyTree.toList({
// const paths = ['../packages/core/pattern.mjs', '../packages/core/hap.mjs'].map((rel) => resolve(__dirname, rel));
const undocumented = Object.fromEntries(
await Promise.all(paths.map(async (path) => [path, await getUndocumented(path, docs)])),
await Promise.all(
paths.map(async (path) => [path.replace(resolve(__dirname, '..'), ''), await getUndocumented(path, docs)]),
),
);
console.log(JSON.stringify(undocumented, null, 2));
+2 -4
View File
@@ -1,8 +1,6 @@
{
"packages": [
"packages/*"
],
"packages": ["packages/*"],
"version": "independent",
"npmClient": "pnpm",
"useWorkspaces": true
"$schema": "node_modules/lerna/schemas/lerna-schema.json"
}
+13 -14
View File
@@ -18,12 +18,12 @@
"build": "npm run prebuild && cd website && npm run build",
"preview": "cd website && npm run preview",
"osc": "cd packages/osc && npm run server",
"jsdoc": "jsdoc packages/ -c jsdoc.config.json",
"jsdoc-json": "jsdoc packages/ --template ./node_modules/jsdoc-json --destination doc.json -c jsdoc.config.json",
"jsdoc": "jsdoc packages/ -c jsdoc/jsdoc.config.json",
"jsdoc-json": "jsdoc packages/ --template ./node_modules/jsdoc-json --destination doc.json -c jsdoc/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",
"report-undocumented": "npm run jsdoc-json && node jsdoc/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"
},
@@ -50,23 +50,22 @@
"@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"
"@strudel.cycles/xen": "workspace:*"
},
"devDependencies": {
"@tauri-apps/cli": "^1.4.0",
"@vitest/ui": "^0.28.0",
"dependency-tree": "^10.0.9",
"@tauri-apps/cli": "^1.5.9",
"@vitest/ui": "^1.1.0",
"canvas": "^2.11.2",
"eslint": "^8.39.0",
"eslint-plugin-import": "^2.27.5",
"eslint": "^8.56.0",
"eslint-plugin-import": "^2.29.1",
"events": "^3.3.0",
"jsdoc": "^4.0.2",
"jsdoc-json": "^2.0.2",
"jsdoc-to-markdown": "^8.0.0",
"lerna": "^6.6.1",
"prettier": "^2.8.8",
"rollup-plugin-visualizer": "^5.8.1",
"vitest": "^0.33.0"
"lerna": "^8.0.1",
"prettier": "^3.1.1",
"rollup-plugin-visualizer": "^5.12.0",
"vitest": "^1.1.0"
}
}
-10
View File
@@ -3,13 +3,3 @@
Each folder represents one of the @strudel.cycles/* packages [published to npm](https://www.npmjs.com/org/strudel.cycles).
To understand how those pieces connect, refer to the [Technical Manual](https://github.com/tidalcycles/strudel/wiki/Technical-Manual) or the individual READMEs.
This is a graphical view of all the packages: [full screen](https://raw.githubusercontent.com/tidalcycles/strudel/main/dependencies.svg)
![dependencies](https://raw.githubusercontent.com/tidalcycles/strudel/main/dependencies.svg)
Generated with
```sh
npx depcruise --include-only "^packages" -X "node_modules" --output-type dot packages | dot -T svg > dependencygraph.svg
```
@@ -1,5 +1,7 @@
import { createRoot } from 'react-dom/client';
import jsdoc from '../../../../doc.json';
import jsdoc from '../../doc.json';
// import { javascriptLanguage } from '@codemirror/lang-javascript';
import { autocompletion } from '@codemirror/autocomplete';
import { h } from './html';
const getDocLabel = (doc) => doc.name || doc.longname;
const getInnerText = (html) => {
@@ -8,35 +10,32 @@ const getInnerText = (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) => {
navigator.clipboard.writeText(example);
e.stopPropagation();
}}
>
{example}
</pre>
</div>
))}
</div>
</div>
);
export function Autocomplete({ doc, label }) {
return h`<div class="prose dark:prose-invert max-h-[400px] overflow-auto p-2">
<h1 class="pt-0 mt-0">${label || getDocLabel(doc)}</h1>
${doc.description}
<ul>
${doc.params?.map(
({ name, type, description }) =>
`<li>${name} : ${type.names?.join(' | ')} ${description ? ` - ${getInnerText(description)}` : ''}</li>`,
)}
</ul>
<div>
${doc.examples?.map((example) => `<div><pre>${example}</pre></div>`)}
</div>
</div>`[0];
/*
<pre
className="cursor-pointer"
onMouseDown={(e) => {
console.log('ola!');
navigator.clipboard.writeText(example);
e.stopPropagation();
}}
>
{example}
</pre>
*/
}
const jsdocCompletions = jsdoc.docs
@@ -51,13 +50,7 @@ const jsdocCompletions = jsdoc.docs
.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;
},
info: () => Autocomplete({ doc }),
type: 'function', // https://codemirror.net/docs/ref/#autocomplete.Completion.type
}));
@@ -74,3 +67,12 @@ export const strudelAutocomplete = (context /* : CompletionContext */) => {
], */
};
};
export function isAutoCompletionEnabled(on) {
return on
? [
autocompletion({ override: [strudelAutocomplete] }),
//javascriptLanguage.data.of({ autocomplete: strudelAutocomplete }),
]
: []; // autocompletion({ override: [] })
}
+273 -47
View File
@@ -1,37 +1,112 @@
import { defaultKeymap } from '@codemirror/commands';
import { closeBrackets } from '@codemirror/autocomplete';
// import { search, highlightSelectionMatches } from '@codemirror/search';
import { history } from '@codemirror/commands';
import { javascript } from '@codemirror/lang-javascript';
import { defaultHighlightStyle, syntaxHighlighting } from '@codemirror/language';
import { EditorState } from '@codemirror/state';
import { EditorView, highlightActiveLineGutter, keymap, lineNumbers } from '@codemirror/view';
import { Drawer, repl } from '@strudel.cycles/core';
import { flashField, flash } from './flash.mjs';
import { highlightExtension, highlightMiniLocations } from './highlight.mjs';
import { oneDark } from './themes/one-dark';
import { Compartment, EditorState, Prec } from '@codemirror/state';
import {
EditorView,
highlightActiveLineGutter,
highlightActiveLine,
keymap,
lineNumbers,
drawSelection,
} from '@codemirror/view';
import { Pattern, Drawer, repl, cleanupDraw } from '@strudel.cycles/core';
import { isAutoCompletionEnabled } from './autocomplete.mjs';
import { isTooltipEnabled } from './tooltip.mjs';
import { flash, isFlashEnabled } from './flash.mjs';
import { highlightMiniLocations, isPatternHighlightingEnabled, updateMiniLocations } from './highlight.mjs';
import { keybindings } from './keybindings.mjs';
import { initTheme, activateTheme, theme } from './themes.mjs';
import { updateWidgets, sliderPlugin } from './slider.mjs';
import { persistentAtom } from '@nanostores/persistent';
const extensions = {
isLineWrappingEnabled: (on) => (on ? EditorView.lineWrapping : []),
isLineNumbersDisplayed: (on) => (on ? lineNumbers() : []),
theme,
isAutoCompletionEnabled,
isTooltipEnabled,
isPatternHighlightingEnabled,
isActiveLineHighlighted: (on) => (on ? [highlightActiveLine(), highlightActiveLineGutter()] : []),
isFlashEnabled,
keybindings,
};
const compartments = Object.fromEntries(Object.keys(extensions).map((key) => [key, new Compartment()]));
export const defaultSettings = {
keybindings: 'codemirror',
isLineNumbersDisplayed: true,
isActiveLineHighlighted: false,
isAutoCompletionEnabled: false,
isPatternHighlightingEnabled: true,
isFlashEnabled: true,
isTooltipEnabled: false,
isLineWrappingEnabled: false,
theme: 'strudelTheme',
fontFamily: 'monospace',
fontSize: 18,
};
export const codemirrorSettings = persistentAtom('codemirror-settings', defaultSettings, {
encode: JSON.stringify,
decode: JSON.parse,
});
// https://codemirror.net/docs/guide/
export function initEditor({ initialCode = '', onChange, onEvaluate, onStop, theme = oneDark, root }) {
export function initEditor({ initialCode = '', onChange, onEvaluate, onStop, root }) {
const settings = codemirrorSettings.get();
const initialSettings = Object.keys(compartments).map((key) =>
compartments[key].of(extensions[key](parseBooleans(settings[key]))),
);
initTheme(settings.theme);
let state = EditorState.create({
doc: initialCode,
extensions: [
theme,
/* search(),
highlightSelectionMatches(), */
...initialSettings,
javascript(),
lineNumbers(),
highlightExtension,
highlightActiveLineGutter(),
sliderPlugin,
// indentOnInput(), // works without. already brought with javascript extension?
// bracketMatching(), // does not do anything
closeBrackets(),
syntaxHighlighting(defaultHighlightStyle),
keymap.of(defaultKeymap),
flashField,
history(),
EditorView.updateListener.of((v) => onChange(v)),
keymap.of([
{
key: 'Ctrl-Enter',
run: () => onEvaluate(),
drawSelection({ cursorBlinkRate: 0 }),
Prec.highest(
keymap.of([
{
key: 'Ctrl-Enter',
run: () => onEvaluate?.(),
},
{
key: 'Alt-Enter',
run: () => onEvaluate?.(),
},
{
key: 'Ctrl-.',
run: () => onStop?.(),
},
{
key: 'Alt-.',
run: (_, e) => {
e.preventDefault();
onStop?.();
},
},
/* {
key: 'Ctrl-Shift-.',
run: () => (onPanic ? onPanic() : onStop?.()),
},
{
key: 'Ctrl-.',
run: () => onStop(),
},
]),
key: 'Ctrl-Shift-Enter',
run: () => (onReEvaluate ? onReEvaluate() : onEvaluate?.()),
}, */
]),
),
],
});
@@ -43,45 +118,76 @@ export function initEditor({ initialCode = '', onChange, onEvaluate, onStop, the
export class StrudelMirror {
constructor(options) {
const { root, initialCode = '', onDraw, drawTime = [-2, 2], prebake, ...replOptions } = options;
const {
root,
id,
initialCode = '',
onDraw,
drawTime = [0, 0],
autodraw,
prebake,
bgFill = true,
...replOptions
} = options;
this.code = initialCode;
this.root = root;
this.miniLocations = [];
this.widgets = [];
this.painters = [];
this.drawTime = drawTime;
this.onDraw = onDraw;
const self = this;
this.id = id || s4();
this.drawer = new Drawer((haps, time) => {
const currentFrame = haps.filter((hap) => time >= hap.whole.begin && time <= hap.endClipped);
this.highlight(currentFrame, time);
onDraw?.(haps, time, currentFrame);
this.onDraw?.(haps, time, currentFrame, this.painters);
}, drawTime);
const prebaked = prebake();
prebaked.then(async () => {
if (!onDraw) {
return;
}
const { scheduler, evaluate } = await this.repl;
// draw first frame instantly
prebaked.then(async () => {
await evaluate(this.code, false);
this.drawer.invalidate(scheduler);
onDraw?.(this.drawer.visibleHaps, 0, []);
});
});
// this approach does not work with multiple repls on screen
// TODO: refactor onPaint usages + find fix, maybe remove painters here?
Pattern.prototype.onPaint = function (onPaint) {
self.painters.push(onPaint);
return this;
};
this.prebaked = prebake();
autodraw && this.drawFirstFrame();
this.repl = repl({
...replOptions,
onToggle: async (started) => {
onToggle: (started) => {
replOptions?.onToggle?.(started);
const { scheduler } = await this.repl;
if (started) {
this.drawer.start(scheduler);
this.adjustDrawTime();
this.drawer.start(this.repl.scheduler);
// stop other repls when this one is started
document.dispatchEvent(
new CustomEvent('start-repl', {
detail: this.id,
}),
);
} else {
this.drawer.stop();
updateMiniLocations(this.editor, []);
cleanupDraw(false);
}
},
beforeEval: async () => {
await prebaked;
cleanupDraw();
this.painters = [];
await this.prebaked;
await replOptions?.beforeEval?.();
},
afterEval: (options) => {
// remember for when highlighting is toggled on
this.miniLocations = options.meta?.miniLocations;
this.widgets = options.meta?.widgets;
updateWidgets(this.editor, this.widgets);
updateMiniLocations(this.editor, this.miniLocations);
replOptions?.afterEval?.(options);
this.adjustDrawTime();
this.drawer.invalidate();
},
});
@@ -89,25 +195,145 @@ export class StrudelMirror {
root,
initialCode,
onChange: (v) => {
this.code = v.state.doc.toString();
if (v.docChanged) {
this.code = v.state.doc.toString();
this.repl.setCode?.(this.code);
}
},
onEvaluate: () => this.evaluate(),
onStop: () => this.stop(),
});
const cmEditor = this.root.querySelector('.cm-editor');
if (cmEditor) {
this.root.style.display = 'block';
if (bgFill) {
this.root.style.backgroundColor = 'var(--background)';
}
cmEditor.style.backgroundColor = 'transparent';
}
const settings = codemirrorSettings.get();
this.setFontSize(settings.fontSize);
this.setFontFamily(settings.fontFamily);
// stop this repl when another repl is started
this.onStartRepl = (e) => {
if (e.detail !== this.id) {
this.stop();
}
};
document.addEventListener('start-repl', this.onStartRepl);
}
// adjusts draw time depending on if there are painters
adjustDrawTime() {
// when no painters are set, [0,0] is enough (just highlighting)
this.drawer.setDrawTime(this.painters.length ? this.drawTime : [0, 0]);
}
async drawFirstFrame() {
if (!this.onDraw) {
return;
}
// draw first frame instantly
await this.prebaked;
try {
await this.repl.evaluate(this.code, false);
this.drawer.invalidate(this.repl.scheduler, -0.001);
// draw at -0.001 to avoid haps at 0 to be visualized as active
this.onDraw?.(this.drawer.visibleHaps, -0.001, [], this.painters);
} catch (err) {
console.warn('first frame could not be painted');
}
}
async evaluate() {
const { evaluate } = await this.repl;
this.flash();
await evaluate(this.code);
await this.repl.evaluate(this.code);
}
async stop() {
const { scheduler } = await this.repl;
scheduler.stop();
this.repl.scheduler.stop();
}
async toggle() {
if (this.repl.scheduler.started) {
this.repl.stop();
} else {
this.evaluate();
}
}
flash(ms) {
flash(this.editor, ms);
}
highlight(haps, time) {
highlightMiniLocations(this.editor.view, time, haps);
highlightMiniLocations(this.editor, time, haps);
}
setFontSize(size) {
this.root.style.fontSize = size + 'px';
}
setFontFamily(family) {
this.root.style.fontFamily = family;
const scroller = this.root.querySelector('.cm-scroller');
if (scroller) {
scroller.style.fontFamily = family;
}
}
reconfigureExtension(key, value) {
if (!extensions[key]) {
console.warn(`extension ${key} is not known`);
return;
}
value = parseBooleans(value);
const newValue = extensions[key](value, this);
this.editor.dispatch({
effects: compartments[key].reconfigure(newValue),
});
if (key === 'theme') {
activateTheme(value);
}
}
setLineWrappingEnabled(enabled) {
this.reconfigureExtension('isLineWrappingEnabled', enabled);
}
setLineNumbersDisplayed(enabled) {
this.reconfigureExtension('isLineNumbersDisplayed', enabled);
}
setTheme(theme) {
this.reconfigureExtension('theme', theme);
}
setAutocompletionEnabled(enabled) {
this.reconfigureExtension('isAutoCompletionEnabled', enabled);
}
updateSettings(settings) {
this.setFontSize(settings.fontSize);
this.setFontFamily(settings.fontFamily);
for (let key in extensions) {
this.reconfigureExtension(key, settings[key]);
}
const updated = { ...codemirrorSettings.get(), ...settings };
codemirrorSettings.set(updated);
}
changeSetting(key, value) {
if (extensions[key]) {
this.reconfigureExtension(key, value);
return;
} else if (key === 'fontFamily') {
this.setFontFamily(value);
} else if (key === 'fontSize') {
this.setFontSize(value);
}
}
setCode(code) {
const changes = { from: 0, to: this.editor.state.doc.length, insert: code };
this.editor.dispatch({ changes });
}
clear() {
this.onStartRepl && document.removeEventListener('start-repl', this.onStartRepl);
}
}
function parseBooleans(value) {
return { true: true, false: false }[value] ?? value;
}
// helper function to generate repl ids
function s4() {
return Math.floor((1 + Math.random()) * 0x10000)
.toString(16)
.substring(1);
}
+2
View File
@@ -33,3 +33,5 @@ export const flash = (view, ms = 200) => {
view.dispatch({ effects: setFlash.of(false) });
}, ms);
};
export const isFlashEnabled = (on) => (on ? flashField : []);
+9
View File
@@ -124,3 +124,12 @@ const miniLocationHighlights = EditorView.decorations.compute([miniLocations, vi
});
export const highlightExtension = [miniLocations, visibleMiniLocations, miniLocationHighlights];
export const isPatternHighlightingEnabled = (on, config) => {
on &&
config &&
setTimeout(() => {
updateMiniLocations(config.editor, config.miniLocations);
}, 100);
return on ? highlightExtension : [];
};
+17
View File
@@ -0,0 +1,17 @@
const parser = typeof DOMParser !== 'undefined' ? new DOMParser() : null;
export let html = (string) => {
return parser?.parseFromString(string, 'text/html').querySelectorAll('*');
};
let parseChunk = (chunk) => {
if (Array.isArray(chunk)) return chunk.flat().join('');
if (chunk === undefined) return '';
return chunk;
};
export let h = (strings, ...vars) => {
let string = '';
for (let i in strings) {
string += parseChunk(strings[i]);
string += parseChunk(vars[i]);
}
return html(string);
};
+1
View File
@@ -2,3 +2,4 @@ export * from './codemirror.mjs';
export * from './highlight.mjs';
export * from './flash.mjs';
export * from './slider.mjs';
export * from './themes.mjs';
+31
View File
@@ -0,0 +1,31 @@
import { Prec } from '@codemirror/state';
import { keymap, ViewPlugin } from '@codemirror/view';
// import { searchKeymap } from '@codemirror/search';
import { emacs } from '@replit/codemirror-emacs';
import { vim } from '@replit/codemirror-vim';
import { vscodeKeymap } from '@replit/codemirror-vscode-keymap';
import { defaultKeymap, historyKeymap } from '@codemirror/commands';
const vscodePlugin = ViewPlugin.fromClass(
class {
constructor() {}
},
{
provide: () => {
return Prec.highest(keymap.of([...vscodeKeymap]));
},
},
);
const vscodeExtension = (options) => [vscodePlugin].concat(options ?? []);
const keymaps = {
vim,
emacs,
vscode: vscodeExtension,
};
export function keybindings(name) {
const active = keymaps[name];
return [keymap.of(defaultKeymap), keymap.of(historyKeymap), active ? active() : []];
// keymap.of(searchKeymap),
}
+17 -8
View File
@@ -33,15 +33,24 @@
},
"homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": {
"@codemirror/commands": "^6.2.4",
"@codemirror/lang-javascript": "^6.1.7",
"@codemirror/language": "^6.6.0",
"@codemirror/state": "^6.2.0",
"@codemirror/view": "^6.10.0",
"@lezer/highlight": "^1.1.4",
"@strudel.cycles/core": "workspace:*"
"@codemirror/autocomplete": "^6.11.1",
"@codemirror/commands": "^6.3.3",
"@codemirror/lang-javascript": "^6.2.1",
"@codemirror/language": "^6.10.0",
"@codemirror/search": "^6.5.5",
"@codemirror/state": "^6.4.0",
"@codemirror/view": "^6.23.0",
"@lezer/highlight": "^1.2.0",
"@replit/codemirror-emacs": "^6.0.1",
"@replit/codemirror-vim": "^6.1.0",
"@replit/codemirror-vscode-keymap": "^6.0.2",
"@strudel.cycles/core": "workspace:*",
"@uiw/codemirror-themes": "^4.21.21",
"@uiw/codemirror-themes-all": "^4.21.21",
"nanostores": "^0.9.5",
"@nanostores/persistent": "^0.9.1"
},
"devDependencies": {
"vite": "^4.3.3"
"vite": "^5.0.10"
}
}
+9
View File
@@ -111,6 +111,15 @@ export const sliderPlugin = ViewPlugin.fromClass(
},
);
/**
* Displays a slider widget to allow the user manipulate a value
*
* @name slider
* @param {number} value Initial value
* @param {number} min Minimum value - optional, defaults to 0
* @param {number} max Maximum value - optional, defaults to 1
* @param {number} step Step size - optional
*/
export let slider = (value) => {
console.warn('slider will only work when the transpiler is used... passing value as is');
return pure(value);
@@ -30,13 +30,13 @@ import {
xcodeLight,
} from '@uiw/codemirror-themes-all';
import strudelTheme from '@strudel.cycles/react/src/themes/strudel-theme';
import bluescreen, { settings as bluescreenSettings } from '@strudel.cycles/react/src/themes/bluescreen';
import blackscreen, { settings as blackscreenSettings } from '@strudel.cycles/react/src/themes/blackscreen';
import whitescreen, { settings as whitescreenSettings } from '@strudel.cycles/react/src/themes/whitescreen';
import teletext, { settings as teletextSettings } from '@strudel.cycles/react/src/themes/teletext';
import algoboy, { settings as algoboySettings } from '@strudel.cycles/react/src/themes/algoboy';
import terminal, { settings as terminalSettings } from '@strudel.cycles/react/src/themes/terminal';
import strudelTheme from './themes/strudel-theme';
import bluescreen, { settings as bluescreenSettings } from './themes/bluescreen';
import blackscreen, { settings as blackscreenSettings } from './themes/blackscreen';
import whitescreen, { settings as whitescreenSettings } from './themes/whitescreen';
import teletext, { settings as teletextSettings } from './themes/teletext';
import algoboy, { settings as algoboySettings } from './themes/algoboy';
import terminal, { settings as terminalSettings } from './themes/terminal';
export const themes = {
strudelTheme,
@@ -473,6 +473,9 @@ function stringifySafe(json) {
return JSON.stringify(json, getCircularReplacer());
}
export const theme = (theme) => themes[theme] || themes.strudelTheme;
// css style injection helpers
export function injectStyle(rule) {
const newStyle = document.createElement('style');
document.head.appendChild(newStyle);
@@ -480,3 +483,45 @@ export function injectStyle(rule) {
const ruleIndex = styleSheet.insertRule(rule, 0);
return () => styleSheet.deleteRule(ruleIndex);
}
let currentTheme,
resetThemeStyle,
themeStyle,
styleID = 'strudel-theme-vars';
export function initTheme(theme) {
if (!document.getElementById(styleID)) {
themeStyle = document.createElement('style');
themeStyle.id = styleID;
document.head.append(themeStyle);
}
activateTheme(theme);
}
export function activateTheme(name) {
if (currentTheme === name) {
return;
}
currentTheme = name;
if (!settings[name]) {
console.warn('theme', name, 'has no settings.. defaulting to strudelTheme settings');
}
const themeSettings = settings[name] || settings.strudelTheme;
// set css variables
themeStyle.innerHTML = `:root {
${Object.entries(themeSettings)
// important to override fallback
.map(([key, value]) => `--${key}: ${value} !important;`)
.join('\n')}
}`;
// tailwind dark mode
if (themeSettings.light) {
document.documentElement.classList.remove('dark');
} else {
document.documentElement.classList.add('dark');
}
resetThemeStyle?.();
resetThemeStyle = undefined;
if (themeSettings.customStyle) {
resetThemeStyle = injectStyle(themeSettings.customStyle);
}
}
-139
View File
@@ -1,139 +0,0 @@
import { EditorView } from '@codemirror/view';
import { HighlightStyle, syntaxHighlighting } from '@codemirror/language';
import { tags as t } from '@lezer/highlight';
// Using https://github.com/one-dark/vscode-one-dark-theme/ as reference for the colors
const chalky = '#e5c07b',
coral = '#e06c75',
cyan = '#56b6c2',
invalid = '#ffffff',
ivory = '#abb2bf',
stone = '#7d8799', // Brightened compared to original to increase contrast
malibu = '#61afef',
sage = '#98c379',
whiskey = '#d19a66',
violet = '#c678dd',
darkBackground = '#21252b',
highlightBackground = '#2c313a',
background = '#282c34',
tooltipBackground = '#353a42',
selection = '#3E4451',
cursor = '#528bff';
/// The colors used in the theme, as CSS color strings.
export const color = {
chalky,
coral,
cyan,
invalid,
ivory,
stone,
malibu,
sage,
whiskey,
violet,
darkBackground,
highlightBackground,
background,
tooltipBackground,
selection,
cursor,
};
/// The editor theme styles for One Dark.
export const oneDarkTheme = EditorView.theme(
{
'&': {
color: ivory,
backgroundColor: background,
},
'.cm-content': {
caretColor: cursor,
},
'.cm-cursor, .cm-dropCursor': { borderLeftColor: cursor },
'&.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection':
{ backgroundColor: selection },
'.cm-panels': { backgroundColor: darkBackground, color: ivory },
'.cm-panels.cm-panels-top': { borderBottom: '2px solid black' },
'.cm-panels.cm-panels-bottom': { borderTop: '2px solid black' },
'.cm-searchMatch': {
backgroundColor: '#72a1ff59',
outline: '1px solid #457dff',
},
'.cm-searchMatch.cm-searchMatch-selected': {
backgroundColor: '#6199ff2f',
},
'.cm-activeLine': { backgroundColor: '#6699ff0b' },
'.cm-selectionMatch': { backgroundColor: '#aafe661a' },
'&.cm-focused .cm-matchingBracket, &.cm-focused .cm-nonmatchingBracket': {
backgroundColor: '#bad0f847',
},
'.cm-gutters': {
backgroundColor: background,
color: stone,
border: 'none',
},
'.cm-activeLineGutter': {
backgroundColor: highlightBackground,
},
'.cm-foldPlaceholder': {
backgroundColor: 'transparent',
border: 'none',
color: '#ddd',
},
'.cm-tooltip': {
border: 'none',
backgroundColor: tooltipBackground,
},
'.cm-tooltip .cm-tooltip-arrow:before': {
borderTopColor: 'transparent',
borderBottomColor: 'transparent',
},
'.cm-tooltip .cm-tooltip-arrow:after': {
borderTopColor: tooltipBackground,
borderBottomColor: tooltipBackground,
},
'.cm-tooltip-autocomplete': {
'& > ul > li[aria-selected]': {
backgroundColor: highlightBackground,
color: ivory,
},
},
},
{ dark: true },
);
/// The highlighting style for code in the One Dark theme.
export const oneDarkHighlightStyle = HighlightStyle.define([
{ tag: t.keyword, color: violet },
{ tag: [t.name, t.deleted, t.character, t.propertyName, t.macroName], color: coral },
{ tag: [t.function(t.variableName), t.labelName], color: malibu },
{ tag: [t.color, t.constant(t.name), t.standard(t.name)], color: whiskey },
{ tag: [t.definition(t.name), t.separator], color: ivory },
{ tag: [t.typeName, t.className, t.number, t.changed, t.annotation, t.modifier, t.self, t.namespace], color: chalky },
{ tag: [t.operator, t.operatorKeyword, t.url, t.escape, t.regexp, t.link, t.special(t.string)], color: cyan },
{ tag: [t.meta, t.comment], color: stone },
{ tag: t.strong, fontWeight: 'bold' },
{ tag: t.emphasis, fontStyle: 'italic' },
{ tag: t.strikethrough, textDecoration: 'line-through' },
{ tag: t.link, color: stone, textDecoration: 'underline' },
{ tag: t.heading, fontWeight: 'bold', color: coral },
{ tag: [t.atom, t.bool, t.special(t.variableName)], color: whiskey },
{ tag: [t.processingInstruction, t.string, t.inserted], color: sage },
{ tag: t.invalid, color: invalid },
]);
/// Extension to enable the One Dark theme (both the editor theme and
/// the highlight style).
export const oneDark = [oneDarkTheme, syntaxHighlighting(oneDarkHighlightStyle)];
@@ -1,36 +1,40 @@
import { createRoot } from 'react-dom/client';
import { hoverTooltip } from '@codemirror/view';
import jsdoc from '../../../../doc.json';
import { Autocomplete } from './Autocomplete';
import jsdoc from '../../doc.json';
import { Autocomplete } from './autocomplete.mjs';
const getDocLabel = (doc) => doc.name || doc.longname;
let ctrlDown = false;
// Record Control key event to trigger or block the tooltip depending on the state
window.addEventListener(
'keyup',
function (e) {
if (e.key == 'Control') {
ctrlDown = false;
}
},
true,
);
if (typeof window !== 'undefined') {
// Record Control key event to trigger or block the tooltip depending on the state
window.addEventListener(
'keyup',
function (e) {
if (e.key == 'Control') {
ctrlDown = false;
}
},
true,
);
window.addEventListener(
'keydown',
function (e) {
if (e.key == 'Control') {
ctrlDown = true;
}
},
true,
);
window.addEventListener(
'keydown',
function (e) {
if (e.key == 'Control') {
ctrlDown = true;
}
},
true,
);
}
export const strudelTooltip = hoverTooltip(
(view, pos, side) => {
// Word selection from CodeMirror Hover Tooltip example https://codemirror.net/examples/tooltip/#hover-tooltips
if (!ctrlDown) {
return null;
}
let { from, to, text } = view.state.doc.lineAt(pos);
let start = pos,
end = pos;
@@ -47,11 +51,13 @@ export const strudelTooltip = hoverTooltip(
// Get entry from Strudel documentation
let entry = jsdoc.docs.filter((doc) => getDocLabel(doc) === word)[0];
if (!entry) {
return null;
}
if (!ctrlDown) {
return null;
// Try for synonyms
entry = jsdoc.docs.filter((doc) => doc.synonyms && doc.synonyms.includes(word))[0];
if (!entry) {
return null;
}
}
return {
pos: start,
end,
@@ -60,10 +66,13 @@ export const strudelTooltip = hoverTooltip(
create(view) {
let dom = document.createElement('div');
dom.className = 'strudel-tooltip';
createRoot(dom).render(<Autocomplete doc={entry} />);
const ac = Autocomplete({ doc: entry, label: word });
dom.appendChild(ac);
return { dom };
},
};
},
{ hoverTime: 10 },
);
export const isTooltipEnabled = (on) => (on ? strudelTooltip : []);
+1 -1
View File
@@ -9,7 +9,7 @@ export default defineConfig({
lib: {
entry: resolve(__dirname, 'index.mjs'),
formats: ['es', 'cjs'],
fileName: (ext) => ({ es: 'index.mjs', cjs: 'index.js' }[ext]),
fileName: (ext) => ({ es: 'index.mjs', cjs: 'index.js' })[ext],
},
rollupOptions: {
external: [...Object.keys(dependencies)],
+87 -5
View File
@@ -380,6 +380,75 @@ const generic_params = [
*
*/
['coarse'],
/**
* Allows you to set the output channels on the interface
*
* @name channels
* @synonyms ch
*
* @param {number | Pattern} channels pattern the output channels
* @example
* note("e a d b g").channels("3:4")
*
*/
['channels', 'ch'],
['phaserrate', 'phasr'], // superdirt only
/**
* Phaser audio effect that approximates popular guitar pedals.
*
* @name phaser
* @synonyms ph
* @param {number | Pattern} speed speed of modulation
* @example
* n(run(8)).scale("D:pentatonic").s("sawtooth").release(0.5)
* .phaser("<1 2 4 8>")
*
*/
[['phaser', 'phaserdepth', 'phasercenter', 'phasersweep'], 'ph'],
/**
* The frequency sweep range of the lfo for the phaser effect. Defaults to 2000
*
* @name phasersweep
* @synonyms phs
* @param {number | Pattern} phasersweep most useful values are between 0 and 4000
* @example
* n(run(8)).scale("D:pentatonic").s("sawtooth").release(0.5)
* .phaser(2).phasersweep("<800 2000 4000>")
*
*/
['phasersweep', 'phs'],
/**
* The center frequency of the phaser in HZ. Defaults to 1000
*
* @name phasercenter
* @synonyms phc
* @param {number | Pattern} centerfrequency in HZ
* @example
* n(run(8)).scale("D:pentatonic").s("sawtooth").release(0.5)
* .phaser(2).phasercenter("<800 2000 4000>")
*
*/
['phasercenter', 'phc'],
/**
* The amount the signal is affected by the phaser effect. Defaults to 0.75
*
* @name phaserdepth
* @synonyms phd
* @param {number | Pattern} depth number between 0 and 1
* @example
* n(run(8)).scale("D:pentatonic").s("sawtooth").release(0.5)
* .phaser(2).phaserdepth("<0 .5 .75 1>")
*
*/
['phaserdepth', 'phd', 'phasdp'], // also a superdirt control
/**
* choose the channel the pattern is sent to in superdirt
*
@@ -867,7 +936,12 @@ const generic_params = [
*
*/
['lsize'],
// label for pianoroll
/**
* Sets the displayed text for an event on the pianoroll
*
* @name label
* @param {string} label text to display
*/
['activeLabel'],
[['label', 'activeLabel']],
// ['lfo'],
@@ -1031,7 +1105,8 @@ const generic_params = [
*/
['roomfade', 'rfade'],
/**
* Sets the sample to use as an impulse response for the reverb. * * @name iresponse
* Sets the sample to use as an impulse response for the reverb.
* @name iresponse
* @param {string | Pattern} sample to use as an impulse response
* @synonyms ir
* @example
@@ -1152,6 +1227,16 @@ const generic_params = [
* @name waveloss
*/
['waveloss'],
/*
* Noise crackle density
*
* @name density
* @param {number | Pattern} density between 0 and x
* @example
* s("crackle*4").density("<0.01 0.04 0.2 0.5>".slow(4))
*
*/
['density'],
// TODO: midi effects?
['dur'],
// ['modwheel'],
@@ -1169,9 +1254,6 @@ const generic_params = [
*/
['tremolodepth', 'tremdp'],
['tremolorate', 'tremr'],
// TODO: doesn't seem to do anything
['phaserdepth', 'phasdp'],
['phaserrate', 'phasr'],
['fshift'],
['fshiftnote'],
+20 -3
View File
@@ -11,13 +11,14 @@ export class Cyclist {
constructor({ interval, onTrigger, onToggle, onError, getTime, latency = 0.1 }) {
this.started = false;
this.cps = 1;
this.num_ticks_since_cps_change = 0;
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.num_cycles_since_last_cps_change = 0;
this.onToggle = onToggle;
this.latency = latency; // fixed trigger time offset
const round = (x) => Math.round(x * 1000) / 1000;
this.clock = createClock(
getTime,
// called slightly before each cycle
@@ -25,14 +26,24 @@ export class Cyclist {
if (tick === 0) {
this.origin = phase;
}
if (this.num_ticks_since_cps_change === 0) {
this.num_cycles_since_last_cps_change = this.lastEnd;
}
this.num_ticks_since_cps_change++;
try {
const time = getTime();
const begin = this.lastEnd;
this.lastBegin = begin;
const end = round(begin + duration * this.cps);
//convert ticks to cycles, so you can query the pattern for events
const eventLength = duration * this.cps;
const end = this.num_cycles_since_last_cps_change + this.num_ticks_since_cps_change * eventLength;
this.lastEnd = end;
// query the pattern for events
const haps = this.pattern.queryArc(begin, end);
const tickdeadline = phase - time; // time left till phase begins
const tickdeadline = phase - time; // time left until the phase is a whole number
this.lastTick = time + tickdeadline;
haps.forEach((hap) => {
@@ -59,6 +70,8 @@ export class Cyclist {
this.onToggle?.(v);
}
start() {
this.num_ticks_since_cps_change = 0;
this.num_cycles_since_last_cps_change = 0;
if (!this.pattern) {
throw new Error('Scheduler: no pattern set! call .setPattern first.');
}
@@ -84,7 +97,11 @@ export class Cyclist {
}
}
setCps(cps = 1) {
if (this.cps === cps) {
return;
}
this.cps = cps;
this.num_ticks_since_cps_change = 0;
}
log(begin, end, haps) {
const onsets = haps.filter((h) => h.hasOnset());
+8 -4
View File
@@ -111,8 +111,6 @@ export class Framer {
// see vite-vanilla-repl-cm6 for an example
export class Drawer {
constructor(onDraw, drawTime) {
let [lookbehind, lookahead] = drawTime; // e.g. [-2, 2]
lookbehind = Math.abs(lookbehind);
this.visibleHaps = [];
this.lastFrame = null;
this.drawTime = drawTime;
@@ -122,6 +120,8 @@ export class Drawer {
console.warn('Drawer: no scheduler');
return;
}
const lookbehind = Math.abs(this.drawTime[0]);
const lookahead = this.drawTime[1];
// calculate current frame time (think right side of screen for pianoroll)
const phase = this.scheduler.now() + lookahead;
// first frame just captures the phase
@@ -145,12 +145,16 @@ export class Drawer {
},
);
}
invalidate(scheduler = this.scheduler) {
setDrawTime(drawTime) {
this.drawTime = drawTime;
}
invalidate(scheduler = this.scheduler, t) {
if (!scheduler) {
return;
}
// TODO: scheduler.now() seems to move even when it's stopped, this hints at a bug...
t = t ?? scheduler.now();
this.scheduler = scheduler;
const t = scheduler.now();
let [_, lookahead] = this.drawTime;
const [begin, end] = [Math.max(t, 0), t + lookahead + 0.1];
// remove all future haps
+14
View File
@@ -145,6 +145,8 @@ export const { euclidrot, euclidRot } = register(['euclidrot', 'euclidRot'], fun
* so there will be no gaps.
* @name euclidLegato
* @memberof Pattern
* @param {number} pulses the number of onsets / beats
* @param {number} steps the number of steps to fill
* @example
* n("g2").decay(.1).sustain(.3).euclidLegato(3,8)
*/
@@ -166,6 +168,18 @@ export const euclidLegato = register(['euclidLegato'], function (pulses, steps,
return _euclidLegato(pulses, steps, 0, pat);
});
/**
* Similar to `euclid`, but each pulse is held until the next pulse,
* so there will be no gaps, and has an additional parameter for 'rotating'
* the resulting sequence
* @name euclidLegatoRot
* @memberof Pattern
* @param {number} pulses the number of onsets / beats
* @param {number} steps the number of steps to fill
* @param {number} rotation offset in steps
* @example
* note("c3").euclidLegatoRot(3,5,2)
*/
export const euclidLegatoRot = register(['euclidLegatoRot'], function (pulses, steps, rotation, pat) {
return _euclidLegato(pulses, steps, rotation, pat);
});
-5
View File
@@ -47,10 +47,5 @@ export const evaluate = async (code, transpiler) => {
// if no transpiler is given, we expect a single instruction (!wrapExpression)
const options = { wrapExpression: !!transpiler };
let evaluated = await safeEval(code, options);
if (!isPattern(evaluated)) {
console.log('evaluated', evaluated);
const message = `got "${typeof evaluated}" instead of pattern`;
throw new Error(message + (typeof evaluated === 'function' ? ', did you forget to call a function?' : '.'));
}
return { mode: 'javascript', pattern: evaluated, meta };
};
@@ -1,8 +0,0 @@
# vite-vanilla-repl-cm6
This folder demonstrates how to set up a strudel repl using vite and vanilla JS + codemirror. Run it using:
```sh
pnpm i
pnpm dev
```
@@ -1 +0,0 @@
!dist
@@ -1,8 +0,0 @@
# vite-vanilla-repl
This folder demonstrates how to set up a strudel repl using vite and vanilla JS. Run it using:
```sh
npm i
npm run dev
```
@@ -1 +0,0 @@
import{b as s,h as i,m,a as n,c as p,p as t}from"./index.4cbc0a10.js";export{s as SyntaxError,i as h,m as mini,n as minify,p as parse,t as patternifyAST};
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{g as s,f as d,i as t,n as r,l as u,j as o,d as f,k as p,r as g,s as i,w as l,e as m}from"./index.4cbc0a10.js";export{s as getAudioContext,d as getCachedBuffer,t as getLoadedBuffer,r as getLoadedSamples,u as loadBuffer,o as loadGithubSamples,f as panic,p as resetLoadedSamples,g as reverseBuffer,i as samples,l as webaudioOutput,m as webaudioOutputTrigger};
@@ -1,36 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite Vanilla Strudel REPL</title>
<script type="module" crossorigin src="/tidalcycles/strudel/use-acorn/packages/core/examples/vite-vanilla-repl/dist/assets/index.4cbc0a10.js"></script>
</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"
></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>
</body>
</html>
-12
View File
@@ -1,12 +0,0 @@
/*
gist.js - <short description TODO>
Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/strudel/blob/main/packages/core/gist.js>
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/>.
*/
// this is a shortcut to eval code from a gist
// why? to be able to shorten strudel code + e.g. be able to change instruments after links have been generated
export default (route, cache = true) =>
fetch(`https://gist.githubusercontent.com/${route}?cachebust=${cache ? '' : Date.now()}`)
.then((res) => res.text())
.then((code) => eval(code));
-1
View File
@@ -27,7 +27,6 @@ export * from './pianoroll.mjs';
export * from './spiral.mjs';
export * from './ui.mjs';
export { default as drawLine } from './drawLine.mjs';
export { default as gist } from './gist.js';
// below won't work with runtime.mjs (json import fails)
/* import * as p from './package.json';
export const version = p.version; */
+3 -3
View File
@@ -31,11 +31,11 @@
},
"homepage": "https://strudel.cc",
"dependencies": {
"fraction.js": "^4.2.0"
"fraction.js": "^4.3.7"
},
"gitHead": "0e26d4e741500f5bae35b023608f062a794905c2",
"devDependencies": {
"vite": "^4.3.3",
"vitest": "^0.33.0"
"vite": "^5.0.10",
"vitest": "^1.1.0"
}
}
+20 -10
View File
@@ -9,7 +9,7 @@ import Fraction from './fraction.mjs';
import Hap from './hap.mjs';
import State from './state.mjs';
import { unionWithObj } from './value.mjs';
import { char2fraction } from './util.mjs';
import { compose, removeUndefineds, flatten, id, listRange, curry, _mod, numeralArgs, parseNumeral } from './util.mjs';
import drawLine from './drawLine.mjs';
import { logger } from './logger.mjs';
@@ -1178,7 +1178,7 @@ export function reify(thing) {
* @return {Pattern}
* @synonyms polyrhythm, pr
* @example
* stack(g3, b3, [e4, d4]).note() // "g3,b3,[e4,d4]".note()
* stack("g3", "b3", ["e4", "d4"]).note() // "g3,b3,[e4,d4]".note()
*/
export function stack(...pats) {
// Array test here is to avoid infinite recursions..
@@ -1193,7 +1193,7 @@ export function stack(...pats) {
*
* @return {Pattern}
* @example
* slowcat(e5, b4, [d5, c5])
* slowcat("e5", "b4", ["d5", "c5"])
*
*/
export function slowcat(...pats) {
@@ -1237,7 +1237,7 @@ export function slowcatPrime(...pats) {
* @synonyms slowcat
* @return {Pattern}
* @example
* cat(e5, b4, [d5, c5]).note() // "<e5 b4 [d5 c5]>".note()
* cat("e5", "b4", ["d5", "c5"]).note() // "<e5 b4 [d5 c5]>".note()
*
*/
export function cat(...pats) {
@@ -1247,7 +1247,7 @@ export function cat(...pats) {
/** Like {@link Pattern.seq}, but each step has a length, relative to the whole.
* @return {Pattern}
* @example
* timeCat([3,e3],[1, g3]).note() // "e3@3 g3".note()
* timeCat([3,"e3"],[1, "g3"]).note() // "e3@3 g3".note()
*/
export function timeCat(...timepats) {
const total = timepats.map((a) => a[0]).reduce((a, b) => a.add(b), Fraction(0));
@@ -1287,7 +1287,7 @@ export function sequence(...pats) {
/** Like **cat**, but the items are crammed into one cycle.
* @synonyms fastcat, sequence
* @example
* seq(e5, b4, [d5, c5]).note() // "e5 b4 [d5 c5]".note()
* seq("e5", "b4", ["d5", "c5"]).note() // "e5 b4 [d5 c5]".note()
*
*/
export function seq(...pats) {
@@ -1903,7 +1903,6 @@ export const when = register('when', function (on, func, pat) {
* "c3 eb3 g3".off(1/8, x=>x.add(7)).note()
*/
export const off = register('off', function (time_pat, func, pat) {
time_pat = char2fraction(time_pat);
return stack(pat, func(pat.late(time_pat)));
});
@@ -1976,9 +1975,9 @@ export const press = register('press', function (pat) {
* s("hh*3")
* )
*/
export const hush = register('hush', function (pat) {
Pattern.prototype.hush = function () {
return silence;
});
};
/**
* Applies `rev` to a pattern every other cycle, so that the pattern alternates between forwards and backwards.
@@ -1986,7 +1985,7 @@ export const hush = register('hush', function (pat) {
* note("c d e g").palindrome()
*/
export const palindrome = register('palindrome', function (pat) {
return pat.every(2, rev);
return pat.lastOf(2, rev);
});
/**
@@ -2190,8 +2189,19 @@ export const duration = register('duration', function (value, pat) {
return pat.withHapSpan((span) => new TimeSpan(span.begin, span.begin.add(value)));
});
export const hsla = register('hsla', (h, s, l, a, pat) => {
return pat.color(`hsla(${h}turn,${s * 100}%,${l * 100}%,${a})`);
});
export const hsl = register('hsl', (h, s, l, pat) => {
return pat.color(`hsl(${h}turn,${s * 100}%,${l * 100}%)`);
});
/**
* Sets the color of the hap in visualizations like pianoroll or highlighting.
* @name color
* @synonyms colour
* @param {string} color Hexadecimal or CSS color name
*/
// TODO: move this to controls https://github.com/tidalcycles/strudel/issues/288
export const { color, colour } = register(['color', 'colour'], function (color, pat) {
+46 -3
View File
@@ -56,6 +56,40 @@ Pattern.prototype.pianoroll = function (options = {}) {
// this function allows drawing a pianoroll without ties to Pattern.prototype
// it will probably replace the above in the future
/**
* Displays a midi-style piano roll
*
* @name pianoroll
* @param {Object} options Object containing all the optional following parameters as key value pairs:
* @param {integer} cycles number of cycles to be displayed at the same time - defaults to 4
* @param {number} playhead location of the active notes on the time axis - 0 to 1, defaults to 0.5
* @param {boolean} vertical displays the roll vertically - 0 by default
* @param {boolean} labels displays labels on individual notes (see the label function) - 0 by default
* @param {boolean} flipTime reverse the direction of the roll - 0 by default
* @param {boolean} flipValues reverse the relative location of notes on the value axis - 0 by default
* @param {number} overscan lookup X cycles outside of the cycles window to display notes in advance - 1 by default
* @param {boolean} hideNegative hide notes with negative time (before starting playing the pattern) - 0 by default
* @param {boolean} smear notes leave a solid trace - 0 by default
* @param {boolean} fold notes takes the full value axis width - 0 by default
* @param {string} active hexadecimal or CSS color of the active notes - defaults to #FFCA28
* @param {string} inactive hexadecimal or CSS color of the inactive notes - defaults to #7491D2
* @param {string} background hexadecimal or CSS color of the background - defaults to transparent
* @param {string} playheadColor hexadecimal or CSS color of the line representing the play head - defaults to white
* @param {boolean} fill notes are filled with color (otherwise only the label is displayed) - 0 by default
* @param {boolean} fillActive active notes are filled with color - 0 by default
* @param {boolean} stroke notes are shown with colored borders - 0 by default
* @param {boolean} strokeActive active notes are shown with colored borders - 0 by default
* @param {boolean} hideInactive only active notes are shown - 0 by default
* @param {boolean} colorizeInactive use note color for inactive notes - 1 by default
* @param {string} fontFamily define the font used by notes labels - defaults to 'monospace'
* @param {integer} minMidi minimum note value to display on the value axis - defaults to 10
* @param {integer} maxMidi maximum note value to display on the value axis - defaults to 90
* @param {boolean} autorange automatically calculate the minMidi and maxMidi parameters - 0 by default
*
* @example
* note("C2 A2 G2").euclid(5,8).s('piano').clip(1).color('salmon').pianoroll({vertical:1, labels:1})
*/
export function pianoroll({
time,
haps,
@@ -222,12 +256,21 @@ export function getDrawOptions(drawTime, options = {}) {
return { fold: 1, ...options, cycles, playhead };
}
export const getPunchcardPainter =
(options = {}) =>
(ctx, time, haps, drawTime, paintOptions = {}) =>
pianoroll({ ctx, time, haps, ...getDrawOptions(drawTime, { ...paintOptions, ...options }) });
Pattern.prototype.punchcard = function (options) {
return this.onPaint((ctx, time, haps, drawTime, paintOptions = {}) =>
pianoroll({ ctx, time, haps, ...getDrawOptions(drawTime, { ...paintOptions, ...options }) }),
);
return this.onPaint(getPunchcardPainter(options));
};
/**
* Displays a vertical pianoroll with event labels.
* Supports all the same options as pianoroll.
*
* @name wordfall
*/
Pattern.prototype.wordfall = function (options) {
return this.punchcard({ vertical: 1, labels: 1, stroke: 0, fillActive: 1, active: 'white', ...options });
};
+90 -14
View File
@@ -3,7 +3,7 @@ import { evaluate as _evaluate } from './evaluate.mjs';
import { logger } from './logger.mjs';
import { setTime } from './time.mjs';
import { evalScope } from './evaluate.mjs';
import { register } from './pattern.mjs';
import { register, Pattern, isPattern, silence, stack } from './pattern.mjs';
export function repl({
interval,
@@ -16,44 +16,94 @@ export function repl({
transpiler,
onToggle,
editPattern,
onUpdateState,
}) {
const state = {
schedulerError: undefined,
evalError: undefined,
code: '// LOADING',
activeCode: '// LOADING',
pattern: undefined,
miniLocations: [],
widgets: [],
pending: false,
started: false,
};
const updateState = (update) => {
Object.assign(state, update);
state.isDirty = state.code !== state.activeCode;
state.error = state.evalError || state.schedulerError;
onUpdateState?.(state);
};
const scheduler = new Cyclist({
interval,
onTrigger: getTrigger({ defaultOutput, getTime }),
onError: onSchedulerError,
getTime,
onToggle,
onToggle: (started) => {
updateState({ started });
onToggle?.(started);
},
});
let playPatterns = [];
let pPatterns = {};
let allTransform;
const hush = function () {
pPatterns = {};
allTransform = undefined;
return silence;
};
const setPattern = (pattern, autostart = true) => {
pattern = editPattern?.(pattern) || pattern;
scheduler.setPattern(pattern, autostart);
};
setTime(() => scheduler.now()); // TODO: refactor?
const evaluate = async (code, autostart = true) => {
const evaluate = async (code, autostart = true, shouldHush = true) => {
if (!code) {
throw new Error('no code to evaluate');
}
try {
updateState({ code, pending: true });
await beforeEval?.({ code });
playPatterns = [];
shouldHush && hush();
let { pattern, meta } = await _evaluate(code, transpiler);
if (playPatterns.length) {
pattern = pattern.stack(...playPatterns);
if (Object.keys(pPatterns).length) {
pattern = stack(...Object.values(pPatterns));
}
if (allTransform) {
pattern = allTransform(pattern);
}
if (!isPattern(pattern)) {
const message = `got "${typeof evaluated}" instead of pattern`;
throw new Error(message + (typeof evaluated === 'function' ? ', did you forget to call a function?' : '.'));
}
logger(`[eval] code updated`);
setPattern(pattern, autostart);
updateState({
miniLocations: meta?.miniLocations || [],
widgets: meta?.widgets || [],
activeCode: code,
pattern,
evalError: undefined,
schedulerError: undefined,
pending: false,
});
afterEval?.({ code, pattern, meta });
return pattern;
} catch (err) {
// console.warn(`[repl] eval error: ${err.message}`);
logger(`[eval] error: ${err.message}`, 'error');
updateState({ evalError: err, pending: false });
onEvalError?.(err);
}
};
const stop = () => scheduler.stop();
const start = () => scheduler.start();
const pause = () => scheduler.pause();
const toggle = () => scheduler.toggle();
const setCps = (cps) => scheduler.setCps(cps);
const setCpm = (cpm) => scheduler.setCps(cpm / 60);
@@ -62,10 +112,35 @@ export function repl({
return pat.loopAtCps(cycles, scheduler.cps);
});
const play = register('play', (pat) => {
playPatterns.push(pat);
return pat;
});
Pattern.prototype.p = function (id) {
pPatterns[id] = this;
return this;
};
Pattern.prototype.q = function (id) {
return silence;
};
const all = function (transform) {
allTransform = transform;
return silence;
};
try {
for (let i = 1; i < 10; ++i) {
Object.defineProperty(Pattern.prototype, `d${i}`, {
get() {
return this.p(i);
},
});
Object.defineProperty(Pattern.prototype, `p${i}`, {
get() {
return this.p(i);
},
});
Pattern.prototype[`q${i}`] = silence;
}
} catch (err) {
// already defined..
}
const fit = register('fit', (pat) =>
pat.withHap((hap) =>
@@ -80,14 +155,15 @@ export function repl({
evalScope({
loopAt,
fit,
play,
all,
hush,
setCps,
setcps: setCps,
setCpm,
setcpm: setCpm,
});
return { scheduler, evaluate, start, stop, pause, setCps, setPattern };
const setCode = (code) => updateState({ code });
return { scheduler, evaluate, start, stop, pause, setCps, setPattern, setCode, toggle, state };
}
export const getTrigger =
+1
View File
@@ -101,6 +101,7 @@ const timeToRand = (x) => Math.abs(intSeedToRand(timeToIntSeed(x)));
const timeToRandsPrime = (seed, n) => {
const result = [];
// eslint-disable-next-line
for (let i = 0; i < n; ++n) {
result.push(intSeedToRand(seed));
seed = xorwise(seed);
+46 -11
View File
@@ -20,6 +20,7 @@ import {
slowcat,
cat,
sequence,
palindrome,
polymeter,
polymeterSteps,
polyrhythm,
@@ -182,13 +183,17 @@ describe('Pattern', () => {
});
it('can Trig() structure', () => {
sameFirst(
slowcat(sequence(1, 2, 3, 4), 5, sequence(6, 7, 8, 9), 10).add.trig(20, 30).early(2),
slowcat(sequence(1, 2, 3, 4), 5, sequence(6, 7, 8, 9), 10)
.add.trig(20, 30)
.early(2),
sequence(26, 27, 36, 37),
);
});
it('can Trigzero() structure', () => {
sameFirst(
slowcat(sequence(1, 2, 3, 4), 5, sequence(6, 7, 8, 9), 10).add.trigzero(20, 30).early(2),
slowcat(sequence(1, 2, 3, 4), 5, sequence(6, 7, 8, 9), 10)
.add.trigzero(20, 30)
.early(2),
sequence(21, 22, 31, 32),
);
});
@@ -230,13 +235,17 @@ describe('Pattern', () => {
});
it('can Trig() structure', () => {
sameFirst(
slowcat(sequence(1, 2, 3, 4), 5, sequence(6, 7, 8, 9), 10).keep.trig(20, 30).early(2),
slowcat(sequence(1, 2, 3, 4), 5, sequence(6, 7, 8, 9), 10)
.keep.trig(20, 30)
.early(2),
sequence(6, 7, 6, 7),
);
});
it('can Trigzero() structure', () => {
sameFirst(
slowcat(sequence(1, 2, 3, 4), 5, sequence(6, 7, 8, 9), 10).keep.trigzero(20, 30).early(2),
slowcat(sequence(1, 2, 3, 4), 5, sequence(6, 7, 8, 9), 10)
.keep.trigzero(20, 30)
.early(2),
sequence(1, 2, 1, 2),
);
});
@@ -272,13 +281,17 @@ describe('Pattern', () => {
});
it('can Trig() structure', () => {
sameFirst(
slowcat(sequence(1, 2, 3, 4), 5, sequence(6, 7, 8, 9), 10).keepif.trig(false, true).early(2),
slowcat(sequence(1, 2, 3, 4), 5, sequence(6, 7, 8, 9), 10)
.keepif.trig(false, true)
.early(2),
sequence(silence, silence, 6, 7),
);
});
it('can Trigzero() structure', () => {
sameFirst(
slowcat(sequence(1, 2, 3, 4), 5, sequence(6, 7, 8, 9), 10).keepif.trigzero(false, true).early(2),
slowcat(sequence(1, 2, 3, 4), 5, sequence(6, 7, 8, 9), 10)
.keepif.trigzero(false, true)
.early(2),
sequence(silence, silence, 1, 2),
);
});
@@ -571,6 +584,18 @@ describe('Pattern', () => {
expect(sequence(1, 2, 3).firstCycle()).toStrictEqual(fastcat(1, 2, 3).firstCycle());
});
});
describe('palindrome()', () => {
it('Can create palindrome', () => {
expect(
fastcat('a', 'b', 'c')
.palindrome()
.fast(2)
.firstCycle()
.sort((a, b) => a.part.begin.sub(b.part.begin))
.map((a) => a.value),
).toStrictEqual(['a', 'b', 'c', 'c', 'b', 'a']);
});
});
describe('polyrhythm()', () => {
it('Can layer up cycles', () => {
expect(polyrhythm(['a', 'b'], ['c']).firstCycle()).toStrictEqual(
@@ -638,7 +663,11 @@ describe('Pattern', () => {
});
describe('struct()', () => {
it('Can restructure a discrete pattern', () => {
expect(sequence('a', 'b').struct(sequence(true, true, true)).firstCycle()).toStrictEqual([
expect(
sequence('a', 'b')
.struct(sequence(true, true, true))
.firstCycle(),
).toStrictEqual([
hap(ts(0, third), ts(0, third), 'a'),
hap(ts(third, twothirds), ts(third, 0.5), 'a'),
hap(ts(third, twothirds), ts(0.5, twothirds), 'b'),
@@ -669,7 +698,11 @@ describe('Pattern', () => {
});
describe('mask()', () => {
it('Can fragment a pattern', () => {
expect(sequence('a', 'b').mask(sequence(true, true, true)).firstCycle()).toStrictEqual([
expect(
sequence('a', 'b')
.mask(sequence(true, true, true))
.firstCycle(),
).toStrictEqual([
hap(ts(0, 0.5), ts(0, third), 'a'),
hap(ts(0, 0.5), ts(third, 0.5), 'a'),
hap(ts(0.5, 1), ts(0.5, twothirds), 'b'),
@@ -938,9 +971,11 @@ describe('Pattern', () => {
expect(stack(pure('a').mask(1, 0), pure('a').mask(0, 1)).defragmentHaps().firstCycle().length).toStrictEqual(1);
});
it('Doesnt merge two overlapping haps', () => {
expect(stack(pure('a').mask(1, 1, 0), pure('a').mask(0, 1)).defragmentHaps().firstCycle().length).toStrictEqual(
2,
);
expect(
stack(pure('a').mask(1, 1, 0), pure('a').mask(0, 1))
.defragmentHaps()
.firstCycle().length,
).toStrictEqual(2);
});
it('Doesnt merge two touching haps with different values', () => {
expect(stack(pure('a').mask(1, 0), pure('b').mask(0, 1)).defragmentHaps().firstCycle().length).toStrictEqual(2);
+49 -27
View File
@@ -1,11 +1,11 @@
import Fraction from './fraction.mjs';
/*
util.mjs - <short description TODO>
Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/strudel/blob/main/packages/core/util.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 { logger } from './logger.mjs';
// returns true if the given string is a note
export const isNoteWithOctave = (name) => /^[a-gA-G][#bs]*[0-9]$/.test(name);
export const isNote = (name) => /^[a-gA-G][#bsf]*[0-9]?$/.test(name);
@@ -86,6 +86,18 @@ export const midi2note = (n) => {
// modulo that works with negative numbers e.g. _mod(-1, 3) = 2. Works on numbers (rather than patterns of numbers, as @mod@ from pattern.mjs does)
export const _mod = (n, m) => ((n % m) + m) % m;
export function nanFallback(value, fallback) {
if (isNaN(Number(value))) {
logger(`"${value}" is not a number, falling back to ${fallback}`, 'warning');
return fallback;
}
return value;
}
// round to nearest int, negative numbers will output a subtracted index
export const getSoundIndex = (n, numSounds) => {
return _mod(Math.round(nanFallback(n ?? 0, 0)), numSounds);
};
export const getPlayableNoteValue = (hap) => {
let { value, context } = hap;
let note = value;
@@ -264,33 +276,43 @@ export const sol2note = (n, notation = 'letters') => {
notation === 'solfeggio'
? solfeggio /*check if its is any of the following*/
: notation === 'indian'
? indian
: notation === 'german'
? german
: notation === 'byzantine'
? byzantine
: notation === 'japanese'
? japanese
: english; /*if not use standard version*/
? indian
: notation === 'german'
? german
: notation === 'byzantine'
? byzantine
: notation === 'japanese'
? japanese
: english; /*if not use standard version*/
const note = pc[n % 12]; /*calculating the midi value to the note*/
const oct = Math.floor(n / 12) - 1;
return note + oct;
};
const fractionCharacters = {
w: Fraction(1),
h: Fraction(0.5),
q: Fraction(0.25),
e: Fraction(0.125),
s: Fraction(0.0625),
t: Fraction(1 / 3),
f: Fraction(0.2),
x: Fraction(1 / 6),
};
const validFractionCharacters = Object.keys(fractionCharacters);
export const char2fraction = (maybeChar) => {
if (typeof maybeChar === 'string' && validFractionCharacters.includes(maybeChar)) {
return fractionCharacters[maybeChar];
}
return maybeChar;
};
// code hashing helpers
export function unicodeToBase64(text) {
const utf8Bytes = new TextEncoder().encode(text);
const base64String = btoa(String.fromCharCode(...utf8Bytes));
return base64String;
}
export function base64ToUnicode(base64String) {
const utf8Bytes = new Uint8Array(
atob(base64String)
.split('')
.map((char) => char.charCodeAt(0)),
);
const decodedText = new TextDecoder().decode(utf8Bytes);
return decodedText;
}
export function code2hash(code) {
return encodeURIComponent(unicodeToBase64(code));
//return '#' + encodeURIComponent(btoa(code));
}
export function hash2code(hash) {
return base64ToUnicode(decodeURIComponent(hash));
//return atob(decodeURIComponent(codeParam || ''));
}
+1 -1
View File
@@ -9,7 +9,7 @@ export default defineConfig({
lib: {
entry: resolve(__dirname, 'index.mjs'),
formats: ['es', 'cjs'],
fileName: (ext) => ({ es: 'index.mjs', cjs: 'index.js' }[ext]),
fileName: (ext) => ({ es: 'index.mjs', cjs: 'index.js' })[ext],
},
rollupOptions: {
external: [...Object.keys(dependencies)],
+1 -1
View File
@@ -137,7 +137,7 @@ export async function loadOrc(url) {
export const csoundm = register('csoundm', (instrument, pat) => {
let p1 = instrument;
if (typeof instrument === 'string') {
p1 = `"{instrument}"`;
p1 = `"${instrument}"`;
}
init(); // not async to support csound inside other patterns + to be able to call pattern methods after it
return pat.onTrigger((tidal_time, hap) => {
+1 -1
View File
@@ -37,6 +37,6 @@
"@strudel.cycles/webaudio": "workspace:*"
},
"devDependencies": {
"vite": "^4.3.3"
"vite": "^5.0.10"
}
}
+1 -1
View File
@@ -9,7 +9,7 @@ export default defineConfig({
lib: {
entry: resolve(__dirname, 'index.mjs'),
formats: ['es', 'cjs'],
fileName: (ext) => ({ es: 'index.mjs', cjs: 'index.js' }[ext]),
fileName: (ext) => ({ es: 'index.mjs', cjs: 'index.js' })[ext],
},
rollupOptions: {
external: [...Object.keys(dependencies)],
+1 -1
View File
@@ -23,7 +23,7 @@
},
"dependencies": {
"@strudel.cycles/core": "workspace:*",
"@tauri-apps/api": "^1.4.0"
"@tauri-apps/api": "^1.5.3"
},
"homepage": "https://github.com/tidalcycles/strudel#readme"
}
+6
View File
@@ -12,6 +12,12 @@ await initHydra();
Then you can use hydra below!
### options
You can also pass options to the `initHydra` function. These can be used to set [hydra options](https://github.com/hydra-synth/hydra-synth#api) + these strudel specific options:
- `feedStrudel`: sends the strudel canvas to `s0`. The strudel canvas is used to draw `pianoroll`, `spiral`, `scope` etc..
## Usage via npm
```sh
+33 -8
View File
@@ -1,14 +1,39 @@
import { getDrawContext } from '@strudel.cycles/core';
export async function initHydra() {
let latestOptions;
function appendCanvas(c) {
const { canvas: testCanvas } = getDrawContext();
c.canvas.id = 'hydra-canvas';
c.canvas.style.position = 'fixed';
c.canvas.style.top = '0px';
testCanvas.after(c.canvas);
return testCanvas;
}
export async function initHydra(options = {}) {
// reset if options have changed since last init
if (latestOptions && JSON.stringify(latestOptions) !== JSON.stringify(options)) {
document.getElementById('hydra-canvas').remove();
}
latestOptions = options;
//load and init hydra
if (!document.getElementById('hydra-canvas')) {
const { canvas: testCanvas } = getDrawContext();
await import('https://unpkg.com/hydra-synth');
const hydraCanvas = testCanvas.cloneNode(true);
hydraCanvas.id = 'hydra-canvas';
testCanvas.after(hydraCanvas);
new Hydra({ canvas: hydraCanvas, detectAudio: false });
s0.init({ src: testCanvas });
console.log('reinit..');
const {
src = 'https://unpkg.com/hydra-synth',
feedStrudel = false,
...hydraConfig
} = { detectAudio: false, ...options };
await import(/* @vite-ignore */ src);
const hydra = new Hydra(hydraConfig);
if (feedStrudel) {
const { canvas } = getDrawContext();
canvas.style.display = 'none';
hydra.synth.s0.init({ src: canvas });
}
appendCanvas(hydra);
}
}
+1 -1
View File
@@ -38,6 +38,6 @@
},
"devDependencies": {
"pkg": "^5.8.1",
"vite": "^4.3.3"
"vite": "^5.0.10"
}
}
+1 -1
View File
@@ -9,7 +9,7 @@ export default defineConfig({
lib: {
entry: resolve(__dirname, 'hydra.mjs'),
formats: ['es', 'cjs'],
fileName: (ext) => ({ es: 'index.mjs', cjs: 'index.js' }[ext]),
fileName: (ext) => ({ es: 'index.mjs', cjs: 'index.js' })[ext],
},
rollupOptions: {
external: [...Object.keys(dependencies)],
+2 -2
View File
@@ -31,9 +31,9 @@
"dependencies": {
"@strudel.cycles/core": "workspace:*",
"@strudel.cycles/webaudio": "workspace:*",
"webmidi": "^3.1.5"
"webmidi": "^3.1.8"
},
"devDependencies": {
"vite": "^4.3.3"
"vite": "^5.0.10"
}
}
+1 -1
View File
@@ -9,7 +9,7 @@ export default defineConfig({
lib: {
entry: resolve(__dirname, 'index.mjs'),
formats: ['es', 'cjs'],
fileName: (ext) => ({ es: 'index.mjs', cjs: 'index.js' }[ext]),
fileName: (ext) => ({ es: 'index.mjs', cjs: 'index.js' })[ext],
},
rollupOptions: {
external: [...Object.keys(dependencies)],
+2 -2
View File
@@ -219,7 +219,7 @@ function peg$parse(input, options) {
var peg$r0 = /^[1-9]/;
var peg$r1 = /^[eE]/;
var peg$r2 = /^[0-9]/;
var peg$r3 = /^[ \n\r\t]/;
var peg$r3 = /^[ \n\r\t\xA0]/;
var peg$r4 = /^[0-9~]/;
var peg$r5 = /^[^\n]/;
var peg$r6 = /^[a-z\xB5\xDF-\xF6\xF8-\xFF\u0101\u0103\u0105\u0107\u0109\u010B\u010D\u010F\u0111\u0113\u0115\u0117\u0119\u011B\u011D\u011F\u0121\u0123\u0125\u0127\u0129\u012B\u012D\u012F\u0131\u0133\u0135\u0137-\u0138\u013A\u013C\u013E\u0140\u0142\u0144\u0146\u0148-\u0149\u014B\u014D\u014F\u0151\u0153\u0155\u0157\u0159\u015B\u015D\u015F\u0161\u0163\u0165\u0167\u0169\u016B\u016D\u016F\u0171\u0173\u0175\u0177\u017A\u017C\u017E-\u0180\u0183\u0185\u0188\u018C-\u018D\u0192\u0195\u0199-\u019B\u019E\u01A1\u01A3\u01A5\u01A8\u01AA-\u01AB\u01AD\u01B0\u01B4\u01B6\u01B9-\u01BA\u01BD-\u01BF\u01C6\u01C9\u01CC\u01CE\u01D0\u01D2\u01D4\u01D6\u01D8\u01DA\u01DC-\u01DD\u01DF\u01E1\u01E3\u01E5\u01E7\u01E9\u01EB\u01ED\u01EF-\u01F0\u01F3\u01F5\u01F9\u01FB\u01FD\u01FF\u0201\u0203\u0205\u0207\u0209\u020B\u020D\u020F\u0211\u0213\u0215\u0217\u0219\u021B\u021D\u021F\u0221\u0223\u0225\u0227\u0229\u022B\u022D\u022F\u0231\u0233-\u0239\u023C\u023F-\u0240\u0242\u0247\u0249\u024B\u024D\u024F-\u0293\u0295-\u02AF\u0371\u0373\u0377\u037B-\u037D\u0390\u03AC-\u03CE\u03D0-\u03D1\u03D5-\u03D7\u03D9\u03DB\u03DD\u03DF\u03E1\u03E3\u03E5\u03E7\u03E9\u03EB\u03ED\u03EF-\u03F3\u03F5\u03F8\u03FB-\u03FC\u0430-\u045F\u0461\u0463\u0465\u0467\u0469\u046B\u046D\u046F\u0471\u0473\u0475\u0477\u0479\u047B\u047D\u047F\u0481\u048B\u048D\u048F\u0491\u0493\u0495\u0497\u0499\u049B\u049D\u049F\u04A1\u04A3\u04A5\u04A7\u04A9\u04AB\u04AD\u04AF\u04B1\u04B3\u04B5\u04B7\u04B9\u04BB\u04BD\u04BF\u04C2\u04C4\u04C6\u04C8\u04CA\u04CC\u04CE-\u04CF\u04D1\u04D3\u04D5\u04D7\u04D9\u04DB\u04DD\u04DF\u04E1\u04E3\u04E5\u04E7\u04E9\u04EB\u04ED\u04EF\u04F1\u04F3\u04F5\u04F7\u04F9\u04FB\u04FD\u04FF\u0501\u0503\u0505\u0507\u0509\u050B\u050D\u050F\u0511\u0513\u0515\u0517\u0519\u051B\u051D\u051F\u0521\u0523\u0525\u0527\u0529\u052B\u052D\u052F\u0560-\u0588\u10D0-\u10FA\u10FD-\u10FF\u13F8-\u13FD\u1C80-\u1C88\u1D00-\u1D2B\u1D6B-\u1D77\u1D79-\u1D9A\u1E01\u1E03\u1E05\u1E07\u1E09\u1E0B\u1E0D\u1E0F\u1E11\u1E13\u1E15\u1E17\u1E19\u1E1B\u1E1D\u1E1F\u1E21\u1E23\u1E25\u1E27\u1E29\u1E2B\u1E2D\u1E2F\u1E31\u1E33\u1E35\u1E37\u1E39\u1E3B\u1E3D\u1E3F\u1E41\u1E43\u1E45\u1E47\u1E49\u1E4B\u1E4D\u1E4F\u1E51\u1E53\u1E55\u1E57\u1E59\u1E5B\u1E5D\u1E5F\u1E61\u1E63\u1E65\u1E67\u1E69\u1E6B\u1E6D\u1E6F\u1E71\u1E73\u1E75\u1E77\u1E79\u1E7B\u1E7D\u1E7F\u1E81\u1E83\u1E85\u1E87\u1E89\u1E8B\u1E8D\u1E8F\u1E91\u1E93\u1E95-\u1E9D\u1E9F\u1EA1\u1EA3\u1EA5\u1EA7\u1EA9\u1EAB\u1EAD\u1EAF\u1EB1\u1EB3\u1EB5\u1EB7\u1EB9\u1EBB\u1EBD\u1EBF\u1EC1\u1EC3\u1EC5\u1EC7\u1EC9\u1ECB\u1ECD\u1ECF\u1ED1\u1ED3\u1ED5\u1ED7\u1ED9\u1EDB\u1EDD\u1EDF\u1EE1\u1EE3\u1EE5\u1EE7\u1EE9\u1EEB\u1EED\u1EEF\u1EF1\u1EF3\u1EF5\u1EF7\u1EF9\u1EFB\u1EFD\u1EFF-\u1F07\u1F10-\u1F15\u1F20-\u1F27\u1F30-\u1F37\u1F40-\u1F45\u1F50-\u1F57\u1F60-\u1F67\u1F70-\u1F7D\u1F80-\u1F87\u1F90-\u1F97\u1FA0-\u1FA7\u1FB0-\u1FB4\u1FB6-\u1FB7\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FC7\u1FD0-\u1FD3\u1FD6-\u1FD7\u1FE0-\u1FE7\u1FF2-\u1FF4\u1FF6-\u1FF7\u210A\u210E-\u210F\u2113\u212F\u2134\u2139\u213C-\u213D\u2146-\u2149\u214E\u2184\u2C30-\u2C5E\u2C61\u2C65-\u2C66\u2C68\u2C6A\u2C6C\u2C71\u2C73-\u2C74\u2C76-\u2C7B\u2C81\u2C83\u2C85\u2C87\u2C89\u2C8B\u2C8D\u2C8F\u2C91\u2C93\u2C95\u2C97\u2C99\u2C9B\u2C9D\u2C9F\u2CA1\u2CA3\u2CA5\u2CA7\u2CA9\u2CAB\u2CAD\u2CAF\u2CB1\u2CB3\u2CB5\u2CB7\u2CB9\u2CBB\u2CBD\u2CBF\u2CC1\u2CC3\u2CC5\u2CC7\u2CC9\u2CCB\u2CCD\u2CCF\u2CD1\u2CD3\u2CD5\u2CD7\u2CD9\u2CDB\u2CDD\u2CDF\u2CE1\u2CE3-\u2CE4\u2CEC\u2CEE\u2CF3\u2D00-\u2D25\u2D27\u2D2D\uA641\uA643\uA645\uA647\uA649\uA64B\uA64D\uA64F\uA651\uA653\uA655\uA657\uA659\uA65B\uA65D\uA65F\uA661\uA663\uA665\uA667\uA669\uA66B\uA66D\uA681\uA683\uA685\uA687\uA689\uA68B\uA68D\uA68F\uA691\uA693\uA695\uA697\uA699\uA69B\uA723\uA725\uA727\uA729\uA72B\uA72D\uA72F-\uA731\uA733\uA735\uA737\uA739\uA73B\uA73D\uA73F\uA741\uA743\uA745\uA747\uA749\uA74B\uA74D\uA74F\uA751\uA753\uA755\uA757\uA759\uA75B\uA75D\uA75F\uA761\uA763\uA765\uA767\uA769\uA76B\uA76D\uA76F\uA771-\uA778\uA77A\uA77C\uA77F\uA781\uA783\uA785\uA787\uA78C\uA78E\uA791\uA793-\uA795\uA797\uA799\uA79B\uA79D\uA79F\uA7A1\uA7A3\uA7A5\uA7A7\uA7A9\uA7AF\uA7B5\uA7B7\uA7B9\uA7FA\uAB30-\uAB5A\uAB60-\uAB65\uAB70-\uABBF\uFB00-\uFB06\uFB13-\uFB17\uFF41-\uFF5A]/;
@@ -238,7 +238,7 @@ function peg$parse(input, options) {
var peg$e6 = peg$literalExpectation("0", false);
var peg$e7 = peg$classExpectation([["0", "9"]], false, false);
var peg$e8 = peg$otherExpectation("whitespace");
var peg$e9 = peg$classExpectation([" ", "\n", "\r", "\t"], false, false);
var peg$e9 = peg$classExpectation([" ", "\n", "\r", "\t", "\xA0"], false, false);
var peg$e10 = peg$literalExpectation(",", false);
var peg$e11 = peg$literalExpectation("|", false);
var peg$e12 = peg$literalExpectation("\"", false);
+1 -1
View File
@@ -95,7 +95,7 @@ DIGIT = [0-9]
// ------------------ delimiters ---------------------------
ws "whitespace" = [ \n\r\t]*
ws "whitespace" = [ \n\r\t\u00A0]*
comma = ws "," ws
pipe = ws "|" ws
quote = '"' / "'"
+14 -6
View File
@@ -42,7 +42,7 @@ const applyOptions = (parent, enter) => (pat, i) => {
}
case 'tail': {
const friend = enter(op.arguments_.element);
pat = pat.fmap((a) => (b) => Array.isArray(a) ? [...a, b] : [a, b]).appLeft(friend);
pat = pat.fmap((a) => (b) => (Array.isArray(a) ? [...a, b] : [a, b])).appLeft(friend);
break;
}
case 'range': {
@@ -160,11 +160,19 @@ export const getLeafLocation = (code, leaf, globalOffset = 0) => {
};
// takes quoted mini string, returns ast
export const mini2ast = (code) => krill.parse(code);
export const mini2ast = (code, start, userCode) => {
try {
return krill.parse(code);
} catch (error) {
const region = [error.location.start.offset + start, error.location.end.offset + start];
const line = userCode.slice(0, region[0]).split('\n').length;
throw new Error(`[mini] parse error at line ${line}: ${error.message}`);
}
};
// takes quoted mini string, returns all nodes that are leaves
export const getLeaves = (code) => {
const ast = mini2ast(code);
export const getLeaves = (code, start, userCode) => {
const ast = mini2ast(code, start, userCode);
let leaves = [];
patternifyAST(
ast,
@@ -180,8 +188,8 @@ export const getLeaves = (code) => {
};
// takes quoted mini string, returns locations [fromCol,toCol] of all leaf nodes
export const getLeafLocations = (code, offset = 0) => {
return getLeaves(code).map((l) => getLeafLocation(code, l, offset));
export const getLeafLocations = (code, start = 0, userCode) => {
return getLeaves(code, start, userCode).map((l) => getLeafLocation(code, l, start));
};
// mini notation only (wraps in "")

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