Compare commits

...

156 Commits

Author SHA1 Message Date
Karol Libardi 3daacba2c3 Added ability to mute a synth/sound after it was triggered 2024-03-04 10:27:55 +01:00
Felix Roos fa7c16dbe7 Merge pull request #959 from tidalcycles/euclid-inverted
feat: can now invert euclid pulses with negative numbers
2024-02-29 15:33:12 +01:00
Felix Roos 6a43eb6755 Merge pull request #950 from eefano/pickr-pickrmod
pickOut(), pickRestart(), pickReset()
2024-02-29 15:31:23 +01:00
Felix Roos c524ebc7f3 fix: add name tags 2024-02-29 15:25:31 +01:00
Felix Roos 0deb905cdf rename pick*Outer > pickOut
+ add pick*Squeeze alias to inhabit*
2024-02-29 15:21:44 +01:00
Felix Roos 4cadbcbbdf Merge pull request #965 from tidalcycles/legato-fix
remove legacy legato + duration implementations
2024-02-29 15:12:45 +01:00
Felix Roos 96eef75f74 fix: end / begin. sampler now needs clip to choose duration... 2024-02-29 10:33:27 +01:00
Felix Roos 51d9e02e07 Merge pull request #966 from eefano/transpose-fix
fix for transpose(): preserve hap value object structure
2024-02-29 08:47:53 +01:00
Felix Roos c4c21c4f0c Merge pull request #968 from tidalcycles/log-debounce
add debounce to logger
2024-02-29 08:32:00 +01:00
Felix Roos fcb247b62a add debounce to logger 2024-02-29 04:25:00 +01:00
Felix Roos 7556da7839 snapshot 2024-02-29 04:06:15 +01:00
Felix Roos eb93a6c149 refactor: duration is now a regular control 2024-02-29 04:05:05 +01:00
eefano b870ab204a fix for transpose(): preserve hap value object structure 2024-02-29 00:13:08 +01:00
Felix Roos f779e81993 remove old snapshots 2024-02-28 18:52:08 +01:00
Felix Roos 5c71bb95a0 remove legacy legato
+ make legato a synonym of clip
2024-02-28 18:51:07 +01:00
Felix Roos 1525992d73 Merge pull request #962 from tidalcycles/control-refactoring
controls refactoring: simplify exports
2024-02-28 18:43:51 +01:00
Felix Roos abe25be05e fix examples 2024-02-28 00:36:59 +01:00
Felix Roos 664a19a5c4 refactor: remove now redundant controls imports 2024-02-28 00:36:44 +01:00
Felix Roos 8efadd2547 refactor: controls are now exported directly 2024-02-28 00:34:39 +01:00
eefano ed1f3e1e8c Merge remote-tracking branch 'upstream/main' into pickr-pickrmod 2024-02-26 01:15:27 +01:00
eefano 4aaf9eaed4 mux operator, muxers.mjs with pick variants
prettier

prettier2

clamp?

Revert "clamp?"

This reverts commit 83a3b9123aece9f0e366c83d97f99e8b8d20db54.

Revert "prettier2"

This reverts commit de13871a8be4a450b79a4d9e65b9d1982485577e.

Revert "prettier"

This reverts commit 1550bf49527f591d05fa2766ca66ccb16e142011.

Revert "mux operator, muxers.mjs with pick variants"

This reverts commit af062f93c23ded4440f968186bfc69851d8aec67.
2024-02-26 01:11:56 +01:00
Felix Roos 0416e4d212 Merge pull request #960 from tidalcycles/reset-global-fx
fix: reset global fx on pattern change
2024-02-25 19:17:00 +01:00
Felix Roos b9247db001 fix: reset global fx on pattern change 2024-02-25 15:09:15 +01:00
eefano 74ef157fa1 Merge remote-tracking branch 'upstream/main' into pickr-pickrmod 2024-02-25 14:52:12 +01:00
Felix Roos d4724b0e12 feat: can now invert euclid pulses with negative numbers 2024-02-25 14:43:02 +01:00
eefano dbfdbcf2b7 typo 2024-02-25 14:39:38 +01:00
eefano 0986b14508 added pickOuter, pickmodOuter 2024-02-25 14:31:07 +01:00
Felix Roos 325152c782 Merge pull request #936 from Enelg52/midi-issues
fix midi issue on firefox and added quote error
2024-02-25 14:15:30 +01:00
Felix Roos c136d12194 Merge pull request #956 from eefano/bracketMatching
'Enable Bracket Matching' option in Codemirror
2024-02-25 13:19:46 +01:00
Felix Roos 7557952155 format 2024-02-25 13:17:52 +01:00
Felix Roos b0b41e6718 revert unintended change 2024-02-25 13:17:29 +01:00
Felix Roos 22e3067b8c set bracket matching true by default 2024-02-25 13:16:53 +01:00
Felix Roos e9ab01ab06 Merge pull request #957 from tidalcycles/web-iife
fix script importable packages (web + repl)
2024-02-23 14:37:45 +01:00
Felix Roos 058e554b0f update example 2024-02-23 14:33:07 +01:00
Felix Roos 6d9327aebc adapt repl package similar to web package 2024-02-23 14:30:29 +01:00
Felix Roos c23d7850c7 simplify web example 2024-02-23 14:30:07 +01:00
Felix Roos 2854f0cf34 try different main / module setting + bump 2024-02-23 14:28:43 +01:00
Felix Roos a89e87ca2e add web package example using script tag 2024-02-23 14:21:59 +01:00
Felix Roos 5419ca431b bump web package version 2024-02-23 14:17:11 +01:00
Felix Roos e8b674badd add iife build to web package 2024-02-23 14:16:54 +01:00
eefano 8af40a9c61 added <> for mininotation 2024-02-22 23:35:00 +01:00
eefano f54aa3d823 'Enable Bracket Matching' option in Codemirror 2024-02-22 22:04:57 +01:00
Jade (Rose) Rowland 5e91287e3e Merge pull request #954 from daslyfe/midi_duration_fix
account for cps in midi time duration
2024-02-21 10:17:36 -05:00
Felix Roos 4e3d4a7060 hotfix: some samples were broken 2024-02-21 15:35:59 +01:00
Felix Roos 3d80da9816 Merge pull request #955 from tidalcycles/auto-await-samples
Auto await samples
2024-02-21 10:27:12 +01:00
Felix Roos bf63b716af refactor: remove await before samples calls in docs etc..
+ remove /master from dirt-samples calls (there is now a main branch)
+ remove /main endings from some places
2024-02-21 10:17:45 +01:00
Felix Roos 38d389b838 add test 2024-02-21 09:50:21 +01:00
Felix Roos ebdca32c80 automatically add await to samples call 2024-02-21 09:48:26 +01:00
Jade (Rose) Rowland c06a3710c1 fixed 2024-02-19 23:43:35 -05:00
eefano d5e67fe13f Update signal.mjs
typo
2024-02-18 19:01:05 +01:00
eefano 00c7da45c0 Update signal.mjs
now pickRestart() and pickReset()
2024-02-18 18:55:05 +01:00
eefano 3347817350 Update signal.mjs
using trigZeroJoin in pickr() instead
2024-02-18 18:41:06 +01:00
eefano 88ece92a78 Update signal.mjs
Make pickr() handle dictionaries also
2024-02-18 16:40:27 +01:00
eefano 03a73fb95e Update signal.mjs
Fixed pickr() description
2024-02-18 16:21:50 +01:00
eefano 23c55c9636 Update signal.mjs
prettier fix
2024-02-18 16:03:01 +01:00
eefano 12c6f4e95f Update signal.mjs
added pickr() and pickrmod()
2024-02-18 15:30:24 +01:00
Felix Roos a468ddb85b Merge pull request #945 from tidalcycles/cjs-fixes
remove cjs builds
2024-02-08 13:16:15 +01:00
Felix Roos 051bdaccc7 Publish
- @strudel/codemirror@1.0.1
 - @strudel/core@1.0.1
 - @strudel/csound@1.0.1
 - @strudel/hydra@1.0.1
 - @strudel/midi@1.0.1
 - @strudel/mini@1.0.1
 - @strudel/osc@1.0.1
 - @strudel/repl@1.0.1
 - @strudel/serial@1.0.1
 - @strudel/soundfonts@1.0.1
 - superdough@1.0.1
 - @strudel/tonal@1.0.1
 - @strudel/transpiler@1.0.1
 - @strudel/web@1.0.1
 - @strudel/webaudio@1.0.1
 - @strudel/xen@1.0.1
2024-02-08 13:04:25 +01:00
Felix Roos 5dc04d9574 make sure to overwrite main field 2024-02-08 12:36:04 +01:00
Felix Roos 60129413d3 remove cjs builds
+ add type module everywhere
2024-02-08 12:19:11 +01:00
Felix Roos d2bf9370ea Update README.md
add link to usage example for repl package
2024-02-04 23:24:43 +01:00
Felix Roos fd11aa5eec Merge pull request #941 from mhetrick/patch-1
Minor documentation error: Update first-sounds.mdx
2024-02-04 23:15:37 +01:00
Michael Hetrick fb7c2b61d3 Update first-sounds.mdx
Update the tutorial text to reflect that the default cpm setting has changed from 60 to 30.
2024-01-26 07:13:09 -08:00
Felix Roos dd64bf0fc9 Merge pull request #938 from tidalcycles/pianoroll-sorting
fix: pianoroll sorting
2024-01-24 16:48:57 +01:00
Felix Roos 434eb3b256 fix: pianoroll sorting 2024-01-24 16:40:26 +01:00
Enelg52 6da98a5f31 fix midi issue on firefox and quote error 2024-01-23 16:47:46 +01:00
Felix Roos 8fef75385a Update README.md 2024-01-23 01:35:28 +01:00
Felix Roos c66b9219a8 Update README.md 2024-01-23 01:31:11 +01:00
Felix Roos 2a1f8823e8 Merge pull request #935 from tidalcycles/v1-release-notes
V1 release notes
2024-01-23 00:04:03 +01:00
Felix Roos 3da65f90e3 shorter titles for release notes 2024-01-22 23:55:57 +01:00
Felix Roos a36cc24087 v1 release notes 2024-01-22 23:55:49 +01:00
Felix Roos 1829d8cadf Publish
- @strudel/codemirror@1.0.0
 - @strudel/core@1.0.0
 - @strudel/csound@1.0.0
 - @strudel/embed@1.0.0
 - @strudel/hydra@1.0.0
 - @strudel/midi@1.0.0
 - @strudel/mini@1.0.0
 - @strudel/osc@1.0.0
 - @strudel/repl@1.0.0
 - @strudel/serial@1.0.0
 - @strudel/soundfonts@1.0.0
 - superdough@1.0.0
 - @strudel/tonal@1.0.0
 - @strudel/transpiler@1.0.0
 - @strudel/web@1.0.0
 - @strudel/webaudio@1.0.0
 - @strudel/xen@1.0.0
2024-01-22 22:46:35 +01:00
Felix Roos 02efc090ef hotfix: examples were still 1 cps based 2024-01-22 20:46:10 +01:00
Felix Roos b184348f55 hotfix: youtube embed styles were lost on prod 2024-01-22 20:38:34 +01:00
Felix Roos 06743cb072 Merge pull request #929 from tidalcycles/2-years
2 years blog post
2024-01-22 20:20:52 +01:00
Felix Roos 6fa66fb1d0 finish year 2 post 2024-01-22 20:19:01 +01:00
Felix Roos 23c0ab9648 offset blog post anchor links 2024-01-22 20:18:51 +01:00
Alex McLean 6422047cff make 0.5hz cps the default (#931)
* 0.5 default cps

* 1 -> 0.5 cps defaults

* start moving examples to 2Hz

* more 2Hz doc edits

* small tweaks

* format

* adapt cycles page

* adapt pitch page

* tonal page

* accumulation

* synth page

* adapt conditional-modifiers

* audio effects page

* adapt signals doc

* fix: errors for signals

* adapt signals page

* start time modifiers

* adapt time modifiers

* adapt factories

* hydra + pattern intro

* adapt mini notation page

* start recipes

* adapt recipes page

* use code_v1 table

* delete old dbdump + add new csv based tool

* fix: tests

* fix: cpm

* shuffle featured patterns

* fix: snapshot

---------

Co-authored-by: Felix Roos <flix91@gmail.com>
2024-01-22 19:02:34 +00:00
Jade (Rose) Rowland 46c2c72637 Merge pull request #934 from daslyfe/patterntab_fix
Fix pattern tab not showing patterns without created date
2024-01-21 18:52:01 -05:00
Jade (Rose) Rowland c088590244 remove console statement 2024-01-21 18:51:06 -05:00
Jade (Rose) Rowland 192ba69682 fixed if statement 2024-01-21 18:49:06 -05:00
Jade (Rose) Rowland 2cc428e968 Merge pull request #897 from daslyfe/pattern_selection
Add useful pattern selection behavior for performing.
2024-01-21 14:46:27 -05:00
Jade (Rose) Rowland 15c451a6d9 default pattern filtername 2024-01-21 14:35:17 -05:00
Jade (Rose) Rowland b46e8c07be reset/autoplay when window PARENT does not exist or does not contain oodles 2024-01-21 12:44:14 -05:00
Jade (Rose) Rowland 961e931c31 autoplay and reset cps on default 2024-01-21 12:12:33 -05:00
Jade (Rose) Rowland b1d49552ae title order 2024-01-21 11:27:48 -05:00
Jade (Rose) Rowland d31d154841 fix oven imports 2024-01-21 11:24:58 -05:00
Jade (Rose) Rowland d03892bbcf Merge branch 'tidalcycles:main' into pattern_selection 2024-01-21 11:19:06 -05:00
Felix Roos 0cf0c1c585 Merge pull request #933 from tidalcycles/refactor-cps-functions
Refactor cps functions
2024-01-21 01:30:27 +01:00
Felix Roos 0695b4bee9 clean + comment 2024-01-21 01:29:31 +01:00
Felix Roos 0cfec3a7bd show repls in community bakery 2024-01-21 01:21:21 +01:00
Felix Roos 6224cd40d4 mini repl max height option 2024-01-21 01:21:13 +01:00
Felix Roos 18ae82174d fix: "can't redefine non-configurable property" 2024-01-21 01:13:32 +01:00
Felix Roos aa1b9d11dc inject scheduler into Pattern methods right before eval 2024-01-21 00:59:31 +01:00
Felix Roos 0a82471112 implement loopAt with pat state 2024-01-21 00:45:40 +01:00
Felix Roos 8052e10f0c implement fit with pat state 2024-01-21 00:39:26 +01:00
Jade (Rose) Rowland 1146f6ba52 fix annoying div 2024-01-20 18:14:42 -05:00
Alex McLean 738cea0025 Make splice cps-aware (#932)
* make splice cps-aware

* format

* copypaste fix
2024-01-20 22:47:31 +00:00
Jade (Rose) Rowland 6c28398d20 fix scroll 2024-01-20 14:25:42 -05:00
Jade (Rose) Rowland 4fe75143d0 cleanup 2024-01-20 14:10:46 -05:00
Jade (Rose) Rowland 3cd59cdea5 cleaning up 2024-01-20 14:02:48 -05:00
Jade (Rose) Rowland 9dca7eb878 add pattern filter behaves like sounds tab 2024-01-20 13:59:32 -05:00
Jade (Rose) Rowland b45fd5ce3a fix invalid date 2024-01-20 13:00:33 -05:00
Felix Roos 229b1470f7 fix: localhost links 2024-01-20 10:59:34 +01:00
Jade (Rose) Rowland 0a349e1707 avoid json errors in firefox 2024-01-19 23:04:41 -05:00
Felix Roos d176e9ccf0 add stats 2024-01-19 21:51:52 +01:00
Felix Roos 710c3acb5d more writing 2024-01-19 21:21:33 +01:00
Felix Roos a7f5d0e0c1 Merge pull request #923 from tidalcycles/feed
community bakery
2024-01-19 18:50:57 +01:00
Felix Roos 9928526dda Merge pull request #924 from geikha/main
add pickF and pickmodF
2024-01-19 18:49:53 +01:00
Renzo Torr- 2f8111be0e update the .snap file 2024-01-19 14:47:03 -03:00
Renzo Torr- 68113937ff code format 2024-01-19 14:06:24 -03:00
Felix Roos c598fa026b write more 2024-01-19 15:49:16 +01:00
Alex McLean 491c99c348 Mini-notation additions towards tidal compatibility (#926)
- `.` feet now work, e.g. `"a b c . d e . f"` is the same as `"[a b c] [d e] [f]"`
- `_` elongation now works, e.g. `"a _ _ b"` is the same as `"a@3 b"` (`"a @ @ b"` also works, like in tidal)
- standalone `!` now works, e.g. `"a ! ! b"` is the same as `"a!3 b"`

Reworks #47 and #49
Ref #30
2024-01-19 14:10:47 +00:00
Felix Roos d49aa8242a begin 2 year post 2024-01-19 13:50:28 +01:00
Renzo Torr- 9ffbc06cf9 add pickF and pickmodF
allows to pick functions via a pattern of numbers, to apply to another pattern
2024-01-19 02:45:54 -03:00
Jade (Rose) Rowland af52ac30ba prettier 2024-01-19 00:28:55 -05:00
Jade (Rose) Rowland 7aea1b5fc8 fixed imports 2024-01-19 00:27:09 -05:00
Jade (Rose) Rowland 500ddf3946 Merge branch 'main' into pattern_selection 2024-01-19 00:23:06 -05:00
Jade (Rose) Rowland 3431eaf309 cleaning up 2024-01-19 00:20:10 -05:00
Jade (Rose) Rowland 9a13c58583 cleaning up 2024-01-19 00:14:08 -05:00
Jade (Rose) Rowland 77a48e8351 persistent pattern data 2024-01-18 22:30:53 -05:00
Jade (Rose) Rowland 6cb156d876 cleaning up 2024-01-18 20:01:54 -05:00
Felix Roos d8677c6261 hotfix: rss links 2024-01-18 23:40:22 +01:00
Felix Roos 6ef9ca18e9 Merge pull request #919 from tidalcycles/blog-improvements
Blog improvements
2024-01-18 23:34:10 +01:00
Felix Roos 6667738dc9 more rename 2024-01-18 21:34:20 +01:00
Felix Roos 4f7415b0ec rename 2024-01-18 21:31:21 +01:00
Felix Roos c6f3a8b7c7 more text 2024-01-18 18:30:57 +01:00
Felix Roos 4229d673c9 basic community oven 2024-01-18 18:21:56 +01:00
Alex McLean 98b7859605 pick, pickmod, inhabit, inhabitmod (#921)
* the args for `pick` are now reversed as standard (old behaviour still supported to avoid breaking change)
* `pick` is also now a pattern method
* `pick` now also accepts a lookup table for pick-by-name as an alternative to pick-by-index from a list
* `inhabit` added with same behaviour as `pick`, except cycles from source patterns are squeezed into events of inhabited patterns
* Also some general doc tidying, sorry for the noise..
* There is also `pickmod` and `inhabitmod`, for wrapping indexes around rather than clamping them
2024-01-18 17:08:29 +00:00
Alex McLean a8db707440 Revert "pick now accepts lookup tables, with alternate cycle squeezing behaviour as new inhabit function" (#920) 2024-01-18 17:04:26 +00:00
Alex McLean 873a412179 pick now accepts lookup tables, with alternate cycle squeezing behaviour as new inhabit function (#918)
- the args for `pick` are now reversed (breaking change!)
- `pick` is also now a method on the inhabited pattern
- `pick` now also accepts a lookup table
- `inhabit` added with same behaviour as `pick`, except cycles from source patterns are squeezed into events of inhabited patterns
- Also some general doc tidying, sorry for the noise..
- There is now also `pickmod` and `inhabitmod`, for wrapping indexes around rather than clamping them
2024-01-18 16:45:39 +00:00
Felix Roos 0778b4809e format 2024-01-18 13:47:34 +01:00
Felix Roos f85fb35c66 minirepl multi tune support + add other getting-started examples (more to come) 2024-01-18 13:46:35 +01:00
Felix Roos 5ada0daf40 add rss link to top + fix typo 2024-01-18 13:01:06 +01:00
Felix Roos 2ed5f5aa65 blog style fixes 2024-01-18 12:50:35 +01:00
Felix Roos ae5e6e3ab8 add rss.xml 2024-01-18 12:49:22 +01:00
Felix Roos 9ba7b9b4d4 Merge pull request #917 from tidalcycles/deprecate-old-package-names
rename @strudel.cycles/* packages to @strudel/*
2024-01-18 10:30:08 +01:00
Felix Roos a45f1fba45 update versions in example html files 2024-01-18 10:25:24 +01:00
Felix Roos e6b47d2cf2 fix link 2024-01-18 10:04:53 +01:00
Felix Roos b04a262433 Publish
- @strudel/codemirror@0.11.0
 - @strudel/core@0.11.0
 - @strudel/csound@0.11.0
 - @strudel/embed@0.11.0
 - @strudel/hydra@0.11.0
 - @strudel/midi@0.11.0
 - @strudel/mini@0.11.0
 - @strudel/osc@0.11.0
 - @strudel/repl@0.11.0
 - @strudel/serial@0.11.0
 - @strudel/soundfonts@0.11.0
 - @strudel/tonal@0.11.0
 - @strudel/transpiler@0.11.0
 - @strudel/web@0.11.0
 - @strudel/webaudio@0.11.0
 - @strudel/xen@0.11.0
2024-01-18 09:59:57 +01:00
Felix Roos 96bafa7f0b the big rename: @strudel.cycles/* -> @strudel/* 2024-01-18 09:54:37 +01:00
Jade (Rose) Rowland 7adfe6f2df fixing edge cases... 2024-01-17 09:45:00 -05:00
Jade (Rose) Rowland 45df7bfea3 making things consistent 2024-01-16 00:45:00 -05:00
Jade (Rose) Rowland 106772e480 Merge branch 'main' into pattern_selection 2024-01-15 18:37:18 -05:00
Felix Roos e7e60d3ed8 Merge branch 'main' into pattern_selection 2024-01-14 00:18:57 +01:00
Jade (Rose) Rowland 78f6fdf169 reset for example patterns 2024-01-08 10:49:26 -05:00
Jade (Rose) Rowland e01c6dc18d prettier 2024-01-08 00:25:58 -05:00
Jade (Rose) Rowland 51dd408733 all working 2024-01-08 00:15:07 -05:00
Jade (Rose) Rowland 1ff7548b52 refactoring 2024-01-07 22:48:07 -05:00
Jade (Rose) Rowland f7fe44063a handle deletion cases 2024-01-07 00:58:03 -05:00
Jade (Rose) Rowland 0355c358b8 fixed create and duplicate behavior 2024-01-06 23:49:37 -05:00
Jade (Rose) Rowland 4e0a60db00 example code logic 2024-01-06 22:10:14 -05:00
Jade (Rose) Rowland 3604d3940c mutate viewing pattern 2024-01-06 17:53:51 -05:00
Jade (Rose) Rowland 02b9bd82cf highlight viewing pattern 2024-01-06 17:33:24 -05:00
Jade (Rose) Rowland 1bc86c359e state 2024-01-03 01:04:39 -05:00
Jade (Rose) Rowland 1e2c26123b working 2024-01-03 00:35:47 -05:00
206 changed files with 7929 additions and 5098 deletions
+1 -1
View File
@@ -114,7 +114,7 @@ You can run the same check with `pnpm check`
## Package Workflow
The project is split into multiple [packages](https://github.com/tidalcycles/strudel/tree/main/packages) with independent versioning.
When you run `pnpm i` on the root folder, [pnpm workspaces](https://pnpm.io/workspaces) will install all dependencies of all subpackages. This will allow any js file to import `@strudel.cycles/<package-name>` to get the local version,
When you run `pnpm i` on the root folder, [pnpm workspaces](https://pnpm.io/workspaces) will install all dependencies of all subpackages. This will allow any js file to import `@strudel/<package-name>` to get the local version,
allowing to develop multiple packages at the same time.
## Package Publishing
+2 -1
View File
@@ -2,12 +2,13 @@
[![Strudel test status](https://github.com/tidalcycles/strudel/actions/workflows/test.yml/badge.svg)](https://github.com/tidalcycles/strudel/actions)
An experiment in making a [Tidal](https://github.com/tidalcycles/tidal/) using web technologies. This software is slowly stabilising, but please continue to tread carefully.
An experiment in making a [Tidal](https://github.com/tidalcycles/tidal/) using web technologies. This software is a bit more stable now, but please continue to tread carefully.
- Try it here: <https://strudel.cc>
- Docs: <https://strudel.cc/learn>
- Technical Blog Post: <https://loophole-letters.vercel.app/strudel>
- 1 Year of Strudel Blog Post: <https://loophole-letters.vercel.app/strudel1year>
- 2 Years of Strudel Blog Post: <https://strudel.cc/blog/#year-2>
## Running Locally
+1 -1
View File
@@ -7,7 +7,7 @@
/>
<div id="output"></div>
<script type="module">
const strudel = await import('https://cdn.skypack.dev/@strudel.cycles/core@0.6.8');
const strudel = await import('https://cdn.skypack.dev/@strudel/core@0.6.8');
Object.assign(window, strudel); // assign all strudel functions to global scope to use with eval
const input = document.getElementById('text');
const getEvents = () => {
+1 -1
View File
@@ -8,7 +8,7 @@
/>
<canvas id="canvas"></canvas>
<script type="module">
const strudel = await import('https://cdn.skypack.dev/@strudel.cycles/core@0.6.8');
const strudel = await import('https://cdn.skypack.dev/@strudel/core@0.6.8');
// this adds all strudel functions to the global scope, to be used by eval
Object.assign(window, strudel);
// setup elements
@@ -6,7 +6,7 @@
<script type="module">
import { initStrudel } from 'https://cdn.skypack.dev/@strudel/web@0.8.2';
initStrudel({
prebake: () => samples('github:tidalcycles/Dirt-Samples/master'),
prebake: () => samples('github:tidalcycles/dirt-samples'),
});
const click = (id, action) => document.getElementById(id).addEventListener('click', action);
click('a', () => evaluate(`s('bd,jvbass(3,8)').jux(rev)`));
+20 -13
View File
@@ -16,38 +16,45 @@
</div>
<div id="output"></div>
<script type="module">
import { controls, repl, evalScope } from 'https://cdn.skypack.dev/@strudel.cycles/core@0.6.8';
import { mini } from 'https://cdn.skypack.dev/@strudel.cycles/mini@0.6.0';
import { transpiler } from 'https://cdn.skypack.dev/@strudel.cycles/transpiler@0.6.0';
// TODO: refactor to use newer version without controls import
import { controls, repl, evalScope } from 'https://cdn.skypack.dev/@strudel/core@0.11.0';
import { mini } from 'https://cdn.skypack.dev/@strudel/mini@0.11.0';
import { transpiler } from 'https://cdn.skypack.dev/@strudel/transpiler@0.11.0';
import {
getAudioContext,
webaudioOutput,
initAudioOnFirstClick,
} from 'https://cdn.skypack.dev/@strudel.cycles/webaudio@0.6.0';
registerSynthSounds,
} from 'https://cdn.skypack.dev/@strudel/webaudio@0.11.0';
initAudioOnFirstClick();
const ctx = getAudioContext();
const input = document.getElementById('text');
input.innerHTML = getTune();
evalScope(
const loadModules = evalScope(
controls,
import('https://cdn.skypack.dev/@strudel.cycles/core@0.6.8'),
import('https://cdn.skypack.dev/@strudel.cycles/mini@0.6.0'),
import('https://cdn.skypack.dev/@strudel.cycles/tonal@0.6.0'),
import('https://cdn.skypack.dev/@strudel.cycles/webaudio@0.6.0'),
import('https://cdn.skypack.dev/@strudel/core@0.11.0'),
import('https://cdn.skypack.dev/@strudel/mini@0.11.0'),
import('https://cdn.skypack.dev/@strudel/tonal@0.11.0'),
import('https://cdn.skypack.dev/@strudel/webaudio@0.11.0'),
);
const initAudio = Promise.all([initAudioOnFirstClick(), registerSynthSounds()]);
const { evaluate } = repl({
defaultOutput: webaudioOutput,
getTime: () => ctx.currentTime,
transpiler,
});
document.getElementById('start').addEventListener('click', () => evaluate(input.value));
document.getElementById('start').addEventListener('click', async () => {
await loadModules;
await initAudio;
evaluate(input.value);
});
function getTune() {
return `await samples('github:tidalcycles/Dirt-Samples/master')
return `samples('github:tidalcycles/dirt-samples')
setcps(1);
stack(
// amen
n("0 1 2 3 4 5 6 7")
+1 -1
View File
@@ -1,4 +1,4 @@
<script src="https://unpkg.com/@strudel.cycles/embed@latest"></script>
<script src="https://unpkg.com/@strudel/embed@0.11.0"></script>
<!-- <script src="./embed.js"></script> -->
<strudel-repl>
<!--
@@ -1,4 +1,4 @@
<script src="https://unpkg.com/@strudel/repl@0.9.4"></script>
<script src="https://unpkg.com/@strudel/repl@1.0.2"></script>
<strudel-editor>
<!--
// @date 23-08-15
+8
View File
@@ -0,0 +1,8 @@
<script src="https://unpkg.com/@strudel/web@1.0.3"></script>
<button id="play">PLAY</button>
<script>
initStrudel({
prebake: () => samples('github:tidalcycles/dirt-samples'),
});
document.getElementById('play').addEventListener('click', () => s('bd sd').play());
</script>
+9 -10
View File
@@ -1,11 +1,11 @@
import { StrudelMirror } from '@strudel/codemirror';
import { funk42 } from './tunes';
import { drawPianoroll, evalScope, controls } from '@strudel.cycles/core';
import { drawPianoroll, evalScope } from '@strudel/core';
import './style.css';
import { initAudioOnFirstClick } from '@strudel.cycles/webaudio';
import { transpiler } from '@strudel.cycles/transpiler';
import { getAudioContext, webaudioOutput, registerSynthSounds } from '@strudel.cycles/webaudio';
import { registerSoundfonts } from '@strudel.cycles/soundfonts';
import { initAudioOnFirstClick } from '@strudel/webaudio';
import { transpiler } from '@strudel/transpiler';
import { getAudioContext, webaudioOutput, registerSynthSounds } from '@strudel/webaudio';
import { registerSoundfonts } from '@strudel/soundfonts';
// init canvas
const canvas = document.getElementById('roll');
@@ -25,11 +25,10 @@ const editor = new StrudelMirror({
prebake: async () => {
initAudioOnFirstClick(); // needed to make the browser happy (don't await this here..)
const loadModules = evalScope(
controls,
import('@strudel.cycles/core'),
import('@strudel.cycles/mini'),
import('@strudel.cycles/tonal'),
import('@strudel.cycles/webaudio'),
import('@strudel/core'),
import('@strudel/mini'),
import('@strudel/tonal'),
import('@strudel/webaudio'),
);
await Promise.all([loadModules, registerSynthSounds(), registerSoundfonts()]);
},
+6 -6
View File
@@ -13,11 +13,11 @@
},
"dependencies": {
"@strudel/codemirror": "workspace:*",
"@strudel.cycles/core": "workspace:*",
"@strudel.cycles/mini": "workspace:*",
"@strudel.cycles/soundfonts": "workspace:*",
"@strudel.cycles/tonal": "workspace:*",
"@strudel.cycles/transpiler": "workspace:*",
"@strudel.cycles/webaudio": "workspace:*"
"@strudel/core": "workspace:*",
"@strudel/mini": "workspace:*",
"@strudel/soundfonts": "workspace:*",
"@strudel/tonal": "workspace:*",
"@strudel/transpiler": "workspace:*",
"@strudel/webaudio": "workspace:*"
}
}
+6 -6
View File
@@ -1,6 +1,6 @@
export const bumpStreet = `// froos - "22 bump street", licensed with CC BY-NC-SA 4.0
await samples('github:felixroos/samples/main')
await samples('https://strudel.cc/tidal-drum-machines.json', 'github:ritchse/tidal-drum-machines/main/machines/')
samples('github:felixroos/samples')
samples('https://strudel.cc/tidal-drum-machines.json', 'github:ritchse/tidal-drum-machines/main/machines/')
"<[0,<6 7 9>,13,<17 20 22 26>]!2>/2"
// make it 22 edo
@@ -33,8 +33,8 @@ await samples('https://strudel.cc/tidal-drum-machines.json', 'github:ritchse/tid
export const trafficFlam = `// froos - "traffic flam", licensed with CC BY-NC-SA 4.0
await samples('github:felixroos/samples/main')
await samples('https://strudel.cc/tidal-drum-machines.json', 'github:ritchse/tidal-drum-machines/main/machines/')
samples('github:felixroos/samples')
samples('https://strudel.cc/tidal-drum-machines.json', 'github:ritchse/tidal-drum-machines/main/machines/')
addVoicings('hip', {
m11: ['2M 3m 4P 7m'],
@@ -69,8 +69,8 @@ export const funk42 = `// froos - how to funk in 42 lines of code
// adapted from "how to funk in two minutes" by marc rebillet https://www.youtube.com/watch?v=3vBwRfQbXkg
// thanks to peach for the transcription: https://www.youtube.com/watch?v=8eiPXvIgda4
await samples('github:felixroos/samples/main')
await samples('https://strudel.cc/tidal-drum-machines.json', 'github:ritchse/tidal-drum-machines/main/machines/')
samples('github:felixroos/samples')
samples('https://strudel.cc/tidal-drum-machines.json', 'github:ritchse/tidal-drum-machines/main/machines/')
setcps(.5)
+1 -1
View File
@@ -15,7 +15,7 @@
<script type="module">
import { initStrudel } from '@strudel/web';
initStrudel({
prebake: () => samples('github:tidalcycles/Dirt-Samples/master'),
prebake: () => samples('github:tidalcycles/dirt-samples'),
});
const click = (id, action) => document.getElementById(id).addEventListener('click', action);
+5 -10
View File
@@ -1,20 +1,15 @@
import { controls, repl, evalScope } from '@strudel.cycles/core';
import { getAudioContext, webaudioOutput, initAudioOnFirstClick } from '@strudel.cycles/webaudio';
import { transpiler } from '@strudel.cycles/transpiler';
import { repl, evalScope } from '@strudel/core';
import { getAudioContext, webaudioOutput, initAudioOnFirstClick, registerSynthSounds } from '@strudel/webaudio';
import { transpiler } from '@strudel/transpiler';
import tune from './tune.mjs';
const ctx = getAudioContext();
const input = document.getElementById('text');
input.innerHTML = tune;
initAudioOnFirstClick();
registerSynthSounds();
evalScope(
controls,
import('@strudel.cycles/core'),
import('@strudel.cycles/mini'),
import('@strudel.cycles/webaudio'),
import('@strudel.cycles/tonal'),
);
evalScope(import('@strudel/core'), import('@strudel/mini'), import('@strudel/webaudio'), import('@strudel/tonal'));
const { evaluate } = repl({
defaultOutput: webaudioOutput,
+5 -5
View File
@@ -13,10 +13,10 @@
"vite": "^5.0.10"
},
"dependencies": {
"@strudel.cycles/core": "workspace:*",
"@strudel.cycles/mini": "workspace:*",
"@strudel.cycles/transpiler": "workspace:*",
"@strudel.cycles/webaudio": "workspace:*",
"@strudel.cycles/tonal": "workspace:*"
"@strudel/core": "workspace:*",
"@strudel/mini": "workspace:*",
"@strudel/transpiler": "workspace:*",
"@strudel/webaudio": "workspace:*",
"@strudel/tonal": "workspace:*"
}
}
+2 -2
View File
@@ -1,5 +1,5 @@
export default `await samples('github:tidalcycles/Dirt-Samples/master')
export default `samples('github:tidalcycles/dirt-samples')
setcps(1)
stack(
// amen
n("0 1 2 3 4 5 6 7")
+1 -1
View File
@@ -12,7 +12,7 @@
const init = Promise.all([
initAudioOnFirstClick(),
samples('github:tidalcycles/Dirt-Samples/master'),
samples('github:tidalcycles/dirt-samples'),
registerSynthSounds(),
]);
+7 -7
View File
@@ -1,5 +1,5 @@
{
"name": "@strudel.cycles/monorepo",
"name": "@strudel/monorepo",
"version": "0.5.0",
"private": true,
"description": "Port of tidalcycles to javascript",
@@ -45,12 +45,12 @@
},
"homepage": "https://strudel.cc",
"dependencies": {
"@strudel.cycles/core": "workspace:*",
"@strudel.cycles/mini": "workspace:*",
"@strudel.cycles/tonal": "workspace:*",
"@strudel.cycles/transpiler": "workspace:*",
"@strudel.cycles/webaudio": "workspace:*",
"@strudel.cycles/xen": "workspace:*"
"@strudel/core": "workspace:*",
"@strudel/mini": "workspace:*",
"@strudel/tonal": "workspace:*",
"@strudel/transpiler": "workspace:*",
"@strudel/webaudio": "workspace:*",
"@strudel/xen": "workspace:*"
},
"devDependencies": {
"dependency-tree": "^10.0.9",
+1 -1
View File
@@ -1,5 +1,5 @@
# Packages
Each folder represents one of the @strudel.cycles/* packages [published to npm](https://www.npmjs.com/org/strudel.cycles).
Each folder represents one of the @strudel/* packages [published to npm](https://www.npmjs.com/org/strudel).
To understand how those pieces connect, refer to the [Technical Manual](https://github.com/tidalcycles/strudel/wiki/Technical-Manual) or the individual READMEs.
+7 -2
View File
@@ -2,7 +2,7 @@ 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 { defaultHighlightStyle, syntaxHighlighting, bracketMatching } from '@codemirror/language';
import { Compartment, EditorState, Prec } from '@codemirror/state';
import {
EditorView,
@@ -12,7 +12,7 @@ import {
lineNumbers,
drawSelection,
} from '@codemirror/view';
import { Pattern, Drawer, repl, cleanupDraw } from '@strudel.cycles/core';
import { Pattern, Drawer, repl, cleanupDraw } from '@strudel/core';
import { isAutoCompletionEnabled } from './autocomplete.mjs';
import { isTooltipEnabled } from './tooltip.mjs';
import { flash, isFlashEnabled } from './flash.mjs';
@@ -24,6 +24,7 @@ import { persistentAtom } from '@nanostores/persistent';
const extensions = {
isLineWrappingEnabled: (on) => (on ? EditorView.lineWrapping : []),
isBracketMatchingEnabled: (on) => (on ? bracketMatching({ brackets: '()[]{}<>' }) : []),
isLineNumbersDisplayed: (on) => (on ? lineNumbers() : []),
theme,
isAutoCompletionEnabled,
@@ -37,6 +38,7 @@ const compartments = Object.fromEntries(Object.keys(extensions).map((key) => [ke
export const defaultSettings = {
keybindings: 'codemirror',
isBracketMatchingEnabled: false,
isLineNumbersDisplayed: true,
isActiveLineHighlighted: false,
isAutoCompletionEnabled: false,
@@ -290,6 +292,9 @@ export class StrudelMirror {
setLineWrappingEnabled(enabled) {
this.reconfigureExtension('isLineWrappingEnabled', enabled);
}
setBracketMatchingEnabled(enabled) {
this.reconfigureExtension('isBracketMatchingEnabled', enabled);
}
setLineNumbersDisplayed(enabled) {
this.reconfigureExtension('isLineNumbersDisplayed', enabled);
}
+3 -1
View File
@@ -57,6 +57,9 @@ const visibleMiniLocations = StateField.define({
// this is why we need to find a way to update the existing decorations, showing the ones that have an active range
const haps = new Map();
for (let hap of e.value.haps) {
if (!hap.context?.locations || !hap.whole) {
continue;
}
for (let { start, end } of hap.context.locations) {
let id = `${start}:${end}`;
if (!haps.has(id) || haps.get(id).whole.begin.lt(hap.whole.begin)) {
@@ -64,7 +67,6 @@ const visibleMiniLocations = StateField.define({
}
}
}
visible = { atTime: e.value.atTime, haps };
}
}
+3 -4
View File
@@ -1,11 +1,10 @@
{
"name": "@strudel/codemirror",
"version": "0.10.0",
"version": "1.0.1",
"description": "Codemirror Extensions for Strudel",
"main": "index.mjs",
"publishConfig": {
"main": "dist/index.js",
"module": "dist/index.mjs"
"main": "dist/index.mjs"
},
"scripts": {
"build": "vite build",
@@ -45,7 +44,7 @@
"@replit/codemirror-emacs": "^6.0.1",
"@replit/codemirror-vim": "^6.1.0",
"@replit/codemirror-vscode-keymap": "^6.0.2",
"@strudel.cycles/core": "workspace:*",
"@strudel/core": "workspace:*",
"@uiw/codemirror-themes": "^4.21.21",
"@uiw/codemirror-themes-all": "^4.21.21",
"nanostores": "^0.9.5"
+1 -1
View File
@@ -1,4 +1,4 @@
import { ref, pure } from '@strudel.cycles/core';
import { ref, pure } from '@strudel/core';
import { WidgetType, ViewPlugin, Decoration } from '@codemirror/view';
import { StateEffect, StateField } from '@codemirror/state';
+2 -2
View File
@@ -8,8 +8,8 @@ export default defineConfig({
build: {
lib: {
entry: resolve(__dirname, 'index.mjs'),
formats: ['es', 'cjs'],
fileName: (ext) => ({ es: 'index.mjs', cjs: 'index.js' })[ext],
formats: ['es'],
fileName: (ext) => ({ es: 'index.mjs' })[ext],
},
rollupOptions: {
external: [...Object.keys(dependencies)],
+4 -6
View File
@@ -1,19 +1,17 @@
# @strudel.cycles/core
**DEPRECATION NOTE**: This package is old and won't get any updates! The newer version goes by the name of [@strudel/core](https://www.npmjs.com/package/@strudel/core).
# @strudel/core
This package contains the bare essence of strudel.
## Install
```sh
npm i @strudel.cycles/core --save
npm i @strudel/core --save
```
## Example
```js
import { sequence } from '@strudel.cycles/core';
import { sequence } from '@strudel/core';
const pattern = sequence('a', ['b', 'c']);
@@ -35,7 +33,7 @@ b: 3/2 - 7/4
c: 7/4 - 2
```
- [play with @strudel.cycles/core on codesandbox](https://codesandbox.io/s/strudel-core-test-forked-9ywhv7?file=/src/index.js).
- [play with @strudel/core on codesandbox](https://codesandbox.io/s/strudel-core-test-forked-9ywhv7?file=/src/index.js).
- [open color pattern example](https://raw.githack.com/tidalcycles/strudel/main/packages/core/examples/canvas.html)
- [open minimal repl example](https://raw.githack.com/tidalcycles/strudel/main/packages/core/examples/vanilla.html)
- [open minimal vite example](./examples/vite-vanilla-repl/)
+1 -3
View File
@@ -1,6 +1,4 @@
import { Pattern, getDrawContext, silence, register, pure } from './index.mjs';
import controls from './controls.mjs'; // do not import from index.mjs as it breaks for some reason..
const { createParams } = controls;
import { Pattern, getDrawContext, silence, register, pure, createParams } from './index.mjs';
let clearColor = '#22222210';
+1446 -1417
View File
File diff suppressed because it is too large Load Diff
+3 -3
View File
@@ -10,7 +10,7 @@ import { logger } from './logger.mjs';
export class Cyclist {
constructor({ interval, onTrigger, onToggle, onError, getTime, latency = 0.1 }) {
this.started = false;
this.cps = 1;
this.cps = 0.5;
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
@@ -41,7 +41,7 @@ export class Cyclist {
this.lastEnd = end;
// query the pattern for events
const haps = this.pattern.queryArc(begin, end);
const haps = this.pattern.queryArc(begin, end, { _cps: this.cps });
const tickdeadline = phase - time; // time left until the phase is a whole number
this.lastTick = time + tickdeadline;
@@ -96,7 +96,7 @@ export class Cyclist {
this.start();
}
}
setCps(cps = 1) {
setCps(cps = 0.5) {
if (this.cps === cps) {
return;
}
+1 -1
View File
@@ -134,7 +134,7 @@ export class Drawer {
this.lastFrame = phase;
this.visibleHaps = (this.visibleHaps || [])
// filter out haps that are too far in the past (think left edge of screen for pianoroll)
.filter((h) => h.whole.end >= phase - lookbehind - lookahead)
.filter((h) => h.endClipped >= phase - lookbehind - lookahead)
// add new haps with onset (think right edge bars scrolling in)
.concat(haps.filter((h) => h.hasOnset()));
const time = phase - lookahead;
+8 -2
View File
@@ -41,11 +41,17 @@ const _bjork = function (n, x) {
};
export const bjork = function (ons, steps) {
const inverted = ons < 0;
ons = Math.abs(ons);
const offs = steps - ons;
const x = Array(ons).fill([1]);
const y = Array(offs).fill([0]);
const result = _bjork([ons, offs], [x, y]);
return flatten(result[1][0]).concat(flatten(result[1][1]));
const p = flatten(result[1][0]).concat(flatten(result[1][1]));
if (inverted) {
return p.map((x) => (x === 0 ? 1 : 0));
}
return p;
};
/**
@@ -148,7 +154,7 @@ export const { euclidrot, euclidRot } = register(['euclidrot', 'euclidRot'], fun
* @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)
* note("c3").euclidLegato(3,8)
*/
const _euclidLegato = function (pulses, steps, rotation, pat) {
+11 -1
View File
@@ -3,6 +3,7 @@ hap.mjs - <short description TODO>
Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/strudel/blob/main/packages/core/hap.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 Fraction from './fraction.mjs';
export class Hap {
/*
@@ -32,7 +33,16 @@ export class Hap {
}
get duration() {
return this.whole.end.sub(this.whole.begin).mul(typeof this.value?.clip === 'number' ? this.value?.clip : 1);
let duration;
if (typeof this.value?.duration === 'number') {
duration = Fraction(this.value.duration);
} else {
duration = this.whole.end.sub(this.whole.begin);
}
if (typeof this.value?.clip === 'number') {
return duration.mul(this.value.clip);
}
return duration;
}
get endClipped() {
+5 -4
View File
@@ -4,11 +4,12 @@ Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/st
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import controls from './controls.mjs';
import * as controls from './controls.mjs'; // legacy
export * from './euclid.mjs';
import Fraction from './fraction.mjs';
import { logger } from './logger.mjs';
export { Fraction, controls };
export * from './controls.mjs';
export * from './hap.mjs';
export * from './pattern.mjs';
export * from './signal.mjs';
@@ -30,12 +31,12 @@ export { default as drawLine } from './drawLine.mjs';
// below won't work with runtime.mjs (json import fails)
/* import * as p from './package.json';
export const version = p.version; */
logger('🌀 @strudel.cycles/core loaded 🌀');
logger('🌀 @strudel/core loaded 🌀');
if (globalThis._strudelLoaded) {
console.warn(
`@strudel.cycles/core was loaded more than once...
`@strudel/core was loaded more than once...
This might happen when you have multiple versions of strudel installed.
Please check with "npm ls @strudel.cycles/core".`,
Please check with "npm ls @strudel/core".`,
);
}
globalThis._strudelLoaded = true;
+10
View File
@@ -1,6 +1,16 @@
export const logKey = 'strudel.log';
let debounce = 1000,
lastMessage,
lastTime;
export function logger(message, type, data = {}) {
let t = performance.now();
if (lastMessage === message && t - lastTime < debounce) {
return;
}
lastMessage = message;
lastTime = t;
console.log(`%c${message}`, 'background-color: black;color:white;border-radius:15px');
if (typeof document !== 'undefined' && typeof CustomEvent !== 'undefined') {
document.dispatchEvent(
+3 -4
View File
@@ -1,12 +1,11 @@
{
"name": "@strudel.cycles/core",
"version": "0.10.0",
"name": "@strudel/core",
"version": "1.0.1",
"description": "Port of Tidal Cycles to JavaScript",
"main": "index.mjs",
"type": "module",
"publishConfig": {
"main": "dist/index.js",
"module": "dist/index.mjs"
"main": "dist/index.mjs"
},
"scripts": {
"test": "vitest run",
+125 -125
View File
@@ -26,7 +26,7 @@ export class Pattern {
/**
* Create a pattern. As an end user, you will most likely not create a Pattern directly.
*
* @param {function} query - The function that maps a {@link State} to an array of {@link Hap}.
* @param {function} query - The function that maps a `State` to an array of `Hap`.
* @noAutocomplete
*/
constructor(query) {
@@ -39,7 +39,7 @@ export class Pattern {
/**
* Returns a new pattern, with the function applied to the value of
* each hap. It has the alias {@link Pattern#fmap}.
* each hap. It has the alias `fmap`.
* @synonyms fmap
* @param {Function} func to to apply to the value
* @returns Pattern
@@ -51,7 +51,7 @@ export class Pattern {
}
/**
* see {@link Pattern#withValue}
* see `withValue`
* @noAutocomplete
*/
fmap(func) {
@@ -115,7 +115,7 @@ export class Pattern {
}
/**
* As with {@link Pattern#appBoth}, but the `whole` timespan is not the intersection,
* As with `appBoth`, but the `whole` timespan is not the intersection,
* but the timespan from the function of patterns that this method is called
* on. In practice, this means that the pattern structure, including onsets,
* are preserved from the pattern of functions (often referred to as the left
@@ -148,7 +148,7 @@ export class Pattern {
}
/**
* As with {@link Pattern#appLeft}, but `whole` timespans are instead taken from the
* As with `appLeft`, but `whole` timespans are instead taken from the
* pattern of values, i.e. structure is preserved from the right hand/outer
* pattern.
* @param {Pattern} pat_val
@@ -340,9 +340,9 @@ export class Pattern {
* silence
* @noAutocomplete
*/
queryArc(begin, end) {
queryArc(begin, end, controls = {}) {
try {
return this.query(new State(new TimeSpan(begin, end)));
return this.query(new State(new TimeSpan(begin, end), controls));
} catch (err) {
logger(`[query]: ${err.message}`, 'error');
return [];
@@ -387,7 +387,7 @@ export class Pattern {
}
/**
* As with {@link Pattern#withQuerySpan}, but the function is applied to both the
* As with `withQuerySpan`, but the function is applied to both the
* begin and end time of the query timespan.
* @param {Function} func the function to apply
* @returns Pattern
@@ -398,7 +398,7 @@ export class Pattern {
}
/**
* Similar to {@link Pattern#withQuerySpan}, but the function is applied to the timespans
* Similar to `withQuerySpan`, but the function is applied to the timespans
* of all haps returned by pattern queries (both `part` timespans, and where
* present, `whole` timespans).
* @param {Function} func
@@ -410,7 +410,7 @@ export class Pattern {
}
/**
* As with {@link Pattern#withHapSpan}, but the function is applied to both the
* As with `withHapSpan`, but the function is applied to both the
* begin and end time of the hap timespans.
* @param {Function} func the function to apply
* @returns Pattern
@@ -427,11 +427,11 @@ export class Pattern {
* @noAutocomplete
*/
withHaps(func) {
return new Pattern((state) => func(this.query(state)));
return new Pattern((state) => func(this.query(state), state));
}
/**
* As with {@link Pattern#withHaps}, but applies the function to every hap, rather than every list of haps.
* As with `withHaps`, but applies the function to every hap, rather than every list of haps.
* @param {Function} func
* @returns Pattern
* @noAutocomplete
@@ -499,7 +499,7 @@ export class Pattern {
}
/**
* As with {@link Pattern#filterHaps}, but the function is applied to values
* As with `filterHaps`, but the function is applied to values
* inside haps.
* @param {Function} value_test
* @returns Pattern
@@ -621,7 +621,7 @@ export class Pattern {
}
/**
* More human-readable version of the {@link Pattern#firstCycleValues} accessor.
* More human-readable version of the `firstCycleValues` accessor.
* @noAutocomplete
*/
get showFirstCycle() {
@@ -691,13 +691,13 @@ export class Pattern {
// Methods without corresponding toplevel functions
/**
* Layers the result of the given function(s). Like {@link Pattern.superimpose}, but without the original pattern:
* Layers the result of the given function(s). Like `superimpose`, but without the original pattern:
* @name layer
* @memberof Pattern
* @synonyms apply
* @returns Pattern
* @example
* "<0 2 4 6 ~ 4 ~ 2 0!3 ~!5>*4"
* "<0 2 4 6 ~ 4 ~ 2 0!3 ~!5>*8"
* .layer(x=>x.add("0,2"))
* .scale('C minor').note()
*/
@@ -711,7 +711,7 @@ export class Pattern {
* @memberof Pattern
* @returns Pattern
* @example
* "<0 2 4 6 ~ 4 ~ 2 0!3 ~!5>*4"
* "<0 2 4 6 ~ 4 ~ 2 0!3 ~!5>*8"
* .superimpose(x=>x.add(2))
* .scale('C minor').note()
*/
@@ -727,8 +727,8 @@ export class Pattern {
* @name stack
* @memberof Pattern
* @example
* s("hh*2").stack(
* note("c2(3,8)")
* s("hh*4").stack(
* note("c4(5,8)")
* )
*/
stack(...pats) {
@@ -745,8 +745,8 @@ export class Pattern {
* @memberof Pattern
* @synonyms sequence, fastcat
* @example
* s("hh*2").seq(
* note("c2(3,8)")
* s("hh*4").seq(
* note("c4(5,8)")
* )
*/
seq(...pats) {
@@ -759,8 +759,8 @@ export class Pattern {
* @memberof Pattern
* @synonyms slowcat
* @example
* s("hh*2").cat(
* note("c2(3,8)")
* s("hh*4").cat(
* note("c4(5,8)")
* )
*/
cat(...pats) {
@@ -858,7 +858,7 @@ Pattern.prototype.arpWith = function (func) {
* Selects indices in in stacked notes.
* @example
* note("<[c,eb,g]!2 [c,f,ab] [d,f,ab]>")
* .arp("0 [0,2] 1 [0,2]").slow(2)
* .arp("0 [0,2] 1 [0,2]")
* */
Pattern.prototype.arp = function (pat) {
return this.arpWith((haps) => pat.fmap((i) => haps[i % haps.length]));
@@ -929,14 +929,14 @@ function _composeOp(a, b, func) {
* @memberof Pattern
* @example
* // Here, the triad 0, 2, 4 is shifted by different amounts
* "0 2 4".add("<0 3 4 0>").scale('C major').note()
* n("0 2 4".add("<0 3 4 0>")).scale("C:major")
* // Without add, the equivalent would be:
* // "<[0 2 4] [3 5 7] [4 6 8] [0 2 4]>".scale('C major').note()
* // n("<[0 2 4] [3 5 7] [4 6 8] [0 2 4]>").scale("C:major")
* @example
* // You can also use add with notes:
* "c3 e3 g3".add("<0 5 7 0>").note()
* note("c3 e3 g3".add("<0 5 7 0>"))
* // Behind the scenes, the notes are converted to midi numbers:
* // "48 52 55".add("<0 5 7 0>").note()
* // note("48 52 55".add("<0 5 7 0>"))
*/
add: [numeralArgs((a, b) => a + b)], // support string concatenation
/**
@@ -945,7 +945,7 @@ function _composeOp(a, b, func) {
* @name sub
* @memberof Pattern
* @example
* "0 2 4".sub("<0 1 2 3>").scale('C4 minor').note()
* n("0 2 4".sub("<0 1 2 3>")).scale("C4:minor")
* // See add for more information.
*/
sub: [numeralArgs((a, b) => a - b)],
@@ -955,7 +955,7 @@ function _composeOp(a, b, func) {
* @name mul
* @memberof Pattern
* @example
* "1 1.5 [1.66, <2 2.33>]".mul(150).freq()
* "<1 1.5 [1.66, <2 2.33>]>*4".mul(150).freq()
*/
mul: [numeralArgs((a, b) => a * b)],
/**
@@ -1049,9 +1049,9 @@ function _composeOp(a, b, func) {
* Applies the given structure to the pattern:
*
* @example
* note("c3,eb3,g3")
* note("c,eb,g")
* .struct("x ~ x ~ ~ x ~ x ~ ~ ~ x ~ x ~ ~")
* .slow(4)
* .slow(2)
*/
Pattern.prototype.struct = function (...args) {
return this.keepif.out(...args);
@@ -1063,7 +1063,7 @@ function _composeOp(a, b, func) {
* Returns silence when mask is 0 or "~"
*
* @example
* note("c [eb,g] d [eb,g]").mask("<1 [0 1]>").slow(2)
* note("c [eb,g] d [eb,g]").mask("<1 [0 1]>")
*/
Pattern.prototype.mask = function (...args) {
return this.keepif.in(...args);
@@ -1075,7 +1075,7 @@ function _composeOp(a, b, func) {
* Resets the pattern to the start of the cycle for each onset of the reset pattern.
*
* @example
* s("<bd lt> sd, hh*4").reset("<x@3 x(3,8)>")
* s("[<bd lt> sd]*2, hh*8").reset("<x@3 x(5,8)>")
*/
Pattern.prototype.reset = function (...args) {
return this.keepif.trig(...args);
@@ -1088,7 +1088,7 @@ function _composeOp(a, b, func) {
* While reset will only reset the current cycle, restart will start from cycle 0.
*
* @example
* s("<bd lt> sd, hh*4").restart("<x@3 x(3,8)>")
* s("[<bd lt> sd]*2, hh*8").restart("<x@3 x(5,8)>")
*/
Pattern.prototype.restart = function (...args) {
return this.keepif.trigzero(...args);
@@ -1154,8 +1154,8 @@ export function isPattern(thing) {
/* if (!thing instanceof Pattern) {
console.warn(
`Found Pattern that fails "instanceof Pattern" check.
This may happen if you are using multiple versions of @strudel.cycles/core.
Please check by running "npm ls @strudel.cycles/core".`,
This may happen if you are using multiple versions of @strudel/core.
Please check by running "npm ls @strudel/core".`,
);
console.log(thing);
} */
@@ -1178,7 +1178,8 @@ 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..
@@ -1189,7 +1190,7 @@ export function stack(...pats) {
/** Concatenation: combines a list of patterns, switching between them successively, one per cycle:
*
* synonyms: {@link cat}
* synonyms: `cat`
*
* @return {Pattern}
* @example
@@ -1237,17 +1238,19 @@ 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) {
return slowcat(...pats);
}
/** Like {@link Pattern.seq}, but each step has a length, relative to the whole.
/** Like `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));
@@ -1267,7 +1270,10 @@ export function timeCat(...timepats) {
*
* @return {Pattern}
* @example
* arrange([4, "<c a f e>(3,8)"],[2, "<g a>(5,8)"]).note()
* arrange(
* [4, "<c a f e>(3,8)"],
* [2, "<g a>(5,8)"]
* ).note()
*/
export function arrange(...sections) {
const total = sections.reduce((sum, [cycles]) => sum + cycles, 0);
@@ -1279,7 +1285,7 @@ export function fastcat(...pats) {
return slowcat(...pats)._fast(pats.length);
}
/** See {@link fastcat} */
/** See `fastcat` */
export function sequence(...pats) {
return fastcat(...pats);
}
@@ -1287,7 +1293,8 @@ 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) {
@@ -1313,9 +1320,9 @@ function _sequenceCount(x) {
* @param {number} steps how many items are placed in one cycle
* @param {any[]} sequences one or more arrays of Patterns / values
* @example
* polymeterSteps(2, ["c", "d", "e", "f", "g", "f", "e", "d"])
* .note().stack(s("bd")) // 1 cycle = 1 bd = 2 notes
* // note("{c d e f g f e d}%2").stack(s("bd"))
* polymeterSteps(4, ["c", "d", "e"])
* .note().stack(s("bd"))
* // note("{c d e}%4").stack(s("bd"))
*/
export function polymeterSteps(steps, ...args) {
const seqs = args.map((a) => _sequenceCount(a));
@@ -1463,7 +1470,7 @@ export function register(name, func, patternify = true) {
* @memberof Pattern
* @returns Pattern
* @example
* "0.5 1.5 2.5".round().scale('C major').note()
* n("0.5 1.5 2.5".round()).scale("C:major")
*/
export const round = register('round', function (pat) {
return pat.asNumber().fmap((v) => Math.round(v));
@@ -1477,7 +1484,7 @@ export const round = register('round', function (pat) {
* @memberof Pattern
* @returns Pattern
* @example
* "42 42.1 42.5 43".floor().note()
* note("42 42.1 42.5 43".floor())
*/
export const floor = register('floor', function (pat) {
return pat.asNumber().fmap((v) => Math.floor(v));
@@ -1491,7 +1498,7 @@ export const floor = register('floor', function (pat) {
* @memberof Pattern
* @returns Pattern
* @example
* "42 42.1 42.5 43".ceil().note()
* note("42 42.1 42.5 43".ceil())
*/
export const ceil = register('ceil', function (pat) {
return pat.asNumber().fmap((v) => Math.ceil(v));
@@ -1524,7 +1531,8 @@ export const fromBipolar = register('fromBipolar', function (pat) {
* @memberof Pattern
* @returns Pattern
* @example
* s("bd sd,hh*4").cutoff(sine.range(500,2000).slow(4))
* s("[bd sd]*2,hh*8")
* .cutoff(sine.range(500,4000))
*/
export const range = register('range', function (min, max, pat) {
return pat.mul(max - min).add(min);
@@ -1538,7 +1546,8 @@ export const range = register('range', function (min, max, pat) {
* @memberof Pattern
* @returns Pattern
* @example
* s("bd sd,hh*4").cutoff(sine.rangex(500,2000).slow(4))
* s("[bd sd]*2,hh*8")
* .cutoff(sine.rangex(500,4000))
*/
export const rangex = register('rangex', function (min, max, pat) {
return pat._range(Math.log(min), Math.log(max)).fmap(Math.exp);
@@ -1551,7 +1560,8 @@ export const rangex = register('rangex', function (min, max, pat) {
* @memberof Pattern
* @returns Pattern
* @example
* s("bd sd,hh*4").cutoff(sine2.range2(500,2000).slow(4))
* s("[bd sd]*2,hh*8")
* .cutoff(sine2.range2(500,4000))
*/
export const range2 = register('range2', function (min, max, pat) {
return pat.fromBipolar()._range(min, max);
@@ -1564,7 +1574,8 @@ export const range2 = register('range2', function (min, max, pat) {
* @memberof Pattern
* @returns Pattern
* @example
* ratio("1, 5:4, 3:2").mul(110).freq().s("piano").slow(2)
* ratio("1, 5:4, 3:2").mul(110)
* .freq().s("piano")
*/
export const ratio = register('ratio', (pat) =>
pat.fmap((v) => {
@@ -1636,7 +1647,7 @@ export const { fastGap, fastgap } = register(['fastGap', 'fastgap'], function (f
});
/**
* Similar to compress, but doesn't leave gaps, and the 'focus' can be bigger than a cycle
* Similar to `compress`, but doesn't leave gaps, and the 'focus' can be bigger than a cycle
* @example
* s("bd hh sd hh").focus(1/4, 3/4)
*/
@@ -1667,7 +1678,7 @@ export const ply = register('ply', function (factor, pat) {
* @param {number | Pattern} factor speed up factor
* @returns Pattern
* @example
* s("<bd sd> hh").fast(2) // s("[<bd sd> hh]*2")
* s("bd hh sd hh").fast(2) // s("[bd hh sd hh]*2")
*/
export const { fast, density } = register(['fast', 'density'], function (factor, pat) {
if (factor === 0) {
@@ -1696,7 +1707,7 @@ export const hurry = register('hurry', function (r, pat) {
* @param {number | Pattern} factor slow down factor
* @returns Pattern
* @example
* s("<bd sd> hh").slow(2) // s("[<bd sd> hh]/2")
* s("bd hh sd hh").slow(2) // s("[bd hh sd hh]/2")
*/
export const { slow, sparsity } = register(['slow', 'sparsity'], function (factor, pat) {
if (factor === 0) {
@@ -1753,7 +1764,7 @@ export const lastOf = register('lastOf', function (n, func, pat) {
*/
/**
* An alias for {@link firstOf}
* An alias for `firstOf`
* @name every
* @memberof Pattern
* @param {number} n how many cycles
@@ -1785,9 +1796,9 @@ export const apply = register('apply', function (func, pat) {
* @example
* s("<bd sd>,hh*2").cpm(90) // = 90 bpm
*/
// TODO - global clock
// this is redefined in repl.mjs, using the current cps as divisor
export const cpm = register('cpm', function (cpm, pat) {
return pat._fast(cpm / 60);
return pat._fast(cpm / 60 / 1);
});
/**
@@ -1860,7 +1871,7 @@ export const linger = register('linger', function (t, pat) {
* Samples the pattern at a rate of n events per cycle. Useful for turning a continuous pattern into a discrete one.
* @param {number} segments number of segments per cycle
* @example
* note(saw.range(0,12).segment(24)).add(40)
* note(saw.range(40,52).segment(24))
*/
export const segment = register('segment', function (rate, pat) {
return pat.struct(pure(true)._fast(rate));
@@ -1886,7 +1897,7 @@ export const { invert, inv } = register(['invert', 'inv'], function (pat) {
* @param {function} func
* @returns Pattern
* @example
* "c3 eb3 g3".when("<0 1>/2", x=>x.sub(5)).note()
* "c3 eb3 g3".when("<0 1>/2", x=>x.sub("5")).note()
*/
export const when = register('when', function (on, func, pat) {
return on ? func(pat) : pat;
@@ -1923,7 +1934,7 @@ export const brak = register('brak', function (pat) {
* @memberof Pattern
* @returns Pattern
* @example
* note("c3 d3 e3 g3").rev()
* note("c d e g").rev()
*/
export const rev = register('rev', function (pat) {
const query = function (state) {
@@ -1993,7 +2004,7 @@ export const palindrome = register('palindrome', function (pat) {
* @name juxBy
* @synonyms juxby
* @example
* s("lt ht mt ht hh").juxBy("<0 .5 1>/2", rev)
* s("bd lt [~ ht] mt cp ~ bd hh").juxBy("<0 .5 1>/2", rev)
*/
export const { juxBy, juxby } = register(['juxBy', 'juxby'], function (by, func, pat) {
by /= 2;
@@ -2012,7 +2023,11 @@ export const { juxBy, juxby } = register(['juxBy', 'juxby'], function (by, func,
/**
* The jux function creates strange stereo effects, by applying a function to a pattern, but only in the right-hand channel.
* @example
* s("lt ht mt ht hh").jux(rev)
* s("bd lt [~ ht] mt cp ~ bd hh").jux(rev)
* @example
* s("bd lt [~ ht] mt cp ~ bd hh").jux(press)
* @example
* s("bd lt [~ ht] mt cp ~ bd hh").jux(iter(4))
*/
export const jux = register('jux', function (func, pat) {
return pat._juxBy(1, func, pat);
@@ -2028,7 +2043,7 @@ export const jux = register('jux', function (func, pat) {
* @example
* "<0 [2 4]>"
* .echoWith(4, 1/8, (p,n) => p.add(n*2))
* .scale('C minor').note().clip(.2)
* .scale("C:minor").note()
*/
export const { echoWith, echowith, stutWith, stutwith } = register(
['echoWith', 'echowith', 'stutWith', 'stutwith'],
@@ -2121,7 +2136,8 @@ const { repeatCycles } = register('repeatCycles', _repeatCycles);
* @memberof Pattern
* @returns Pattern
* @example
* "0 1 2 3".chunk(4, x=>x.add(7)).scale('A minor').note()
* "0 1 2 3".chunk(4, x=>x.add(7))
* .scale("A:minor").note()
*/
const _chunk = function (n, func, pat, back = false, fast = false) {
const binary = Array(n - 1).fill(false);
@@ -2146,7 +2162,8 @@ const { chunk, slowchunk, slowChunk } = register(['chunk', 'slowchunk', 'slowChu
* @memberof Pattern
* @returns Pattern
* @example
* "0 1 2 3".chunkBack(4, x=>x.add(7)).scale('A minor').note()
* "0 1 2 3".chunkBack(4, x=>x.add(7))
* .scale("A:minor").note()
*/
export const { chunkBack, chunkback } = register(['chunkBack', 'chunkback'], function (n, func, pat) {
return _chunk(n, func, pat, true);
@@ -2160,10 +2177,11 @@ export const { chunkBack, chunkback } = register(['chunkBack', 'chunkback'], fun
* @memberof Pattern
* @returns Pattern
* @example
* "<0 8> 1 2 3 4 5 6 7".fastChunk(4, x => x.color('red')).slow(4).scale("C2:major").note()
.s("folkharp")
* "<0 8> 1 2 3 4 5 6 7"
* .fastChunk(4, x => x.color('red')).slow(2)
* .scale("C2:major").note()
*/
const { fastchunk, fastChunk } = register(['fastchunk', 'fastChunk'], function (n, func, pat) {
export const { fastchunk, fastChunk } = register(['fastchunk', 'fastChunk'], function (n, func, pat) {
return _chunk(n, func, pat, false, true);
});
@@ -2178,17 +2196,13 @@ export const bypass = register('bypass', function (on, pat) {
* @param {number} offset start point of loop in cycles
* @param {number} cycles loop length in cycles
* @example
* note("<c d e f>").ribbon(1, 2).fast(2)
* @example
* // Looping a portion of randomness
* note(irand(8).segment(4).scale('C3 minor')).ribbon(1337, 2)
*/
export const ribbon = register('ribbon', (offset, cycles, pat) => pat.early(offset).restart(pure(1).slow(cycles)));
// sets absolute duration of haps
// TODO - fix
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})`);
});
@@ -2221,21 +2235,6 @@ export const velocity = register('velocity', function (velocity, pat) {
return pat.withContext((context) => ({ ...context, velocity: (context.velocity || 1) * velocity }));
});
/**
*
* Multiplies the hap duration with the given factor.
* With samples, `clip` might be a better function to use ([more info](https://github.com/tidalcycles/strudel/pull/598))
* @name legato
* @memberof Pattern
* @example
* note("c3 eb3 g3 c4").legato("<.25 .5 1 2>")
*/
// TODO - fix
export const legato = register('legato', function (value, pat) {
value = Fraction(value);
return pat.withHapSpan((span) => new TimeSpan(span.begin, span.begin.add(span.end.sub(span.begin).mul(value))));
});
//////////////////////////////////////////////////////////////////////
// Control-related functions, i.e. ones that manipulate patterns of
// objects
@@ -2251,7 +2250,7 @@ export const legato = register('legato', function (value, pat) {
* s("rhodes")
* .chop(4)
* .rev() // reverse order of chops
* .loopAt(4) // fit sample into 4 cycles
* .loopAt(2) // fit sample into 2 cycles
*
*/
export const chop = register('chop', function (n, pat) {
@@ -2269,7 +2268,7 @@ export const chop = register('chop', function (n, pat) {
* @memberof Pattern
* @returns Pattern
* @example
* s("numbers:0 numbers:1 numbers:2").striate(6).slow(6)
* s("numbers:0 numbers:1 numbers:2").striate(6).slow(3)
*/
export const striate = register('striate', function (n, pat) {
const slices = Array.from({ length: n }, (x, i) => i);
@@ -2285,10 +2284,10 @@ export const striate = register('striate', function (n, pat) {
* @returns Pattern
* @example
* samples({ rhodes: 'https://cdn.freesound.org/previews/132/132051_316502-lq.mp3' })
* s("rhodes").loopAt(4)
* s("rhodes").loopAt(2)
*/
// TODO - global cps clock
const _loopAt = function (factor, pat, cps = 1) {
const _loopAt = function (factor, pat, cps = 0.5) {
return pat
.speed((1 / factor) * cps)
.unit('c')
@@ -2302,11 +2301,11 @@ const _loopAt = function (factor, pat, cps = 1) {
* @memberof Pattern
* @returns Pattern
* @example
* await samples('github:tidalcycles/Dirt-Samples/master')
* s("breaks165").slice(8, "0 1 <2 2*2> 3 [4 0] 5 6 7".every(3, rev)).slow(1.5)
* samples('github:tidalcycles/dirt-samples')
* s("breaks165").slice(8, "0 1 <2 2*2> 3 [4 0] 5 6 7".every(3, rev)).slow(0.75)
* @example
* await samples('github:tidalcycles/Dirt-Samples/master')
* s("breaks125/2").fit().slice([0,.25,.5,.75], "0 1 1 <2 3>")
* samples('github:tidalcycles/dirt-samples')
* s("breaks125").fit().slice([0,.25,.5,.75], "0 1 1 <2 3>")
*/
export const slice = register(
@@ -2331,53 +2330,54 @@ export const slice = register(
* Works the same as slice, but changes the playback speed of each slice to match the duration of its step.
* @name splice
* @example
* await samples('github:tidalcycles/Dirt-Samples/master')
* samples('github:tidalcycles/dirt-samples')
* s("breaks165")
* .splice(8, "0 1 [2 3 0]@2 3 0@2 7")
* .hurry(0.65)
*/
export const splice = register(
'splice',
function (npat, ipat, opat) {
const sliced = slice(npat, ipat, opat);
return sliced.withHap(function (hap) {
return hap.withValue((v) => ({
...{
speed: (1 / v._slices / hap.whole.duration) * (v.speed || 1),
unit: 'c',
},
...v,
}));
return new Pattern((state) => {
// TODO - default cps to 0.5
const cps = state.controls._cps || 1;
const haps = sliced.query(state);
return haps.map((hap) =>
hap.withValue((v) => ({
...{
speed: (cps / v._slices / hap.whole.duration) * (v.speed || 1),
unit: 'c',
},
...v,
})),
);
});
},
false, // turns off auto-patternification
);
// this function will be redefined in repl.mjs to use the correct cps value.
// It is still here to work in cases where repl.mjs is not used
export const { loopAt, loopat } = register(['loopAt', 'loopat'], function (factor, pat) {
return _loopAt(factor, pat, 1);
return new Pattern((state) => _loopAt(factor, pat, state.controls._cps).query(state));
});
// the fit function will be redefined in repl.mjs to use the correct cps value.
// It is still here to work in cases where repl.mjs is not used
/**
* Makes the sample fit its event duration. Good for rhythmical loops like drum breaks.
* Similar to loopAt.
* Similar to `loopAt`.
* @name fit
* @example
* samples({ rhodes: 'https://cdn.freesound.org/previews/132/132051_316502-lq.mp3' })
* s("rhodes/4").fit()
* s("rhodes/2").fit()
*/
export const fit = register('fit', (pat) =>
pat.withHap((hap) =>
hap.withValue((v) => ({
...v,
speed: 1 / hap.whole.duration,
unit: 'c',
})),
pat.withHaps((haps, state) =>
haps.map((hap) =>
hap.withValue((v) => ({
...v,
speed: (state.controls._cps || 1) / hap.whole.duration,
unit: 'c',
})),
),
),
);
+7 -2
View File
@@ -160,8 +160,13 @@ export function pianoroll({
maxMidi = max;
valueExtent = maxMidi - minMidi + 1;
}
// foldValues = values.sort((a, b) => a - b);
foldValues = values.sort((a, b) => String(a).localeCompare(String(b)));
foldValues = values.sort((a, b) =>
typeof a === 'number' && typeof b === 'number'
? a - b
: typeof a === 'number'
? 1
: String(a).localeCompare(String(b)),
);
barThickness = fold ? valueAxis / foldValues.length : valueAxis / valueExtent;
ctx.fillStyle = background;
ctx.globalAlpha = 1; // reset!
+55 -63
View File
@@ -61,12 +61,67 @@ export function repl({
scheduler.setPattern(pattern, autostart);
};
setTime(() => scheduler.now()); // TODO: refactor?
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);
const all = function (transform) {
allTransform = transform;
return silence;
};
// set pattern methods that use this repl via closure
const injectPatternMethods = () => {
Pattern.prototype.p = function (id) {
pPatterns[id] = this;
return this;
};
Pattern.prototype.q = function (id) {
return silence;
};
try {
for (let i = 1; i < 10; ++i) {
Object.defineProperty(Pattern.prototype, `d${i}`, {
get() {
return this.p(i);
},
configurable: true,
});
Object.defineProperty(Pattern.prototype, `p${i}`, {
get() {
return this.p(i);
},
configurable: true,
});
Pattern.prototype[`q${i}`] = silence;
}
} catch (err) {
console.warn('injectPatternMethods: error:', err);
}
const cpm = register('cpm', function (cpm, pat) {
return pat._fast(cpm / 60 / scheduler.cps);
});
evalScope({
all,
hush,
cpm,
setCps,
setcps: setCps,
setCpm,
setcpm: setCpm,
});
};
const evaluate = async (code, autostart = true, shouldHush = true) => {
if (!code) {
throw new Error('no code to evaluate');
}
try {
updateState({ code, pending: true });
injectPatternMethods();
await beforeEval?.({ code });
shouldHush && hush();
let { pattern, meta } = await _evaluate(code, transpiler);
@@ -94,74 +149,11 @@ export function repl({
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);
// the following functions use the cps value, which is why they are defined here..
const loopAt = register('loopAt', (cycles, pat) => {
return pat.loopAtCps(cycles, scheduler.cps);
});
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) =>
hap.withValue((v) => ({
...v,
speed: scheduler.cps / hap.whole.duration, // overwrite speed completely?
unit: 'c',
})),
),
);
evalScope({
loopAt,
fit,
all,
hush,
setCps,
setcps: setCps,
setCpm,
setcpm: setCpm,
});
const setCode = (code) => updateState({ code });
return { scheduler, evaluate, start, stop, pause, setCps, setPattern, setCode, toggle, state };
}
+192 -36
View File
@@ -7,7 +7,7 @@ This program is free software: you can redistribute it and/or modify it under th
import { Hap } from './hap.mjs';
import { Pattern, fastcat, reify, silence, stack, register } from './pattern.mjs';
import Fraction from './fraction.mjs';
import { id, _mod, clamp } from './util.mjs';
import { id, _mod, clamp, objectMap } from './util.mjs';
export function steady(value) {
// A continuous value
@@ -27,9 +27,11 @@ export const isaw2 = isaw.toBipolar();
*
* @return {Pattern}
* @example
* "c3 [eb3,g3] g2 [g3,bb3]".note().clip(saw.slow(4))
* note("<c3 [eb3,g3] g2 [g3,bb3]>*8")
* .clip(saw.slow(2))
* @example
* saw.range(0,8).segment(8).scale('C major').slow(4).note()
* n(saw.range(0,8).segment(8))
* .scale('C major')
*
*/
export const saw = signal((t) => t % 1);
@@ -42,7 +44,8 @@ export const sine2 = signal((t) => Math.sin(Math.PI * 2 * t));
*
* @return {Pattern}
* @example
* sine.segment(16).range(0,15).slow(2).scale('C minor').note()
* n(sine.segment(16).range(0,15))
* .scale("C:minor")
*
*/
export const sine = sine2.fromBipolar();
@@ -52,7 +55,8 @@ export const sine = sine2.fromBipolar();
*
* @return {Pattern}
* @example
* stack(sine,cosine).segment(16).range(0,15).slow(2).scale('C minor').note()
* n(stack(sine,cosine).segment(16).range(0,15))
* .scale("C:minor")
*
*/
export const cosine = sine._early(Fraction(1).div(4));
@@ -63,7 +67,7 @@ export const cosine2 = sine2._early(Fraction(1).div(4));
*
* @return {Pattern}
* @example
* square.segment(2).range(0,7).scale('C minor').note()
* n(square.segment(4).range(0,7)).scale("C:minor")
*
*/
export const square = signal((t) => Math.floor((t * 2) % 2));
@@ -74,7 +78,7 @@ export const square2 = square.toBipolar();
*
* @return {Pattern}
* @example
* tri.segment(8).range(0,7).scale('C minor').note()
* n(tri.segment(8).range(0,7)).scale("C:minor")
*
*/
export const tri = fastcat(isaw, saw);
@@ -118,8 +122,8 @@ const timeToRands = (t, n) => timeToRandsPrime(timeToIntSeed(t), n);
/**
* A discrete pattern of numbers from 0 to n-1
* @example
* run(4).scale('C4 major').note()
* // "0 1 2 3".scale('C4 major').note()
* n(run(4)).scale("C4:pentatonic")
* // n("0 1 2 3").scale("C4:pentatonic")
*/
export const run = (n) => saw.range(0, n).floor().segment(n);
@@ -129,7 +133,7 @@ export const run = (n) => saw.range(0, n).floor().segment(n);
* @name rand
* @example
* // randomly change the cutoff
* s("bd sd,hh*4").cutoff(rand.range(500,2000))
* s("bd*4,hh*8").cutoff(rand.range(500,8000))
*
*/
export const rand = signal(timeToRand);
@@ -151,36 +155,188 @@ export const _irand = (i) => rand.fmap((x) => Math.trunc(x * i));
* @param {number} n max value (exclusive)
* @example
* // randomly select scale notes from 0 - 7 (= C to C)
* irand(8).struct("x(3,8)").scale('C minor').note()
* n(irand(8)).struct("x x*2 x x*3").scale("C:minor")
*
*/
export const irand = (ipat) => reify(ipat).fmap(_irand).innerJoin();
/**
* pick from the list of values (or patterns of values) via the index using the given
* pattern of integers
const _pick = function (lookup, pat, modulo = true) {
const array = Array.isArray(lookup);
const len = Object.keys(lookup).length;
lookup = objectMap(lookup, reify);
if (len === 0) {
return silence;
}
return pat.fmap((i) => {
let key = i;
if (array) {
key = modulo ? Math.round(key) % len : clamp(Math.round(key), 0, lookup.length - 1);
}
return lookup[key];
});
};
/** * Picks patterns (or plain values) either from a list (by index) or a lookup table (by name).
* Similar to `inhabit`, but maintains the structure of the original patterns.
* @param {Pattern} pat
* @param {*} xs
* @returns {Pattern}
* @example
* note(pick("<0 1 [2!2] 3>", ["g a", "e f", "f g f g" , "g a c d"]))
* note("<0 1 2!2 3>".pick(["g a", "e f", "f g f g" , "g c d"]))
* @example
* sound("<0 1 [2,0]>".pick(["bd sd", "cp cp", "hh hh"]))
* @example
* sound("<0!2 [0,1] 1>".pick(["bd(3,8)", "sd sd"]))
* @example
* s("<a!2 [a,b] b>".pick({a: "bd(3,8)", b: "sd sd"}))
*/
export const pick = (pat, xs) => {
xs = xs.map(reify);
if (xs.length == 0) {
return silence;
export const pick = function (lookup, pat) {
// backward compatibility - the args used to be flipped
if (Array.isArray(pat)) {
[pat, lookup] = [lookup, pat];
}
return pat
.fmap((i) => {
const key = clamp(Math.round(i), 0, xs.length - 1);
return xs[key];
})
.innerJoin();
return __pick(lookup, pat);
};
const __pick = register('pick', function (lookup, pat) {
return _pick(lookup, pat, false).innerJoin();
});
/** * The same as `pick`, but if you pick a number greater than the size of the list,
* it wraps around, rather than sticking at the maximum value.
* For example, if you pick the fifth pattern of a list of three, you'll get the
* second one.
* @param {Pattern} pat
* @param {*} xs
* @returns {Pattern}
*/
export const pickmod = register('pickmod', function (lookup, pat) {
return _pick(lookup, pat, true).innerJoin();
});
/** * pickF lets you use a pattern of numbers to pick which function to apply to another pattern.
* @param {Pattern} pat
* @param {Pattern} lookup a pattern of indices
* @param {function[]} funcs the array of functions from which to pull
* @returns {Pattern}
* @example
* s("bd [rim hh]").pickF("<0 1 2>", [rev,jux(rev),fast(2)])
* @example
* note("<c2 d2>(3,8)").s("square")
* .pickF("<0 2> 1", [jux(rev),fast(2),x=>x.lpf(800)])
*/
export const pickF = register('pickF', function (lookup, funcs, pat) {
return pat.apply(pick(lookup, funcs));
});
/** * The same as `pickF`, but if you pick a number greater than the size of the functions list,
* it wraps around, rather than sticking at the maximum value.
* @param {Pattern} pat
* @param {Pattern} lookup a pattern of indices
* @param {function[]} funcs the array of functions from which to pull
* @returns {Pattern}
*/
export const pickmodF = register('pickmodF', function (lookup, funcs, pat) {
return pat.apply(pickmod(lookup, funcs));
});
/** * Similar to `pick`, but it applies an outerJoin instead of an innerJoin.
* @param {Pattern} pat
* @param {*} xs
* @returns {Pattern}
*/
export const pickOut = register('pickOut', function (lookup, pat) {
return _pick(lookup, pat, false).outerJoin();
});
/** * The same as `pickOut`, but if you pick a number greater than the size of the list,
* it wraps around, rather than sticking at the maximum value.
* @param {Pattern} pat
* @param {*} xs
* @returns {Pattern}
*/
export const pickmodOut = register('pickmodOut', function (lookup, pat) {
return _pick(lookup, pat, true).outerJoin();
});
/** * Similar to `pick`, but the choosen pattern is restarted when its index is triggered.
* @param {Pattern} pat
* @param {*} xs
* @returns {Pattern}
*/
export const pickRestart = register('pickRestart', function (lookup, pat) {
return _pick(lookup, pat, false).trigzeroJoin();
});
/** * The same as `pickRestart`, but if you pick a number greater than the size of the list,
* it wraps around, rather than sticking at the maximum value.
* @param {Pattern} pat
* @param {*} xs
* @returns {Pattern}
*/
export const pickmodRestart = register('pickmodRestart', function (lookup, pat) {
return _pick(lookup, pat, true).trigzeroJoin();
});
/** * Similar to `pick`, but the choosen pattern is reset when its index is triggered.
* @param {Pattern} pat
* @param {*} xs
* @returns {Pattern}
*/
export const pickReset = register('pickReset', function (lookup, pat) {
return _pick(lookup, pat, false).trigJoin();
});
/** * The same as `pickReset`, but if you pick a number greater than the size of the list,
* it wraps around, rather than sticking at the maximum value.
* @param {Pattern} pat
* @param {*} xs
* @returns {Pattern}
*/
export const pickmodReset = register('pickmodReset', function (lookup, pat) {
return _pick(lookup, pat, true).trigJoin();
});
/**
* pick from the list of values (or patterns of values) via the index using the given
/** * Picks patterns (or plain values) either from a list (by index) or a lookup table (by name).
* Similar to `pick`, but cycles are squeezed into the target ('inhabited') pattern.
* @name inhabit
* @synonyms pickSqueeze
* @param {Pattern} pat
* @param {*} xs
* @returns {Pattern}
* @example
* "<a b [a,b]>".inhabit({a: s("bd(3,8)"),
b: s("cp sd")
})
* @example
* s("a@2 [a b] a".inhabit({a: "bd(3,8)", b: "sd sd"})).slow(4)
*/
export const { inhabit, pickSqueeze } = register(['inhabit', 'pickSqueeze'], function (lookup, pat) {
return _pick(lookup, pat, false).squeezeJoin();
});
/** * The same as `inhabit`, but if you pick a number greater than the size of the list,
* it wraps around, rather than sticking at the maximum value.
* For example, if you pick the fifth pattern of a list of three, you'll get the
* second one.
* @name inhabitmod
* @synonyms pickmodSqueeze
* @param {Pattern} pat
* @param {*} xs
* @returns {Pattern}
*/
export const { inhabitmod, pickmodSqueeze } = register(['inhabitmod', 'pickmodSqueeze'], function (lookup, pat) {
return _pick(lookup, pat, true).squeezeJoin();
});
/**
* Pick from the list of values (or patterns of values) via the index using the given
* pattern of integers. The selected pattern will be compressed to fit the duration of the selecting event
* @param {Pattern} pat
* @param {*} xs
@@ -269,9 +425,9 @@ Pattern.prototype.choose2 = function (...xs) {
* Picks one of the elements at random each cycle.
* @returns {Pattern}
* @example
* chooseCycles("bd", "hh", "sd").s().fast(4)
* chooseCycles("bd", "hh", "sd").s().fast(8)
* @example
* "bd | hh | sd".s().fast(4)
* s("bd | hh | sd").fast(8)
*/
export const chooseCycles = (...xs) => chooseInWith(rand.segment(1), xs);
@@ -314,7 +470,7 @@ export const perlinWith = (pat) => {
* @name perlin
* @example
* // randomly change the cutoff
* s("bd sd,hh*4").cutoff(perlin.range(500,2000))
* s("bd*4,hh*8").cutoff(perlin.range(500,8000))
*
*/
export const perlin = perlinWith(time.fmap((v) => Number(v)));
@@ -356,7 +512,7 @@ export const degradeBy = register('degradeBy', function (x, pat) {
export const degrade = register('degrade', (pat) => pat._degradeBy(0.5));
/**
* Inverse of {@link Pattern#degradeBy}: Randomly removes events from the pattern by a given amount.
* Inverse of `degradeBy`: Randomly removes events from the pattern by a given amount.
* 0 = 100% chance of removal
* 1 = 0% chance of removal
* Events that would be removed by degradeBy are let through by undegradeBy and vice versa (see second example).
@@ -380,7 +536,7 @@ export const undegrade = register('undegrade', (pat) => pat._undegradeBy(0.5));
/**
*
* Randomly applies the given function by the given probability.
* Similar to {@link Pattern#someCyclesBy}
* Similar to `someCyclesBy`
*
* @name sometimesBy
* @memberof Pattern
@@ -388,7 +544,7 @@ export const undegrade = register('undegrade', (pat) => pat._undegradeBy(0.5));
* @param {function} function - the transformation to apply
* @returns Pattern
* @example
* s("hh(3,8)").sometimesBy(.4, x=>x.speed("0.5"))
* s("hh*8").sometimesBy(.4, x=>x.speed("0.5"))
*/
export const sometimesBy = register('sometimesBy', function (patx, func, pat) {
@@ -406,7 +562,7 @@ export const sometimesBy = register('sometimesBy', function (patx, func, pat) {
* @param {function} function - the transformation to apply
* @returns Pattern
* @example
* s("hh*4").sometimes(x=>x.speed("0.5"))
* s("hh*8").sometimes(x=>x.speed("0.5"))
*/
export const sometimes = register('sometimes', function (func, pat) {
return pat._sometimesBy(0.5, func);
@@ -415,7 +571,7 @@ export const sometimes = register('sometimes', function (func, pat) {
/**
*
* Randomly applies the given function by the given probability on a cycle by cycle basis.
* Similar to {@link Pattern#sometimesBy}
* Similar to `sometimesBy`
*
* @name someCyclesBy
* @memberof Pattern
@@ -423,7 +579,7 @@ export const sometimes = register('sometimes', function (func, pat) {
* @param {function} function - the transformation to apply
* @returns Pattern
* @example
* s("hh(3,8)").someCyclesBy(.3, x=>x.speed("0.5"))
* s("bd,hh*8").someCyclesBy(.3, x=>x.speed("0.5"))
*/
export const someCyclesBy = register('someCyclesBy', function (patx, func, pat) {
@@ -445,7 +601,7 @@ export const someCyclesBy = register('someCyclesBy', function (patx, func, pat)
* @memberof Pattern
* @returns Pattern
* @example
* s("hh(3,8)").someCycles(x=>x.speed("0.5"))
* s("bd,hh*8").someCycles(x=>x.speed("0.5"))
*/
export const someCycles = register('someCycles', function (func, pat) {
return pat._someCyclesBy(0.5, func);
+5 -5
View File
@@ -4,23 +4,23 @@ Copyright (C) 2023 Strudel contributors - see <https://github.com/tidalcycles/st
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import controls from '../controls.mjs';
import { s } from '../controls.mjs';
import { mini } from '../../mini/mini.mjs';
import { describe, it, expect } from 'vitest';
describe('controls', () => {
it('should support controls', () => {
expect(controls.s('bd').firstCycleValues).toEqual([{ s: 'bd' }]);
expect(s('bd').firstCycleValues).toEqual([{ s: 'bd' }]);
});
it('should support compound controls', () => {
expect(controls.s(mini('bd:3')).firstCycleValues).toEqual([{ s: 'bd', n: 3 }]);
expect(controls.s(mini('bd:3 sd:4:1.4')).firstCycleValues).toEqual([
expect(s(mini('bd:3')).firstCycleValues).toEqual([{ s: 'bd', n: 3 }]);
expect(s(mini('bd:3 sd:4:1.4')).firstCycleValues).toEqual([
{ s: 'bd', n: 3 },
{ s: 'sd', n: 4, gain: 1.4 },
]);
});
it('should support ignore extra elements in compound controls', () => {
expect(controls.s(mini('bd:3:0.4 sd:4:0.5:3:17')).firstCycleValues).toEqual([
expect(s(mini('bd:3:0.4 sd:4:0.5:3:17')).firstCycleValues).toEqual([
{ s: 'bd', n: 3, gain: 0.4 },
{ s: 'sd', n: 4, gain: 0.5 },
]);
+61 -2
View File
@@ -46,13 +46,13 @@ import {
rev,
time,
run,
pick,
} from '../index.mjs';
import { steady } from '../signal.mjs';
import controls from '../controls.mjs';
import { n, s } from '../controls.mjs';
const { n, s } = controls;
const st = (begin, end) => new State(ts(begin, end));
const ts = (begin, end) => new TimeSpan(Fraction(begin), Fraction(end));
const hap = (whole, part, value, context = {}) => new Hap(whole, part, value, context);
@@ -1057,4 +1057,63 @@ describe('Pattern', () => {
expect(slowcat(0, 1).repeatCycles(2).fast(6).firstCycleValues).toStrictEqual([0, 0, 1, 1, 0, 0]);
});
});
describe('inhabit', () => {
it('Can pattern named patterns', () => {
expect(
sameFirst(
sequence('a', 'b', stack('a', 'b')).inhabit({ a: sequence(1, 2), b: sequence(10, 20, 30) }),
sequence([1, 2], [10, 20, 30], stack([1, 2], [10, 20, 30])),
),
);
});
it('Can pattern indexed patterns', () => {
expect(
sameFirst(
sequence('0', '1', stack('0', '1')).inhabit([sequence(1, 2), sequence(10, 20, 30)]),
sequence([1, 2], [10, 20, 30], stack([1, 2], [10, 20, 30])),
),
);
});
});
describe('pick', () => {
it('Can pattern named patterns', () => {
expect(
sameFirst(
sequence('a', 'b', 'a', stack('a', 'b')).pick({ a: sequence(1, 2, 3, 4), b: sequence(10, 20, 30, 40) }),
sequence(1, 20, 3, stack(4, 40)),
),
);
});
it('Can pattern indexed patterns', () => {
expect(
sameFirst(
sequence(0, 1, 0, stack(0, 1)).pick([sequence(1, 2, 3, 4), sequence(10, 20, 30, 40)]),
sequence(1, 20, 3, stack(4, 40)),
),
);
});
it('Clamps indexes', () => {
expect(
sameFirst(sequence(0, 1, 2, 3).pick([sequence(1, 2, 3, 4), sequence(10, 20, 30, 40)]), sequence(1, 20, 30, 40)),
);
});
it('Is backwards compatible', () => {
expect(
sameFirst(
pick([sequence('a', 'b'), sequence('c', 'd')], sequence(0, 1)),
pick(sequence(0, 1), [sequence('a', 'b'), sequence('c', 'd')]),
),
);
});
});
describe('pickmod', () => {
it('Wraps indexes', () => {
expect(
sameFirst(
sequence(0, 1, 2, 3).pickmod([sequence(1, 2, 3, 4), sequence(10, 20, 30, 40)]),
sequence(1, 20, 3, 40),
),
);
});
});
});
+1 -2
View File
@@ -6,8 +6,7 @@ This program is free software: you can redistribute it and/or modify it under th
import { describe, it, expect } from 'vitest';
import { map, valued, mul } from '../value.mjs';
import controls from '../controls.mjs';
const { n } = controls;
import { n } from '../controls.mjs';
describe('Value', () => {
it('unionWith', () => {
+7
View File
@@ -316,3 +316,10 @@ export function hash2code(hash) {
return base64ToUnicode(decodeURIComponent(hash));
//return atob(decodeURIComponent(codeParam || ''));
}
export function objectMap(obj, fn) {
if (Array.isArray(obj)) {
return obj.map(fn);
}
return Object.fromEntries(Object.entries(obj).map(([k, v], i) => [k, fn(v, k, i)]));
}
+2 -2
View File
@@ -8,8 +8,8 @@ export default defineConfig({
build: {
lib: {
entry: resolve(__dirname, 'index.mjs'),
formats: ['es', 'cjs'],
fileName: (ext) => ({ es: 'index.mjs', cjs: 'index.js' })[ext],
formats: ['es'],
fileName: (ext) => ({ es: 'index.mjs' })[ext],
},
rollupOptions: {
external: [...Object.keys(dependencies)],
+1 -3
View File
@@ -1,3 +1 @@
# @strudel.cycles/csound
**DEPRECATION NOTE**: This package is old and won't get any updates! The newer version goes by the name of [@strudel/csound](https://www.npmjs.com/package/@strudel/csound).
# @strudel/csound
+2 -2
View File
@@ -1,5 +1,5 @@
import { getFrequency, logger, register } from '@strudel.cycles/core';
import { getAudioContext } from '@strudel.cycles/webaudio';
import { getFrequency, logger, register } from '@strudel/core';
import { getAudioContext } from '@strudel/webaudio';
import csd from './project.csd?raw';
// import livecodeOrc from './livecode.orc?raw';
import presetsOrc from './presets.orc?raw';
+6 -6
View File
@@ -1,11 +1,11 @@
{
"name": "@strudel.cycles/csound",
"version": "0.10.0",
"name": "@strudel/csound",
"version": "1.0.1",
"description": "csound bindings for strudel",
"main": "index.mjs",
"type": "module",
"publishConfig": {
"main": "dist/index.js",
"module": "dist/index.mjs"
"main": "dist/index.mjs"
},
"scripts": {
"build": "vite build",
@@ -33,8 +33,8 @@
"homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": {
"@csound/browser": "6.18.7",
"@strudel.cycles/core": "workspace:*",
"@strudel.cycles/webaudio": "workspace:*"
"@strudel/core": "workspace:*",
"@strudel/webaudio": "workspace:*"
},
"devDependencies": {
"vite": "^5.0.10"
+2 -2
View File
@@ -8,8 +8,8 @@ export default defineConfig({
build: {
lib: {
entry: resolve(__dirname, 'index.mjs'),
formats: ['es', 'cjs'],
fileName: (ext) => ({ es: 'index.mjs', cjs: 'index.js' })[ext],
formats: ['es'],
fileName: (ext) => ({ es: 'index.mjs' })[ext],
},
rollupOptions: {
external: [...Object.keys(dependencies)],
+3 -3
View File
@@ -1,16 +1,16 @@
import { Invoke } from './utils.mjs';
import { Pattern, noteToMidi } from '@strudel.cycles/core';
import { Pattern, noteToMidi } from '@strudel/core';
const ON_MESSAGE = 0x90;
const OFF_MESSAGE = 0x80;
const CC_MESSAGE = 0xb0;
Pattern.prototype.midi = function (output) {
return this.onTrigger((time, hap, currentTime) => {
return this.onTrigger((time, hap, currentTime, cps) => {
const { note, nrpnn, nrpv, ccn, ccv } = hap.value;
const offset = (time - currentTime) * 1000;
const velocity = Math.floor((hap.context?.velocity ?? 0.9) * 100); // TODO: refactor velocity
const duration = Math.floor(hap.duration.valueOf() * 1000 - 10);
const duration = Math.floor((hap.duration.valueOf() / cps) * 1000 - 10);
const roundedOffset = Math.round(offset);
const midichan = (hap.value.midichan ?? 1) - 1;
const requestedport = output ?? 'IAC';
+1 -1
View File
@@ -1,4 +1,4 @@
import { parseNumeral, Pattern } from '@strudel.cycles/core';
import { parseNumeral, Pattern } from '@strudel/core';
import { Invoke } from './utils.mjs';
Pattern.prototype.osc = function () {
+1 -1
View File
@@ -22,7 +22,7 @@
"url": "https://github.com/tidalcycles/strudel/issues"
},
"dependencies": {
"@strudel.cycles/core": "workspace:*",
"@strudel/core": "workspace:*",
"@tauri-apps/api": "^1.5.3"
},
"homepage": "https://github.com/tidalcycles/strudel#readme"
+3 -5
View File
@@ -1,15 +1,13 @@
# @strudel.cycles/embed
**DEPRECATION NOTE**: This package is old and won't get any updates! The newer version goes by the name of [@strudel/embed](https://www.npmjs.com/package/@strudel/embed).
# @strudel/embed
This package contains a embeddable web component for the Strudel REPL.
## Usage
Either install with `npm i @strudel.cycles/embed` or just use a cdn to import the script:
Either install with `npm i @strudel/embed` or just use a cdn to import the script:
```html
<script src="https://unpkg.com/@strudel.cycles/embed@latest"></script>
<script src="https://unpkg.com/@strudel/embed@latest"></script>
<strudel-repl>
<!--
note(`[[e5 [b4 c5] d5 [c5 b4]]
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@strudel.cycles/embed",
"version": "0.10.0",
"name": "@strudel/embed",
"version": "1.0.0",
"description": "Embeddable Web Component to load a Strudel REPL into an iframe",
"main": "embed.js",
"type": "module",
+1 -1
View File
@@ -27,7 +27,7 @@ npm i @strudel/hydra
Then add the import to your evalScope:
```js
import { evalScope } from '@strudel.cycles/core';
import { evalScope } from '@strudel/core';
evalScope(
import('@strudel/hydra')
+1 -1
View File
@@ -1,4 +1,4 @@
import { getDrawContext } from '@strudel.cycles/core';
import { getDrawContext } from '@strudel/core';
let latestOptions;
+4 -4
View File
@@ -1,11 +1,11 @@
{
"name": "@strudel/hydra",
"version": "0.10.0",
"version": "1.0.1",
"description": "Hydra integration for strudel",
"main": "hydra.mjs",
"type": "module",
"publishConfig": {
"main": "dist/index.js",
"module": "dist/index.mjs"
"main": "dist/index.mjs"
},
"scripts": {
"server": "node server.js",
@@ -33,7 +33,7 @@
},
"homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": {
"@strudel.cycles/core": "workspace:*",
"@strudel/core": "workspace:*",
"hydra-synth": "^1.3.29"
},
"devDependencies": {
+2 -2
View File
@@ -8,8 +8,8 @@ export default defineConfig({
build: {
lib: {
entry: resolve(__dirname, 'hydra.mjs'),
formats: ['es', 'cjs'],
fileName: (ext) => ({ es: 'index.mjs', cjs: 'index.js' })[ext],
formats: ['es'],
fileName: (ext) => ({ es: 'index.mjs' })[ext],
},
rollupOptions: {
external: [...Object.keys(dependencies)],
+2 -4
View File
@@ -1,11 +1,9 @@
# @strudel.cycles/midi
**DEPRECATION NOTE**: This package is old and won't get any updates! The newer version goes by the name of [@strudel/midi](https://www.npmjs.com/package/@strudel/midi).
# @strudel/midi
This package adds midi functionality to strudel Patterns.
## Install
```sh
npm i @strudel.cycles/midi --save
npm i @strudel/midi --save
```
+11 -5
View File
@@ -5,8 +5,8 @@ This program is free software: you can redistribute it and/or modify it under th
*/
import * as _WebMidi from 'webmidi';
import { Pattern, isPattern, logger, ref } from '@strudel.cycles/core';
import { noteToMidi } from '@strudel.cycles/core';
import { Pattern, isPattern, logger, ref } from '@strudel/core';
import { noteToMidi } from '@strudel/core';
import { Note } from 'webmidi';
// if you use WebMidi from outside of this package, make sure to import that instance:
export const { WebMidi } = _WebMidi;
@@ -129,7 +129,7 @@ Pattern.prototype.midi = function (output) {
const velocity = hap.context?.velocity ?? 0.9; // TODO: refactor velocity
// note off messages will often a few ms arrive late, try to prevent glitching by subtracting from the duration length
const duration = Math.floor(hap.duration.valueOf() * 1000 - 10);
const duration = Math.floor((hap.duration.valueOf() / cps) * 1000 - 10);
if (note != null) {
const midiNumber = typeof note === 'number' ? note : noteToMidi(note);
const midiNote = new Note(midiNumber, { attack: velocity, duration });
@@ -167,10 +167,16 @@ let listeners = {};
const refs = {};
export async function midin(input) {
if (isPattern(input)) {
throw new Error(
`.midi does not accept Pattern input. Make sure to pass device name with single quotes. Example: .midi('${
WebMidi.outputs?.[0]?.name || 'IAC Driver Bus 1'
}')`,
);
}
const initial = await enableWebMidi(); // only returns on first init
const device = getDevice(input, WebMidi.inputs);
if (initial) {
if (initial || WebMidi.enabled) {
const otherInputs = WebMidi.inputs.filter((o) => o.name !== device.name);
logger(
`Midi enabled! Using "${device.name}". ${
+6 -6
View File
@@ -1,11 +1,11 @@
{
"name": "@strudel.cycles/midi",
"version": "0.10.0",
"name": "@strudel/midi",
"version": "1.0.1",
"description": "Midi API for strudel",
"main": "index.mjs",
"type": "module",
"publishConfig": {
"main": "dist/index.js",
"module": "dist/index.mjs"
"main": "dist/index.mjs"
},
"scripts": {
"build": "vite build",
@@ -29,8 +29,8 @@
},
"homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": {
"@strudel.cycles/core": "workspace:*",
"@strudel.cycles/webaudio": "workspace:*",
"@strudel/core": "workspace:*",
"@strudel/webaudio": "workspace:*",
"webmidi": "^3.1.8"
},
"devDependencies": {
+2 -2
View File
@@ -8,8 +8,8 @@ export default defineConfig({
build: {
lib: {
entry: resolve(__dirname, 'index.mjs'),
formats: ['es', 'cjs'],
fileName: (ext) => ({ es: 'index.mjs', cjs: 'index.js' })[ext],
formats: ['es'],
fileName: (ext) => ({ es: 'index.mjs' })[ext],
},
rollupOptions: {
external: [...Object.keys(dependencies)],
+4 -6
View File
@@ -1,19 +1,17 @@
# @strudel.cycles/mini
**DEPRECATION NOTE**: This package is old and won't get any updates! The newer version goes by the name of [@strudel/mini](https://www.npmjs.com/package/@strudel/mini).
# @strudel/mini
This package contains the mini notation parser and pattern generator.
## Install
```sh
npm i @strudel.cycles/mini --save
npm i @strudel/mini --save
```
## Example
```js
import { mini } from '@strudel.cycles/mini';
import { mini } from '@strudel/mini';
const pattern = mini('a [b c*2]');
@@ -30,7 +28,7 @@ yields:
(7/8 -> 1/1, 7/8 -> 1/1, c)
```
[Play with @strudel.cycles/mini codesandbox](https://codesandbox.io/s/strudel-mini-example-oe9wcu?file=/src/index.js)
[Play with @strudel/mini codesandbox](https://codesandbox.io/s/strudel-mini-example-oe9wcu?file=/src/index.js)
## Mini Notation API
+193 -96
View File
@@ -288,48 +288,50 @@ function peg$parse(input, options) {
var peg$f0 = function() { return parseFloat(text()); };
var peg$f1 = function() { return parseInt(text()); };
var peg$f2 = function(chars) { return new AtomStub(chars.join("")) };
var peg$f3 = function(s) { return s };
var peg$f4 = function(s, stepsPerCycle) { s.arguments_.stepsPerCycle = stepsPerCycle ; return s; };
var peg$f5 = function(a) { return a };
var peg$f6 = function(s) { s.arguments_.alignment = 'polymeter_slowcat'; return s; };
var peg$f7 = function(a) { return x => x.options_['weight'] = a };
var peg$f8 = function(a) { return x => x.options_['reps'] = a };
var peg$f9 = function(p, s, r) { return x => x.options_['ops'].push({ type_: "bjorklund", arguments_ :{ pulse: p, step:s, rotation:r }}) };
var peg$f10 = function(a) { return x => x.options_['ops'].push({ type_: "stretch", arguments_ :{ amount:a, type: 'slow' }}) };
var peg$f11 = function(a) { return x => x.options_['ops'].push({ type_: "stretch", arguments_ :{ amount:a, type: 'fast' }}) };
var peg$f12 = function(a) { return x => x.options_['ops'].push({ type_: "degradeBy", arguments_ :{ amount:a, seed: seed++ } }) };
var peg$f13 = function(s) { return x => x.options_['ops'].push({ type_: "tail", arguments_ :{ element:s } }) };
var peg$f14 = function(s) { return x => x.options_['ops'].push({ type_: "range", arguments_ :{ element:s } }) };
var peg$f15 = function(s, ops) { const result = new ElementStub(s, {ops: [], weight: 1, reps: 1});
var peg$f2 = function(chars) { const s = chars.join(""); return (s === ".") || (s === "_") };
var peg$f3 = function(chars) { return new AtomStub(chars.join("")) };
var peg$f4 = function(s) { return s };
var peg$f5 = function(s, stepsPerCycle) { s.arguments_.stepsPerCycle = stepsPerCycle ; return s; };
var peg$f6 = function(a) { return a };
var peg$f7 = function(s) { s.arguments_.alignment = 'polymeter_slowcat'; return s; };
var peg$f8 = function(a) { return x => x.options_['weight'] = (x.options_['weight'] ?? 1) + (a ?? 2) - 1 };
var peg$f9 = function(a) { return x => x.options_['reps'] = (x.options_['reps'] ?? 1) + (a ?? 2) - 1 };
var peg$f10 = function(p, s, r) { return x => x.options_['ops'].push({ type_: "bjorklund", arguments_ :{ pulse: p, step:s, rotation:r }}) };
var peg$f11 = function(a) { return x => x.options_['ops'].push({ type_: "stretch", arguments_ :{ amount:a, type: 'slow' }}) };
var peg$f12 = function(a) { return x => x.options_['ops'].push({ type_: "stretch", arguments_ :{ amount:a, type: 'fast' }}) };
var peg$f13 = function(a) { return x => x.options_['ops'].push({ type_: "degradeBy", arguments_ :{ amount:a, seed: seed++ } }) };
var peg$f14 = function(s) { return x => x.options_['ops'].push({ type_: "tail", arguments_ :{ element:s } }) };
var peg$f15 = function(s) { return x => x.options_['ops'].push({ type_: "range", arguments_ :{ element:s } }) };
var peg$f16 = function(s, ops) { const result = new ElementStub(s, {ops: [], weight: 1, reps: 1});
for (const op of ops) {
op(result);
}
return result;
};
var peg$f16 = function(s) { return new PatternStub(s, 'fastcat'); };
var peg$f17 = function(tail) { return { alignment: 'stack', list: tail }; };
var peg$f18 = function(tail) { return { alignment: 'rand', list: tail, seed: seed++ }; };
var peg$f19 = function(head, tail) { if (tail && tail.list.length > 0) { return new PatternStub([head, ...tail.list], tail.alignment, tail.seed); } else { return head; } };
var peg$f20 = function(head, tail) { return new PatternStub(tail ? [head, ...tail.list] : [head], 'polymeter'); };
var peg$f21 = function(sc) { return sc; };
var peg$f22 = function(s) { return { name: "struct", args: { mini:s }}};
var peg$f23 = function(s) { return { name: "target", args : { name:s}}};
var peg$f24 = function(p, s, r) { return { name: "bjorklund", args :{ pulse: p, step:parseInt(s) }}};
var peg$f25 = function(a) { return { name: "stretch", args :{ amount: a}}};
var peg$f26 = function(a) { return { name: "shift", args :{ amount: "-"+a}}};
var peg$f27 = function(a) { return { name: "shift", args :{ amount: a}}};
var peg$f28 = function(a) { return { name: "stretch", args :{ amount: "1/"+a}}};
var peg$f29 = function(s) { return { name: "scale", args :{ scale: s.join("")}}};
var peg$f30 = function(s, v) { return v};
var peg$f31 = function(s, ss) { ss.unshift(s); return new PatternStub(ss, 'slowcat'); };
var peg$f32 = function(sg) {return sg};
var peg$f33 = function(o, soc) { return new OperatorStub(o.name,o.args,soc)};
var peg$f34 = function(sc) { return sc };
var peg$f35 = function(c) { return c };
var peg$f36 = function(v) { return new CommandStub("setcps", { value: v})};
var peg$f37 = function(v) { return new CommandStub("setcps", { value: (v/120/2)})};
var peg$f38 = function() { return new CommandStub("hush")};
var peg$f17 = function(s) { return new PatternStub(s, 'fastcat'); };
var peg$f18 = function(tail) { return { alignment: 'stack', list: tail }; };
var peg$f19 = function(tail) { return { alignment: 'rand', list: tail, seed: seed++ }; };
var peg$f20 = function(tail) { return { alignment: 'feet', list: tail, seed: seed++ }; };
var peg$f21 = function(head, tail) { if (tail && tail.list.length > 0) { return new PatternStub([head, ...tail.list], tail.alignment, tail.seed); } else { return head; } };
var peg$f22 = function(head, tail) { return new PatternStub(tail ? [head, ...tail.list] : [head], 'polymeter'); };
var peg$f23 = function(sc) { return sc; };
var peg$f24 = function(s) { return { name: "struct", args: { mini:s }}};
var peg$f25 = function(s) { return { name: "target", args : { name:s}}};
var peg$f26 = function(p, s, r) { return { name: "bjorklund", args :{ pulse: p, step:parseInt(s) }}};
var peg$f27 = function(a) { return { name: "stretch", args :{ amount: a}}};
var peg$f28 = function(a) { return { name: "shift", args :{ amount: "-"+a}}};
var peg$f29 = function(a) { return { name: "shift", args :{ amount: a}}};
var peg$f30 = function(a) { return { name: "stretch", args :{ amount: "1/"+a}}};
var peg$f31 = function(s) { return { name: "scale", args :{ scale: s.join("")}}};
var peg$f32 = function(s, v) { return v};
var peg$f33 = function(s, ss) { ss.unshift(s); return new PatternStub(ss, 'slowcat'); };
var peg$f34 = function(sg) {return sg};
var peg$f35 = function(o, soc) { return new OperatorStub(o.name,o.args,soc)};
var peg$f36 = function(sc) { return sc };
var peg$f37 = function(c) { return c };
var peg$f38 = function(v) { return new CommandStub("setcps", { value: v})};
var peg$f39 = function(v) { return new CommandStub("setcps", { value: (v/120/2)})};
var peg$f40 = function() { return new CommandStub("hush")};
var peg$currPos = 0;
var peg$savedPos = 0;
var peg$posDetailsCache = [{ line: 1, column: 1 }];
@@ -821,6 +823,30 @@ function peg$parse(input, options) {
return s0;
}
function peg$parsedot() {
var s0, s1, s2, s3;
s0 = peg$currPos;
s1 = peg$parsews();
if (input.charCodeAt(peg$currPos) === 46) {
s2 = peg$c0;
peg$currPos++;
} else {
s2 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$e1); }
}
if (s2 !== peg$FAILED) {
s3 = peg$parsews();
s1 = [s1, s2, s3];
s0 = s1;
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
return s0;
}
function peg$parsequote() {
var s0;
@@ -913,7 +939,7 @@ function peg$parse(input, options) {
}
function peg$parsestep() {
var s0, s1, s2, s3;
var s0, s1, s2, s3, s4;
s0 = peg$currPos;
s1 = peg$parsews();
@@ -929,8 +955,20 @@ function peg$parse(input, options) {
}
if (s2 !== peg$FAILED) {
s3 = peg$parsews();
peg$savedPos = s0;
s0 = peg$f2(s2);
peg$savedPos = peg$currPos;
s4 = peg$f2(s2);
if (s4) {
s4 = peg$FAILED;
} else {
s4 = undefined;
}
if (s4 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f3(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
}
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -966,7 +1004,7 @@ function peg$parse(input, options) {
if (s6 !== peg$FAILED) {
s7 = peg$parsews();
peg$savedPos = s0;
s0 = peg$f3(s4);
s0 = peg$f4(s4);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -1014,7 +1052,7 @@ function peg$parse(input, options) {
}
s8 = peg$parsews();
peg$savedPos = s0;
s0 = peg$f4(s4, s7);
s0 = peg$f5(s4, s7);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -1046,7 +1084,7 @@ function peg$parse(input, options) {
s2 = peg$parseslice();
if (s2 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f5(s2);
s0 = peg$f6(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -1086,7 +1124,7 @@ function peg$parse(input, options) {
if (s6 !== peg$FAILED) {
s7 = peg$parsews();
peg$savedPos = s0;
s0 = peg$f6(s4);
s0 = peg$f7(s4);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -1150,25 +1188,33 @@ function peg$parse(input, options) {
}
function peg$parseop_weight() {
var s0, s1, s2;
var s0, s1, s2, s3;
s0 = peg$currPos;
s1 = peg$parsews();
if (input.charCodeAt(peg$currPos) === 64) {
s1 = peg$c18;
s2 = peg$c18;
peg$currPos++;
} else {
s1 = peg$FAILED;
s2 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$e26); }
}
if (s1 !== peg$FAILED) {
s2 = peg$parsenumber();
if (s2 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f7(s2);
if (s2 === peg$FAILED) {
if (input.charCodeAt(peg$currPos) === 95) {
s2 = peg$c10;
peg$currPos++;
} else {
peg$currPos = s0;
s0 = peg$FAILED;
s2 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$e18); }
}
}
if (s2 !== peg$FAILED) {
s3 = peg$parsenumber();
if (s3 === peg$FAILED) {
s3 = null;
}
peg$savedPos = s0;
s0 = peg$f8(s3);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -1178,25 +1224,24 @@ function peg$parse(input, options) {
}
function peg$parseop_replicate() {
var s0, s1, s2;
var s0, s1, s2, s3;
s0 = peg$currPos;
s1 = peg$parsews();
if (input.charCodeAt(peg$currPos) === 33) {
s1 = peg$c19;
s2 = peg$c19;
peg$currPos++;
} else {
s1 = peg$FAILED;
s2 = peg$FAILED;
if (peg$silentFails === 0) { peg$fail(peg$e27); }
}
if (s1 !== peg$FAILED) {
s2 = peg$parsenumber();
if (s2 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f8(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
if (s2 !== peg$FAILED) {
s3 = peg$parsenumber();
if (s3 === peg$FAILED) {
s3 = null;
}
peg$savedPos = s0;
s0 = peg$f9(s3);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -1246,7 +1291,7 @@ function peg$parse(input, options) {
}
if (s13 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f9(s3, s7, s11);
s0 = peg$f10(s3, s7, s11);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -1286,7 +1331,7 @@ function peg$parse(input, options) {
s2 = peg$parseslice();
if (s2 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f10(s2);
s0 = peg$f11(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -1314,7 +1359,7 @@ function peg$parse(input, options) {
s2 = peg$parseslice();
if (s2 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f11(s2);
s0 = peg$f12(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -1344,7 +1389,7 @@ function peg$parse(input, options) {
s2 = null;
}
peg$savedPos = s0;
s0 = peg$f12(s2);
s0 = peg$f13(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -1368,7 +1413,7 @@ function peg$parse(input, options) {
s2 = peg$parseslice();
if (s2 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f13(s2);
s0 = peg$f14(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -1396,7 +1441,7 @@ function peg$parse(input, options) {
s2 = peg$parseslice();
if (s2 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f14(s2);
s0 = peg$f15(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -1422,7 +1467,7 @@ function peg$parse(input, options) {
s3 = peg$parseslice_op();
}
peg$savedPos = s0;
s0 = peg$f15(s1, s2);
s0 = peg$f16(s1, s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -1447,7 +1492,7 @@ function peg$parse(input, options) {
}
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
s1 = peg$f16(s1);
s1 = peg$f17(s1);
}
s0 = s1;
@@ -1496,7 +1541,7 @@ function peg$parse(input, options) {
}
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
s1 = peg$f17(s1);
s1 = peg$f18(s1);
}
s0 = s1;
@@ -1545,7 +1590,56 @@ function peg$parse(input, options) {
}
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
s1 = peg$f18(s1);
s1 = peg$f19(s1);
}
s0 = s1;
return s0;
}
function peg$parsedot_tail() {
var s0, s1, s2, s3, s4;
s0 = peg$currPos;
s1 = [];
s2 = peg$currPos;
s3 = peg$parsedot();
if (s3 !== peg$FAILED) {
s4 = peg$parsesequence();
if (s4 !== peg$FAILED) {
s2 = s4;
} else {
peg$currPos = s2;
s2 = peg$FAILED;
}
} else {
peg$currPos = s2;
s2 = peg$FAILED;
}
if (s2 !== peg$FAILED) {
while (s2 !== peg$FAILED) {
s1.push(s2);
s2 = peg$currPos;
s3 = peg$parsedot();
if (s3 !== peg$FAILED) {
s4 = peg$parsesequence();
if (s4 !== peg$FAILED) {
s2 = s4;
} else {
peg$currPos = s2;
s2 = peg$FAILED;
}
} else {
peg$currPos = s2;
s2 = peg$FAILED;
}
}
} else {
s1 = peg$FAILED;
}
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
s1 = peg$f20(s1);
}
s0 = s1;
@@ -1561,12 +1655,15 @@ function peg$parse(input, options) {
s2 = peg$parsestack_tail();
if (s2 === peg$FAILED) {
s2 = peg$parsechoose_tail();
if (s2 === peg$FAILED) {
s2 = peg$parsedot_tail();
}
}
if (s2 === peg$FAILED) {
s2 = null;
}
peg$savedPos = s0;
s0 = peg$f19(s1, s2);
s0 = peg$f21(s1, s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -1586,7 +1683,7 @@ function peg$parse(input, options) {
s2 = null;
}
peg$savedPos = s0;
s0 = peg$f20(s1, s2);
s0 = peg$f22(s1, s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -1609,7 +1706,7 @@ function peg$parse(input, options) {
s6 = peg$parsequote();
if (s6 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f21(s4);
s0 = peg$f23(s4);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -1671,7 +1768,7 @@ function peg$parse(input, options) {
s3 = peg$parsemini_or_operator();
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f22(s3);
s0 = peg$f24(s3);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -1704,7 +1801,7 @@ function peg$parse(input, options) {
s5 = peg$parsequote();
if (s5 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f23(s4);
s0 = peg$f25(s4);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -1749,7 +1846,7 @@ function peg$parse(input, options) {
s7 = null;
}
peg$savedPos = s0;
s0 = peg$f24(s3, s5, s7);
s0 = peg$f26(s3, s5, s7);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -1782,7 +1879,7 @@ function peg$parse(input, options) {
s3 = peg$parsenumber();
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f25(s3);
s0 = peg$f27(s3);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -1811,7 +1908,7 @@ function peg$parse(input, options) {
s3 = peg$parsenumber();
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f26(s3);
s0 = peg$f28(s3);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -1840,7 +1937,7 @@ function peg$parse(input, options) {
s3 = peg$parsenumber();
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f27(s3);
s0 = peg$f29(s3);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -1869,7 +1966,7 @@ function peg$parse(input, options) {
s3 = peg$parsenumber();
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f28(s3);
s0 = peg$f30(s3);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -1911,7 +2008,7 @@ function peg$parse(input, options) {
s5 = peg$parsequote();
if (s5 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f29(s4);
s0 = peg$f31(s4);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -2003,7 +2100,7 @@ function peg$parse(input, options) {
s9 = peg$parsemini_or_operator();
if (s9 !== peg$FAILED) {
peg$savedPos = s7;
s7 = peg$f30(s5, s9);
s7 = peg$f32(s5, s9);
} else {
peg$currPos = s7;
s7 = peg$FAILED;
@@ -2020,7 +2117,7 @@ function peg$parse(input, options) {
s9 = peg$parsemini_or_operator();
if (s9 !== peg$FAILED) {
peg$savedPos = s7;
s7 = peg$f30(s5, s9);
s7 = peg$f32(s5, s9);
} else {
peg$currPos = s7;
s7 = peg$FAILED;
@@ -2040,7 +2137,7 @@ function peg$parse(input, options) {
}
if (s8 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f31(s5, s6);
s0 = peg$f33(s5, s6);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -2086,7 +2183,7 @@ function peg$parse(input, options) {
s4 = peg$parsecomment();
}
peg$savedPos = s0;
s0 = peg$f32(s1);
s0 = peg$f34(s1);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -2108,7 +2205,7 @@ function peg$parse(input, options) {
s5 = peg$parsemini_or_operator();
if (s5 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f33(s1, s5);
s0 = peg$f35(s1, s5);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -2133,7 +2230,7 @@ function peg$parse(input, options) {
s1 = peg$parsemini_or_operator();
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
s1 = peg$f34(s1);
s1 = peg$f36(s1);
}
s0 = s1;
if (s0 === peg$FAILED) {
@@ -2166,7 +2263,7 @@ function peg$parse(input, options) {
if (s2 !== peg$FAILED) {
s3 = peg$parsews();
peg$savedPos = s0;
s0 = peg$f35(s2);
s0 = peg$f37(s2);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -2191,7 +2288,7 @@ function peg$parse(input, options) {
s3 = peg$parsenumber();
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f36(s3);
s0 = peg$f38(s3);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -2220,7 +2317,7 @@ function peg$parse(input, options) {
s3 = peg$parsenumber();
if (s3 !== peg$FAILED) {
peg$savedPos = s0;
s0 = peg$f37(s3);
s0 = peg$f39(s3);
} else {
peg$currPos = s0;
s0 = peg$FAILED;
@@ -2246,7 +2343,7 @@ function peg$parse(input, options) {
}
if (s1 !== peg$FAILED) {
peg$savedPos = s0;
s1 = peg$f38();
s1 = peg$f40();
}
s0 = s1;
+13 -7
View File
@@ -98,6 +98,7 @@ DIGIT = [0-9]
ws "whitespace" = [ \n\r\t\u00A0]*
comma = ws "," ws
pipe = ws "|" ws
dot = ws "." ws
quote = '"' / "'"
// ------------------ steps and cycles ---------------------------
@@ -105,7 +106,8 @@ quote = '"' / "'"
// single step definition (e.g bd)
step_char "a letter, a number, \"-\", \"#\", \".\", \"^\", \"_\"" =
unicode_letter / [0-9~] / "-" / "#" / "." / "^" / "_"
step = ws chars:step_char+ ws { return new AtomStub(chars.join("")) }
step = ws chars:step_char+ ws !{ const s = chars.join(""); return (s === ".") || (s === "_") } { return new AtomStub(chars.join("")) }
// define a sub cycle e.g. [1 2, 3 [4]]
sub_cycle = ws "[" ws s:stack_or_choose ws "]" ws { return s }
@@ -129,11 +131,11 @@ slice = step / sub_cycle / polymeter / slow_sequence
// at this point, we assume we can represent them as regular sequence operators
slice_op = op_weight / op_bjorklund / op_slow / op_fast / op_replicate / op_degrade / op_tail / op_range
op_weight = "@" a:number
{ return x => x.options_['weight'] = a }
op_weight = ws ("@" / "_") a:number?
{ return x => x.options_['weight'] = (x.options_['weight'] ?? 1) + (a ?? 2) - 1 }
op_replicate = "!"a:number
{ return x => x.options_['reps'] = a }
op_replicate = ws "!" a:number?
{ return x => x.options_['reps'] = (x.options_['reps'] ?? 1) + (a ?? 2) - 1 }
op_bjorklund = "(" ws p:slice_with_ops ws comma ws s:slice_with_ops ws comma? ws r:slice_with_ops? ws ")"
{ return x => x.options_['ops'].push({ type_: "bjorklund", arguments_ :{ pulse: p, step:s, rotation:r }}) }
@@ -175,9 +177,13 @@ stack_tail = tail:(comma @sequence)+
choose_tail = tail:(pipe @sequence)+
{ return { alignment: 'rand', list: tail, seed: seed++ }; }
// a foot separates subsequences, as an alternative to wrapping them in []
dot_tail = tail:(dot @sequence)+
{ return { alignment: 'feet', list: tail, seed: seed++ }; }
// if the stack contains only one element, we don't create a stack but return the
// underlying element
stack_or_choose = head:sequence tail:(stack_tail / choose_tail)?
stack_or_choose = head:sequence tail:(stack_tail / choose_tail / dot_tail)?
{ if (tail && tail.list.length > 0) { return new PatternStub([head, ...tail.list], tail.alignment, tail.seed); } else { return head; } }
polymeter_stack = head:sequence tail:stack_tail?
@@ -287,4 +293,4 @@ Lt = [\u01C5\u01C8\u01CB\u01F2\u1F88-\u1F8F\u1F98-\u1F9F\u1FA8-\u1FAF\u1FBC\u1FC
Lu = [\u0041-\u005A\u00C0-\u00D6\u00D8-\u00DE\u0100\u0102\u0104\u0106\u0108\u010A\u010C\u010E\u0110\u0112\u0114\u0116\u0118\u011A\u011C\u011E\u0120\u0122\u0124\u0126\u0128\u012A\u012C\u012E\u0130\u0132\u0134\u0136\u0139\u013B\u013D\u013F\u0141\u0143\u0145\u0147\u014A\u014C\u014E\u0150\u0152\u0154\u0156\u0158\u015A\u015C\u015E\u0160\u0162\u0164\u0166\u0168\u016A\u016C\u016E\u0170\u0172\u0174\u0176\u0178-\u0179\u017B\u017D\u0181-\u0182\u0184\u0186-\u0187\u0189-\u018B\u018E-\u0191\u0193-\u0194\u0196-\u0198\u019C-\u019D\u019F-\u01A0\u01A2\u01A4\u01A6-\u01A7\u01A9\u01AC\u01AE-\u01AF\u01B1-\u01B3\u01B5\u01B7-\u01B8\u01BC\u01C4\u01C7\u01CA\u01CD\u01CF\u01D1\u01D3\u01D5\u01D7\u01D9\u01DB\u01DE\u01E0\u01E2\u01E4\u01E6\u01E8\u01EA\u01EC\u01EE\u01F1\u01F4\u01F6-\u01F8\u01FA\u01FC\u01FE\u0200\u0202\u0204\u0206\u0208\u020A\u020C\u020E\u0210\u0212\u0214\u0216\u0218\u021A\u021C\u021E\u0220\u0222\u0224\u0226\u0228\u022A\u022C\u022E\u0230\u0232\u023A-\u023B\u023D-\u023E\u0241\u0243-\u0246\u0248\u024A\u024C\u024E\u0370\u0372\u0376\u037F\u0386\u0388-\u038A\u038C\u038E-\u038F\u0391-\u03A1\u03A3-\u03AB\u03CF\u03D2-\u03D4\u03D8\u03DA\u03DC\u03DE\u03E0\u03E2\u03E4\u03E6\u03E8\u03EA\u03EC\u03EE\u03F4\u03F7\u03F9-\u03FA\u03FD-\u042F\u0460\u0462\u0464\u0466\u0468\u046A\u046C\u046E\u0470\u0472\u0474\u0476\u0478\u047A\u047C\u047E\u0480\u048A\u048C\u048E\u0490\u0492\u0494\u0496\u0498\u049A\u049C\u049E\u04A0\u04A2\u04A4\u04A6\u04A8\u04AA\u04AC\u04AE\u04B0\u04B2\u04B4\u04B6\u04B8\u04BA\u04BC\u04BE\u04C0-\u04C1\u04C3\u04C5\u04C7\u04C9\u04CB\u04CD\u04D0\u04D2\u04D4\u04D6\u04D8\u04DA\u04DC\u04DE\u04E0\u04E2\u04E4\u04E6\u04E8\u04EA\u04EC\u04EE\u04F0\u04F2\u04F4\u04F6\u04F8\u04FA\u04FC\u04FE\u0500\u0502\u0504\u0506\u0508\u050A\u050C\u050E\u0510\u0512\u0514\u0516\u0518\u051A\u051C\u051E\u0520\u0522\u0524\u0526\u0528\u052A\u052C\u052E\u0531-\u0556\u10A0-\u10C5\u10C7\u10CD\u13A0-\u13F5\u1C90-\u1CBA\u1CBD-\u1CBF\u1E00\u1E02\u1E04\u1E06\u1E08\u1E0A\u1E0C\u1E0E\u1E10\u1E12\u1E14\u1E16\u1E18\u1E1A\u1E1C\u1E1E\u1E20\u1E22\u1E24\u1E26\u1E28\u1E2A\u1E2C\u1E2E\u1E30\u1E32\u1E34\u1E36\u1E38\u1E3A\u1E3C\u1E3E\u1E40\u1E42\u1E44\u1E46\u1E48\u1E4A\u1E4C\u1E4E\u1E50\u1E52\u1E54\u1E56\u1E58\u1E5A\u1E5C\u1E5E\u1E60\u1E62\u1E64\u1E66\u1E68\u1E6A\u1E6C\u1E6E\u1E70\u1E72\u1E74\u1E76\u1E78\u1E7A\u1E7C\u1E7E\u1E80\u1E82\u1E84\u1E86\u1E88\u1E8A\u1E8C\u1E8E\u1E90\u1E92\u1E94\u1E9E\u1EA0\u1EA2\u1EA4\u1EA6\u1EA8\u1EAA\u1EAC\u1EAE\u1EB0\u1EB2\u1EB4\u1EB6\u1EB8\u1EBA\u1EBC\u1EBE\u1EC0\u1EC2\u1EC4\u1EC6\u1EC8\u1ECA\u1ECC\u1ECE\u1ED0\u1ED2\u1ED4\u1ED6\u1ED8\u1EDA\u1EDC\u1EDE\u1EE0\u1EE2\u1EE4\u1EE6\u1EE8\u1EEA\u1EEC\u1EEE\u1EF0\u1EF2\u1EF4\u1EF6\u1EF8\u1EFA\u1EFC\u1EFE\u1F08-\u1F0F\u1F18-\u1F1D\u1F28-\u1F2F\u1F38-\u1F3F\u1F48-\u1F4D\u1F59\u1F5B\u1F5D\u1F5F\u1F68-\u1F6F\u1FB8-\u1FBB\u1FC8-\u1FCB\u1FD8-\u1FDB\u1FE8-\u1FEC\u1FF8-\u1FFB\u2102\u2107\u210B-\u210D\u2110-\u2112\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u2130-\u2133\u213E-\u213F\u2145\u2183\u2C00-\u2C2E\u2C60\u2C62-\u2C64\u2C67\u2C69\u2C6B\u2C6D-\u2C70\u2C72\u2C75\u2C7E-\u2C80\u2C82\u2C84\u2C86\u2C88\u2C8A\u2C8C\u2C8E\u2C90\u2C92\u2C94\u2C96\u2C98\u2C9A\u2C9C\u2C9E\u2CA0\u2CA2\u2CA4\u2CA6\u2CA8\u2CAA\u2CAC\u2CAE\u2CB0\u2CB2\u2CB4\u2CB6\u2CB8\u2CBA\u2CBC\u2CBE\u2CC0\u2CC2\u2CC4\u2CC6\u2CC8\u2CCA\u2CCC\u2CCE\u2CD0\u2CD2\u2CD4\u2CD6\u2CD8\u2CDA\u2CDC\u2CDE\u2CE0\u2CE2\u2CEB\u2CED\u2CF2\uA640\uA642\uA644\uA646\uA648\uA64A\uA64C\uA64E\uA650\uA652\uA654\uA656\uA658\uA65A\uA65C\uA65E\uA660\uA662\uA664\uA666\uA668\uA66A\uA66C\uA680\uA682\uA684\uA686\uA688\uA68A\uA68C\uA68E\uA690\uA692\uA694\uA696\uA698\uA69A\uA722\uA724\uA726\uA728\uA72A\uA72C\uA72E\uA732\uA734\uA736\uA738\uA73A\uA73C\uA73E\uA740\uA742\uA744\uA746\uA748\uA74A\uA74C\uA74E\uA750\uA752\uA754\uA756\uA758\uA75A\uA75C\uA75E\uA760\uA762\uA764\uA766\uA768\uA76A\uA76C\uA76E\uA779\uA77B\uA77D-\uA77E\uA780\uA782\uA784\uA786\uA78B\uA78D\uA790\uA792\uA796\uA798\uA79A\uA79C\uA79E\uA7A0\uA7A2\uA7A4\uA7A6\uA7A8\uA7AA-\uA7AE\uA7B0-\uA7B4\uA7B6\uA7B8\uFF21-\uFF3A]
// Number, Letter
Nl = [\u16EE-\u16F0\u2160-\u2182\u2185-\u2188\u3007\u3021-\u3029\u3038-\u303A\uA6E6-\uA6EF]
Nl = [\u16EE-\u16F0\u2160-\u2182\u2185-\u2188\u3007\u3021-\u3029\u3038-\u303A\uA6E6-\uA6EF]
+4 -1
View File
@@ -5,7 +5,7 @@ This program is free software: you can redistribute it and/or modify it under th
*/
import * as krill from './krill-parser.js';
import * as strudel from '@strudel.cycles/core';
import * as strudel from '@strudel/core';
const randOffset = 0.0003;
@@ -107,6 +107,9 @@ export function patternifyAST(ast, code, onEnter, offset = 0) {
if (alignment === 'rand') {
return strudel.chooseInWith(strudel.rand.early(randOffset * ast.arguments_.seed).segment(1), children);
}
if (alignment === 'feet') {
return strudel.fastcat(...children);
}
const weightedChildren = ast.source_.some((child) => !!child.options_?.weight);
if (weightedChildren) {
const weightSum = ast.source_.reduce((sum, child) => sum + (child.options_?.weight || 1), 0);
+4 -5
View File
@@ -1,12 +1,11 @@
{
"name": "@strudel.cycles/mini",
"version": "0.10.0",
"name": "@strudel/mini",
"version": "1.0.1",
"description": "Mini notation for strudel",
"main": "index.mjs",
"type": "module",
"publishConfig": {
"main": "dist/index.js",
"module": "dist/index.mjs"
"main": "dist/index.mjs"
},
"scripts": {
"test": "vitest run",
@@ -32,7 +31,7 @@
},
"homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": {
"@strudel.cycles/core": "workspace:*"
"@strudel/core": "workspace:*"
},
"devDependencies": {
"peggy": "^3.0.2",
+15 -1
View File
@@ -5,7 +5,7 @@ This program is free software: you can redistribute it and/or modify it under th
*/
import { getLeafLocation, getLeafLocations, mini, mini2ast } from '../mini.mjs';
import '@strudel.cycles/core/euclid.mjs';
import '@strudel/core/euclid.mjs';
import { describe, expect, it } from 'vitest';
describe('mini', () => {
@@ -73,6 +73,10 @@ describe('mini', () => {
expect(minS('a!3 b')).toEqual(['a: 0 - 1/4', 'a: 1/4 - 1/2', 'a: 1/2 - 3/4', 'b: 3/4 - 1']);
expect(minS('[<a b c>]!3 d')).toEqual(minS('<a b c> <a b c> <a b c> d'));
});
it('supports replication via repeated !', () => {
expect(minS('a ! ! b')).toEqual(['a: 0 - 1/4', 'a: 1/4 - 1/2', 'a: 1/2 - 3/4', 'b: 3/4 - 1']);
expect(minS('[<a b c>]!! d')).toEqual(minS('<a b c> <a b c> <a b c> d'));
});
it('supports euclidean rhythms', () => {
expect(minS('a(3, 8)')).toEqual(['a: 0 - 1/8', 'a: 3/8 - 1/2', 'a: 3/4 - 7/8']);
});
@@ -190,6 +194,16 @@ describe('mini', () => {
it('supports patterned ranges', () => {
expect(minS('[<0 1> .. <2 4>]*2')).toEqual(minS('[0 1 2] [1 2 3 4]'));
});
it('supports the . operator', () => {
expect(minS('a . b c')).toEqual(minS('a [b c]'));
expect(minS('a . b c . [d e f . g h]')).toEqual(minS('a [b c] [[d e f] [g h]]'));
});
it('supports the _ operator', () => {
expect(minS('a _ b _ _')).toEqual(minS('a@2 b@3'));
});
it('_ and @ are almost interchangeable', () => {
expect(minS('a @ b @ @')).toEqual(minS('a _2 b _3'));
});
});
describe('getLeafLocation', () => {
+2 -2
View File
@@ -8,8 +8,8 @@ export default defineConfig({
build: {
lib: {
entry: resolve(__dirname, 'index.mjs'),
formats: ['es', 'cjs'],
fileName: (ext) => ({ es: 'index.mjs', cjs: 'index.js' })[ext],
formats: ['es'],
fileName: (ext) => ({ es: 'index.mjs' })[ext],
},
rollupOptions: {
external: [...Object.keys(dependencies)],
+1 -3
View File
@@ -1,6 +1,4 @@
# @strudel.cycles/osc
**DEPRECATION NOTE**: This package is old and won't get any updates! The newer version goes by the name of [@strudel/osc](https://www.npmjs.com/package/@strudel/osc).
# @strudel/osc
OSC output for strudel patterns! Currently only tested with super collider / super dirt.
+1 -1
View File
@@ -6,7 +6,7 @@ This program is free software: you can redistribute it and/or modify it under th
import OSC from 'osc-js';
import { logger, parseNumeral, Pattern } from '@strudel.cycles/core';
import { logger, parseNumeral, Pattern } from '@strudel/core';
let connection; // Promise<OSC>
function connect() {
+5 -5
View File
@@ -1,11 +1,11 @@
{
"name": "@strudel.cycles/osc",
"version": "0.10.0",
"name": "@strudel/osc",
"version": "1.0.1",
"description": "OSC messaging for strudel",
"main": "osc.mjs",
"type": "module",
"publishConfig": {
"main": "dist/index.js",
"module": "dist/index.mjs"
"main": "dist/index.mjs"
},
"scripts": {
"server": "node server.js",
@@ -36,7 +36,7 @@
},
"homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": {
"@strudel.cycles/core": "workspace:*",
"@strudel/core": "workspace:*",
"osc-js": "^2.4.0"
},
"devDependencies": {
+2 -2
View File
@@ -8,8 +8,8 @@ export default defineConfig({
build: {
lib: {
entry: resolve(__dirname, 'osc.mjs'),
formats: ['es', 'cjs'],
fileName: (ext) => ({ es: 'index.mjs', cjs: 'index.js' })[ext],
formats: ['es'],
fileName: (ext) => ({ es: 'index.mjs' })[ext],
},
rollupOptions: {
external: [...Object.keys(dependencies)],
+2
View File
@@ -1,3 +1,5 @@
# @strudel/repl
The Strudel REPL as a web component.
[Usage example](https://github.com/tidalcycles/strudel/blob/main/examples/buildless/web-component-no-iframe.html)
+10 -10
View File
@@ -1,8 +1,8 @@
{
"name": "@strudel/repl",
"version": "0.10.0",
"version": "1.0.2",
"description": "Strudel REPL as a Web Component",
"main": "index.mjs",
"module": "index.mjs",
"publishConfig": {
"main": "dist/index.js",
"module": "dist/index.mjs"
@@ -33,15 +33,15 @@
},
"homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": {
"@strudel.cycles/core": "workspace:*",
"@strudel.cycles/midi": "workspace:*",
"@strudel.cycles/mini": "workspace:*",
"@strudel.cycles/soundfonts": "workspace:*",
"@strudel.cycles/tonal": "workspace:*",
"@strudel.cycles/transpiler": "workspace:*",
"@strudel.cycles/webaudio": "workspace:*",
"@strudel/codemirror": "workspace:*",
"@strudel/hydra": "workspace:*"
"@strudel/core": "workspace:*",
"@strudel/hydra": "workspace:*",
"@strudel/midi": "workspace:*",
"@strudel/mini": "workspace:*",
"@strudel/soundfonts": "workspace:*",
"@strudel/tonal": "workspace:*",
"@strudel/transpiler": "workspace:*",
"@strudel/webaudio": "workspace:*"
},
"devDependencies": {
"@rollup/plugin-replace": "^5.0.5",
+16 -17
View File
@@ -1,23 +1,22 @@
import { controls, noteToMidi, valueToMidi, Pattern, evalScope } from '@strudel.cycles/core';
import { registerSynthSounds, registerZZFXSounds, samples } from '@strudel.cycles/webaudio';
import * as core from '@strudel.cycles/core';
import { noteToMidi, valueToMidi, Pattern, evalScope } from '@strudel/core';
import { registerSynthSounds, registerZZFXSounds, samples } from '@strudel/webaudio';
import * as core from '@strudel/core';
export async function prebake() {
const modulesLoading = evalScope(
// import('@strudel.cycles/core'),
// import('@strudel/core'),
core,
import('@strudel.cycles/mini'),
import('@strudel.cycles/tonal'),
import('@strudel.cycles/webaudio'),
import('@strudel/mini'),
import('@strudel/tonal'),
import('@strudel/webaudio'),
import('@strudel/codemirror'),
import('@strudel/hydra'),
import('@strudel.cycles/soundfonts'),
import('@strudel.cycles/midi'),
// import('@strudel.cycles/xen'),
// import('@strudel.cycles/serial'),
// import('@strudel.cycles/csound'),
// import('@strudel.cycles/osc'),
controls, // sadly, this cannot be exported from core directly (yet)
import('@strudel/soundfonts'),
import('@strudel/midi'),
// import('@strudel/xen'),
// import('@strudel/serial'),
// import('@strudel/csound'),
// import('@strudel/osc'),
);
// load samples
const ds = 'https://raw.githubusercontent.com/felixroos/dough-samples/main/';
@@ -26,10 +25,10 @@ export async function prebake() {
registerSynthSounds(),
registerZZFXSounds(),
//registerSoundfonts(),
// need dynamic import here, because importing @strudel.cycles/soundfonts fails on server:
// => getting "window is not defined", as soon as "@strudel.cycles/soundfonts" is imported statically
// need dynamic import here, because importing @strudel/soundfonts fails on server:
// => getting "window is not defined", as soon as "@strudel/soundfonts" is imported statically
// seems to be a problem with soundfont2
import('@strudel.cycles/soundfonts').then(({ registerSoundfonts }) => registerSoundfonts()),
import('@strudel/soundfonts').then(({ registerSoundfonts }) => registerSoundfonts()),
samples(`${ds}/tidal-drum-machines.json`),
samples(`${ds}/piano.json`),
samples(`${ds}/Dirt-Samples.json`),
+3 -3
View File
@@ -1,6 +1,6 @@
import { getDrawContext, silence } from '@strudel.cycles/core';
import { transpiler } from '@strudel.cycles/transpiler';
import { getAudioContext, webaudioOutput } from '@strudel.cycles/webaudio';
import { getDrawContext, silence } from '@strudel/core';
import { transpiler } from '@strudel/transpiler';
import { getAudioContext, webaudioOutput } from '@strudel/webaudio';
import { StrudelMirror, codemirrorSettings } from '@strudel/codemirror';
import { prebake } from './prebake.mjs';
+1 -3
View File
@@ -1,5 +1,3 @@
# @strudel.cycles/serial
**DEPRECATION NOTE**: This package is old and won't get any updates! The newer version goes by the name of [@strudel/serial](https://www.npmjs.com/package/@strudel/serial).
# @strudel/serial
This package adds webserial functionality to strudel Patterns, for e.g. sending messages to arduino microcontrollers.
+5 -5
View File
@@ -1,11 +1,11 @@
{
"name": "@strudel.cycles/serial",
"version": "0.10.0",
"name": "@strudel/serial",
"version": "1.0.1",
"description": "Webserial API for strudel",
"main": "serial.mjs",
"type": "module",
"publishConfig": {
"main": "dist/index.js",
"module": "dist/index.mjs"
"main": "dist/index.mjs"
},
"scripts": {
"build": "vite build",
@@ -29,7 +29,7 @@
},
"homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": {
"@strudel.cycles/core": "workspace:*"
"@strudel/core": "workspace:*"
},
"devDependencies": {
"vite": "^5.0.10"
+1 -1
View File
@@ -4,7 +4,7 @@ Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/st
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { Pattern, isPattern } from '@strudel.cycles/core';
import { Pattern, isPattern } from '@strudel/core';
var writeMessagers = {};
var choosing = false;
+2 -2
View File
@@ -8,8 +8,8 @@ export default defineConfig({
build: {
lib: {
entry: resolve(__dirname, 'serial.mjs'),
formats: ['es', 'cjs'],
fileName: (ext) => ({ es: 'index.mjs', cjs: 'index.js' })[ext],
formats: ['es'],
fileName: (ext) => ({ es: 'index.mjs' })[ext],
},
rollupOptions: {
external: [...Object.keys(dependencies)],
+1 -3
View File
@@ -1,3 +1 @@
# @strudel.cycles/soundfonts
**DEPRECATION NOTE**: This package is old and won't get any updates! The newer version goes by the name of [@strudel/soundfonts](https://www.npmjs.com/package/@strudel/soundfonts).
# @strudel/soundfonts
+2 -2
View File
@@ -1,4 +1,4 @@
import { noteToMidi, freqToMidi, getSoundIndex } from '@strudel.cycles/core';
import { noteToMidi, freqToMidi, getSoundIndex } from '@strudel/core';
import {
getAudioContext,
registerSound,
@@ -6,7 +6,7 @@ import {
getADSRValues,
getPitchEnvelope,
getVibratoOscillator,
} from '@strudel.cycles/webaudio';
} from '@strudel/webaudio';
import gm from './gm.mjs';
let loadCache = {};
+5 -6
View File
@@ -1,11 +1,10 @@
{
"name": "@strudel.cycles/soundfonts",
"version": "0.10.0",
"name": "@strudel/soundfonts",
"version": "1.0.1",
"description": "Soundsfont support for strudel",
"main": "index.mjs",
"publishConfig": {
"main": "dist/index.js",
"module": "dist/index.mjs"
"main": "dist/index.mjs"
},
"scripts": {
"build": "vite build",
@@ -30,8 +29,8 @@
},
"homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": {
"@strudel.cycles/core": "workspace:*",
"@strudel.cycles/webaudio": "workspace:*",
"@strudel/core": "workspace:*",
"@strudel/webaudio": "workspace:*",
"sfumato": "^0.1.2",
"soundfont2": "^0.4.0"
},
+2 -2
View File
@@ -1,5 +1,5 @@
import { Pattern, getPlayableNoteValue, noteToMidi } from '@strudel.cycles/core';
import { getAudioContext, registerSound } from '@strudel.cycles/webaudio';
import { Pattern, getPlayableNoteValue, noteToMidi } from '@strudel/core';
import { getAudioContext, registerSound } from '@strudel/webaudio';
import { loadSoundfont as _loadSoundfont, startPresetNote } from 'sfumato';
Pattern.prototype.soundfont = function (sf, n = 0) {
+2 -2
View File
@@ -8,8 +8,8 @@ export default defineConfig({
build: {
lib: {
entry: resolve(__dirname, 'index.mjs'),
formats: ['es', 'cjs'],
fileName: (ext) => ({ es: 'index.mjs', cjs: 'index.js' })[ext],
formats: ['es'],
fileName: (ext) => ({ es: 'index.mjs' })[ext],
},
rollupOptions: {
external: [...Object.keys(dependencies)],
+2 -2
View File
@@ -19,7 +19,7 @@ import { superdough, samples, initAudioOnFirstClick, registerSynthSounds } from
const init = Promise.all([
initAudioOnFirstClick(),
samples('github:tidalcycles/Dirt-Samples/master'),
samples('github:tidalcycles/dirt-samples'),
registerSynthSounds(),
]);
@@ -148,7 +148,7 @@ The json file is expected to have the same format as described above.
Because it is common to use github for samples, there is a short way to load a sample map from github:
```js
samples('github:tidalcycles/Dirt-Samples/master')
samples('github:tidalcycles/dirt-samples')
```
The format is `github:<user>/<repo>/<branch>`.
+2 -3
View File
@@ -1,12 +1,11 @@
{
"name": "superdough",
"version": "0.10.0",
"version": "1.0.1",
"description": "simple web audio synth and sampler intended for live coding. inspired by superdirt and webdirt.",
"main": "index.mjs",
"type": "module",
"publishConfig": {
"main": "dist/index.cjs",
"module": "dist/index.mjs"
"main": "dist/index.mjs"
},
"directories": {
"example": "examples"
+32 -16
View File
@@ -99,6 +99,27 @@ export const getLoadedBuffer = (url) => {
return bufferCache[url];
};
function resolveSpecialPaths(base) {
if (base.startsWith('bubo:')) {
const [_, repo] = base.split(':');
base = `github:Bubobubobubobubo/dough-${repo}`;
}
return base;
}
function githubPath(base, subpath = '') {
if (!base.startsWith('github:')) {
throw new Error('expected "github:" at the start of pseudoUrl');
}
let [_, path] = base.split('github:');
path = path.endsWith('/') ? path.slice(0, -1) : path;
if (path.split('/').length === 2) {
// assume main as default branch if none set
path += '/main';
}
return `https://raw.githubusercontent.com/${path}/${subpath}`;
}
export const processSampleMap = (sampleMap, fn, baseUrl = sampleMap._base || '') => {
return Object.entries(sampleMap).forEach(([key, value]) => {
if (typeof value === 'string') {
@@ -108,15 +129,19 @@ export const processSampleMap = (sampleMap, fn, baseUrl = sampleMap._base || '')
throw new Error('wrong sample map format for ' + key);
}
baseUrl = value._base || baseUrl;
const replaceUrl = (v) => (baseUrl + v).replace('github:', 'https://raw.githubusercontent.com/');
baseUrl = resolveSpecialPaths(baseUrl);
if (baseUrl.startsWith('github:')) {
baseUrl = githubPath(baseUrl, '');
}
const fullUrl = (v) => baseUrl + v;
if (Array.isArray(value)) {
//return [key, value.map(replaceUrl)];
value = value.map(replaceUrl);
value = value.map(fullUrl);
} else {
// must be object
value = Object.fromEntries(
Object.entries(value).map(([note, samples]) => {
return [note, (typeof samples === 'string' ? [samples] : samples).map(replaceUrl)];
return [note, (typeof samples === 'string' ? [samples] : samples).map(fullUrl)];
}),
);
}
@@ -142,7 +167,7 @@ function getSamplesPrefixHandler(url) {
/**
* Loads a collection of samples to use with `s`
* @example
* samples('github:tidalcycles/Dirt-Samples/master');
* samples('github:tidalcycles/dirt-samples');
* s("[bd ~]*2, [~ hh]*2, ~ sd")
* @example
* samples({
@@ -165,18 +190,9 @@ export const samples = async (sampleMap, baseUrl = sampleMap._base || '', option
if (handler) {
return handler(sampleMap);
}
if (sampleMap.startsWith('bubo:')) {
const [_, repo] = sampleMap.split(':');
sampleMap = `github:Bubobubobubobubo/dough-${repo}`;
}
sampleMap = resolveSpecialPaths(sampleMap);
if (sampleMap.startsWith('github:')) {
let [_, path] = sampleMap.split('github:');
path = path.endsWith('/') ? path.slice(0, -1) : path;
if (path.split('/').length === 2) {
// assume main as default branch if none set
path += '/main';
}
sampleMap = `https://raw.githubusercontent.com/${path}/strudel.json`;
sampleMap = githubPath(sampleMap, 'strudel.json');
}
if (sampleMap.startsWith('shabda:')) {
let [_, path] = sampleMap.split('shabda:');
@@ -235,7 +251,7 @@ export async function onTriggerSample(t, value, onended, bank, resolveUrl) {
nudge = 0, // TODO: is this in seconds?
cut,
loop,
clip = undefined, // if 1, samples will be cut off when the hap ends
clip = undefined, // if set, samples will be cut off when the hap ends
n = 0,
note,
speed = 1, // sample playback speed
+6
View File
@@ -253,6 +253,11 @@ function effectSend(input, effect, wet) {
return send;
}
export function resetGlobalEffects() {
delays = {};
reverbs = {};
}
export const superdough = async (value, deadline, hapDuration) => {
const ac = getAudioContext();
if (typeof value !== 'object') {
@@ -513,6 +518,7 @@ export const superdough = async (value, deadline, hapDuration) => {
// toDisconnect = all the node that should be disconnected in onended callback
// this is crucial for performance
toDisconnect = chain.concat([delaySend, reverbSend, analyserSend]);
return { mute: (dt) => post.gain.linearRampToValueAtTime(0, ac.currentTime + dt) };
};
export const superdoughTrigger = (t, hap, ct, cps) => superdough(hap, t - ct, hap.duration / cps, cps);
+1 -1
View File
@@ -8,7 +8,7 @@ export default defineConfig({
build: {
lib: {
entry: resolve(__dirname, 'index.mjs'),
formats: ['es', 'cjs'],
formats: ['es'],
fileName: (ext) => ({ es: 'index.mjs', cjs: 'index.cjs' })[ext],
},
rollupOptions: {
+5 -7
View File
@@ -1,20 +1,18 @@
# @strudel.cycles/tonal
**DEPRECATION NOTE**: This package is old and won't get any updates! The newer version goes by the name of [@strudel/tonal](https://www.npmjs.com/package/@strudel/tonal).
# @strudel/tonal
This package adds tonal / harmonic functions to strudel Patterns.
## Install
```sh
npm i @strudel.cycles/tonal --save
npm i @strudel/tonal --save
```
## Example
```js
import { sequence } from '@strudel.cycles/core';
import '@strudel.cycles/tonal';
import { sequence } from '@strudel/core';
import '@strudel/tonal';
const pattern = sequence(0, [1, 2]).scale('C major');
@@ -29,7 +27,7 @@ yields:
(3/4 -> 1/1, 3/4 -> 1/1, E3)
```
[play with @strudel.cycles/tonal codesandbox](https://codesandbox.io/s/strudel-tonal-example-rgc5if?file=/src/index.js)
[play with @strudel/tonal codesandbox](https://codesandbox.io/s/strudel-tonal-example-rgc5if?file=/src/index.js)
## Tonal API
+4 -5
View File
@@ -1,11 +1,10 @@
{
"name": "@strudel.cycles/tonal",
"version": "0.10.0",
"name": "@strudel/tonal",
"version": "1.0.1",
"description": "Tonal functions for strudel",
"main": "index.mjs",
"publishConfig": {
"main": "dist/index.js",
"module": "dist/index.mjs"
"main": "dist/index.mjs"
},
"scripts": {
"build": "vite build",
@@ -31,7 +30,7 @@
},
"homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": {
"@strudel.cycles/core": "workspace:*",
"@strudel/core": "workspace:*",
"@tonaljs/tonal": "^4.7.2",
"chord-voicings": "^0.0.1",
"webmidi": "^3.1.8"
+1 -2
View File
@@ -7,10 +7,9 @@ This program is free software: you can redistribute it and/or modify it under th
// import { strict as assert } from 'assert';
import '../tonal.mjs'; // need to import this to add prototypes
import { pure, controls, seq } from '@strudel.cycles/core';
import { pure, n, seq } from '@strudel/core';
import { describe, it, expect } from 'vitest';
import { mini } from '../../mini/mini.mjs';
const { n } = controls;
describe('tonal', () => {
it('Should run tonal functions ', () => {
+10 -3
View File
@@ -5,7 +5,7 @@ This program is free software: you can redistribute it and/or modify it under th
*/
import { Note, Interval, Scale } from '@tonaljs/tonal';
import { register, _mod, silence, logger, pure, isNote } from '@strudel.cycles/core';
import { register, _mod, silence, logger, pure, isNote } from '@strudel/core';
import { stepInNamedScale } from './tonleiter.mjs';
const octavesInterval = (octaves) => (octaves <= 0 ? -1 : 1) + octaves * 7 + 'P';
@@ -103,6 +103,8 @@ export const transpose = register('transpose', function (intervalOrSemitones, pa
const semitones = typeof interval === 'string' ? Interval.semitones(interval) || 0 : interval;
return hap.withValue(() => hap.value + semitones);
}
if (typeof hap.value === 'object')
return hap.withValue(() => ({ ...hap.value, note: Note.simplify(Note.transpose(hap.value.note, interval)) }));
// TODO: move simplify to player to preserve enharmonics
// tone.js doesn't understand multiple sharps flats e.g. F##3 has to be turned into G3
return hap.withValue(() => Note.simplify(Note.transpose(hap.value, interval)));
@@ -133,6 +135,11 @@ export const scaleTranspose = register('scaleTranspose', function (offset /* : n
if (!hap.context.scale) {
throw new Error('can only use scaleTranspose after .scale');
}
if (typeof hap.value === 'object')
return hap.withValue(() => ({
...hap.value,
note: scaleOffset(hap.context.scale, Number(offset), hap.value.note),
}));
if (typeof hap.value !== 'string') {
throw new Error('can only use scaleTranspose with notes');
}
@@ -157,9 +164,9 @@ export const scaleTranspose = register('scaleTranspose', function (offset /* : n
* .scale("C:<major minor>/2")
* .s("piano")
* @example
* n(rand.range(0,12).segment(8).round())
* n(rand.range(0,12).segment(8))
* .scale("C:ritusen")
* .s("folkharp")
* .s("piano")
*/
export const scale = register('scale', function (scale, pat) {
+1 -1
View File
@@ -1,4 +1,4 @@
import { isNote, isNoteWithOctave, _mod, noteToMidi, tokenizeNote } from '@strudel.cycles/core';
import { isNote, isNoteWithOctave, _mod, noteToMidi, tokenizeNote } from '@strudel/core';
import { Interval, Scale } from '@tonaljs/tonal';
// https://codesandbox.io/s/stateless-voicings-g2tmz0?file=/src/lib.js:0-2515
+2 -2
View File
@@ -8,8 +8,8 @@ export default defineConfig({
build: {
lib: {
entry: resolve(__dirname, 'index.mjs'),
formats: ['es', 'cjs'],
fileName: (ext) => ({ es: 'index.mjs', cjs: 'index.js' })[ext],
formats: ['es'],
fileName: (ext) => ({ es: 'index.mjs' })[ext],
},
rollupOptions: {
external: [...Object.keys(dependencies)],
+1 -1
View File
@@ -4,7 +4,7 @@ Copyright (C) 2022 Strudel contributors - see <https://github.com/tidalcycles/st
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { stack, register, silence, logger } from '@strudel.cycles/core';
import { stack, register, silence, logger } from '@strudel/core';
import { renderVoicing } from './tonleiter.mjs';
import _voicings from 'chord-voicings';
import { complex, simple } from './ireal.mjs';
+4 -6
View File
@@ -1,6 +1,4 @@
# @strudel.cycles/transpiler
**DEPRECATION NOTE**: This package is old and won't get any updates! The newer version goes by the name of [@strudel/transpiler](https://www.npmjs.com/package/@strudel/transpiler).
# @strudel/transpiler
This package contains a JS code transpiler with the following features:
@@ -11,14 +9,14 @@ This package contains a JS code transpiler with the following features:
## Install
```sh
npm i @strudel.cycles/transpiler
npm i @strudel/transpiler
```
## Use
```js
import { transpiler } from '@strudel.cycles/core';
import { evaluate } from '@strudel.cycles/core';
import { transpiler } from '@strudel/core';
import { evaluate } from '@strudel/core';
transpiler('note("c3 [e3,g3]")', { wrapAsync: false, addReturn: false, simpleLocs: true });
/* mini('c3 [e3,g3]').withMiniLocation(7,17) */
+1 -1
View File
@@ -1,4 +1,4 @@
import { evaluate as _evaluate } from '@strudel.cycles/core';
import { evaluate as _evaluate } from '@strudel/core';
import { transpiler } from './transpiler.mjs';
export * from './transpiler.mjs';
+6 -6
View File
@@ -1,11 +1,11 @@
{
"name": "@strudel.cycles/transpiler",
"version": "0.10.0",
"name": "@strudel/transpiler",
"version": "1.0.1",
"description": "Transpiler for strudel user code. Converts syntactically correct but semantically meaningless JS into evaluatable strudel code.",
"main": "index.mjs",
"type": "module",
"publishConfig": {
"main": "dist/index.js",
"module": "dist/index.mjs"
"main": "dist/index.mjs"
},
"scripts": {
"build": "vite build",
@@ -30,8 +30,8 @@
},
"homepage": "https://github.com/tidalcycles/strudel#readme",
"dependencies": {
"@strudel.cycles/core": "workspace:*",
"@strudel.cycles/mini": "workspace:*",
"@strudel/core": "workspace:*",
"@strudel/mini": "workspace:*",
"acorn": "^8.11.3",
"escodegen": "^2.1.0",
"estree-walker": "^3.0.1"
@@ -23,6 +23,9 @@ describe('transpiler', () => {
it('supports top level await', () => {
expect(transpiler("await samples('xxx');", simple).output).toEqual("await samples('xxx');");
});
it('adds await to bare samples call', () => {
expect(transpiler("samples('xxx');", simple).output).toEqual("await samples('xxx');");
});
/* it('parses dynamic imports', () => {
expect(
transpiler("const { default: foo } = await import('https://bar.com/foo.js');", {

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