diff --git a/.github/FUNDING.yml b/.forgejo/FUNDING.yml similarity index 100% rename from .github/FUNDING.yml rename to .forgejo/FUNDING.yml diff --git a/.forgejo/workflows/deploy.yml b/.forgejo/workflows/deploy.yml new file mode 100644 index 000000000..561fee658 --- /dev/null +++ b/.forgejo/workflows/deploy.yml @@ -0,0 +1,37 @@ +name: Build and Deploy + +on: [workflow_dispatch] + +# Allow one concurrent deployment +concurrency: + group: "pages" + cancel-in-progress: true + +jobs: + build: + runs-on: docker + env: + SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }} + steps: + - uses: actions/checkout@v4 + - uses: pnpm/action-setup@v4 + with: + version: 9.12.2 + - uses: actions/setup-node@v4 + with: + node-version: 20 + # cache: "pnpm" + - name: Install Dependencies + run: pnpm install + + - name: Build + run: pnpm build + + - name: Deploy + run: | + eval $(ssh-agent -s) + echo "$SSH_PRIVATE_KEY" | ssh-add - + apt update && apt install -y rsync + mkdir ~/.ssh + ssh-keyscan matrix.toplap.org > ~/.ssh/known_hosts + rsync -atv --delete --delete-after --progress ./website/dist/ strudel@matrix.toplap.org:/home/strudel/deploy \ No newline at end of file diff --git a/.github/workflows/test.yml b/.forgejo/workflows/test.yml similarity index 80% rename from .github/workflows/test.yml rename to .forgejo/workflows/test.yml index 180f87521..90cb7e258 100644 --- a/.github/workflows/test.yml +++ b/.forgejo/workflows/test.yml @@ -1,16 +1,18 @@ name: Strudel tests -on: [push, pull_request] +on: [push] jobs: build: - runs-on: ubuntu-latest + runs-on: docker strategy: matrix: node-version: [20] steps: - uses: actions/checkout@v4 + - name: apt install ztd + run: apt update && apt install -y zstd - uses: pnpm/action-setup@v4 with: version: 9.12.2 diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml deleted file mode 100644 index 9a853c783..000000000 --- a/.github/workflows/deploy.yml +++ /dev/null @@ -1,49 +0,0 @@ -name: Build and Deploy - -on: [workflow_dispatch] - -# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages -permissions: - contents: read - pages: write - id-token: write - deployments: write - -# Allow one concurrent deployment -concurrency: - group: "pages" - cancel-in-progress: true - -jobs: - build: - runs-on: ubuntu-latest - environment: - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} - steps: - - uses: actions/checkout@v4 - - uses: pnpm/action-setup@v4 - with: - version: 9.12.2 - - uses: actions/setup-node@v4 - with: - node-version: 20 - cache: "pnpm" - - name: Install Dependencies - run: pnpm install - - - name: Build - run: pnpm build - - - name: Setup Pages - uses: actions/configure-pages@v2 - - - name: Upload artifact - uses: actions/upload-pages-artifact@v3 - with: - # Upload entire repository - path: "./website/dist" - - - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@v4 diff --git a/.gitignore b/.gitignore index 59d9940e1..2be3ee698 100644 --- a/.gitignore +++ b/.gitignore @@ -130,3 +130,4 @@ fabric.properties samples/* !samples/README.md +.idea/ diff --git a/.prettierignore b/.prettierignore index 950e59f19..c9584aca0 100644 --- a/.prettierignore +++ b/.prettierignore @@ -13,3 +13,4 @@ pnpm-workspace.yaml website/.astro !tidal-drum-machines.json !tidal-drum-machines-alias.json +.pnpm-store diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7d8170cfc..f455741f5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,12 +2,22 @@ Thanks for wanting to contribute!!! There are many ways you can add value to this project +## Move to codeberg + +We are currently in the process of moving from github to codeberg -- not everything is working, please bear with us. + +To update your local clone, you can run this command: + +``` +git remote set-url origin git@codeberg.org:uzu/strudel.git +``` + + ## Communication Channels To get in touch with the contributors, either -- open a [github discussion](https://github.com/tidalcycles/strudel/discussions) or -- [join the Tidal Discord Channel](https://discord.gg/remJ6gQA) and go to the #strudel channel +- [join the Tidal Discord Channel](https://discord.com/invite/HGEdXmRkzT) and go to the #strudel channel - Find related discussions on the [tidal club forum](https://club.tidalcycles.org/) ## Ask a Question @@ -32,7 +42,7 @@ Use one of the Communication Channels listed above. ## Improve the Docs If you find some weak spots in the [docs](https://strudel.cc/workshop/getting-started/), -you can edit each file directly on github via the "Edit this page" link located in the right sidebar. +you can edit each file directly on codeburg. (we are currently fixing the "Edit this page" links in the right sidebar) ## Propose a Feature @@ -41,7 +51,7 @@ Maybe you even want to help with the implementation of that feature! ## Report a Bug -If you've found a bug, or some behaviour that does not seem right, you are welcome to file an [issue](https://github.com/tidalcycles/strudel/issues). +If you've found a bug, or some behaviour that does not seem right, you are welcome to file an [issue](https://codeberg.org/uzu/strudel/issues). Please check that it has not been reported before. ## Fix a Bug @@ -71,7 +81,7 @@ To get the project up and running for development, make sure you have installed: then, do the following: ```sh -git clone https://github.com/tidalcycles/strudel.git && cd strudel +git clone https://codeberg.org/uzu/strudel.git && cd strudel pnpm i # install at root to symlink packages pnpm start # start repl ``` @@ -113,7 +123,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. +The project is split into multiple [packages](https://codeberg.org/uzu/strudel/src/branch/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/` to get the local version, allowing to develop multiple packages at the same time. diff --git a/README.md b/README.md index 12ee85035..d54302ac6 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,9 @@ # strudel -[![Strudel test status](https://github.com/tidalcycles/strudel/actions/workflows/test.yml/badge.svg)](https://github.com/tidalcycles/strudel/actions) [![DOI](https://zenodo.org/badge/450927247.svg)](https://doi.org/10.5281/zenodo.6659278) +Live coding patterns on the web +https://strudel.cc/ -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. +Development is moving to https://codeberg.org/uzu/strudel - Try it here: - Docs: @@ -31,7 +32,7 @@ This project is organized into many [packages](./packages), which are also avail Read more about how to use these in your own project [here](https://strudel.cc/technical-manual/project-start). -You will need to abide by the terms of the [GNU Affero Public Licence v3](LICENSE.md). As such, Strudel code can only be shared within free/open source projects under the same license -- see the license for details. +You will need to abide by the terms of the [GNU Affero Public Licence v3](LICENSE). As such, Strudel code can only be shared within free/open source projects under the same license -- see the license for details. Licensing info for the default sound banks can be found over on the [dough-samples](https://github.com/felixroos/dough-samples/blob/main/README.md) repository. @@ -39,7 +40,7 @@ Licensing info for the default sound banks can be found over on the [dough-sampl There are many ways to contribute to this project! See [contribution guide](./CONTRIBUTING.md). - + diff --git a/paper/Makefile b/docs/iclc2023-paper/Makefile similarity index 100% rename from paper/Makefile rename to docs/iclc2023-paper/Makefile diff --git a/paper/README.md b/docs/iclc2023-paper/README.md similarity index 100% rename from paper/README.md rename to docs/iclc2023-paper/README.md diff --git a/paper/bin/code-filter.py b/docs/iclc2023-paper/bin/code-filter.py similarity index 100% rename from paper/bin/code-filter.py rename to docs/iclc2023-paper/bin/code-filter.py diff --git a/paper/citations.json b/docs/iclc2023-paper/citations.json similarity index 100% rename from paper/citations.json rename to docs/iclc2023-paper/citations.json diff --git a/paper/demo-preprocessed.md b/docs/iclc2023-paper/demo-preprocessed.md similarity index 99% rename from paper/demo-preprocessed.md rename to docs/iclc2023-paper/demo-preprocessed.md index e22823d90..2f0871e7c 100644 --- a/paper/demo-preprocessed.md +++ b/docs/iclc2023-paper/demo-preprocessed.md @@ -201,7 +201,7 @@ interfaces. The Strudel REPL is available at , including an interactive tutorial. The repository is at -, all the code is open source +, all the code is open source under the GPL-3.0 License. # Acknowledgments diff --git a/paper/demo.md b/docs/iclc2023-paper/demo.md similarity index 98% rename from paper/demo.md rename to docs/iclc2023-paper/demo.md index 23fe27754..841fb6064 100644 --- a/paper/demo.md +++ b/docs/iclc2023-paper/demo.md @@ -128,7 +128,7 @@ For the future, it is planned to integrate alternative sound engines such as Gli # Links The Strudel REPL is available at , including an interactive tutorial. -The repository is at , all the code is open source under the GPL-3.0 License. +The repository is at , all the code is open source under the GPL-3.0 License. # Acknowledgments diff --git a/paper/demo.pdf b/docs/iclc2023-paper/demo.pdf similarity index 100% rename from paper/demo.pdf rename to docs/iclc2023-paper/demo.pdf diff --git a/paper/iclc-reviews.md b/docs/iclc2023-paper/iclc-reviews.md similarity index 100% rename from paper/iclc-reviews.md rename to docs/iclc2023-paper/iclc-reviews.md diff --git a/docs/iclc2023-paper/iclc2023.html b/docs/iclc2023-paper/iclc2023.html new file mode 100644 index 000000000..add08377f --- /dev/null +++ b/docs/iclc2023-paper/iclc2023.html @@ -0,0 +1,954 @@ + + + + + + + + Strudel: live coding patterns on the Web + + + + + + + +

Abstract

+
+

+ This paper introduces Strudel, which brings the TidalCycles approach to live coding algorithmic patterns to + native JavaScript and the web. We begin by giving a little background of the first year of development, before + sharing some detail about its implementation and examples of use. We go on to outline the wide range of + synthesis and other outputs available in Strudel, including WebAudio, MIDI, OSC (for SuperDirt), WebSerial and + CSound, and introduce Strudel’s REPL live editor, including its built-in visualisations. We then compare Strudel + with Tidal, the trade-offs involved between JavaScript and Haskell, and the unique capabilities offered by + Strudel for aligning patterns. +

+
+ +

1 Introduction

+

+ In the following paper, we introduce Strudel, an alternative implementation of the TidalCycles (or + ‘Tidal’ for short) live coding system, using the JavaScript programming language. Strudel is an attempt to make + live coding more accessible, by creating a system that runs entirely in the browser, while opening Tidal’s + approach to algorithmic patterns + (Mclean 2020) up to modern audio/visual + web technologies. The Strudel REPL is a live code editor dedicated to manipulating patterns while they play, with + builtin visual feedback. While Strudel is written in JavaScript, the API is optimized for simplicity and + readability by applying code transformations on the syntax tree level, allowing language operations that would + otherwise be impossible. The application supports multiple ways to output sound, including Tone.js, Web Audio + Nodes, OSC (Open Sound Control) messages, Web Serial, Web MIDI and Csound. The project is split into multiple + packages, allowing granular reuse in other applications. Apart from TidalCycles, Strudel draws inspiration from + many prior existing projects like TidalVortex + (McLean et al. 2022), Gibber + (Roberts and Kuchera-morin 2012), Estuary + (Ogborn et al. 2017), Hydra + (Jack [2022] 2022), Ocarina + (Solomon [2021] 2022) and Feedforward + (McLean 2020). This paper expands the Strudel + Demo paper for the Web Audio Conference 2022 + (Roos and McLean 2022). +

+

+ The first tentative commit to the Strudel project was on 22nd January 2022 by Alex McLean, with the core + representation implemented over the following few days. Although this was his first attempt at a JavaScript-based + application, by 27th January, Alex had managed to upload the initial version to the ‘npm’ javascript package + database, sharing with the wider community for comment. By 4th February, Felix Roos had discovered Strudel and + contributed a ‘REPL’ user interface to it, and then contributed a scheduler the next day, so that Strudel could + already make sound. At this point, Alex and Felix shared ownership to the repository, and the project has since + proved to be a productive confluence of Felix’s own work into music representation and visualisation, with Alex’s + experience with making Tidal. Felix has since become the primary contributor to Strudel, with Alex continuing to + jump between developing both Strudel and Tidal. Aspects of Strudel’s development have therefore fed back into + TidalCycles, and both systems have maintained a shared conceptual underpinning. We plan to continue working + towards feature parity between these systems, although within the syntactical trade-offs and library ecosystems of + JavaScript and Haskell, some divergence is inevitable and healthy. +

+

+ Over the first year of its life, Strudel is now a fully-fledged live coding environment, porting Tidal’s core + represention of patterns, pattern transformations, and mininotation for polymetric sequences, combined with a + wealth of features for synthesising and visualising those patterns. +

+

+ 2 From Tidal to Strudel and back +

+

+ As mentioned above, the original Tidal is implemented as a domain specific language (DSL) embedded in the Haskell + pure functional programming language, and takes advantage of Haskell’s terse syntax and advanced, ‘strong’ type + system. JavaScript on the other hand, is a multi-paradigm programming language, with a dynamic type system. + Because Tidal leans heavily on many of Haskell’s more unique features, it was not always clear that it could + meaningfully be ported to a multi-paradigm scripting language. However, this possibility was already demonstrated + with an earlier port to Python [TidalVortex; + McLean et al. (2022)], and we have now + successfully implemented Tidal’s pure functional representation of patterns in Strudel, including partial + application, currying, and the functor, applicative and monadic structures that underlie Tidal’s expressive + pattern transformations. The result is a terse and highly composable system, where everything is either a pattern, + or a function for combining and manipulating patterns, offering a rich creative ground for exploration. +

+

+ This development process has been far from a one-way port, however. The process of porting Tidal’s concepts has + also opened up new possibilities, some just from revisiting every design decision, and some from the particular + affordances and constraints offered by JavaScript. This has lead to new features (and indeed bugfixes) that have + found their way back to Tidal where appropriate, and ongoing work that we will return to in the conclusion of this + paper. +

+

+ 3 Representing Patterns +

+

+ Patterns are the essence of Tidal. Its patterns are abstract entities that represent flows of time as functions, + adapting a technique called pure functional reactive programming. Taking a time span as its input, a Pattern can + output a set of events that happen within that time span. It depends on the structure of the Pattern how the + events are located in time. From now on, this process of generating events from a time span will be called + querying. Example: +

+
+
const pattern = sequence(c3, [e3, g3])
+const events = pattern.queryArc(0, 1)
+console.log(events.map(e => e.show()))
+
+

+ In this example, we create a pattern using the sequence function and query it for + the time span from 0 to 1. Those numbers represent units of time called + cycles. The length of one cycle depends on the tempo, which defaults to one cycle per second. The + resulting events are: +

+
+
[{ value: 'c3', begin: 0, end: 1/2 },
+{ value: 'e3', begin: 1/2, end: 3/4 },
+{ value: 'g3', begin: 3/4, end: 1 }]
+
+

+ Each event has a value, a begin time and an end time, where time is represented as a fraction. In the above case, + the events are placed in sequential order, where c3 takes the first half, and e3 and g3 together take the second + half. This temporal placement is the result of the sequence function, which divides its arguments + equally over one cycle. If an argument is an array, the same rule applies to that part of the cycle. In the + example, e3 and g3 are divided equally over the second half of the whole cycle. +

+

+ The above examples do not represent how Strudel is used in practice. In the live coding editor, the user only has + to type in the pattern itself, the querying will be handled by the scheduler. The scheduler will repeatedly query + the pattern for events, which are then scheduled as sound synthesis or other event triggers. Also, the above event + data structure has been simplified for readability. +

+
+ Screenshot of the Strudel ‘REPL’ live coding editor, including piano-roll visualisation. + +
+

4 Making Patterns

+

+ In practice, the end-user live coder will not deal with constructing patterns directly, but will rather build + patterns using Strudel’s extensive combinator library to create, combine and transform patterns. +

+

+ The live coder will rarely use the sequence function as seen above, as sequencing is implicit in many + functions. For example in the following, the note function constructs a pattern of notes, sequencing + its arguments in the same manner as the previous example. +

+
+
note(c3, [e3, g3])
+
+

+ Perhaps more often, they will use the mini-notation for even terser notation of rhythmic sequences: [^This last + example is also valid Tidal code, albeit the parenthesis is not required in its Haskell syntax in this case. Tidal + does not support passing sequences as lists directly to the note function, however.]. +

+
+
note("c3 [e3 g3]")
+
+

+ Such sequences are often treated only a starting point for manipulation, where they then undergo pattern + transformations such as repetition, symmetry, interference/combination or randomisation, potentially at multiple + timescales. Because Strudel patterns are represented as pure functions of time rather than as data structures, + very long and complex generative results can be represented and manipulated without having to store the resulting + sequences in memory. +

+

5 Pattern Example

+

+ The following example showcases how patterns can be utilized to create musical complexity from simple parts, using + repetition and interference: +

+
+
"<0 2 [4 6](3,4,1) 3>"
+.off(1/4, add(2))
+.off(1/2, add(6))
+.scale('D minor')
+.legato(.25)
+.note().s("sawtooth square")
+.delay(.8).delaytime(.125)
+
+

+ The pattern starts with a rhythm of numbers in mini notation, which are later interpreted inside the scale of D + minor. The first line could also be expressed without mini notation: +

+
+
cat(0, 2, [4, 6].euclid(3, 4, 1), 3)
+
+

+ These numbers then undergo various pattern transformations. Here is a short description of all the functions used: +

+
    +
  • cat: play elements sequentially, where each lasts one cycle
  • +
  • brackets: elements inside brackets are divided equally over the time of their parent
  • +
  • + .euclid(p, s, o): place p pulses evenly over s steps, with offset o + (Toussaint 2005) +
  • +
  • + .off(n, f): layers a pattern on top of itself, with the new layer offset by n cycles, and with + function f applied +
  • +
  • .legato(n): multiply the duration of all events in a pattern by a factor of n
  • +
  • + .echo(t, n, v): copy each event t times, with n cycles in between each copy, decreasing velocity by + v +
  • +
  • .note(): interpretes values as notes
  • +
  • .s(name): play back each event with the given sound
  • +
  • .delay(wet): add delay
  • +
  • .delaytime(t): set delay time
  • +
+

Much of the above will be familiar to Tidal users.

+ +

+ 6 Ways to make Sound (and other events) +

+

To generate sound, Strudel supports bindings for different outputs:

+
    +
  • Tone.js (deprecated)
  • +
  • Web Audio API
  • +
  • WebDirt, a js recreation of Tidal’s Dirt sample engine (deprecated)
  • +
  • OSC via osc-js, compatible with superdirt
  • +
  • Csound via the Csound WebAssembly build
  • +
  • MIDI via WebMIDI
  • +
  • Serial via WebSerial
  • +
+

+ At first, we used Tone.js as sound output, but it proved to be limited for the use case of Strudel, where each + individual event could potentially have a completely different audio graph. While the Web Audio API takes a + fire-and-forget approach, creating a lot of Tone.js instruments and effects causes performance issues + quickly. For that reason, we chose to search for alternatives. +

+

+ Strudel’s new default output uses the Web Audio API to create a new audio graph for each event. It currently + supports basic oscillators, sample playback, various effects and an experimental support for soundfonts. +

+

+ WebDirt (Ogborn [2016] 2022) was created as part + of the Estuary Live Coding System + (Ogborn et al. 2017), and + proved to be a solid choice for handling samples in Strudel as well. We are however focused on working more + directly with the Web Audio API to be able to integrate new features more tightly. +

+

+ Using the OSC protocol via Strudel’s provided Node.js-based OSC proxy server, it is possible to send network + messages to trigger events. This is mainly used to render sound using SuperDirt + (SuperDirt [2015] 2022), which is the + well-developed Supercollider-based synthesis framework that Tidal live coders generally use as standard. +

+

+ Recently, the experimental integration of Csound proved to bring a new dimension of sound design capabilities to + Strudel. Thanks to the WebAssembly distribution of this classic system + (Yi, Lazzarini, and Costello 2018), Csound + ‘orchestra’ synthesisers can be embedded in and then patterned with Strudel code. +

+

+ MIDI output can also be used to send MIDI messages to either external instruments or to other programs on the same + device. Unlike OSC, Strudel is able to send MIDI directly without requiring additional proxy software, but only + from web browsers that support it (at the time of writing, this means Chromium-based browsers). +

+

+ Finally, Strudel supports Serial output, for example to trigger events via microcontrollers. This has already been + explored for robot choreography by Kate Sicchio and Alex McLean, via a performance presented at the International + Conference on Live Interfaces 2022. +

+

7 The Strudel REPL

+

+ While Strudel can be used as a library in any JavaScript codebase, its main, reference user interface is the + Strudel REPL[^REPL stands for read, evaluate, print/play, loop. It is friendly jargon for an interactive + programming interface from computing heritage, usually for a commandline interface but also applied to live coding + editors.], which is a browser-based live coding environment. This live code editor is dedicated to manipulating + Strudel patterns while they play. The REPL features built-in visual feedback, which highlights which elements in + the patterned (mini-notation) sequences are influencing the event that is currently being played. This feedback is + designed to support both learning and live use of Strudel. +

+

+ Besides a UI for playback control and meta information, the main part of the REPL interface is the code editor + powered by CodeMirror. In it, the user can edit and evaluate pattern code live, using one of the available + synthesis outputs to create music and/or sound art. The control flow of the REPL follows 3 basic steps: +

+
    +
  1. + The user writes and updates code. Each update transpiles and evaluates it to create a + Pattern instance +
  2. +
  3. + While the REPL is running, the Scheduler queries the active Pattern by a regular + interval, generating Events (also known as Haps in Strudel) for the next time span. +
  4. +
  5. + For each scheduling tick, all generated Events are triggered by calling their + onTrigger method, which is set by the output. +
  6. +
+
+ REPL control flow + +
+

7.1 User Code

+

To create a Pattern from the user code, two steps are needed:

+
    +
  1. Transpile the JS input code to make it functional
  2. +
  3. Evaluate the transpiled code
  4. +
+

+ 7.1.1 Transpilation & Evaluation +

+

+ In the JavaScript world, using transpilation is a common practise to be able to use language features that are not + supported by the base language. Tools like babel will transpile code that contains unsupported + language features into a version of the code without those features. +

+

+ In the same tradition, Strudel can add a transpilation step to simplify the user code in the context of live + coding. For example, the Strudel REPL lets the user create mini notation patterns using just double quoted + strings, while single quoted strings remain what they are: +

+
+
"c3 [e3 g3]*2"
+
+

is transpiled to:

+
+
mini("c3 [e3 g3]*2").withMiniLocation([1,0,0],[1,14,14])
+
+

+ Here, the string is wrapped in mini, which will create a pattern from a mini notation string. + Additionally, the withMiniLocation method passes the original source code location of the string to + the pattern, which enables highlighting active events. +

+

+ Other convenient features like pseudo variables, operator overloading and top level await are possible with + transpilation. +

+

After the transpilation, the code is ready to be evaluated into a Pattern.

+

+ Behind the scenes, the user code string is parsed with acorn, turning it into an Abstract Syntax Tree + (AST). The AST allows changing the structure of the code before generating the transpiled version using + escodegen. +

+

7.1.2 Mini Notation

+

+ While the transpilation allows JavaScript to express Patterns in a less verbose way, it is still preferable to use + the Mini Notation as a more compact way to express rhythm. Strudel aims to provide the same Mini Notation features + and syntax as used in Tidal. +

+

+ The Mini Notation parser is implemented using peggy, which allows generating performant parsers for + Domain Specific Languages (DSLs) using a concise grammar notation. The generated parser turns the Mini Notation + string into an AST which is used to call the respective Strudel functions with the given structure. For example, + "c3 [e3 g3]*2" will result in the following calls: +

+
+
seq(
+  reify('c3').withLocation([1,1,1], [1,4,4]),
+  seq(
+    reify('e3').withLocation([1,5,5], [1,8,8]),
+    reify('g3').withLocation([1,8,8], [1,10,10]),
+  ).fast(2)
+)
+
+

+ 7.1.3 Highlighting Locations +

+

+ As seen in the examples above, both the JS and the Mini Notation parser add source code locations using + withMiniLocation and withLocation methods. While the JS parser adds locations relative + to the user code as a whole, the Mini Notation adds locations relative to the position of the mini notation + string. The absolute location of elements within Mini Notation can be calculated by simply adding both locations + together. This absolute location can be used to highlight active events in real time. +

+

7.2 Scheduling Events

+

+ After an instance of Pattern is obtained from the user code, it is used by the scheduler to get + queried for events. Once started, the scheduler runs at a fixed interval to query active pattern for events + withing the current interval’s time span. A simplified implementation looks like this: +

+
+
let pattern = seq('c3', ['e3', 'g3']); // pattern from user
+let interval = 0.5; // query interval in seconds
+let time = 0; // beginning of current time span
+let minLatency = .1; // min time before a hap should trigger
+setInterval(() => {
+  const haps = pattern.queryArc(time, time + interval);
+  time += interval; // increment time
+  haps.forEach((hap) => {
+    const deadline = hap.whole.begin - time + minLatency;
+    onTrigger(hap, deadline, duration);
+  });
+}, interval * 1000); // query each "interval" seconds
+
+

+ Note that the above code is simplified for illustrative purposes. The actual implementation has to work around + imprecise callbacks of setInterval. More about the implementation details can be read in + this blog post. +

+

+ The fact that Pattern.queryArc is a pure function that maps a time span to a set of events allows us + to choose any interval we like without changing the resulting output. It also means that when the pattern is + changed from outside, the next scheduling callback will work with the new pattern, keeping its clock running. +

+

+ The latency between the time the pattern is evaluated and the change is heard is between + minLatency and interval + minLatency, in our example between 100ms and 600ms. In + Strudel, the current query interval is 50ms with a minLatency of 100ms, meaning the latency is between 50ms and + 150ms. +

+

7.3 Output

+

+ The last step is to trigger each event in the chosen output. This is where the given time and value of each event + is used to generate audio or any other form of time based output. The default output of the Strudel REPL is the + WebAudio output. To understand what an output does, we first have to understand what control parameters are. +

+

+ 7.3.1 Control Parameters +

+

+ To be able to manipulate multiple aspects of sound in parallel, so called control parameters are used to shape the + value of each event. Example: +

+
+
note("c3 e3").cutoff(1000).s('sawtooth')
+  .queryArc(0, 1).map(hap => hap.value)
+/* [
+  { note: 'c3', cutoff: 1000, s: 'sawtooth' }
+  { note: 'e3', cutoff: 1000, s: 'sawtooth' }
+] */
+
+

+ Here, the control parameter functions note, cutoff and s are used, where + each controls a different property in the value object. Each control parameter function accepts a primitive value, + a list of values to be sequenced into a Pattern, or a Pattern. In the example, + note gets a Pattern from a Mini Notation expression (double quoted), while + cutoff and s are given a Number and a (single quoted) + String respectively. +

+

+ Strudel comes with a large default set of control parameter functions that are based on the ones used by Tidal and + SuperDirt, focusing on music and audio terminology. It is however possible to create custom control paramters for + any purpose: +

+
+
const { x, y } = createParams('x', 'y')
+x(sine.range(0, 200)).y(cosine.range(0,200))
+
+

+ This example creates the custom control parameters x and y which are then used to form a + pattern that descibes the coordinates of a circle. +

+

7.3.2 Outputs

+

+ Now that we know how the value of an event is manipulated using control parameters, we can look at how outputs can + use that value to generate anything. The scheduler above was calling the onTrigger function which is + used to implement the output. A very simple version of the web audio output could look like this: +

+
+
function onTrigger(hap, deadline, duration) {
+  const { note } = hap.value;
+  const time = getAudioContext().currentTime + deadline;
+  const o = getAudioContext().createOscillator();
+  o.frequency.value = getFreq(note);
+  o.start(time);
+  o.stop(time + event.duration);
+  o.connect(getAudioContext().destination);
+}
+
+

+ The above example will create an OscillatorNode for each event, where the frequency is controlled by + the note param. In essence, this is how the WebAudio API output of Strudel works, only with many more + parameters to control synths, samples and effects. +

+

+ 8 Pattern alignment and combination +

+

+ One core aspect of Strudel, inherited from Tidal, is the flexible way that patterns can be combined, irrespective + of their structure. Its declarative approach means a live coder does not have to think about the details of + how this is done, only what is to be done. +

+

+ As a simple example, consider two number patterns "0 [1 2] 3", and "10 20". The first + has three contiguous steps of equal lengths, with the second step broken down into two substeps, giving four + events in total. There are a very large number of ways in which the structure of these two patterns could be + combined, but the default method in both Strudel and Tidal is to line up the cycles of the two patterns, and then + take events from the first pattern and match them with those in the second pattern. Therefore, the following two + lines are equivalent: +

+
+
"0 [1 2] 3".add("10 20")
+"10 [11 22] 23"
+
+

+ Where the events only partially overlap, they are treated as fragments of the event in the first pattern. This is + a little difficult to conceptualise, but lets start by comparing the two patterns in the following example: +

+
+
"0 1 2".add("10 20")
+"10 [11 21] 20"
+
+

+ They are similar to the previous example in that the number 1 is split in two, with its two halves + added to 10 and 20 respectively. However, the 11 ‘remembers’ that it is a + fragment of that original 1 event, and so is treated as having a duration of a third of a cycle, + despite only being active for a sixth of a cycle. Likewise, the 21 is also a fragment of that + original 1 event, but a fragment of its second half. Because the start of its event is missing, it + wouldn’t actually trigger a sound (unless it underwent further pattern transformations/combinations). +

+

+ In practice, the effect of this default, implicit method for combining two patterns is that the second pattern is + added in to the first one, and indeed this can be made explicit: +

+
+
"0 1 2".add.in("10 20")
+
+

This makes way for other ways to align the pattern, and several are already defined, in particular:

+
    +
  • + in - as explained above, aligns cycles, and applies values from the pattern on the right + in to the pattern on the left. +
  • +
  • + out - as with in, but values are applied out of the pattern on the left + (i.e. in to the one on the right). +
  • +
  • + mix - structures from both patterns are combined, so that the new events are not fragments but are + created at intersections of events from both sides. +
  • +
  • + squeeze - cycles from the pattern on the right are squeezed into events on the left. So that + e.g. "0 1 2".add.squeeze("10 20") is equivalent to "[10 20] [11 21] [12 22]". +
  • +
  • + squeezeout - as with squeeze, but cycles from the left are squeezed into events on the + right. So, "0 1 2".add.squeezeout("10 20") is equivalent to [10 11 12] [20 21 22]. +
  • +
  • + trig is similar to squeezeout in that cycles from the right are aligned with events on + the left. However those cycles are not ‘squeezed’, rather they are truncated to fit the event. So + "0 1 2 3 4 5 6 7".add.trig("10 [20 30]") would be equivalent to + 10 11 12 13 20 21 30 31. In effect, events on the right ‘trigger’ cycles on the left. +
  • +
  • + trigzero is similar to trig, but the pattern is ‘triggered’ from its very first cycle, + rather than from the current cycle. trig and trigzero therefore only give different + results where the leftmost pattern differs from one cycle to the next. +
  • +
+

+ We will save going deeper into the background, design and practicalities of these alignment functions for future + publications. However in the next section, we take them as a case study for looking at the different design + affordances offered by Haskell to Tidal, and JavaScript to Strudel. +

+

+ 9 Comparing Strudel and Haskell in use +

+

+ Unlike Haskell, JavaScript lacks the ability to define custom infix operators, or change the meaning of existing + ones. So the above Strudel example of "0 1 2".add.out("10 20") is equivalent to the Tidal expression + "0 1 2" +| "10 20", where the vertical bar in the operator +| stands for + out (where a |+ b would be equivalent of a.add.in(b)). +

+

+ From this we can already see that Tidal tends towards brevity through mixing infix operators with functions, and + Strudel tends towards spelling out operations which are joined together with the . operator. This + then is the design trade-off of Tidal’s tersity, versus Strudel’s simplicity. +

+

To demonstrate this, consider the following Tidal pattern:

+
iter 4 $ every 3 (||+ n "10 20") $ (n "0 1 3") # s "triangle" # crush 4
+

This can be directly translated to the Strudel equivalent:

+
+
iter(4, every(3, add.squeeze("10 20"), n("0 1 3").s("triangle").crush(4)))
+
+

Although for a more canonical Strudel expression, we would reorder it as:

+
+
n("0 1 3").every(3, add.squeeze("10 20")).iter(4).s("triangle").crush(4)
+
+

+ The Strudel example uses the . method call operator for all operations and combinations, whereas the + Tidal example has # for the default method for combining patterns and uses infix operators for other + methods. The lack of parenthesis in the Tidal example is partly due to the way that arguments are applied to + Haskell’s functions, and partly due to the use of the $ operator as an alternative way to establish + precedence and control the order of evaluation. +

+

+ Considering the above, we argue that the Haskell syntax is a little cleaner, but that the Strudel syntax is easier + to learn. Our informal observation is that while Haskell’s dollar $ operator is very useful in making + code easier to work with, it is one of the most difficult aspects of Tidal use for beginners to learn. On the + other hand, the deeper levels of parenthesis in Strudel code can be difficult to keep track of, especially while + coding under pressure of live musical performance. However this difficulty can be largely be mitigated by + reordering expressions, and further mitigated by supporting editor features. +

+

+ With Strudel, we have little choice but to embrace the affordances and constraints offered by JavaScript, and + while designing a domain-specific language entirely based on method calls is a challenge, through creative + adoption of functional programming techniques like partial application, we are so far very happy with the results. + Tidal’s functional reactive approach to pattern-making has in general translated well to JavaScript, and + opportunities and constraints have overall traded off to create a very approachable and useable live coding + environment. +

+

+ 9.1 The trade-off of flexible typing +

+

+ We have identified one problem with porting Tidal to JavaScript where we have missed Haskell’s strict typing and + type inference. In both Tidal and Strudel, time is rational, where any point in time is represented as the ratio + of two integers. This allows representation of musical ratios such that are impossible to represent accurately + using the more common floating point numbers. However while libraries are available that support rational numbers + in JavaScript, the lack of strict typing means that it is easy to implement pattern methods where computationally + expensive conversion from floating point to rational numbers are performed late, and therefore often enough to + overload the CPUs, due to the large number of iterative calculations required to estimate a ratio for a given + floating point number. To mitigate this problem, we might consider moving to TypeScript in the future. +

+

10 Future Outlook

+

+ The project is still young, with many features on the horizon. As general guiding principles, Strudel aims to be +

+
    +
  1. accessible
  2. +
  3. consistent with Tidal’s approach to pattern
  4. +
  5. modular and extensible
  6. +
+

+ While Haskell’s type system makes it a great language for the ongoing development of Tidal’s inner representation + of pattern, JavaScript’s vibrant ecosystem, flexibility and accessibility makes it a great host for more ad-hoc + experiments, including interface design. For the future, it is planned to integrate additional alternative sound + engines such as Glicol (Lan [2020] 2022) and + Faust + (Faust - Programming Language for Audio Applications and Plugins [2016] 2022). Strudel is already approaching feature parity with Tidal, but there are more Tidal functions to be ported, and + work to be done to improve compatibility with Tidal’s mininotation. Tidal version 2.0 is under development, which + brings a new representation for sequences to its patterns, which will then be brought to Strudel. Besides sound, + other ways to render events are being explored, such as graphical, and choreographic output. We are also looking + into alternative ways of editing patterns, including multi-user editing for network music, parsing a novel syntax + to escape the constraints of javascript, and developing hardware/e-textile interfaces. In summary, there is a lot + of fun ahead. +

+

11 Links

+

+ The Strudel REPL is available at https://strudel.cc, including an + interactive tutorial. The repository is at + https://github.com/tidalcycles/strudel, all the + code is open source under the AGPL-3.0 License. +

+

12 Acknowledgments

+

+ Thanks to the Strudel and wider Tidal, live coding, WebAudio and free/open source software communities for + inspiration and support. Alex McLean’s work on this project is supported by a UKRI Future Leaders Fellowship + [grant number MR/V025260/1]. +

+

References

+
+
+ Faust - Programming Language for Audio Applications and Plugins. (2016) 2022. C++. GRAME. + https://github.com/grame-cncm/faust. +
+
+ Jack, Olivia. (2022) 2022. Hydra. + https://github.com/ojack/hydra. +
+
+ Lan, Qichao. (2020) 2022. Chaosprint/Glicol. Rust. + https://github.com/chaosprint/glicol. +
+
+ Mclean, Alex. 2020. “Algorithmic Pattern.” In + Proceedings of the International Conference on New Interfaces for Musical Expression, 265--270. + Birmingham, UK. https://zenodo.org/record/4813352. +
+
+ McLean, Alex. 2020. “Feedforward.” In + Proceedings of New Interfaces for Musical Expression. Birmingham. + https://zenodo.org/record/6353969. +
+
+ McLean, Alex, Raphaël Forment, Sylvain Le Beux, and Damián Silvani. 2022. “TidalVortex Zero.” In + Proceedings of the 7th International Conference on Live Coding. Limerick, Ireland: Zenodo. + https://doi.org/10.5281/zenodo.6456380. +
+
+ Ogborn, David. (2016) 2022. Dktr0/WebDirt. JavaScript. + https://github.com/dktr0/WebDirt. +
+
+ Ogborn, David, Jamie Beverley, Luis Navarro del Angel, Eldad Tsabary, and Alex McLean. 2017. + “Estuary: Browser-Based Collaborative Projectional Live Coding of Musical Patterns.” In + Proceedings of the International Conference on Live Coding, 11. Morelia. +
+
+ Roberts, Charles, and Joann Kuchera-morin. 2012. “Gibber: Live Coding Audio in the Browser.” In + In Proceedings of the 2012 International Computer Music Conference. +
+
+ Roos, Felix, and Alex McLean. 2022. “Strudel: Algorithmic Patterns for the Web.” In. Zenodo. + https://doi.org/10.5281/zenodo.6768844. +
+
+ Solomon, Mike. (2021) 2022. Purescript-Ocarina. PureScript. + https://github.com/mikesol/purescript-ocarina. +
+
+ SuperDirt. (2015) 2022. SuperCollider. musikinformatik. + https://github.com/musikinformatik/SuperDirt. +
+
+ Toussaint, Godfried. 2005. “The Euclidean Algorithm Generates Traditional Musical Rhythms.” In + In Proceedings of BRIDGES: Mathematical Connections in Art, Music and Science, 47–56. + http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.62.231. +
+
+ Yi, Steven, Victor Lazzarini, and Edward Costello. 2018. + “WebAssembly AudioWorklet Csound.” In. Berlin, Germany. + https://mural.maynoothuniversity.ie/16018/. +
+
+ + diff --git a/paper/iclc2023.md b/docs/iclc2023-paper/iclc2023.md similarity index 99% rename from paper/iclc2023.md rename to docs/iclc2023-paper/iclc2023.md index 3afb27828..fdc9f0ca4 100644 --- a/paper/iclc2023.md +++ b/docs/iclc2023-paper/iclc2023.md @@ -451,7 +451,7 @@ While Haskell's type system makes it a great language for the ongoing developmen # Links The Strudel REPL is available at , including an interactive tutorial. -The repository is at , all the code is open source under the AGPL-3.0 License. +The repository is at , all the code is open source under the AGPL-3.0 License. # Acknowledgments diff --git a/paper/iclc2023.pdf b/docs/iclc2023-paper/iclc2023.pdf similarity index 100% rename from paper/iclc2023.pdf rename to docs/iclc2023-paper/iclc2023.pdf diff --git a/paper/iclc2023x.pdf b/docs/iclc2023-paper/iclc2023x.pdf similarity index 100% rename from paper/iclc2023x.pdf rename to docs/iclc2023-paper/iclc2023x.pdf diff --git a/paper/images/cc.png b/docs/iclc2023-paper/images/cc.png similarity index 100% rename from paper/images/cc.png rename to docs/iclc2023-paper/images/cc.png diff --git a/paper/images/strudel-screenshot.png b/docs/iclc2023-paper/images/strudel-screenshot.png similarity index 100% rename from paper/images/strudel-screenshot.png rename to docs/iclc2023-paper/images/strudel-screenshot.png diff --git a/paper/images/strudel-screenshot2.png b/docs/iclc2023-paper/images/strudel-screenshot2.png similarity index 100% rename from paper/images/strudel-screenshot2.png rename to docs/iclc2023-paper/images/strudel-screenshot2.png diff --git a/paper/images/strudelflow.png b/docs/iclc2023-paper/images/strudelflow.png similarity index 100% rename from paper/images/strudelflow.png rename to docs/iclc2023-paper/images/strudelflow.png diff --git a/paper/inconsolata.sty b/docs/iclc2023-paper/inconsolata.sty similarity index 100% rename from paper/inconsolata.sty rename to docs/iclc2023-paper/inconsolata.sty diff --git a/paper/make.sh b/docs/iclc2023-paper/make.sh similarity index 100% rename from paper/make.sh rename to docs/iclc2023-paper/make.sh diff --git a/docs/iclc2023-paper/pandoc/iclc.html b/docs/iclc2023-paper/pandoc/iclc.html new file mode 100755 index 000000000..c47d6595c --- /dev/null +++ b/docs/iclc2023-paper/pandoc/iclc.html @@ -0,0 +1,63 @@ +$if(false)$ This is a pandoc template and should not be edited. $endif$ + + + + + + + $for(author-meta)$ + + $endfor$ $if(date-meta)$ + + $endif$ + $if(title-prefix)$$title-prefix$ - $endif$$pagetitle$ + + $if(quotes)$ + + $endif$ $if(highlighting-css)$ + + $endif$ + + $for(css)$ + + $endfor$ $if(math)$ $math$ $endif$ $for(header-includes)$ $header-includes$ $endfor$ + + + $for(include-before)$ $include-before$ $endfor$ $if(title)$ +
+

$title$

+ $if(subtitle)$ +

$subtitle$

+ $endif$ +
    + $for(author)$ +
  • $author$
  • + $endfor$ +
+ $if(date)$ +

$date$

+ $endif$ +
+ $endif$ $if(toc)$ +
$toc$
+ $endif$ + +

Abstract

+
+ $if(abstract)$ $abstract$ $else$ Please provide an abstract in the metadata block at the top of your markdown + document. Refer to template.txt for details. $endif$ +
+ + $body$ $for(include-after)$ $include-after$ $endfor$ + + diff --git a/paper/pandoc/iclc.latex b/docs/iclc2023-paper/pandoc/iclc.latex similarity index 100% rename from paper/pandoc/iclc.latex rename to docs/iclc2023-paper/pandoc/iclc.latex diff --git a/paper/pandoc/iclc.sty b/docs/iclc2023-paper/pandoc/iclc.sty similarity index 100% rename from paper/pandoc/iclc.sty rename to docs/iclc2023-paper/pandoc/iclc.sty diff --git a/paper/paper-preprocessed.md b/docs/iclc2023-paper/paper-preprocessed.md similarity index 100% rename from paper/paper-preprocessed.md rename to docs/iclc2023-paper/paper-preprocessed.md diff --git a/paper/paper.md b/docs/iclc2023-paper/paper.md similarity index 100% rename from paper/paper.md rename to docs/iclc2023-paper/paper.md diff --git a/paper/paper.pdf b/docs/iclc2023-paper/paper.pdf similarity index 100% rename from paper/paper.pdf rename to docs/iclc2023-paper/paper.pdf diff --git a/paper/tex/latex-template.tex b/docs/iclc2023-paper/tex/latex-template.tex similarity index 100% rename from paper/tex/latex-template.tex rename to docs/iclc2023-paper/tex/latex-template.tex diff --git a/paper/tex/sig-alternate.cls b/docs/iclc2023-paper/tex/sig-alternate.cls similarity index 100% rename from paper/tex/sig-alternate.cls rename to docs/iclc2023-paper/tex/sig-alternate.cls diff --git a/paper/tex/waccopyright.sty b/docs/iclc2023-paper/tex/waccopyright.sty similarity index 100% rename from paper/tex/waccopyright.sty rename to docs/iclc2023-paper/tex/waccopyright.sty diff --git a/docs/technical-manual/index.md b/docs/technical-manual/index.md new file mode 100644 index 000000000..16c9a5533 --- /dev/null +++ b/docs/technical-manual/index.md @@ -0,0 +1,197 @@ +This document introduces you to Strudel in a technical sense. + +It is rather out of date, but there might still be useful info below. + +If you just want to *use* Strudel, have a look at the [Tutorial](https://strudel.tidalcycles.org/tutorial/). + +## Strudel Packages + +There are different packages for different purposes. They.. + +- split up the code into smaller chunks +- can be selectively used to implement some sort of time based system + +Please refer to the individual README files in the [packages folder](https://codeberg.org/uzu/strudel/src/branch/main/packages) + +## REPL + +The [REPL](https://strudel.tidalcycles.org/) is the place where all packages come together to form a live coding system. It can also be seen as a reference implementation for users of the library. + +More info in the [REPL README](https://codeberg.org/uzu/strudel/src/branch/main/packages/repl/README.md) + +# High Level Overview + + + +## 1. End User Code + +The End User Code is written in JavaScript with added syntax sugar. The [eval package](https://github.com/tidalcycles/strudel/tree/main/packages/eval#strudelcycleseval) evaluates the user code +after a transpilation step, which resolves the syntax sugar. If you don't want the syntax sugar, you can omit the eval package and call the native javascript `eval` instead. + +### 🍭 Syntax Sugar + +JavaScript Transpilation = converting valid JavaScript to valid JavaScript: + +```js +"c3 [e3 g3]".fast(2) +``` + +becomes + +```js +mini('c3 [e3 g3]') + .withMiniLocation([1, 0, 0], [1, 11, 11]) // source location + .fast(2); +``` + +- double quoted strings and backtick strings are turned into `mini` calls (single quoted strings are left as is) +- The source location is added by chaining `withMiniLocation`, which enables the real time highlighting later +- (psuedo) variable names that look like notes (like `c4`, `bb2` or `fs3`) are turned into strings +- support for top level await +- operator overloading could be implemented in the future + +This is how it works: + + + +- The user code is parsed with a [shift parser](https://github.com/shapesecurity/shift-parser-js), generating an AST +- The AST is transformed to resolve the syntax sugar +- The AST is used to generate code again (shift-codegen) + +Shift will most likely be replaced with acorn in the future, see https://github.com/tidalcycles/strudel/issues/174 + +### Mini Notation + +Another important part of the user code is the mini notation, which allows to express rhythms in a short manner. + +- the mini notation is [implemented as a PEG grammar](https://github.com/tidalcycles/strudel/blob/main/packages/mini/krill.pegjs), living in the [mini package](https://github.com/tidalcycles/strudel/tree/main/packages/mini) +- it is based on [krill](https://github.com/Mdashdotdashn/krill) by Mdashdotdashn +- the peg grammar is used to generate a parser with [peggyjs](https://peggyjs.org/) +- the generated parser takes a mini notation string and outputs an AST +- the AST can then be used to construct a pattern using the regular Strudel API + +Here's an example AST: + +```json +{ + "type_": "pattern", + "arguments_": { "alignment": "h" }, + "source_": [ + { + "type_": "element", "source_": "c3", + "location_": { "start": { "offset": 1, "line": 1, "column": 2 }, "end": { "offset": 4, "line": 1, "column": 5 } } + }, + { + "type_": "element", + "location_": { "start": { "offset": 4, "line": 1, "column": 5 }, "end": { "offset": 11, "line": 1, "column": 12 } } + "source_": { + "type_": "pattern", "arguments_": { "alignment": "h" }, + "source_": [ + { + "type_": "element", "source_": "e3", + "location_": { "start": { "offset": 5, "line": 1, "column": 6 }, "end": { "offset": 8, "line": 1, "column": 9 } } + }, + { + "type_": "element", "source_": "g3", + "location_": { "start": { "offset": 8, "line": 1, "column": 9 }, "end": { "offset": 10, "line": 1, "column": 11 } } + } + ] + }, + } + ] +} +``` + +which translates to `seq(c3, seq(e3, g3))` + +## 2. Querying & Scheduling + +When the user code has been evaluated, we hopefully get a Pattern instance, which we can use to query events from. +These events can then be used to trigger side effects in the real world. On that note, Events are mostly called Hap(s) in the codebase, because JS already has a built in `Event` class. + +### Querying + +> Querying = Asking a Pattern for Events within a certain time span + +```js +seq('c3', ['e3', 'g3']) // <--- Pattern + .queryArc(0, 2) // query events within 0 and 2 cycles + .map((hap) => hap.showWhole()); // make readable +``` + +yields + +```js +[ + '0/1 -> 1/2: c3', // cycle 0 + '1/2 -> 3/4: e3', + '3/4 -> 1/1: g3', + '1/1 -> 3/2: c3', // cycle 1 + '3/2 -> 7/4: e3', + '7/4 -> 2/1: g3', +]; +``` + +### 🗓️ Scheduling + +The scheduler will query events repeatedly, creating a possibly endless loop of time slices. +Here is a simplified example of how it works + +```js +let step = 0.5; // query interval in seconds +let tick = 0; // how many intervals have passed +let pattern = seq('c3', ['e3', 'g3']); // pattern from user +setInterval(() => { + const events = pattern.queryArc(tick * step, ++tick * step); + events.forEach((event) => { + console.log(event.showWhole()); + const o = getAudioContext().createOscillator(); + o.frequency.value = getFreq(event.value); + o.start(event.whole.begin); + o.stop(event.whole.begin + event.duration); + o.connect(getAudioContext().destination); + }); +}, step * 1000); // query each "step" seconds +``` + +## 3. Sound Output + +The third and last step is to use the scheduled events to make sound. +Patterns are wrapped with param functions to compose different properties of the sound. + +```js +note("[c2(3,8) [ bb1]]") // sets frequency + .s("") // sound source + .gain(.5) // turn down volume + .cutoff(sine.range(200,1000).slow(4)) // modulated cutoff + .slow(2) + .out().logValues()`, + ]} +/> +``` + +Here is an example Hap value with different properties: + +```js +{ note: 'a4', s: 'sawtooth', gain: 0.5, cutoff: 267 } +``` + + +
+ +- Patterns represent just values in time! +- Suitable for any time based output (music, visuals, movement, .. ?) + +### Supported Outputs + +At the time of writing this doc, the following outputs are supported: + +- Web Audio API `.out()` see [/webaudio](https://github.com/tidalcycles/strudel/tree/main/packages/webaudio) +- MIDI `.midi()` see [/midi](https://github.com/tidalcycles/strudel/tree/main/packages/midi) +- OSC `.osc()` see [/osc](https://github.com/tidalcycles/strudel/tree/main/packages/osc) +- Serial `.serial()` see [/serial](https://github.com/tidalcycles/strudel/tree/main/packages/serial) +- Tone.js `.tone()` (deprecated?) [/tone](https://github.com/tidalcycles/strudel/tree/main/packages/tone) +- WebDirt `.webdirt()` (deprecated?) [/webdirt](https://github.com/tidalcycles/strudel/tree/main/packages/webdirt) +- Speech `.speak()` (experimental) part of [/core](https://github.com/tidalcycles/strudel/tree/main/packages/core) + +These could change, so make sure to check the [packages folder](https://github.com/tidalcycles/strudel/tree/main/packages). diff --git a/docs/technical-manual/shiftflow.png b/docs/technical-manual/shiftflow.png new file mode 100644 index 000000000..8d91658dc Binary files /dev/null and b/docs/technical-manual/shiftflow.png differ diff --git a/docs/technical-manual/strudelflow.png b/docs/technical-manual/strudelflow.png new file mode 100644 index 000000000..72cdf494b Binary files /dev/null and b/docs/technical-manual/strudelflow.png differ diff --git a/docs/technical-manual/waa-nodes.png b/docs/technical-manual/waa-nodes.png new file mode 100644 index 000000000..cece1aa91 Binary files /dev/null and b/docs/technical-manual/waa-nodes.png differ diff --git a/examples/tidal-repl/package.json b/examples/tidal-repl/package.json index 4da2f086b..21c8ee177 100644 --- a/examples/tidal-repl/package.json +++ b/examples/tidal-repl/package.json @@ -11,7 +11,7 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/tidalcycles/strudel.git" + "url": "git+https://codeberg.org/uzu/strudel.git" }, "keywords": [ "titdalcycles", @@ -23,9 +23,9 @@ "author": "Felix Roos ", "license": "AGPL-3.0-or-later", "bugs": { - "url": "https://github.com/tidalcycles/strudel/issues" + "url": "https://codeberg.org/uzu/strudel/issues" }, - "homepage": "https://github.com/tidalcycles/strudel#readme", + "homepage": "https://codeberg.org/uzu/strudel#readme", "dependencies": { "@strudel/web": "workspace:*", "hs2js": "workspace:*" diff --git a/jsdoc/jsdoc-synonyms.js b/jsdoc/jsdoc-synonyms.js index 09190846f..0b52420bc 100644 --- a/jsdoc/jsdoc-synonyms.js +++ b/jsdoc/jsdoc-synonyms.js @@ -1,6 +1,6 @@ /* jsdoc-synonyms.js - Add support for @synonym tag -Copyright (C) 2023 Strudel contributors - see +Copyright (C) 2023 Strudel contributors - see 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 . */ diff --git a/my-patterns/README.md b/my-patterns/README.md index c8d694ea8..5283ad81b 100644 --- a/my-patterns/README.md +++ b/my-patterns/README.md @@ -5,22 +5,24 @@ made into a pattern swatch. Example: +Please note: These instructions have not been fully tested/adapted since strudel moved to codeberg from github. PRs welcome! + ## deploy -### 1. fork the [strudel repo on github](https://github.com/tidalcycles/strudel.git) +### 1. fork the [strudel repo on codeberg](https://codeberg.org/uzu/strudel.git) -### 2. clone your fork to your machine `git clone https://github.com//strudel.git strudel && cd strudel` +### 2. clone your fork to your machine `git clone https://codeberg.org//strudel.git strudel && cd strudel` ### 3. create a separate branch like `git branch patternuary && git checkout patternuary` ### 4. save one or more .txt files in the my-patterns folder -### 5. edit `website/public/CNAME` to contain `.github.io/strudel` +### 5. edit `website/public/CNAME` to contain `.codeberg.page/strudel` -### 6. edit `website/astro.config.mjs` to use site: `https://.github.io` and base `/strudel`, like this +### 6. edit `website/astro.config.mjs` to use site: `https://.codeberg.page` and base `/strudel`, like this ```js -const site = 'https://.github.io'; +const site = 'https://.codeberg.page'; const base = '/strudel'; ``` @@ -30,15 +32,9 @@ const base = '/strudel'; git add . && git commit -m "site config" && git push --set-upstream origin ``` -### 8. deploy to github pages +### 8. deploy to codeberg pages -- go to settings -> pages and select "Github Actions" as source -- go to settings -> environments -> github-pages and press the edit button next to `main` and type in `patternuary` (under "Deployment branches") -- go to Actions -> `Build and Deploy` and click `Run workflow` with branch `patternuary` - -### 9. view your patterns at `.github.io/strudel/swatch/` - -Alternatively, github pages allows you to use a custom domain, like https://mycooldomain.org/swatch/. [See their documentation for details](https://docs.github.com/en/pages/configuring-a-custom-domain-for-your-github-pages-site). +### 9. view your patterns at `.codeberg.page/strudel/swatch/` ### 10. optional: automatic deployment diff --git a/package.json b/package.json index 01c3e7d5d..d18291eb2 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/tidalcycles/strudel.git" + "url": "git+https://codeberg.org/uzu/strudel.git" }, "keywords": [ "tidalcycles", @@ -43,7 +43,7 @@ "author": "Alex McLean (https://slab.org)", "license": "AGPL-3.0-or-later", "bugs": { - "url": "https://github.com/tidalcycles/strudel/issues" + "url": "https://codeberg.org/uzu/strudel/issues" }, "homepage": "https://strudel.cc", "dependencies": { @@ -74,4 +74,4 @@ "vitest": "^3.0.4", "vite-plugin-bundle-audioworklet": "workspace:*" } -} +} \ No newline at end of file diff --git a/packages/README.md b/packages/README.md index 7a7681b67..98938d6c3 100644 --- a/packages/README.md +++ b/packages/README.md @@ -2,4 +2,4 @@ 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. +To understand how those pieces connect, refer to the [Technical Manual](https://codeberg.org/uzu/strudel/wiki/Technical-Manual) or the individual READMEs. diff --git a/packages/codemirror/codemirror.mjs b/packages/codemirror/codemirror.mjs index e96b533ec..193d96b55 100644 --- a/packages/codemirror/codemirror.mjs +++ b/packages/codemirror/codemirror.mjs @@ -62,7 +62,7 @@ export const codemirrorSettings = persistentAtom('codemirror-settings', defaultS }); // https://codemirror.net/docs/guide/ -export function initEditor({ initialCode = '', onChange, onEvaluate, onStop, root }) { +export function initEditor({ initialCode = '', onChange, onEvaluate, onStop, root, mondo }) { const settings = codemirrorSettings.get(); const initialSettings = Object.keys(compartments).map((key) => compartments[key].of(extensions[key](parseBooleans(settings[key]))), @@ -75,7 +75,7 @@ export function initEditor({ initialCode = '', onChange, onEvaluate, onStop, roo /* search(), highlightSelectionMatches(), */ ...initialSettings, - javascript(), + mondo ? [] : javascript(), sliderPlugin, widgetPlugin, // indentOnInput(), // works without. already brought with javascript extension? @@ -209,6 +209,7 @@ export class StrudelMirror { }, onEvaluate: () => this.evaluate(), onStop: () => this.stop(), + mondo: replOptions.mondo, }); const cmEditor = this.root.querySelector('.cm-editor'); if (cmEditor) { diff --git a/packages/codemirror/highlight.mjs b/packages/codemirror/highlight.mjs index d333986cc..f9f977c6b 100644 --- a/packages/codemirror/highlight.mjs +++ b/packages/codemirror/highlight.mjs @@ -1,4 +1,4 @@ -import { RangeSetBuilder, StateEffect, StateField } from '@codemirror/state'; +import { RangeSetBuilder, StateEffect, StateField, Prec } from '@codemirror/state'; import { Decoration, EditorView } from '@codemirror/view'; export const setMiniLocations = StateEffect.define(); @@ -134,5 +134,5 @@ export const isPatternHighlightingEnabled = (on, config) => { setTimeout(() => { updateMiniLocations(config.editor, config.miniLocations); }, 100); - return on ? highlightExtension : []; + return on ? Prec.highest(highlightExtension) : []; }; diff --git a/packages/codemirror/package.json b/packages/codemirror/package.json index 5036cd8a3..4f8508c90 100644 --- a/packages/codemirror/package.json +++ b/packages/codemirror/package.json @@ -13,7 +13,7 @@ "type": "module", "repository": { "type": "git", - "url": "git+https://github.com/tidalcycles/strudel.git" + "url": "git+https://codeberg.org/uzu/strudel.git" }, "keywords": [ "tidalcycles", @@ -28,9 +28,9 @@ ], "license": "AGPL-3.0-or-later", "bugs": { - "url": "https://github.com/tidalcycles/strudel/issues" + "url": "https://codeberg.org/uzu/strudel/issues" }, - "homepage": "https://github.com/tidalcycles/strudel#readme", + "homepage": "https://codeberg.org/uzu/strudel#readme", "dependencies": { "@codemirror/autocomplete": "^6.18.4", "@codemirror/commands": "^6.8.0", diff --git a/packages/codemirror/themes.mjs b/packages/codemirror/themes.mjs index 6aa70471d..1a523b391 100644 --- a/packages/codemirror/themes.mjs +++ b/packages/codemirror/themes.mjs @@ -8,6 +8,11 @@ import CutiePi, { settings as CutiePiSettings } from './themes/CutiePi.mjs'; import sonicPink, { settings as sonicPinkSettings } from './themes/sonic-pink.mjs'; import redText, { settings as redTextSettings } from './themes/red-text.mjs'; import greenText, { settings as greenTextSettings } from './themes/green-text.mjs'; +import archBtw, { settings as archBtwSettings } from './themes/archBtw.mjs'; +import fruitDaw, { settings as fruitDawSettings } from './themes/fruitDaw.mjs'; + +import bluescreenlight, { settings as bluescreenlightsettings } from './themes/bluescreenlight.mjs'; + import androidstudio, { settings as androidstudioSettings } from './themes/androidstudio.mjs'; import atomone, { settings as atomOneSettings } from './themes/atomone.mjs'; import aura, { settings as auraSettings } from './themes/aura.mjs'; @@ -39,17 +44,20 @@ import { setTheme } from '@strudel/draw'; export const themes = { strudelTheme, algoboy, + archBtw, androidstudio, atomone, aura, bbedit, blackscreen, bluescreen, + bluescreenlight, CutiePi, darcula, dracula, duotoneDark, eclipse, + fruitDaw, githubDark, githubLight, greenText, @@ -78,10 +86,12 @@ export const themes = { export const settings = { strudelTheme: strudelThemeSettings, bluescreen: bluescreenSettings, + bluescreenlight: bluescreenlightsettings, blackscreen: blackscreenSettings, whitescreen: whitescreenSettings, teletext: teletextSettings, algoboy: algoboySettings, + archBtw: archBtwSettings, androidstudio: androidstudioSettings, atomone: atomOneSettings, aura: auraSettings, @@ -92,9 +102,11 @@ export const settings = { eclipse: eclipseSettings, CutiePi: CutiePiSettings, sonicPink: sonicPinkSettings, + fruitDaw: fruitDawSettings, githubLight: githubLightSettings, githubDark: githubDarkSettings, greenText: greenTextSettings, + gruvboxDark: gruvboxDarkSettings, gruvboxLight: gruvboxLightSettings, materialDark: materialDarkSettings, diff --git a/packages/codemirror/themes/archBtw.mjs b/packages/codemirror/themes/archBtw.mjs new file mode 100644 index 000000000..87546569c --- /dev/null +++ b/packages/codemirror/themes/archBtw.mjs @@ -0,0 +1,38 @@ +/* + * Arch Btw + * Modern terminal inspired theme + * made by Jade + */ +import { tags as t } from '@lezer/highlight'; +import { createTheme } from './theme-helper.mjs'; + +const hex = ['rgb(0, 0, 0)', 'rgb(82, 208, 250)', 'rgba(113, 208, 250, .4)', 'rgba(113, 208, 250, .15)']; + +export const settings = { + background: hex[0], + lineBackground: 'transparent', + foreground: hex[1], + selection: hex[2], + selectionMatch: hex[0], + gutterBackground: hex[0], + gutterForeground: hex[2], + gutterBorder: 'transparent', + lineHighlight: hex[0], +}; + +export default createTheme({ + theme: 'dark', + settings, + styles: [ + { + tag: [t.function(t.variableName), t.function(t.propertyName), t.url, t.processingInstruction], + color: hex[1], + }, + { tag: [t.atom, t.bool, t.special(t.variableName)], color: hex[1] }, + { tag: [t.comment, t.brace, t.bracket], color: hex[2] }, + { tag: [t.variableName, t.propertyName, t.labelName], color: hex[1] }, + { tag: [t.attributeName, t.number], color: hex[1] }, + { tag: t.keyword, color: hex[1] }, + { tag: [t.string, t.regexp, t.special(t.propertyName)], color: hex[1] }, + ], +}); diff --git a/packages/codemirror/themes/bluescreenlight.mjs b/packages/codemirror/themes/bluescreenlight.mjs new file mode 100644 index 000000000..031a3da33 --- /dev/null +++ b/packages/codemirror/themes/bluescreenlight.mjs @@ -0,0 +1,37 @@ +/* + * A lighter blue screen theme + * made by Jade + */ +import { tags as t } from '@lezer/highlight'; +import { createTheme } from './theme-helper.mjs'; + +const hex = ['rgb(75, 130, 247)', 'rgb(47, 108, 246)', 'rgb(255, 255, 255)', 'rgba(255, 255, 255,.3)']; + +export const settings = { + background: hex[0], + lineBackground: 'transparent', + foreground: hex[2], + selection: hex[3], + selectionMatch: hex[0], + gutterBackground: hex[0], + gutterForeground: hex[2], + gutterBorder: 'transparent', + lineHighlight: hex[1], +}; + +export default createTheme({ + theme: 'dark', + settings, + styles: [ + { + tag: [t.function(t.variableName), t.function(t.propertyName), t.url, t.processingInstruction], + color: hex[2], + }, + { tag: [t.atom, t.bool, t.special(t.variableName)], color: hex[2] }, + { tag: [t.comment, t.bracket, t.brace, t.compareOperator], color: hex[3] }, + { tag: [t.variableName, t.propertyName, t.labelName], color: hex[2] }, + { tag: [t.attributeName, t.number], color: hex[2] }, + { tag: t.keyword, color: hex[2] }, + { tag: [t.string, t.regexp, t.special(t.propertyName)], color: hex[2] }, + ], +}); diff --git a/packages/codemirror/themes/fruitDaw.mjs b/packages/codemirror/themes/fruitDaw.mjs new file mode 100644 index 000000000..c9e557796 --- /dev/null +++ b/packages/codemirror/themes/fruitDaw.mjs @@ -0,0 +1,50 @@ +/* + * Fruit Daw + * made by Jade + */ +import { tags as t } from '@lezer/highlight'; +import { createTheme } from './theme-helper.mjs'; + +const hex = [ + 'rgb(84, 93, 98)', + 'rgb(255, 255, 255)', + 'rgba(255, 255, 255, .25)', + 'rgb(67, 76, 81)', + 'rgb(186, 230, 115)', + 'rgb(252, 184, 67)', + 'rgb(124, 206, 254)', + 'rgb(83, 101, 102)', + 'rgba(46, 62, 72,.5)', + 'rgb(94, 100, 108)', + 'rgb(167, 216, 177)', +]; + +export const settings = { + background: hex[0], + lineBackground: 'transparent', + foreground: hex[10], + selection: hex[8], + selectionMatch: hex[0], + gutterBackground: hex[3], + gutterForeground: hex[2], + gutterBorder: 'transparent', + lineHighlight: hex[3], +}; + +export default createTheme({ + theme: 'dark', + settings, + styles: [ + { + tag: [t.function(t.variableName), t.function(t.propertyName), t.url, t.processingInstruction], + color: hex[1], + }, + { tag: [t.bool, t.special(t.variableName)], color: hex[1] }, + { tag: [t.comment, t.brace, t.bracket], color: hex[2] }, + { tag: [t.variableName], color: hex[1] }, + { tag: [t.labelName, t.propertyName, t.self, t.atom], color: hex[5] }, + { tag: [t.attributeName, t.number], color: hex[6] }, + { tag: t.keyword, color: hex[5] }, + { tag: [t.string, t.regexp, t.special(t.propertyName)], color: hex[4] }, + ], +}); diff --git a/packages/core/clockworker.js b/packages/core/clockworker.js index bcaf28725..77a45362e 100644 --- a/packages/core/clockworker.js +++ b/packages/core/clockworker.js @@ -113,7 +113,7 @@ self.onconnect = function (e) { port.start(); // Required when using addEventListener. Otherwise called implicitly by onmessage setter. }; -// used to consistently schedule events, for use in a service worker - see +// used to consistently schedule events, for use in a service worker - see function createClock( getTime, callback, // called slightly before each cycle diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index 21c183da9..c37960d41 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -1,16 +1,17 @@ /* controls.mjs - Registers audio controls for pattern manipulation and effects. -Copyright (C) 2022 Strudel contributors - see +Copyright (C) 2022 Strudel contributors - see 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 . */ -import { Pattern, register, sequence } from './pattern.mjs'; +import { Pattern, register, reify } from './pattern.mjs'; export function createParam(names) { let isMulti = Array.isArray(names); names = !isMulti ? [names] : names; const name = names[0]; + // todo: make this less confusing const withVal = (xs) => { let bag; // check if we have an object with an unnamed control (.value) @@ -35,25 +36,34 @@ export function createParam(names) { } }; - const func = (...pats) => sequence(...pats).withValue(withVal); - - const setter = function (...pats) { - if (!pats.length) { - return this.fmap(withVal); + // todo: make this less confusing + const func = function (value, pat) { + if (!pat) { + return reify(value).withValue(withVal); } - return this.set(func(...pats)); + if (typeof value === 'undefined') { + return pat.fmap(withVal); + } + return pat.set(reify(value).withValue(withVal)); + }; + Pattern.prototype[name] = function (value) { + return func(value, this); }; - Pattern.prototype[name] = setter; return func; } // maps control alias names to the "main" control name const controlAlias = new Map(); +export function isControlName(name) { + return controlAlias.has(name); +} + export function registerControl(names, ...aliases) { const name = Array.isArray(names) ? names[0] : names; let bag = {}; bag[name] = createParam(names); + controlAlias.set(name, name); aliases.forEach((alias) => { bag[alias] = bag[name]; controlAlias.set(alias, name); @@ -96,7 +106,7 @@ export const { source, src } = registerControl('source', 'src'); * @example * s("bd sd [~ bd] sd,hh*6").n("<0 1>") */ -// also see https://github.com/tidalcycles/strudel/pull/63 +// also see https://codeberg.org/uzu/strudel/pulls/63 export const { n } = registerControl('n'); /** * Plays the given note name or midi number. A note name consists of @@ -348,7 +358,7 @@ export const { bandf, bpf, bp } = registerControl(['bandf', 'bandq', 'bpenv'], ' * s("bd sd [~ bd] sd").bpf(500).bpq("<0 1 2 3>") * */ -// currently an alias of 'bandq' https://github.com/tidalcycles/strudel/issues/496 +// currently an alias of 'bandq' https://codeberg.org/uzu/strudel/issues/496 // ['bpq'], export const { bandq, bpq } = registerControl('bandq', 'bpq'); /** @@ -855,7 +865,7 @@ export const { fanchor } = registerControl('fanchor'); * s("bd sd [~ bd] sd,hh*8").hpf("<2000 2000:25>") * */ -// currently an alias of 'hcutoff' https://github.com/tidalcycles/strudel/issues/496 +// currently an alias of 'hcutoff' https://codeberg.org/uzu/strudel/issues/496 // ['hpf'], /** * Applies a vibrato to the frequency of the oscillator. @@ -922,7 +932,7 @@ export const { hresonance, hpq } = registerControl('hresonance', 'hpq'); * s("bd sd [~ bd] sd,hh*8").lpf(2000).lpq("<0 10 20 30>") * */ -// currently an alias of 'resonance' https://github.com/tidalcycles/strudel/issues/496 +// currently an alias of 'resonance' https://codeberg.org/uzu/strudel/issues/496 export const { resonance, lpq } = registerControl('resonance', 'lpq'); /** * DJ filter, below 0.5 is low pass filter, above is high pass filter. @@ -975,15 +985,31 @@ export const { delayfeedback, delayfb, dfb } = registerControl('delayfeedback', * */ export const { delaytime, delayt, dt } = registerControl('delaytime', 'delayt', 'dt'); -/* // TODO: test + +/** + * Sets the time of the delay effect in cycles. + * + * @name delaysync + * @param {number | Pattern} cycles delay length in cycles + * @synonyms delayt, dt + * @example + * s("bd bd").delay(.25).delaysync("<1 2 3 5>".div(8)) + * + */ +export const { delaysync } = registerControl('delaysync'); + +/** * Specifies whether delaytime is calculated relative to cps. * * @name lock * @param {number | Pattern} enable When set to 1, delaytime is a direct multiple of a cycle. + * @superdirtOnly * @example * s("sd").delay().lock(1).osc() * + * */ + export const { lock } = registerControl('lock'); /** * Set detune for stacked voices of supported oscillators @@ -1288,7 +1314,7 @@ export const { semitone } = registerControl('semitone'); // TODO: synth param export const { voice } = registerControl('voice'); -// voicings // https://github.com/tidalcycles/strudel/issues/506 +// voicings // https://codeberg.org/uzu/strudel/issues/506 // chord to voice, like C Eb Fm7 G7. the symbols can be defined via addVoicings export const { chord } = registerControl('chord'); // which dictionary to use for the voicings diff --git a/packages/core/cyclist.mjs b/packages/core/cyclist.mjs index bd2db1223..f28dc604c 100644 --- a/packages/core/cyclist.mjs +++ b/packages/core/cyclist.mjs @@ -1,6 +1,6 @@ /* -cyclist.mjs - event scheduler for a single strudel instance. for multi-instance scheduler, see - see -Copyright (C) 2022 Strudel contributors - see +cyclist.mjs - event scheduler for a single strudel instance. for multi-instance scheduler, see - see +Copyright (C) 2022 Strudel contributors - see 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 . */ @@ -65,7 +65,7 @@ export class Cyclist { (hap.whole.begin - this.num_cycles_at_cps_change) / this.cps + this.seconds_at_cps_change + latency; const duration = hap.duration / this.cps; // the following line is dumb and only here for backwards compatibility - // see https://github.com/tidalcycles/strudel/pull/1004 + // see https://codeberg.org/uzu/strudel/pulls/1004 const deadline = targetTime - phase; onTrigger?.(hap, deadline, duration, this.cps, targetTime); if (hap.value.cps !== undefined && this.cps != hap.value.cps) { diff --git a/packages/core/drawLine.mjs b/packages/core/drawLine.mjs index 91b86b4ab..7509c0f6f 100644 --- a/packages/core/drawLine.mjs +++ b/packages/core/drawLine.mjs @@ -1,6 +1,6 @@ /* drawLine.mjs - -Copyright (C) 2022 Strudel contributors - see +Copyright (C) 2022 Strudel contributors - see 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 . */ diff --git a/packages/core/euclid.mjs b/packages/core/euclid.mjs index ad0b01486..25e12b07c 100644 --- a/packages/core/euclid.mjs +++ b/packages/core/euclid.mjs @@ -2,7 +2,7 @@ euclid.mjs - Bjorklund/Euclidean/Diaspora rhythms Copyright (C) 2023 Rohan Drape and strudel contributors -See for authors of this file. +See for authors of this file. The Bjorklund algorithm implementation is ported from the Haskell Music Theory Haskell module by Rohan Drape - https://rohandrape.net/?t=hmt @@ -10,9 +10,9 @@ https://rohandrape.net/?t=hmt 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 . */ -import { Pattern, timeCat, register, silence } from './pattern.mjs'; +import { timeCat, register, silence } from './pattern.mjs'; import { rotate, flatten, splitAt, zipWith } from './util.mjs'; -import Fraction from './fraction.mjs'; +import Fraction, { lcm } from './fraction.mjs'; const left = function (n, x) { const [ons, offs] = n; @@ -42,29 +42,26 @@ 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]); - const p = flatten(result[1][0]).concat(flatten(result[1][1])); - if (inverted) { - return p.map((x) => (x === 0 ? 1 : 0)); - } - return p; + const absOns = Math.abs(ons); + const offs = steps - absOns; + const ones = Array(absOns).fill([1]); + const zeros = Array(offs).fill([0]); + const result = _bjork([absOns, offs], [ones, zeros]); + const pattern = flatten(result[1][0]).concat(flatten(result[1][1])); + return inverted ? pattern.map((x) => 1 - x) : pattern; }; /** - * Changes the structure of the pattern to form an euclidean rhythm. - * Euclidian rhythms are rhythms obtained using the greatest common + * Changes the structure of the pattern to form an Euclidean rhythm. + * Euclidean rhythms are rhythms obtained using the greatest common * divisor of two numbers. They were described in 2004 by Godfried - * Toussaint, a canadian computer scientist. Euclidian rhythms are + * Toussaint, a Canadian computer scientist. Euclidean rhythms are * really useful for computer/algorithmic music because they can * describe a large number of rhythms with a couple of numbers. * * @memberof Pattern * @name euclid - * @param {number} pulses the number of onsets / beats + * @param {number} pulses the number of onsets/beats * @param {number} steps the number of steps to fill * @returns Pattern * @example @@ -76,7 +73,7 @@ export const bjork = function (ons, steps) { * Like `euclid`, but has an additional parameter for 'rotating' the resulting sequence. * @memberof Pattern * @name euclidRot - * @param {number} pulses the number of onsets / beats + * @param {number} pulses the number of onsets/beats * @param {number} steps the number of steps to fill * @param {number} rotation offset in steps * @returns Pattern @@ -86,13 +83,13 @@ export const bjork = function (ons, steps) { */ /** - * @example // A thirteenth century Persian rhythm called Khafif-e-ramal. + * @example // A thirteenth-century Persian rhythm called Khafif-e-ramal. * note("c3").euclid(2,5) * @example // The archetypal pattern of the Cumbia from Colombia, as well as a Calypso rhythm from Trinidad. * note("c3").euclid(3,4) * @example // Another thirteenth century Persian rhythm by the name of Khafif-e-ramal, as well as a Rumanian folk-dance rhythm. * note("c3").euclidRot(3,5,2) - * @example // A Ruchenitza rhythm used in a Bulgarian folk-dance. + * @example // A Ruchenitza rhythm used in a Bulgarian folk dance. * note("c3").euclid(3,7) * @example // The Cuban tresillo pattern. * note("c3").euclid(3,8) @@ -142,6 +139,14 @@ export const euclid = register('euclid', function (pulses, steps, pat) { return pat.struct(_euclidRot(pulses, steps, 0)); }); +export const e = register('e', function (euc, pat) { + if (!Array.isArray(euc)) { + euc = [euc]; + } + const [pulses, steps = pulses, rot = 0] = euc; + return pat.struct(_euclidRot(pulses, steps, rot)); +}); + export const { euclidrot, euclidRot } = register(['euclidrot', 'euclidRot'], function (pulses, steps, rotation, pat) { return pat.struct(_euclidRot(pulses, steps, rotation)); }); @@ -151,8 +156,10 @@ export const { euclidrot, euclidRot } = register(['euclidrot', 'euclidRot'], fun * so there will be no gaps. * @name euclidLegato * @memberof Pattern - * @param {number} pulses the number of onsets / beats + * @param {number} pulses the number of onsets/beats * @param {number} steps the number of steps to fill + * @param rotation offset in steps + * @param pat * @example * note("c3").euclidLegato(3,8) */ @@ -161,13 +168,13 @@ const _euclidLegato = function (pulses, steps, rotation, pat) { if (pulses < 1) { return silence; } - const bin_pat = _euclidRot(pulses, steps, rotation); + const bin_pat = _euclidRot(pulses, steps, 0); const gapless = bin_pat .join('') .split('1') .slice(1) .map((s) => [s.length + 1, true]); - return pat.struct(timeCat(...gapless)); + return pat.struct(timeCat(...gapless)).late(Fraction(rotation).div(steps)); }; export const euclidLegato = register(['euclidLegato'], function (pulses, steps, pat) { @@ -180,7 +187,7 @@ export const euclidLegato = register(['euclidLegato'], function (pulses, steps, * the resulting sequence * @name euclidLegatoRot * @memberof Pattern - * @param {number} pulses the number of onsets / beats + * @param {number} pulses the number of onsets/beats * @param {number} steps the number of steps to fill * @param {number} rotation offset in steps * @example diff --git a/packages/core/evaluate.mjs b/packages/core/evaluate.mjs index e3e73d596..0599d8662 100644 --- a/packages/core/evaluate.mjs +++ b/packages/core/evaluate.mjs @@ -1,9 +1,11 @@ /* evaluate.mjs - -Copyright (C) 2022 Strudel contributors - see +Copyright (C) 2022 Strudel contributors - see 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 . */ +export const strudelScope = {}; + export const evalScope = async (...args) => { const results = await Promise.allSettled(args); const modules = results.filter((result) => result.status === 'fulfilled').map((r) => r.value); @@ -18,6 +20,7 @@ export const evalScope = async (...args) => { modules.forEach((module) => { Object.entries(module).forEach(([name, value]) => { globalThis[name] = value; + strudelScope[name] = value; }); }); return modules; diff --git a/packages/core/fraction.mjs b/packages/core/fraction.mjs index dc7fe27a7..2e3bc68ea 100644 --- a/packages/core/fraction.mjs +++ b/packages/core/fraction.mjs @@ -1,6 +1,6 @@ /* fraction.mjs - -Copyright (C) 2022 Strudel contributors - see +Copyright (C) 2022 Strudel contributors - see 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 . */ diff --git a/packages/core/hap.mjs b/packages/core/hap.mjs index 7a9e0a620..a6e3c55ad 100644 --- a/packages/core/hap.mjs +++ b/packages/core/hap.mjs @@ -1,6 +1,6 @@ /* hap.mjs - -Copyright (C) 2022 Strudel contributors - see +Copyright (C) 2022 Strudel contributors - see 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 . */ import Fraction from './fraction.mjs'; diff --git a/packages/core/index.mjs b/packages/core/index.mjs index a10b68b09..e4daf445a 100644 --- a/packages/core/index.mjs +++ b/packages/core/index.mjs @@ -1,6 +1,6 @@ /* index.mjs - -Copyright (C) 2022 Strudel contributors - see +Copyright (C) 2022 Strudel contributors - see 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 . */ diff --git a/packages/core/neocyclist.mjs b/packages/core/neocyclist.mjs index ad22cf006..5c175dc1f 100644 --- a/packages/core/neocyclist.mjs +++ b/packages/core/neocyclist.mjs @@ -1,6 +1,6 @@ /* neocyclist.mjs - event scheduler like cyclist, except recieves clock pulses from clockworker in order to sync across multiple instances. -Copyright (C) 2022 Strudel contributors - see +Copyright (C) 2022 Strudel contributors - see 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 . */ diff --git a/packages/core/package.json b/packages/core/package.json index 89b047d2a..f4170f2b5 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -15,7 +15,7 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/tidalcycles/strudel.git" + "url": "git+https://codeberg.org/uzu/strudel.git" }, "keywords": [ "tidalcycles", @@ -27,7 +27,7 @@ "author": "Alex McLean (https://slab.org)", "license": "AGPL-3.0-or-later", "bugs": { - "url": "https://github.com/tidalcycles/strudel/issues" + "url": "https://codeberg.org/uzu/strudel/issues" }, "homepage": "https://strudel.cc", "dependencies": { diff --git a/packages/core/pattern.mjs b/packages/core/pattern.mjs index efbae1f3e..cbaf8a8a2 100644 --- a/packages/core/pattern.mjs +++ b/packages/core/pattern.mjs @@ -1,6 +1,6 @@ /* pattern.mjs - Core pattern representation for strudel -Copyright (C) 2025 Strudel contributors - see +Copyright (C) 2025 Strudel contributors - see 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 . */ @@ -869,6 +869,31 @@ export class Pattern { console.log(drawLine(this)); return this; } + + ////////////////////////////////////////////////////////////////////// + // methods relating to breaking patterns into subcycles + + // Breaks a pattern into a pattern of patterns, according to the structure of the given binary pattern. + unjoin(pieces, func = id) { + return pieces.withHap((hap) => + hap.withValue((v) => (v ? func(this.ribbon(hap.whole.begin, hap.whole.duration)) : this)), + ); + } + + /** + * Breaks a pattern into pieces according to the structure of a given pattern. + * True values in the given pattern cause the corresponding subcycle of the + * source pattern to be looped, and for an (optional) given function to be + * applied. False values result in the corresponding part of the source pattern + * to be played unchanged. + * @name into + * @memberof Pattern + * @example + * sound("bd sd ht lt").into("1 0", hurry(2)) + */ + into(pieces, func) { + return this.unjoin(pieces, func).innerJoin(); + } } ////////////////////////////////////////////////////////////////////// @@ -904,12 +929,13 @@ Pattern.prototype.collect = function () { * note("<[c,eb,g]!2 [c,f,ab] [d,f,ab]>") * .arpWith(haps => haps[2]) * */ -Pattern.prototype.arpWith = function (func) { - return this.collect() +export const arpWith = register('arpWith', (func, pat) => { + return pat + .collect() .fmap((v) => reify(func(v))) .innerJoin() .withHap((h) => new Hap(h.whole, h.part, h.value.value, h.combineContext(h.value))); -}; +}); /** * Selects indices in in stacked notes. @@ -917,9 +943,11 @@ Pattern.prototype.arpWith = function (func) { * note("<[c,eb,g]!2 [c,f,ab] [d,f,ab]>") * .arp("0 [0,2] 1 [0,2]") * */ -Pattern.prototype.arp = function (pat) { - return this.arpWith((haps) => pat.fmap((i) => haps[i % haps.length])); -}; +export const arp = register( + 'arp', + (indices, pat) => pat.arpWith((haps) => reify(indices).fmap((i) => haps[i % haps.length])), + false, +); /* * Takes a time duration followed by one or more patterns, and shifts the given patterns in time, so they are @@ -1981,6 +2009,7 @@ export const apply = register('apply', function (func, pat) { /** * Plays the pattern at the given cycles per minute. + * @deprecated * @example * s(",hh*2").cpm(90) // = 90 bpm */ @@ -2490,6 +2519,37 @@ export const { fastchunk, fastChunk } = register( true, ); +/** + * Like `chunk`, but the function is applied to a looped subcycle of the source pattern. + * @name chunkInto + * @synonym chunkinto + * @memberof Pattern + * @example + * sound("bd sd ht lt bd - cp lt").chunkInto(4, hurry(2)) + * .bank("tr909") + */ +export const { chunkinto, chunkInto } = register(['chunkinto', 'chunkInto'], function (n, func, pat) { + return pat.into(fastcat(true, ...Array(n - 1).fill(false))._iterback(n), func); +}); + +/** + * Like `chunkInto`, but moves backwards through the chunks. + * @name chunkBackInto + * @synonym chunkbackinto + * @memberof Pattern + * @example + * sound("bd sd ht lt bd - cp lt").chunkInto(4, hurry(2)) + * .bank("tr909") + */ +export const { chunkbackinto, chunkBackInto } = register(['chunkbackinto', 'chunkBackInto'], function (n, func, pat) { + return pat.into( + fastcat(true, ...Array(n - 1).fill(false)) + ._iter(n) + ._early(1), + func, + ); +}); + // TODO - redefine elsewhere in terms of mask export const bypass = register( 'bypass', @@ -2719,7 +2779,7 @@ export function stepcat(...timepats) { if (timepats.length === 0) { return nothing; } - const findsteps = (x) => (Array.isArray(x) ? x : [x._steps, x]); + const findsteps = (x) => (Array.isArray(x) ? x : [x._steps ?? 1, x]); timepats = timepats.map(findsteps); if (timepats.find((x) => x[0] === undefined)) { const times = timepats.map((a) => a[0]).filter((x) => x !== undefined); diff --git a/packages/core/pick.mjs b/packages/core/pick.mjs index 702201fa6..206fa619b 100644 --- a/packages/core/pick.mjs +++ b/packages/core/pick.mjs @@ -1,6 +1,6 @@ /* pick.mjs - methods that use one pattern to pick events from other patterns. -Copyright (C) 2024 Strudel contributors - see +Copyright (C) 2024 Strudel contributors - see 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 . */ diff --git a/packages/core/repl.mjs b/packages/core/repl.mjs index e703909ff..dcc5fcba4 100644 --- a/packages/core/repl.mjs +++ b/packages/core/repl.mjs @@ -21,6 +21,7 @@ export function repl({ setInterval, clearInterval, id, + mondo = false, }) { const state = { schedulerError: undefined, @@ -85,6 +86,17 @@ export function repl({ const pause = () => scheduler.pause(); const toggle = () => scheduler.toggle(); const setCps = (cps) => scheduler.setCps(cps); + + /** + * Changes the global tempo to the given cycles per minute + * + * @name setcpm + * @alias setCpm + * @param {number} cpm cycles per minute + * @example + * setcpm(140/4) // =140 bpm in 4/4 + * $: s("bd*4,[- sd]*2").bank('tr707') + */ const setCpm = (cpm) => scheduler.setCps(cpm / 60); // TODO - not documented as jsdoc examples as the test framework doesn't simulate enough context for `each` and `all`.. @@ -102,8 +114,9 @@ export function repl({ * all(x => x.pianoroll()) * ``` */ + let allTransforms = []; const all = function (transform) { - allTransform = transform; + allTransforms.push(transform); return silence; }; /** Applies a function to each of the running patterns separately. This is intended for future use with upcoming 'stepwise' features. See `all` for a version that applies the function to all the patterns stacked together into a single pattern. @@ -179,7 +192,12 @@ export function repl({ await injectPatternMethods(); setTime(() => scheduler.now()); // TODO: refactor? await beforeEval?.({ code }); + allTransforms = []; // reset all transforms shouldHush && hush(); + + if (mondo) { + code = `mondolang\`${code}\``; + } let { pattern, meta } = await _evaluate(code, transpiler, transpilerOptions); if (Object.keys(pPatterns).length) { let patterns = Object.values(pPatterns); @@ -191,8 +209,10 @@ export function repl({ } else if (eachTransform) { pattern = eachTransform(pattern); } - if (allTransform) { - pattern = allTransform(pattern); + if (allTransforms.length) { + for (let i in allTransforms) { + pattern = allTransforms[i](pattern); + } } if (!isPattern(pattern)) { const message = `got "${typeof evaluated}" instead of pattern`; @@ -225,7 +245,7 @@ export function repl({ export const getTrigger = ({ getTime, defaultOutput }) => async (hap, deadline, duration, cps, t) => { - // TODO: get rid of deadline after https://github.com/tidalcycles/strudel/pull/1004 + // TODO: get rid of deadline after https://codeberg.org/uzu/strudel/pulls/1004 try { if (!hap.context.onTrigger || !hap.context.dominantTrigger) { await defaultOutput(hap, deadline, duration, cps, t); diff --git a/packages/core/signal.mjs b/packages/core/signal.mjs index 5fd83bce3..c933e7fe2 100644 --- a/packages/core/signal.mjs +++ b/packages/core/signal.mjs @@ -1,6 +1,6 @@ /* signal.mjs - continuous patterns -Copyright (C) 2024 Strudel contributors - see +Copyright (C) 2024 Strudel contributors - see 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 . */ @@ -71,7 +71,6 @@ export const sine2 = signal((t) => Math.sin(Math.PI * 2 * t)); /** * A sine signal between 0 and 1. - * * @return {Pattern} * @example * n(sine.segment(16).range(0,15)) @@ -100,7 +99,6 @@ export const cosine2 = sine2._early(Fraction(1).div(4)); /** * A square signal between 0 and 1. - * * @return {Pattern} * @example * n(square.segment(4).range(0,7)).scale("C:minor") @@ -285,7 +283,7 @@ const _rearrangeWith = (ipat, n, pat) => { * @example * note("c d e f").sound("piano").shuffle(4) * @example - * note("c d e f".shuffle(4), "g").sound("piano") + * seq("c d e f".shuffle(4), "g").note().sound("piano") */ export const shuffle = register('shuffle', (n, pat) => { return _rearrangeWith(randrun(n), n, pat); @@ -298,7 +296,7 @@ export const shuffle = register('shuffle', (n, pat) => { * @example * note("c d e f").sound("piano").scramble(4) * @example - * note("c d e f".scramble(4), "g").sound("piano") + * seq("c d e f".scramble(4), "g").note().sound("piano") */ export const scramble = register('scramble', (n, pat) => { return _rearrangeWith(_irand(n)._segment(n), n, pat); @@ -398,6 +396,10 @@ export const chooseInWith = (pat, xs) => { */ export const choose = (...xs) => chooseWith(rand, xs); +// todo: doc +export const chooseIn = (...xs) => chooseInWith(rand, xs); +export const chooseOut = choose; + /** * Chooses from the given list of values (or patterns of values), according * to the pattern that the method is called on. The pattern should be in diff --git a/packages/core/speak.mjs b/packages/core/speak.mjs index 6ae959544..7e548a73b 100644 --- a/packages/core/speak.mjs +++ b/packages/core/speak.mjs @@ -1,6 +1,6 @@ /* speak.mjs - -Copyright (C) 2022 Strudel contributors - see +Copyright (C) 2022 Strudel contributors - see 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 . */ diff --git a/packages/core/state.mjs b/packages/core/state.mjs index db1f77eff..162dc7da9 100644 --- a/packages/core/state.mjs +++ b/packages/core/state.mjs @@ -1,6 +1,6 @@ /* state.mjs - -Copyright (C) 2022 Strudel contributors - see +Copyright (C) 2022 Strudel contributors - see 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 . */ diff --git a/packages/core/test/controls.test.mjs b/packages/core/test/controls.test.mjs index f0af02eb6..667c3a4e5 100644 --- a/packages/core/test/controls.test.mjs +++ b/packages/core/test/controls.test.mjs @@ -1,6 +1,6 @@ /* controls.test.mjs - -Copyright (C) 2023 Strudel contributors - see +Copyright (C) 2023 Strudel contributors - see 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 . */ diff --git a/packages/core/test/drawLine.test.mjs b/packages/core/test/drawLine.test.mjs index 84359a7cf..2240080cd 100644 --- a/packages/core/test/drawLine.test.mjs +++ b/packages/core/test/drawLine.test.mjs @@ -1,6 +1,6 @@ /* drawLine.test.mjs - -Copyright (C) 2022 Strudel contributors - see +Copyright (C) 2022 Strudel contributors - see 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 . */ diff --git a/packages/core/test/euclid.test.js b/packages/core/test/euclid.test.js new file mode 100644 index 000000000..a33ec9514 --- /dev/null +++ b/packages/core/test/euclid.test.js @@ -0,0 +1,89 @@ +import { bjork } from '../euclid.mjs'; +import { describe, expect, it } from 'vitest'; +import { fastcat } from '../pattern.mjs'; + +describe('bjork', () => { + it('should apply bjorklund to ons and steps', () => { + expect(bjork(3, 8)).toStrictEqual([1, 0, 0, 1, 0, 0, 1, 0]); + expect(bjork(-3, 8)).toStrictEqual([0, 1, 1, 0, 1, 1, 0, 1]); + expect(bjork(8, 8)).toStrictEqual([1, 1, 1, 1, 1, 1, 1, 1]); + expect(bjork(-8, 8)).toStrictEqual([0, 0, 0, 0, 0, 0, 0, 0]); + expect(bjork(5, 8)).toStrictEqual([1, 0, 1, 1, 0, 1, 1, 0]); + }); +}); + +describe('euclid', () => { + it('Can create euclid', () => { + expect( + fastcat('a') + .euclid(3, 8) + .firstCycle() + .sort((a, b) => a.part.begin.sub(b.part.begin)) + .map((a) => a.showWhole(true)), + ).toStrictEqual(['0/1 → 1/8: a', '3/8 → 1/2: a', '3/4 → 7/8: a']); + expect( + fastcat('a') + .euclid(5, 8) + .firstCycle() + .sort((a, b) => a.part.begin.sub(b.part.begin)) + .map((a) => a.showWhole(true)), + ).toStrictEqual(['0/1 → 1/8: a', '1/4 → 3/8: a', '3/8 → 1/2: a', '5/8 → 3/4: a', '3/4 → 7/8: a']); + }); +}); + +describe('euclidRot', () => { + it('Can create euclidRot', () => { + expect( + fastcat('a') + .euclidRot(3, 8, 2) + .firstCycle() + .sort((a, b) => a.part.begin.sub(b.part.begin)) + .map((a) => a.showWhole(true)), + ).toStrictEqual(['0/1 → 1/8: a', '1/4 → 3/8: a', '5/8 → 3/4: a']); + expect( + fastcat('a') + .euclidRot(5, 8, 2) + .firstCycle() + .sort((a, b) => a.part.begin.sub(b.part.begin)) + .map((a) => a.showWhole(true)), + ).toStrictEqual(['0/1 → 1/8: a', '1/4 → 3/8: a', '1/2 → 5/8: a', '5/8 → 3/4: a', '7/8 → 1/1: a']); + }); +}); + +describe('euclidLegato', () => { + it('Can create euclidLegato', () => { + expect( + fastcat('a') + .euclidLegato(3, 8) + .firstCycle() + .sort((a, b) => a.part.begin.sub(b.part.begin)) + .map((a) => a.showWhole(true)), + ).toStrictEqual(['0/1 → 3/8: a', '3/8 → 3/4: a', '3/4 → 1/1: a']); + expect( + fastcat('a') + .euclidLegato(5, 8) + .firstCycle() + .sort((a, b) => a.part.begin.sub(b.part.begin)) + .map((a) => a.showWhole(true)), + ).toStrictEqual(['0/1 → 1/4: a', '1/4 → 3/8: a', '3/8 → 5/8: a', '5/8 → 3/4: a', '3/4 → 1/1: a']); + }); +}); + +describe('euclidLegatoRot', () => { + it('Can create euclidLegatoRot', () => { + expect( + fastcat('a') + .euclidLegatoRot(3, 8, 2) + .firstCycle() + .sort((a, b) => a.part.begin.sub(b.part.begin)) + .map((a) => a.showWhole(true)), + ).toStrictEqual(['0/1 → 1/4: a', '1/4 → 5/8: a', '5/8 → 1/1: a']); + expect( + fastcat('a') + .euclidLegatoRot(5, 8, 2) + .firstCycle() + .sort((a, b) => a.part.begin.sub(b.part.begin)) + .map((a) => a.showWhole(true)), + ).toStrictEqual(['0/1 → 1/4: a', '1/4 → 1/2: a', '1/2 → 5/8: a', '5/8 → 7/8: a', '7/8 → 1/1: a']); + }); +}); diff --git a/packages/core/test/fraction.test.mjs b/packages/core/test/fraction.test.mjs index 6b710d043..b167c9483 100644 --- a/packages/core/test/fraction.test.mjs +++ b/packages/core/test/fraction.test.mjs @@ -1,6 +1,6 @@ /* fraction.test.mjs - -Copyright (C) 2022 Strudel contributors - see +Copyright (C) 2022 Strudel contributors - see 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 . */ diff --git a/packages/core/test/pattern.test.mjs b/packages/core/test/pattern.test.mjs index 08611032c..93c4168c9 100644 --- a/packages/core/test/pattern.test.mjs +++ b/packages/core/test/pattern.test.mjs @@ -1,6 +1,6 @@ /* pattern.test.mjs - -Copyright (C) 2022 Strudel contributors - see +Copyright (C) 2022 Strudel contributors - see 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 . */ @@ -52,6 +52,7 @@ import { stackCentre, stepcat, sometimes, + expand, } from '../index.mjs'; import { steady } from '../signal.mjs'; @@ -884,7 +885,7 @@ describe('Pattern', () => { ); }); it('Doesnt drop haps in the 9th cycle', () => { - // fixed with https://github.com/tidalcycles/strudel/commit/72eeaf446e3d5e186d63cc0d2276f0723cde017a + // fixed with https://codeberg.org/uzu/strudel/commit/72eeaf446e3d5e186d63cc0d2276f0723cde017a expect(sequence(1, 2, 3).ply(2).early(8).firstCycle().length).toBe(6); }); }); @@ -1001,7 +1002,7 @@ describe('Pattern', () => { }); describe('hurry', () => { it('Can speed up patterns and sounds', () => { - sameFirst(s('a', 'b').hurry(2), s('a', 'b').fast(2).speed(2)); + sameFirst(s(sequence('a', 'b')).hurry(2), s(sequence('a', 'b')).fast(2).speed(2)); }); }); /*describe('composable functions', () => { @@ -1179,6 +1180,9 @@ describe('Pattern', () => { it('calculates undefined steps as the average', () => { expect(sameFirst(stepcat(pure(1), pure(2), pure(3).setSteps(undefined)), fastcat(1, 2, 3))); }); + it('works with auto-reified values', () => { + expect(sameFirst(stepcat(expand(3, 'bd'), 'rim'), stepcat(expand(3, 'bd'), pure('rim')))); + }); }); describe('shrink', () => { it('can shrink', () => { @@ -1267,4 +1271,39 @@ describe('Pattern', () => { ); }); }); + describe('unjoin', () => { + it('destructures a pattern into subcycles', () => { + sameFirst( + fastcat('a', 'b', 'c', 'd') + .unjoin(fastcat(true, fastcat(true, true))) + .fmap(fast(2)) + .join(), + fastcat('a', 'b', 'a', 'b', 'c', 'c', 'd', 'd'), + ); + }); + }); + describe('into', () => { + it('applies a function to subcycles of a pattern', () => { + sameFirst( + fastcat('a', 'b', 'c', 'd').into(fastcat(fastcat('true', 'true'), 'true'), fast(2)), + fastcat('a', 'a', 'b', 'b', 'c', 'd', 'c', 'd'), + ); + }); + }); + describe('chunkinto', () => { + it('chunks into subcycles', () => { + sameFirst( + fastcat('a', 'b', 'c').chunkInto(3, fast(2)).fast(3), + fastcat(fastcat('a', 'a'), 'b', 'c', 'a', fastcat('b', 'b'), 'c', 'a', 'b', fastcat('c', 'c')), + ); + }); + }); + describe('chunkbackinto', () => { + it('chunks into subcycles backwards', () => { + sameFirst( + fastcat('a', 'b', 'c').chunkBackInto(3, fast(2)).fast(3), + fastcat('a', 'b', fastcat('c', 'c'), 'a', fastcat('b', 'b'), 'c', fastcat('a', 'a'), 'b', 'c'), + ); + }); + }); }); diff --git a/packages/core/test/util.test.mjs b/packages/core/test/util.test.mjs index 6b1053a3f..d3033a82c 100644 --- a/packages/core/test/util.test.mjs +++ b/packages/core/test/util.test.mjs @@ -1,26 +1,30 @@ /* util.test.mjs - Tests for the core 'util' module -Copyright (C) 2022 Strudel contributors - see +Copyright (C) 2022 Strudel contributors - see 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 . */ import { pure } from '../pattern.mjs'; import { - isNote, - tokenizeNote, - noteToMidi, - midiToFreq, - freqToMidi, _mod, compose, + flatten, + fractionalArgs, + freqToMidi, getFrequency, getPlayableNoteValue, - parseNumeral, - parseFractional, + isNote, + midiToFreq, + noteToMidi, numeralArgs, - fractionalArgs, + parseFractional, + parseNumeral, + rotate, + splitAt, + tokenizeNote, + zipWith, } from '../util.mjs'; -import { describe, it, expect } from 'vitest'; +import { describe, expect, it } from 'vitest'; describe('isNote', () => { it('should recognize notes without accidentals', () => { @@ -233,3 +237,46 @@ describe('fractionalArgs', () => { expect(add('q', 2)).toBe(2.25); }); }); + +describe('rotate', () => { + it('should rotate array to the left', () => { + expect(rotate([0, 1, 2, 3], 2)).toStrictEqual([2, 3, 0, 1]); + expect(rotate([0, 1, 2, 3], 0)).toStrictEqual([0, 1, 2, 3]); + expect(rotate([0, 1, 2, 3], -3)).toStrictEqual([1, 2, 3, 0]); + expect(rotate([0, 1, 2, 3], 3)).toStrictEqual([3, 0, 1, 2]); + expect(rotate([0], 3)).toStrictEqual([0]); + expect(rotate([], 3)).toStrictEqual([]); + }); +}); + +describe('flatten', () => { + it('should flatten array by one level', () => { + expect(flatten([0, 1, 2, 3])).toStrictEqual([0, 1, 2, 3]); + expect(flatten([0, 1, [2, 3]])).toStrictEqual([0, 1, 2, 3]); + expect(flatten([0, [1, [2, 3]]])).toStrictEqual([0, 1, [2, 3]]); + expect(flatten([0])).toStrictEqual([0]); + expect(flatten([])).toStrictEqual([]); + }); +}); + +describe('splitAt', () => { + it('should split array into two', () => { + expect(splitAt(2, [0, 1, 2, 3])).toStrictEqual([ + [0, 1], + [2, 3], + ]); + expect(splitAt(0, [0, 1, 2, 3])).toStrictEqual([[], [0, 1, 2, 3]]); + expect(splitAt(-3, [0, 1, 2, 3])).toStrictEqual([[0], [1, 2, 3]]); + expect(splitAt(3, [0, 1, 2, 3])).toStrictEqual([[0, 1, 2], [3]]); + }); +}); + +describe('zipWith', () => { + it('should use the function to combine the two arrays element-wise', () => { + expect(zipWith((a, b) => a + b, [0, 1, 2, 3], [0, 1, 2, 3])).toStrictEqual([0, 2, 4, 6]); + expect(zipWith((a, b) => a + b, [0, 1, 2, 3], [0, 1, 2])).toStrictEqual([0, 2, 4, NaN]); + expect(zipWith((a, b) => a + b, [0, 1, 2], [0, 1, 2, 3])).toStrictEqual([0, 2, 4]); + expect(zipWith((a) => a, [0, 1, 2], [1, 2, 3, 0])).toStrictEqual([0, 1, 2]); + expect(zipWith((a, b) => b, [0, 1, 2], [1, 2, 3, 0])).toStrictEqual([1, 2, 3]); + }); +}); diff --git a/packages/core/test/value.test.mjs b/packages/core/test/value.test.mjs index 87cba57d0..35d9f5e10 100644 --- a/packages/core/test/value.test.mjs +++ b/packages/core/test/value.test.mjs @@ -1,6 +1,6 @@ /* value.test.mjs - -Copyright (C) 2022 Strudel contributors - see +Copyright (C) 2025 Strudel contributors - see 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 . */ diff --git a/packages/core/timespan.mjs b/packages/core/timespan.mjs index 4cbfb999a..0dbc74fc8 100644 --- a/packages/core/timespan.mjs +++ b/packages/core/timespan.mjs @@ -1,6 +1,6 @@ /* timespan.mjs - -Copyright (C) 2022 Strudel contributors - see +Copyright (C) 2022 Strudel contributors - see 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 . */ diff --git a/packages/core/ui.mjs b/packages/core/ui.mjs index 86ceb2863..5a2c54a30 100644 --- a/packages/core/ui.mjs +++ b/packages/core/ui.mjs @@ -1,6 +1,6 @@ /* ui.mjs - -Copyright (C) 2022 Strudel contributors - see +Copyright (C) 2022 Strudel contributors - see 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 . */ diff --git a/packages/core/util.mjs b/packages/core/util.mjs index b7b1e8413..756fac8e8 100644 --- a/packages/core/util.mjs +++ b/packages/core/util.mjs @@ -1,6 +1,6 @@ /* util.mjs - -Copyright (C) 2022 Strudel contributors - see +Copyright (C) 2022 Strudel contributors - see 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 . */ @@ -162,6 +162,7 @@ export const compose = (...funcs) => pipe(...funcs.reverse()); // Removes 'None' values from given list export const removeUndefineds = (xs) => xs.filter((x) => x != undefined); +// flattens by one level export const flatten = (arr) => [].concat(...arr); export const id = (a) => a; @@ -237,6 +238,7 @@ export const splitAt = function (index, value) { return [value.slice(0, index), value.slice(index)]; }; +// Uses the function f to combine the arrays xs, ys element-wise export const zipWith = (f, xs, ys) => xs.map((n, i) => f(n, ys[i])); export const pairs = function (xs) { diff --git a/packages/core/value.mjs b/packages/core/value.mjs index ef98bc370..9496405eb 100644 --- a/packages/core/value.mjs +++ b/packages/core/value.mjs @@ -1,6 +1,6 @@ /* value.mjs - -Copyright (C) 2022 Strudel contributors - see +Copyright (C) 2022 Strudel contributors - see 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 . */ @@ -9,7 +9,7 @@ import { logger } from './logger.mjs'; export function unionWithObj(a, b, func) { if (b?.value !== undefined && Object.keys(b).length === 1) { - // https://github.com/tidalcycles/strudel/issues/1026 + // https://codeberg.org/uzu/strudel/issues/1026 logger(`[warn]: Can't do arithmetic on control pattern.`); return a; } diff --git a/packages/csound/package.json b/packages/csound/package.json index f200cdaf4..04a5ff246 100644 --- a/packages/csound/package.json +++ b/packages/csound/package.json @@ -13,7 +13,7 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/tidalcycles/strudel.git" + "url": "git+https://codeberg.org/uzu/strudel.git" }, "keywords": [ "tidalcycles", @@ -28,9 +28,9 @@ ], "license": "AGPL-3.0-or-later", "bugs": { - "url": "https://github.com/tidalcycles/strudel/issues" + "url": "https://codeberg.org/uzu/strudel/issues" }, - "homepage": "https://github.com/tidalcycles/strudel#readme", + "homepage": "https://codeberg.org/uzu/strudel#readme", "dependencies": { "@csound/browser": "6.18.7", "@strudel/core": "workspace:*", diff --git a/packages/desktopbridge/index.mjs b/packages/desktopbridge/index.mjs index 591bbe34f..ffb88783f 100644 --- a/packages/desktopbridge/index.mjs +++ b/packages/desktopbridge/index.mjs @@ -1,6 +1,6 @@ /* index.mjs - -Copyright (C) 2022 Strudel contributors - see +Copyright (C) 2022 Strudel contributors - see 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 . */ diff --git a/packages/desktopbridge/package.json b/packages/desktopbridge/package.json index 45e89f44e..a01ec1f8a 100644 --- a/packages/desktopbridge/package.json +++ b/packages/desktopbridge/package.json @@ -7,7 +7,7 @@ "type": "module", "repository": { "type": "git", - "url": "git+https://github.com/tidalcycles/strudel.git" + "url": "git+https://codeberg.org/uzu/strudel.git" }, "keywords": [ "tidalcycles", @@ -19,11 +19,11 @@ "author": "Jade Rowland ", "license": "AGPL-3.0-or-later", "bugs": { - "url": "https://github.com/tidalcycles/strudel/issues" + "url": "https://codeberg.org/uzu/strudel/issues" }, "dependencies": { "@strudel/core": "workspace:*", "@tauri-apps/api": "^2.2.0" }, - "homepage": "https://github.com/tidalcycles/strudel#readme" -} \ No newline at end of file + "homepage": "https://codeberg.org/uzu/strudel#readme" +} diff --git a/packages/draw/draw.mjs b/packages/draw/draw.mjs index 0576c297b..a429395ff 100644 --- a/packages/draw/draw.mjs +++ b/packages/draw/draw.mjs @@ -1,6 +1,6 @@ /* draw.mjs - -Copyright (C) 2022 Strudel contributors - see +Copyright (C) 2022 Strudel contributors - see 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 . */ @@ -150,7 +150,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.endClipped >= phase - lookbehind - lookahead) + .filter((h) => h.whole && 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; @@ -175,7 +175,7 @@ export class Drawer { // +0.1 = workaround for weird holes in query.. const [begin, end] = [Math.max(t, 0), t + lookahead + 0.1]; // remove all future haps - this.visibleHaps = this.visibleHaps.filter((h) => h.whole.begin < t); + this.visibleHaps = this.visibleHaps.filter((h) => h.whole?.begin < t); this.painters = []; // will get populated by .onPaint calls attached to the pattern // query future haps const futureHaps = scheduler.pattern.queryArc(begin, end, { painters: this.painters }); diff --git a/packages/draw/package.json b/packages/draw/package.json index 51750da2b..ee1b8dd00 100644 --- a/packages/draw/package.json +++ b/packages/draw/package.json @@ -13,7 +13,7 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/tidalcycles/strudel.git" + "url": "git+https://codeberg.org/uzu/strudel.git" }, "keywords": [ "titdalcycles", @@ -25,9 +25,9 @@ "author": "Felix Roos ", "license": "AGPL-3.0-or-later", "bugs": { - "url": "https://github.com/tidalcycles/strudel/issues" + "url": "https://codeberg.org/uzu/strudel/issues" }, - "homepage": "https://github.com/tidalcycles/strudel#readme", + "homepage": "https://codeberg.org/uzu/strudel#readme", "dependencies": { "@strudel/core": "workspace:*" }, diff --git a/packages/draw/pianoroll.mjs b/packages/draw/pianoroll.mjs index d874c9686..1cf218fa0 100644 --- a/packages/draw/pianoroll.mjs +++ b/packages/draw/pianoroll.mjs @@ -1,6 +1,6 @@ /* pianoroll.mjs - -Copyright (C) 2022 Strudel contributors - see +Copyright (C) 2022 Strudel contributors - see 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 . */ diff --git a/packages/embed/package.json b/packages/embed/package.json index afd887887..a0cc33de1 100644 --- a/packages/embed/package.json +++ b/packages/embed/package.json @@ -6,7 +6,7 @@ "type": "module", "repository": { "type": "git", - "url": "git+https://github.com/tidalcycles/strudel.git" + "url": "git+https://codeberg.org/uzu/strudel.git" }, "keywords": [ "tidalcycles", @@ -18,7 +18,7 @@ "author": "Felix Roos ", "license": "AGPL-3.0-or-later", "bugs": { - "url": "https://github.com/tidalcycles/strudel/issues" + "url": "https://codeberg.org/uzu/strudel/issues" }, - "homepage": "https://github.com/tidalcycles/strudel#readme" + "homepage": "https://codeberg.org/uzu/strudel#readme" } diff --git a/packages/gamepad/docs/gamepad.mdx b/packages/gamepad/docs/gamepad.mdx index 219d8ae0b..e2da594ea 100644 --- a/packages/gamepad/docs/gamepad.mdx +++ b/packages/gamepad/docs/gamepad.mdx @@ -56,14 +56,15 @@ You can use button inputs to control different aspects of your music, such as ga @@ -74,14 +75,13 @@ Analog sticks can be used for continuous control, such as pitch shifting or pann ### Button Sequences @@ -89,6 +89,7 @@ $: note("c4 d3 a3 e3").sound("sawtooth") You can define button sequences to trigger specific actions, like playing a sound when a sequence is detected. ", "license": "AGPL-3.0-or-later", "bugs": { - "url": "https://github.com/tidalcycles/strudel/issues" + "url": "https://codeberg.org/uzu/strudel/issues" }, - "homepage": "https://github.com/tidalcycles/strudel#readme", + "homepage": "https://codeberg.org/uzu/strudel#readme", "dependencies": { "@strudel/core": "workspace:*" }, diff --git a/packages/hs2js/README.md b/packages/hs2js/README.md index 24d9c7749..f84f9cded 100644 --- a/packages/hs2js/README.md +++ b/packages/hs2js/README.md @@ -2,7 +2,7 @@ Experimental haskell in javascript interpreter. Many haskell features are not implemented. This projects mainly exists to be able to write and interpret [Tidal Cycles](https://tidalcycles.org/) code in the browser, -as part of [Strudel](https://github.com/tidalcycles/strudel). This project could only exist thanks to [tree-sitter-haskell](https://github.com/tree-sitter/tree-sitter-haskell). +as part of [Strudel](https://codeberg.org/uzu/strudel). This project could only exist thanks to [tree-sitter-haskell](https://github.com/tree-sitter/tree-sitter-haskell). ## Installation diff --git a/packages/hs2js/package.json b/packages/hs2js/package.json index c650dfc83..c0bf8fa8c 100644 --- a/packages/hs2js/package.json +++ b/packages/hs2js/package.json @@ -16,7 +16,7 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/tidalcycles/strudel.git" + "url": "git+https://codeberg.org/uzu/strudel.git" }, "keywords": [ "haskell", @@ -25,9 +25,9 @@ "author": "Felix Roos ", "license": "AGPL-3.0-or-later", "bugs": { - "url": "https://github.com/tidalcycles/strudel/issues" + "url": "https://codeberg.org/uzu/strudel/issues" }, - "homepage": "https://github.com/tidalcycles/strudel", + "homepage": "https://codeberg.org/uzu/strudel/", "dependencies": { "web-tree-sitter": "^0.24.7" }, diff --git a/packages/hydra/package.json b/packages/hydra/package.json index 4375ebefc..b022de87d 100644 --- a/packages/hydra/package.json +++ b/packages/hydra/package.json @@ -17,7 +17,7 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/tidalcycles/strudel.git" + "url": "git+https://codeberg.org/uzu/strudel.git" }, "keywords": [ "tidalcycles", @@ -29,9 +29,9 @@ "author": "Felix Roos ", "license": "AGPL-3.0-or-later", "bugs": { - "url": "https://github.com/tidalcycles/strudel/issues" + "url": "https://codeberg.org/uzu/strudel/issues" }, - "homepage": "https://github.com/tidalcycles/strudel#readme", + "homepage": "https://codeberg.org/uzu/strudel#readme", "dependencies": { "@strudel/core": "workspace:*", "@strudel/draw": "workspace:*", diff --git a/packages/midi/midi.mjs b/packages/midi/midi.mjs index ce7cdb0e2..af2dd3a62 100644 --- a/packages/midi/midi.mjs +++ b/packages/midi/midi.mjs @@ -1,6 +1,6 @@ /* midi.mjs - -Copyright (C) 2022 Strudel contributors - see +Copyright (C) 2022 Strudel contributors - see 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 . */ diff --git a/packages/midi/package.json b/packages/midi/package.json index 2956b99f3..4efd329d8 100644 --- a/packages/midi/package.json +++ b/packages/midi/package.json @@ -13,7 +13,7 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/tidalcycles/strudel.git" + "url": "git+https://codeberg.org/uzu/strudel.git" }, "keywords": [ "titdalcycles", @@ -25,9 +25,9 @@ "author": "Felix Roos ", "license": "AGPL-3.0-or-later", "bugs": { - "url": "https://github.com/tidalcycles/strudel/issues" + "url": "https://codeberg.org/uzu/strudel/issues" }, - "homepage": "https://github.com/tidalcycles/strudel#readme", + "homepage": "https://codeberg.org/uzu/strudel#readme", "dependencies": { "@strudel/core": "workspace:*", "@strudel/webaudio": "workspace:*", diff --git a/packages/mini/krill.pegjs b/packages/mini/krill.pegjs index a593b4167..8af82caee 100644 --- a/packages/mini/krill.pegjs +++ b/packages/mini/krill.pegjs @@ -1,6 +1,6 @@ /* krill.pegjs - -Copyright (C) 2022 Strudel contributors - see +Copyright (C) 2022 Strudel contributors - see 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 . */ diff --git a/packages/mini/mini.mjs b/packages/mini/mini.mjs index 6277daa91..d138d9fa0 100644 --- a/packages/mini/mini.mjs +++ b/packages/mini/mini.mjs @@ -1,6 +1,6 @@ /* mini.mjs - -Copyright (C) 2022 Strudel contributors - see +Copyright (C) 2022 Strudel contributors - see 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 . */ diff --git a/packages/mini/package.json b/packages/mini/package.json index 5ae0dc242..5d94301d4 100644 --- a/packages/mini/package.json +++ b/packages/mini/package.json @@ -16,7 +16,7 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/tidalcycles/strudel.git" + "url": "git+https://codeberg.org/uzu/strudel.git" }, "keywords": [ "tidalcycles", @@ -28,9 +28,9 @@ "author": "Felix Roos ", "license": "AGPL-3.0-or-later", "bugs": { - "url": "https://github.com/tidalcycles/strudel/issues" + "url": "https://codeberg.org/uzu/strudel/issues" }, - "homepage": "https://github.com/tidalcycles/strudel#readme", + "homepage": "https://codeberg.org/uzu/strudel#readme", "dependencies": { "@strudel/core": "workspace:*" }, diff --git a/packages/mini/test/mini.test.mjs b/packages/mini/test/mini.test.mjs index 15d501111..c38997308 100644 --- a/packages/mini/test/mini.test.mjs +++ b/packages/mini/test/mini.test.mjs @@ -1,6 +1,6 @@ /* mini.test.mjs - -Copyright (C) 2022 Strudel contributors - see +Copyright (C) 2022 Strudel contributors - see 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 . */ diff --git a/packages/mondo/README.md b/packages/mondo/README.md new file mode 100644 index 000000000..954f425a9 --- /dev/null +++ b/packages/mondo/README.md @@ -0,0 +1,39 @@ +# mondo + +a lisp-based language intended to be used as a custom dsl for patterns that can stand on its own feet. +see the `test` folder for usage examples + +more info: + +- [uzulang I](https://garten.salat.dev/uzu/uzulang1.html) +- [uzulang II](https://garten.salat.dev/uzu/uzulang2.html) + +## Example Usage + +```js +import { MondoRunner } from 'mondolang' +// define our library of functions and variables +let lib = { + add: (a, b) => a + b, + mul: (a, b) => a * b, + PI: Math.PI, +}; +// this function will evaluate nodes in the syntax tree +function evaluator(node) { + // check if node is a leaf node (!= list) + if (node.type !== 'list') { + // check lib if we find a match in the lib, otherwise return value + return lib[node.value] ?? node.value; + } + // now it can only be a list.. + const [fn, ...args] = node.children; + // children in a list will already be evaluated + // the first child is expected to be a function + if (typeof fn !== 'function') { + throw new Error(`"${fn}" is not a function`); + } + return fn(...args); +} +const runner = new MondoRunner({ evaluator }); +const pat = runner.run('add 1 (mul 2 PI)') // 7.283185307179586 +``` diff --git a/packages/mondo/mondo.mjs b/packages/mondo/mondo.mjs new file mode 100644 index 000000000..b879f24bf --- /dev/null +++ b/packages/mondo/mondo.mjs @@ -0,0 +1,484 @@ +/* +mondo.mjs - +Copyright (C) 2022 Strudel contributors - see +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 . +*/ + +// evolved from https://garten.salat.dev/lisp/parser.html +export class MondoParser { + // these are the tokens we expect + token_types = { + comment: /^\/\/(.*?)(?=\n|$)/, + quotes_double: /^"(.*?)"/, + quotes_single: /^'(.*?)'/, + open_list: /^\(/, + close_list: /^\)/, + open_angle: /^/, + open_square: /^\[/, + close_square: /^\]/, + open_curly: /^\{/, + close_curly: /^\}/, + number: /^-?[0-9]*\.?[0-9]+/, // before pipe! + // TODO: better error handling when "-" is used as rest, e.g "s [- bd]" + op: /^[*/:!@%?+-]|^\.{2}/, // * / : ! @ % ? .. + // dollar: /^\$/, + pipe: /^#/, + stack: /^[,$]/, + or: /^[|]/, + plain: /^[a-zA-Z0-9-~_^#]+/, + }; + // matches next token + next_token(code, offset = 0) { + for (let type in this.token_types) { + const match = code.match(this.token_types[type]); + if (match) { + let token = { type, value: match[0] }; + if (offset !== -1) { + // add location + token.loc = [offset, offset + match[0].length]; + } + return token; + } + } + throw new Error(`mondo: could not match '${code}'`); + } + // takes code string, returns list of matched tokens (if valid) + tokenize(code, offset = 0) { + let tokens = []; + let locEnabled = offset !== -1; + let trim = () => { + // trim whitespace at start, update offset + offset += code.length - code.trimStart().length; + // trim start and end to not confuse parser + return code.trim(); + }; + code = trim(); + while (code.length > 0) { + code = trim(); + const token = this.next_token(code, locEnabled ? offset : -1); + code = code.slice(token.value.length); + offset += token.value.length; + tokens.push(token); + } + return tokens; + } + // take code, return abstract syntax tree + parse(code, offset) { + this.code = code; + this.offset = offset; + this.tokens = this.tokenize(code, offset); + const expressions = []; + while (this.tokens.length) { + expressions.push(this.parse_expr()); + } + if (expressions.length === 0) { + // empty case + return { type: 'list', children: [] }; + } + // do we have multiple top level expressions or a single non list? + if (expressions.length > 1 || expressions[0].type !== 'list') { + return { + type: 'list', + children: this.desugar(expressions), + }; + } + // we have a single list + return expressions[0]; + } + // parses any valid expression + parse_expr() { + if (!this.tokens[0]) { + throw new Error(`unexpected end of file`); + // TODO: could we allow that? like (((((((( s bd + // return { type: 'list', children: [] }; + } + let next = this.tokens[0]?.type; + if (next === 'open_list') { + return this.parse_list(); + } + if (next === 'open_angle') { + return this.parse_angle(); + } + if (next === 'open_square') { + return this.parse_square(); + } + if (next === 'open_curly') { + return this.parse_curly(); + } + return this.consume(next); + } + // Token[] => Token[][], e.g. (x , y z) => [['x'],['y','z']] + split_children(children, split_type) { + const chunks = []; + while (true) { + let splitIndex = children.findIndex((child) => child.type === split_type); + if (splitIndex === -1) break; + const chunk = children.slice(0, splitIndex); + chunks.push(chunk); + children = children.slice(splitIndex + 1); + } + chunks.push(children); + return chunks; + } + desugar_split(children, split_type, next) { + const chunks = this.split_children(children, split_type); + if (chunks.length === 1) { + return next(children); + } + // collect args of stack function + const args = chunks + .map((chunk) => { + if (!chunk.length) { + return; // useful for things like "$ s bd $ s hh*8" (first chunk is empty) + } + if (chunk.length === 1) { + // chunks of one element can be added to the stack as is + return chunk[0]; + } + // chunks of multiple args + chunk = next(chunk); + return { type: 'list', children: chunk }; + }) + .filter(Boolean); // ignore empty chunks + return [{ type: 'plain', value: split_type }, ...args]; + } + // prevents to get a list, e.g. ((x y)) => (x y) + unwrap_children(children) { + if (children.length === 1) { + return children[0].children; + } + return children; + } + desugar_ops(children) { + while (true) { + let opIndex = children.findIndex((child) => child.type === 'op'); + if (opIndex === -1) break; + const op = { type: 'plain', value: children[opIndex].value }; + if (opIndex === children.length - 1) { + //throw new Error(`cannot use operator as last child.`); + children[opIndex] = op; // ignore operator if last child.. e.g. "note [c -]" + continue; + } + if (opIndex === 0) { + // regular function call (assuming each operator exists as function) + children[opIndex] = op; + continue; + } + // convert infix to prefix notation + const left = children[opIndex - 1]; + const right = children[opIndex + 1]; + if (left.type === 'pipe') { + // "x !* 2" => (* 2 x) + children[opIndex] = op; + continue; + } + // some careful error handling + if (left.type === 'op') { + throw new Error(`got 2 ops in a row: "${left.value}${op.value}"`); + } + if (right.type === 'op') { + let err = `got 2 ops in a row: "${op.value}${right.value}"`; + if (op.value === '-') { + // yes i know this file is not supposed to know about rests x.X + err += '. you probably want a rest, which is "_" in mondo!'; + } + throw new Error(err); + } + const call = { type: 'list', children: [op, right, left] }; + // insert call while keeping other siblings + children = [...children.slice(0, opIndex - 1), call, ...children.slice(opIndex + 2)]; + children = this.unwrap_children(children); + } + return children; + } + get_lambda(args, children) { + // (.fast 2) = (fn (_) (fast _ 2)) + children = this.desugar(children); + const body = children.length === 1 ? children[0] : { type: 'list', children }; + return [{ type: 'plain', value: 'fn' }, { type: 'list', children: args }, body]; + } + // returns location range of given ast (even if desugared) + get_range(ast, range = [Infinity, 0]) { + let union = (a, b) => [Math.min(a[0], b[0]), Math.max(a[1], b[1])]; + if (ast.loc) { + return union(range, ast.loc); + } + if (ast.type !== 'list') { + return range; + } + return ast.children.reduce((range, child) => { + const childrange = this.get_range(child, range); + return union(range, childrange); + }, range); + } + errorhead(ast) { + return `[mondo ${this.get_range(ast)?.join(':') || '?'}]`; + } + // returns original user code where the given ast originates (even if desugared) + get_code_snippet(ast) { + const [min, max] = this.get_range(ast); + return this.code.slice(min - this.offset, max - this.offset); + } + desugar_pipes(children) { + let chunks = this.split_children(children, 'pipe'); + while (chunks.length > 1) { + let [left, right, ...rest] = chunks; + + if (!left.length) { + const arg = { type: 'plain', value: '_' }; + return this.get_lambda([arg], [arg, ...children]); + } + // s jazz hh.fast 2 => (fast 2 (s jazz hh)) + const call = left.length > 1 ? { type: 'list', children: left } : left[0]; + chunks = [[...right, call], ...rest]; + } + // return next(chunks[0]); + return chunks[0]; + } + parse_pair(open_type, close_type) { + const begin = this.tokens[0].loc?.[0]; + this.consume(open_type); + const children = []; + while (this.tokens[0]?.type !== close_type) { + children.push(this.parse_expr()); + } + const end = this.tokens[0].loc?.[1]; + this.consume(close_type); + const node = { type: 'list', children }; + if (begin !== undefined) { + node.loc = [begin, end]; + node.raw = this.code.slice(begin, end); + } + return node; + } + desugar(children, type) { + // if type is given, the first element is expected to contain it as plain value + // e.g. with (square a b, c), we want to split (a b, c) and ignore "square" + children = type ? children.slice(1) : children; + children = this.desugar_split(children, 'stack', (children) => + this.desugar_split(children, 'or', (children) => { + // chunks of multiple args + if (type) { + // the type we've removed before splitting needs to be added back + children = [{ type: 'plain', value: type }, ...children]; + } + children = this.desugar_ops(children); + // children = this.desugar_pipes(children, (children) => this.desugar_dollars(children)); + children = this.desugar_pipes(children); + return children; + }), + ); + return children; + } + parse_list() { + let node = this.parse_pair('open_list', 'close_list'); + node.children = this.desugar(node.children); + return node; + } + parse_angle() { + let node = this.parse_pair('open_angle', 'close_angle'); + node.children.unshift({ type: 'plain', value: 'angle' }); + node.children = this.desugar(node.children, 'angle'); + return node; + } + parse_square() { + let node = this.parse_pair('open_square', 'close_square'); + node.children.unshift({ type: 'plain', value: 'square' }); + node.children = this.desugar(node.children, 'square'); + return node; + } + parse_curly() { + let node = this.parse_pair('open_curly', 'close_curly'); + node.children.unshift({ type: 'plain', value: 'curly' }); + node.children = this.desugar(node.children, 'curly'); + return node; + } + consume(type) { + // shift removes first element and returns it + const token = this.tokens.shift(); + if (token.type !== type) { + throw new Error(`expected token type ${type}, got ${token.type}`); + } + return token; + } + get_locations(code, offset = 0) { + let walk = (ast, locations = []) => { + if (ast.type === 'list') { + return ast.children.forEach((child) => walk(child, locations)); + } + if (ast.loc) { + locations.push(ast.loc); + } + }; + const ast = this.parse(code, offset); + let locations = []; + walk(ast, locations); + return locations; + } +} + +export function printAst(ast, compact = false, lvl = 0) { + const br = compact ? '' : '\n'; + const spaces = compact ? '' : Array(lvl).fill(' ').join(''); + if (ast.type === 'list') { + return `${lvl ? br : ''}${spaces}(${ast.children.map((child) => printAst(child, compact, lvl + 1)).join(' ')}${ + ast.children.find((child) => child.type === 'list') ? `${br}${spaces})` : ')' + }`; + } + return `${ast.value}`; +} + +// lisp runner +export class MondoRunner { + constructor({ evaluator } = {}) { + this.parser = new MondoParser(); + this.evaluator = evaluator; + this.assert(typeof evaluator === 'function', `expected an evaluator function to be passed to new MondoRunner`); + } + // a helper to check conditions and throw if they are not met + assert(condition, error) { + if (!condition) { + throw new Error(error); + } + } + run(code, scope, offset = 0) { + const ast = this.parser.parse(code, offset); + //console.log(printAst(ast)); + return this.evaluate(ast, scope); + } + evaluate_let(ast, scope) { + // (let ((x 3) (y 4)) ...body) + // = ((fn (x y) ...body) 3 4) + const defs = ast.children[1].children; + const args = defs.map((pair) => pair.children[0]); + const vals = defs.map((pair) => pair.children[1]); + const body = ast.children.slice(2); + const lambda = { + type: 'list', + children: [{ type: 'plain', value: 'fn' }, { type: 'list', children: args }, ...body], + }; + return this.evaluate({ type: 'list', children: [lambda, ...vals] }, scope); + } + evaluate_def(ast, scope) { + // function definition special form? + if (ast.children[1].type === 'list') { + // (def (add a b) (+ a b)) + // => (def add (fn (a b) (+ a b)) ) + const args = ast.children[1].children.slice(1); + const lambda = { + // lambda + type: 'list', + children: [ + { type: 'plain', value: 'fn' }, + { type: 'list', children: args }, + ...ast.children.slice(2), // body + ], + }; + // we mutate to make sure the old ast wont make a mess later + ast.children[1] = ast.children[1].children[0]; + ast.children[2] = lambda; + ast.children = ast.children.slice(0, 3); // throw away rest + } + // (def name body) + if (ast.children.length !== 3) { + throw new Error(`expected "def" to have 3 children, but got ${ast.children.length}`); + } + const name = ast.children[1].value; + const body = this.evaluate(ast.children[2], scope); + scope[name] = body; + // def with fall through + } + evaluate_match(ast, scope) { + // (match (p1 e1) (p2 e2) ... (pn en)) + // = cond in lisp + if (ast.children.length < 2) { + return; + } + const [_, ...body] = ast.children; + for (let i = 0; i < body.length; ++i) { + const [predicate, exp] = body[i].children; + if (predicate.value === 'else') { + return this.evaluate(exp, scope); + } + const outcome = this.evaluate(predicate, scope); + if (outcome) { + return this.evaluate(exp, scope); + } + } + return undefined; // nothing was matched + } + evaluate_if(ast, scope) { + // if is a special case of match + if (ast.children.length !== 4) { + return; + } + // (if predicate consequent alternative) + const [_, predicate, consequent, alternative] = ast.children; + // (match (predicate consequent) (else alternative)) + const matcher = { + type: 'list', + children: [ + { type: 'plain', value: 'match' }, + { type: 'list', children: [predicate, consequent] }, + { type: 'list', children: [{ type: 'plain', value: 'else' }, alternative] }, + ], + }; + return this.evaluate_match(matcher, scope); + } + evaluate_lambda(ast, scope) { + // (fn (_) (ply 2 _) + // ^args ^ body + const [_, formalArgs, ...body] = ast.children; + return (...args) => { + const params = Object.fromEntries(formalArgs.children.map((arg, i) => [arg.value, args[i]])); + const closure = { + ...scope, + ...params, + }; + // body can have multiple expressions + const res = body.map((exp) => this.evaluate(exp, closure)); + // last expression is the return value + return res[res.length - 1]; + }; + } + evaluate_list(ast, scope) { + // evaluate all children before evaluating list (dont mutate!!!) + const args = ast.children + .filter((child) => child.type !== 'comment') // ignore comments + .map((arg) => this.evaluate(arg, scope)); + const node = { type: 'list', children: args }; + return this.evaluator(node, scope); + } + evaluate_leaf(ast, scope) { + if (ast.type === 'number') { + ast.value = Number(ast.value); + } else if (['quotes_double', 'quotes_single'].includes(ast.type)) { + ast.value = ast.value.slice(1, -1); + ast.type = 'string'; + } + return this.evaluator(ast, scope); + } + evaluate(ast, scope = {}) { + if (ast.type !== 'list') { + return this.evaluate_leaf(ast, scope); + } + const name = ast.children[0]?.value; + if (name === 'fn') { + return this.evaluate_lambda(ast, scope); + } + if (name === 'match') { + return this.evaluate_match(ast, scope); + } + if (name === 'if') { + return this.evaluate_if(ast, scope); + } + if (name === 'let') { + return this.evaluate_let(ast, scope); + } + if (name === 'def') { + this.evaluate_def(ast, scope); + } + return this.evaluate_list(ast, scope); + } +} diff --git a/packages/mondo/package.json b/packages/mondo/package.json new file mode 100644 index 000000000..277bddb1c --- /dev/null +++ b/packages/mondo/package.json @@ -0,0 +1,37 @@ +{ + "name": "mondolang", + "version": "1.1.0", + "description": "a language for functional composition that translates to js", + "main": "mondo.mjs", + "type": "module", + "publishConfig": { + "main": "dist/mondo.mjs" + }, + "scripts": { + "test": "vitest run", + "bench": "vitest bench", + "build": "vite build", + "prepublishOnly": "npm run build" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/tidalcycles/strudel.git" + }, + "keywords": [ + "tidalcycles", + "strudel", + "pattern", + "livecoding", + "algorave" + ], + "author": "Felix Roos ", + "license": "AGPL-3.0-or-later", + "bugs": { + "url": "https://github.com/tidalcycles/strudel/issues" + }, + "homepage": "https://github.com/tidalcycles/strudel/blob/main/packages/mondo/README.md", + "devDependencies": { + "vite": "^6.0.11", + "vitest": "^3.0.4" + } +} diff --git a/packages/mondo/test/mondo.test.mjs b/packages/mondo/test/mondo.test.mjs new file mode 100644 index 000000000..6393b10f9 --- /dev/null +++ b/packages/mondo/test/mondo.test.mjs @@ -0,0 +1,978 @@ +/* +mondo.test.mjs - +Copyright (C) 2022 Strudel contributors - see +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 . +*/ + +import { describe, expect, it } from 'vitest'; +import { MondoParser, printAst, MondoRunner } from '../mondo.mjs'; + +const parser = new MondoParser(); +const p = (code) => parser.parse(code, -1); + +describe('mondo tokenizer', () => { + const parser = new MondoParser(); + it('should tokenize with locations', () => + expect( + parser + .tokenize('(one two three)') + .map((t) => t.value + '=' + t.loc.join('-')) + .join(' '), + ).toEqual('(=0-1 one=1-4 two=5-8 three=9-14 )=14-15')); + // it('should parse with loangleions', () => expect(parser.parse('(one two three)')).toEqual()); + it('should get loangleions', () => + expect(parser.get_locations('s bd rim')).toEqual([ + [0, 1], + [2, 4], + [5, 8], + ])); +}); +describe('mondo s-expressions parser', () => { + it('should parse an empty string', () => expect(p('')).toEqual({ type: 'list', children: [] })); + it('should parse a single item', () => + expect(p('a')).toEqual({ type: 'list', children: [{ type: 'plain', value: 'a' }] })); + it('should parse an empty list', () => expect(p('()')).toEqual({ type: 'list', children: [] })); + it('should parse a list with 1 item', () => + expect(p('(a)')).toEqual({ type: 'list', children: [{ type: 'plain', value: 'a' }] })); + it('should parse a list with 2 items', () => + expect(p('(a b)')).toEqual({ + type: 'list', + children: [ + { type: 'plain', value: 'a' }, + { type: 'plain', value: 'b' }, + ], + })); + it('should parse a list with 2 items', () => + expect(p('(a (b c))')).toEqual({ + type: 'list', + children: [ + { type: 'plain', value: 'a' }, + { + type: 'list', + children: [ + { type: 'plain', value: 'b' }, + { type: 'plain', value: 'c' }, + ], + }, + ], + })); + it('should parse numbers', () => + expect(p('(1 .2 1.2 10 22.3)')).toEqual({ + type: 'list', + children: [ + { type: 'number', value: '1' }, + { type: 'number', value: '.2' }, + { type: 'number', value: '1.2' }, + { type: 'number', value: '10' }, + { type: 'number', value: '22.3' }, + ], + })); + it('should parse comments', () => + expect(p('a // hello')).toEqual({ + type: 'list', + children: [ + { type: 'plain', value: 'a' }, + { type: 'comment', value: '// hello' }, + ], + })); +}); + +let desguar = (a) => { + return printAst(parser.parse(a), true); +}; + +describe('mondo sugar', () => { + it('should desugar []', () => expect(desguar('[a b c]')).toEqual('(square a b c)')); + it('should desugar [] nested', () => expect(desguar('[a [b c] d]')).toEqual('(square a (square b c) d)')); + it('should desugar <>', () => expect(desguar('')).toEqual('(angle a b c)')); + it('should desugar <> nested', () => expect(desguar(' d>')).toEqual('(angle a (angle b c) d)')); + it('should desugar mixed [] <>', () => expect(desguar('[a ]')).toEqual('(square a (angle b c))')); + it('should desugar mixed <> []', () => expect(desguar('')).toEqual('(angle a (square b c))')); + + it('should desugar #', () => expect(desguar('s jazz # fast 2')).toEqual('(fast 2 (s jazz))')); + it('should desugar # square', () => expect(desguar('[bd cp # fast 2]')).toEqual('(fast 2 (square bd cp))')); + it('should desugar # twice', () => expect(desguar('s jazz # fast 2 # slow 2')).toEqual('(slow 2 (fast 2 (s jazz)))')); + it('should desugar # nested', () => expect(desguar('(s cp # fast 2)')).toEqual('(fast 2 (s cp))')); + it('should desugar # within []', () => expect(desguar('[bd cp # fast 2]')).toEqual('(fast 2 (square bd cp))')); + it('should desugar # within , within []', () => + expect(desguar('[bd cp # fast 2, x]')).toEqual('(stack (fast 2 (square bd cp)) x)')); + + // it('should desugar .(.', () => expect(desguar('[jazz hh.(.fast 2)]')).toEqual('(square jazz (fast 2 hh))')); + + it('should desugar , |', () => expect(desguar('[bd, hh | oh]')).toEqual('(stack bd (or hh oh))')); + it('should desugar , | of []', () => + expect(desguar('[bd, hh | [oh rim]]')).toEqual('(stack bd (or hh (square oh rim)))')); + it('should desugar , square', () => expect(desguar('[bd, hh]')).toEqual('(stack bd hh)')); + it('should desugar , square 2', () => expect(desguar('[bd, hh oh]')).toEqual('(stack bd (square hh oh))')); + it('should desugar , square 3', () => + expect(desguar('[bd cp, hh oh]')).toEqual('(stack (square bd cp) (square hh oh))')); + it('should desugar , angle', () => expect(desguar('')).toEqual('(stack bd hh)')); + it('should desugar , angle 2', () => expect(desguar('')).toEqual('(stack bd (angle hh oh))')); + it('should desugar , angle 3', () => + expect(desguar('')).toEqual('(stack (angle bd cp) (angle hh oh))')); + it('should desugar , ()', () => expect(desguar('(s bd, s cp)')).toEqual('(stack (s bd) (s cp))')); + it('should desugar * /', () => expect(desguar('[a b*2 c d/3 e]')).toEqual('(square a (* 2 b) c (/ 3 d) e)')); + it('should desugar []*x', () => expect(desguar('[a [b c]*3]')).toEqual('(square a (* 3 (square b c)))')); + it('should desugar []*', () => expect(desguar('[a b*<2 3> c]')).toEqual('(square a (* (angle 2 3) b) c)')); + it('should desugar x:y', () => expect(desguar('x:y')).toEqual('(: y x)')); + it('should desugar x:y:z', () => expect(desguar('x:y:z')).toEqual('(: z (: y x))')); + it('should desugar x:y*x', () => expect(desguar('bd:0*2')).toEqual('(* 2 (: 0 bd))')); + it('should desugar a..b', () => expect(desguar('0..2')).toEqual('(.. 2 0)')); + /* it('should desugar x $ y', () => expect(desguar('x $ y')).toEqual('(x y)')); + it('should desugar x $ y z', () => expect(desguar('x $ y z')).toEqual('(x (y z))')); + it('should desugar x $ y . z', () => expect(desguar('x $ y . z')).toEqual('(z (x y))')); */ + + it('should desugar README example', () => + expect(desguar('s [bd hh*2 (cp # crush 4) ] # speed .8')).toEqual( + '(speed .8 (s (square bd (* 2 hh) (crush 4 cp) (angle mt ht lt))))', + )); + + it('should desugar (#)', () => expect(desguar('(#)')).toEqual('(fn (_) _)')); + it('should desugar lambda', () => expect(desguar('(# fast 2)')).toEqual('(fn (_) (fast 2 _))')); + it('should desugar lambda call', () => expect(desguar('((# mul 2) 2)')).toEqual('((fn (_) (mul 2 _)) 2)')); + it('should desugar lambda with pipe', () => + expect(desguar('(# fast 2 # room 1)')).toEqual('(fn (_) (room 1 (fast 2 _)))')); + /* const lambda = parser.parse('(lambda (_) (fast 2 _))'); + const target = { type: 'plain', value: 'xyz' }; + it('should desugar_lambda', () => + expect(printAst(parser.desugar_lambda(lambda.children, target))).toEqual('(fast 2 xyz)')); */ +}); + +describe('mondo arithmetic', () => { + let multi = + (op) => + (init, ...rest) => + rest.reduce((acc, arg) => op(acc, arg), init); + + let lib = { + '+': multi((a, b) => a + b), + add: multi((a, b) => a + b), + '-': multi((a, b) => a - b), + sub: multi((a, b) => a - b), + '*': multi((a, b) => a * b), + '/': multi((a, b) => a / b), + mod: multi((a, b) => a % b), + eq: (a, b) => a === b, + lt: (a, b) => a < b, + gt: (a, b) => a > b, + and: (a, b) => a && b, + or: (a, b) => a || b, + not: (a) => !a, + run: (...args) => args[args.length - 1], + def: () => 0, + sin: Math.sin, + cos: Math.cos, + PI: Math.PI, + cons: (a, b) => [a, ...(Array.isArray(b) ? b : [b])], + car: (pair) => pair[0], + cdr: (pair) => pair.slice(1), + list: (...items) => items, + nil: [], + isnull: (items) => items.length === 0, + concat: (...msgs) => msgs.join(''), + error: (...msgs) => { + throw new Error(msgs.join(' ')); + }, + }; + function evaluator(node, scope) { + if (node.type !== 'list') { + // is leaf + return scope[node.value] ?? lib[node.value] ?? node.value; + } + // is list + const [fn, ...args] = node.children; + if (typeof fn !== 'function') { + throw new Error(`"${fn}": expected function, got ${typeof fn} "${fn}"`); + } + return fn(...args); + } + const runner = new MondoRunner({ evaluator }); + let evaluate = (exp, scope) => runner.run(`run ${exp}`, scope); + let pretty = (exp) => printAst(runner.parser.parse(exp), false); + //it('should eval nested expression', () => expect(runner.run('add 1 (mul 2 PI)').toFixed(2)).toEqual('7.28')); + + it('eval number', () => expect(evaluate('2')).toEqual(2)); + it('eval string', () => expect(evaluate('abc')).toEqual('abc')); + it('eval list', () => expect(evaluate('(+ 1 2)')).toEqual(3)); + it('eval nested list', () => expect(evaluate('(+ 1 (+ 2 3))')).toEqual(6)); + it('def number', () => expect(evaluate('(def a 2) a')).toEqual(2)); + it('def + ref number', () => expect(evaluate('(def a 2) (* a a)')).toEqual(4)); + it('def + call lambda', () => expect(evaluate('(def sqr (fn (x) (* x x))) (sqr 3)')).toEqual(9)); + + // sicp + it('sicp 8.1', () => expect(evaluate('(+ 137 349)')).toEqual(486)); + it('sicp 8.2', () => expect(evaluate('(- 1000 334)')).toEqual(666)); + it('sicp 8.3', () => expect(evaluate('(* 5 99)')).toEqual(495)); + it('sicp 8.4', () => expect(evaluate('(/ 10 5)')).toEqual(2)); + it('sicp 8.5', () => expect(evaluate('(+ 2.7 10)')).toEqual(12.7)); + it('sicp 9.1', () => expect(evaluate('(+ 21 35 12 7)')).toEqual(75)); + it('sicp 9.2', () => expect(evaluate('(* 25 4 12)')).toEqual(1200)); + it('sicp 9.3', () => expect(evaluate('(+ (* 3 5) (- 10 6))')).toEqual(19)); + it('sicp 9.4', () => + expect(pretty('(+ (* 3 (+ (* 2 4) (+ 3 5))) (+ (- 10 7) 6))')).toEqual(`(+ + (* 3 + (+ + (* 2 4) + (+ 3 5) + ) + ) + (+ + (- 10 7) 6 + ) +)`)); // this is not exactly pretty printing by convention.. + + let scope = {}; + it('sicp 11.1', () => expect(evaluate('(def size 2) (* 5 size)', scope)).toEqual(10)); + it('sicp 11.2', () => + expect(evaluate('(def pi 3.14159) (def radius 10) (* pi (* radius radius))', scope)).toEqual(314.159)); + it('sicp 11.3', () => expect(evaluate('(def circumference (* 2 pi radius))', scope)).toEqual(0)); + it('sicp 11.4', () => expect(evaluate('circumference', scope)).toEqual(62.8318)); + it('sicp 13.1', () => expect(evaluate('(* (+ 2 (* 4 6)) (+ 3 5 7))')).toEqual(390)); + it('sicp 16.1', () => expect(evaluate('(def (square x) (* x x))', scope)).toEqual(0)); + // it('sicp 16.1', () => expect(evaluate('(def (square x) (* x x))', scope)).toEqual(0)); + it('sicp 17.1', () => expect(evaluate('(square 21)', scope)).toEqual(441)); + it('sicp 17.2', () => expect(evaluate('(square (+ 2 5))', scope)).toEqual(49)); + it('sicp 17.3', () => expect(evaluate('(square (square 3))', scope)).toEqual(81)); + it('sicp 17.4', () => expect(evaluate(`(def (sumofsquares x y) (+ (square x) (square y)))`, scope)).toEqual(0)); + it('sicp 17.5', () => expect(evaluate(`(sumofsquares 3 4)`, scope)).toEqual(25)); + it('sicp 17.6', () => expect(evaluate(`(def (f a) (sumofsquares (+ a 1) (* a 2))) (f 5)`, scope)).toEqual(136)); + it('sicp 21.1', () => expect(evaluate(`(sumofsquares (+ 5 1) (* 5 2))`, scope)).toEqual(136)); + + it('sicp 22.1', () => + expect( + evaluate( + `(def (abs x) + (match + ((gt x 0) x) + ((eq x 0) 0) + ((lt x 0) (- 0 x)) + ))`, // sicp was doing (- x), which doesnt work with our - + scope, + ), + ).toEqual(0)); + + it('sicp gt1', () => expect(evaluate(`(gt -12 0)`, scope)).toEqual(false)); + it('sicp gt2', () => expect(evaluate(`(gt 0 -12)`, scope)).toEqual(true)); + it('sicp lt1', () => expect(evaluate(`(lt -12 0)`, scope)).toEqual(true)); + it('sicp lt2', () => expect(evaluate(`(lt 0 -12)`, scope)).toEqual(false)); + + it('sicp 24.1', () => expect(evaluate(`(abs (- 3))`, scope)).toEqual(3)); + it('sicp 24.2', () => expect(evaluate(`(abs (+ 3))`, scope)).toEqual(3)); + it('sicp 24.3', () => expect(evaluate(`(abs -12)`, scope)).toEqual(12)); + + it('sicp 24.4', () => expect(evaluate(`(def (abs x) (if (lt x 0) (- 0 x) x))`, scope)).toEqual(0)); + it('sicp 24.5', () => expect(evaluate(`(abs -13)`, scope)).toEqual(13)); + it('sicp 25.1', () => expect(evaluate(`(and (gt 6 5) (lt 6 10))`, scope)).toEqual(true)); + it('sicp 25.2', () => expect(evaluate(`(and (gt 4 5) (lt 6 10))`, scope)).toEqual(false)); + + it('sicp ex1.1.1', () => expect(evaluate(`(def a 3)`, scope)).toEqual(0)); + it('sicp ex1.1.2', () => expect(evaluate(`(def b (+ a 1))`, scope)).toEqual(0)); + it('sicp ex1.1.3', () => expect(evaluate(`(+ a b (* a b))`, scope)).toEqual(19)); + it('sicp ex1.1.4', () => expect(evaluate(`(if (and (gt b a) (lt b (* a b))) b a)`, scope)).toEqual(4)); + it('sicp ex1.1.5', () => expect(evaluate(`(match ((eq a 4) 6) ((eq b 4) (+ 6 7 a)) (else 25))`, scope)).toEqual(16)); + it('sicp ex1.1.6', () => expect(evaluate(`(+ 2 (if (gt b a) b a))`, scope)).toEqual(6)); + it('sicp ex1.1.7', () => + expect(evaluate(`(* (match ((gt a b) a) ((lt a b) b) (else -1)) (+ a 1))`, scope)).toEqual(16)); + + // .. cant use "+" and "-" as standalone expressions, because they are parsed as operators... + it('sicp ex1.4.1', () => expect(evaluate(`(def (foo a b) ((if (gt b 0) add sub) a b))`, scope)).toEqual(0)); + it('sicp ex1.4.1', () => expect(evaluate(`(foo 3 1)`, scope)).toEqual(4)); + it('sicp ex1.4.2', () => expect(evaluate(`(foo 3 -1)`, scope)).toEqual(4)); + + // 1.1.7 Example: Square Roots by Newton’s Method + it('sicp 30.1', () => + expect(evaluate(`(def (goodenuf guess x) (lt (abs (- (square guess) x)) 0.001))`, scope)).toEqual(0)); + it('sicp 30.2', () => expect(evaluate(`(goodenuf 1 1.001)`, scope)).toEqual(true)); + it('sicp 30.3', () => expect(evaluate(`(goodenuf 1 1.002)`, scope)).toEqual(false)); + it('sicp 30.4', () => expect(evaluate(`(def (average x y) (/ (+ x y) 2))`, scope)).toEqual(0)); + it('sicp 30.5', () => expect(evaluate(`(average 18 20)`, scope)).toEqual(19)); + it('sicp 30.6', () => expect(evaluate(`(def (improve guess x) (average guess (/ x guess)))`, scope)).toEqual(0)); + it('sicp 31.1', () => + expect( + evaluate( + `(def (sqrtiter guess x) (if (goodenuf guess x) + guess + (sqrtiter (improve guess x) x)))`, + scope, + ), + ).toEqual(0)); + it('sicp 31.2', () => expect(evaluate(`(def (sqrt x) (sqrtiter 1.0 x))`, scope)).toEqual(0)); + it('sicp 31.3', () => expect(evaluate(`(sqrt 9)`, scope)).toEqual(3.00009155413138)); + it('sicp 31.4', () => expect(evaluate(`(sqrt (+ 100 37))`, scope)).toEqual(11.704699917758145)); + // eslint-disable-next-line no-loss-of-precision + it('sicp 31.5', () => expect(evaluate(`(sqrt (+ (sqrt 2) (sqrt 3)))`, scope)).toEqual(1.77392790232078925)); + it('sicp 31.6', () => expect(evaluate(`(square (sqrt 1000))`, scope)).toEqual(1000.000369924366)); + + // lexical scoping + it('sicp 39.1', () => + expect( + evaluate( + ` +(def (sqrt x) + (def (goodenough guess) + (lt (abs (- (square guess) x)) 0.001)) +(def (improve guess) + (average guess (/ x guess))) +(def (sqrt-iter guess) + (if (goodenough guess) guess (sqrt-iter (improve guess)))) +(sqrtiter 1.0)) + + `, + scope, + ), + ).toEqual(0)); + + // recursive fac + it('sicp 41.1', () => expect(evaluate(`(def (fac n) (if (eq n 1) 1 (* n (fac (- n 1)))))`, scope)).toEqual(0)); + it('sicp 41.2', () => expect(evaluate(`(fac 4)`, scope)).toEqual(24)); + + // iterative fac + it('sicp 41.3', () => + expect( + evaluate( + ` +(def (factorial n) (factiter 1 1 n)) +(def (factiter product counter maxcount) + (if (gt counter maxcount) + product + (factiter (* counter product) + (+ counter 1) + maxcount))) +`, + scope, + ), + ).toEqual(0)); + it('sicp 41.4', () => expect(evaluate(`(fac 4)`, scope)).toEqual(24)); + + // 46.1 + /* (def (+ a b) +(if (= a 0) b (inc (+ (dec a) b)))) +(def (+ a b) +(if (= a 0) b (+ (dec a) (inc b)))) */ + + // Exercise 1.10 + // Ackermann’s function + it('sicp 47.1', () => + expect( + evaluate( + ` +(def (A x y) (match ((eq y 0) 0) +((eq x 0) (* 2 y)) +((eq y 1) 2) +(else (A (- x 1) (A x (- y 1)))))) +`, + scope, + ), + ).toEqual(0)); + it('sicp 47.2', () => expect(evaluate(`(A 1 10)`, scope)).toEqual(1024)); + it('sicp 47.3', () => expect(evaluate(`(A 2 4)`, scope)).toEqual(65536)); + it('sicp 47.4', () => expect(evaluate(`(A 3 3)`, scope)).toEqual(65536)); + it('sicp 47.5', () => + expect( + evaluate( + ` +(def (f n) (A 0 n)) +(def (g n) (A 1 n))) +(def (h n) (A 2 n)) +(def (k n) (* 5 n n)) + `, + scope, + ), + ).toEqual(0)); + + // Tree Recursion + // recursive process + it('sicp 48.1', () => + expect( + evaluate( + ` +(def (fib n) (match ((eq n 0) 0) ((eq n 1) 1) +(else (+ (fib (- n 1)) (fib (- n 2)))))) +(fib 7) + `, + scope, + ), + ).toEqual(13)); + + // iterative process + it('sicp 48.2', () => + expect( + evaluate( + ` +(def (fib n) (fibiter 1 0 n)) +(def (fibiter a b count) (if (eq count 0) + b + (fibiter (+ a b) a (- count 1)))) +(fib 7) + `, + scope, + ), + ).toEqual(13)); + + // example: counting change + it('sicp 52.2', () => + expect( + evaluate( + ` +(def (countchange amount) (cc amount 5)) +(def (cc amount kindsofcoins) + (match + ((eq amount 0) 1) + ((or (lt amount 0) (eq kindsofcoins 0)) 0) + (else (+ + (cc amount (- kindsofcoins 1)) + (cc (- amount (firstdenomination kindsofcoins)) kindsofcoins))))) + +(def (firstdenomination kindsofcoins) +(match + ((eq kindsofcoins 1) 1) + ((eq kindsofcoins 2) 5) + ((eq kindsofcoins 3) 10) + ((eq kindsofcoins 4) 25) + ((eq kindsofcoins 5) 50))) + +(countchange 100) + `, + scope, + ), + ).toEqual(292)); + + // todo: pascals triangle + it('sicp 57.1', () => + expect( + evaluate( + ` +(def (cube x) (* x x x)) +(def (p x) (sub (* 3 x) (* 4 (cube x)))) +(def (sine angle) +(if (not (gt (abs angle) 0.1)) angle +(p (sine (/ angle 3.0))))) + +(sine 12.15) + `, + scope, + ), + ).toEqual(-0.39980345741334)); + + // exponentiation recursive + it('sicp 57.2', () => + expect( + evaluate( + ` +(def (expt b n) (if (eq n 0) 1 (* b (expt b (- n 1))))) +(expt 2 4) +`, + scope, + ), + ).toEqual(16)); + + // exponentiation iterative + it('sicp 58.1b', () => + expect( + evaluate( + ` +(def (expt b n) (exptiter b n 1)) +(def (exptiter b counter product) (if (eq counter 0) + product + (exptiter b (- counter 1) (* b product)))) +(expt 2 5) + `, + scope, + ), + ).toEqual(32)); + + // exponentiation fast + it('sicp 58.2', () => + expect( + evaluate( + ` +(def (fastexpt b n) (match ((eq n 0) 1) +((iseven n) (square (fastexpt b (/ n 2)))) (else (* b (fastexpt b (- n 1)))))) +(def (iseven n) +(eq (mod n 2) 0)) +(fastexpt 2 5) + `, + scope, + ), + ).toEqual(32)); + + // * = repeated addition + it('sicp 60.1', () => + expect( + evaluate( + `(def (mult a b) (if (eq b 0) + 0 + (+ a (* a (- b 1))))) + (mult 3 15) + `, + ), + ).toEqual(45)); + + // gcd / euclid + it('sicp 63.1', () => + expect( + evaluate( + `(def (gcd a b) (if (eq b 0) + a + (gcd b (mod a b)))) + (gcd 20 6) + `, + scope, + ), + ).toEqual(2)); + + // 65 smallest divisor + // 67 fermat test + // .... + + // higher order procedures + + it('sicp 77.1', () => + expect( + evaluate( + ` +(def (sum term a next b) +(if (gt a b) 0 (+ (term a) +(sum term (next a) next b)))) +`, + scope, + ), + ).toEqual(0)); + + it('sicp 78.1', () => + expect( + evaluate( + ` +(def (inc n) (+ n 1)) +(def (cube a) (* a a a)) +(def (sumcubes a b) +(sum cube a inc b)) +(sumcubes 1 10) + `, + scope, + ), + ).toEqual(3025)); + + it('sicp 78.2', () => + expect( + evaluate( + ` + (def (identity x) x) + (def (sumintegers a b) + (sum identity a inc b)) + (sumintegers 1 10) + `, + scope, + ), + ).toEqual(55)); + + // pisum + it('sicp 79.1', () => + expect( + evaluate( + ` +(def (pisum a b) + (def (piterm x) + (/ 1.0 (* x (+ x 2)))) +(def (pinext x) (+ x 4)) +(sum piterm a pinext b)) +(* 8 (pisum 1 1000)) +`, + scope, + ), + ).toEqual(3.139592655589783)); + + // integral + it('sicp 79.2', () => + expect( + evaluate( + ` +(def (integral f a b dx) + (def (adddx x) (+ x dx)) +(* (sum f (+ a (/ dx 2.0)) adddx b) dx)) +(integral cube 0 1 0.01) + `, + scope, + ), + ).toEqual(0.24998750000000042)); + // maximum callstack... + //it('sicp 79.3', () => expect(evaluate(`(integral cube 0 1 0.001)`, scope)).toEqual(0.249999875000001)); + + //lambdas + it('sicp 83.1', () => expect(evaluate(`((fn (x) (+ x 4)) 5)`)).toEqual(9)); + + it('sicp 83.2', () => + expect( + evaluate( + ` +(def (pisum a b) + (sum (fn (x) (/ 1.0 (* x (+ x 2)))) + a + (fn (x) (+ x 4)) + b)) +(* 8 (pisum 1 1000)) +`, + scope, + ), + ).toEqual(3.139592655589783)); + it('sicp 83.3', () => + expect( + evaluate( + ` +(def (integral f a b dx) + (* (sum f + (+ a (/ dx 2.0)) + (fn (x) (+ x dx)) + b) + dx)) +(integral cube 0 1 0.01) +`, + scope, + ), + ).toEqual(0.24998750000000042)); + it('sicp 84.1', () => expect(evaluate(`((fn (x y z) (+ x y (square z))) 1 2 3)`, scope)).toEqual(12)); + + // let expressions + it('sicp 87.1', () => + expect( + evaluate( + ` +(+ (let ((x 3)) +(+ x (* x 10))) x) +`, + { x: 5 }, + ), + ).toEqual(38)); + it('sicp 87.2', () => + expect( + evaluate( + ` +(let ((x 3) +(y (+ x 2))) +(* x y)) + `, + { x: 2 }, + ), + ).toEqual(12)); + it('sicp 88.1', () => + expect( + evaluate( + ` +(def (f g) (g 2)) +(f square) + `, + scope, + ), + ).toEqual(4)); + it('sicp 88.2', () => + expect( + evaluate( + ` + (def (f g) (g 2)) + (f (fn (z) (* z (+ z 1)))) + `, + scope, + ), + ).toEqual(6)); + + // Finding roots of equations by the half-interval method + it('sicp 89.1', () => + expect( + evaluate( + ` +(def (search f negpoint pospoint) + (let ((midpoint (average negpoint pospoint))) + (if (closeenough negpoint pospoint) + midpoint + (let ((testvalue (f midpoint))) + (match ((positive testvalue) + (search f negpoint midpoint)) + ((negative testvalue) + (search f midpoint pospoint)) + (else midpoint)))))) + +(def (closeenough x y) (lt (abs (- x y)) 0.001)) + +(def (negative x) (lt x 0)) +(def (positive x) (gt x 0)) + +(def (halfintervalmethod f a b) (let ((avalue (f a)) +(bvalue (f b))) +(match ((and (negative avalue) (positive bvalue)) +(search f a b)) +((and (negative bvalue) (positive avalue)) +(search f b a)) (else +(error "Values are not of opposite sign" a b))))) + +(halfintervalmethod sin 2.0 4.0) +`, + scope, + ), + ).toEqual(3.14111328125)); + + it('sicp 89.1', () => + expect(evaluate(`(halfintervalmethod (fn (x) (- (* x x x) (* 2 x) 3)) 1.0 2.0)`, scope)).toEqual(1.89306640625)); + + // Finding fixed points of functions + it('sicp 92.1', () => + expect( + evaluate( + ` +(def tolerance 0.00001) +(def (fixedpoint f first-guess) + (def (closeenough v1 v2) (lt (abs (- v1 v2)) tolerance)) + (def (try guess) + (let ((next (f guess))) + (if (closeenough guess next) next (try next)))) + (try first-guess)) + +(fixedpoint cos 1.0) +`, + scope, + ), + ).toEqual(0.7390822985224023)); + it('sicp 93.1', () => + expect(evaluate(`(fixedpoint (fn (y) (+ (sin y) (cos y))) 1.0)`, scope)).toEqual(1.2587315962971173)); + // Maximum call stack size exceeded (expected) + /* it('sicp 93.2', () => + expect(evaluate(`(def (sqrt x) (fixedpoint (fn (y) (/ x y)) 1.0)) (sqrt 4)`, scope)).toEqual(0)); */ + it('sicp 93.3', () => + expect(evaluate(`(def (sqrt x) (fixedpoint (fn (y) (average y (/ x y))) 1.0)) (sqrt 7)`, scope)).toEqual( + 2.6457513110645907, + )); + // Procedures as Returned Values + it('sicp 97.1', () => + expect(evaluate(`(def (averagedamp f) (fn (x) (average x (f x)))) ((averagedamp square) 10)`, scope)).toEqual(55)); + it('sicp 98.1', () => + expect(evaluate(`(def (sqrt x) (fixedpoint (averagedamp (fn (y) (/ x y))) 1.0)) (sqrt 7)`, scope)).toEqual( + 2.6457513110645907, + )); + it('sicp 98.2', () => + expect( + evaluate(`(def (cuberoot x) (fixedpoint (averagedamp (fn (y) (/ x (square y)))) 1.0)) (cuberoot 7)`, scope), + ).toEqual(1.912934258514886)); + it('sicp 99.1', () => + expect( + evaluate( + ` + (def (deriv g) (fn (x) (/ (- (g (+ x dx)) (g x)) dx))) + (def dx 0.00001) + (def (cube x) (* x x x)) + ((deriv cube) 5) + `, + scope, + ), + ).toEqual(75.00014999664018)); + // With the aid of deriv, we can express Newton’s method as a fixed-point process: + it('sicp 100.1', () => + expect( + evaluate( + ` +(def (newtontransform g) +(fn (x) (- x (/ (g x) ((deriv g) x))))) +(def (newtonsmethod g guess) (fixedpoint (newtontransform g) guess)) +(def (sqrt x) (newtonsmethod (fn (y) (- (square y) x)) 1.0)) +(sqrt 7) + `, + scope, + ), + ).toEqual(2.6457513110645907)); + // whatever this is + it('sicp 101.1', () => + expect( + evaluate( + ` + (def (fixedpointoftransform g transform guess) (fixedpoint (transform g) guess)) + (def (sqrt x) (fixedpointoftransform + (fn (y) (/ x y)) averagedamp 1.0)) + (sqrt 7) + `, + scope, + ), + ).toEqual(2.6457513110645907)); + it('sicp 101.2', () => + expect( + evaluate( + ` +(def (sqrt x) (fixedpointoftransform +(fn (y) (- (square y) x)) newtontransform 1.0)) +(sqrt 7) + `, + scope, + ), + ).toEqual(2.6457513110645907)); + + // data abstraction + + // rational arithmetic + it('sicp 114.1', () => + expect( + evaluate( + ` +(def (addrat x y) + (makerat (+ (* + (numer x) (denom y)) + (* (numer y) (denom x))) + (* (denom x) (denom y)))) +(def (subrat x y) + (makerat (- (* (numer x) (denom y)) + (* (numer y) (denom x))) + (* (denom x) (denom y)))) +(def (mulrat x y) + (makerat (* (numer x) (numer y)) + (* (denom x) (denom y)))) +(def (divrat x y) + (makerat (* (numer x) (denom y)) + (* (denom x) (numer y)))) +(def (equalrat x y) + (eq (* (numer x) (denom y)) + (* (numer y) (denom x)))) + `, + scope, + ), + ).toEqual(0)); + + // markerat number denom + it('sicp 117.1', () => + expect( + evaluate( + ` +(def (makerat n d) (cons n d)) +(def (numer x) (car x)) +(def (denom x) (cdr x)) +(def (printrat x) (concat (numer x) ':' (denom x))) + `, + scope, + ), + ).toEqual(0)); + + it('sicp 117.1', () => expect(evaluate(`(def onehalf (makerat 1 2)) (printrat onehalf)`, scope)).toEqual('1:2')); + it('sicp 117.2', () => + expect(evaluate(`(def onethird (makerat 1 3)) (printrat (addrat onehalf onethird))`, scope)).toEqual('5:6')); + it('sicp 117.3', () => expect(evaluate(`(printrat (mulrat onehalf onethird))`, scope)).toEqual('1:6')); + it('sicp 117.4', () => expect(evaluate(`(printrat (addrat onethird onethird))`, scope)).toEqual('6:9')); + it('sicp 118.1', () => + expect(evaluate(`(def (makerat n d) (let ((g (gcd n d))) (cons (/ n g) (/ d g))))`, scope)).toEqual(0)); + it('sicp 118.1', () => expect(evaluate(`(printrat (addrat onethird onethird))`, scope)).toEqual('2:3')); + + let lscope = {}; + // pairs with lambda + it('sicp 124.1', () => + expect( + evaluate( + ` +(def (cons x y) + (def (dispatch m) + (match + ((eq m 0) x) + ((eq m 1) y) + (else (error "argument not 0 or 1: CONS" m))) + ) dispatch) + (def (car z) (z 0)) + (def (cdr z) (z 1)) + `, + lscope, + ), + ).toEqual(0)); + it('sicp 124.1', () => expect(evaluate(`(car (cons first second))`, lscope)).toEqual('first')); + it('sicp 124.2', () => expect(evaluate(`(cdr (cons first second))`, lscope)).toEqual('second')); + // lists + it('sicp 135.1', () => expect(evaluate(`(list 1 2 3 4)`)).toEqual([1, 2, 3, 4])); + it('sicp 137.1', () => expect(evaluate(`(car (list 1 2 3 4))`)).toEqual(1)); + it('sicp 137.2', () => expect(evaluate(`(cdr (list 1 2 3 4))`)).toEqual([2, 3, 4])); + it('sicp 137.3', () => expect(evaluate(`(car (cdr (list 1 2 3 4)))`)).toEqual(2)); + it('sicp 137.4', () => expect(evaluate(`(cons 10 (list 1 2 3 4))`)).toEqual([10, 1, 2, 3, 4])); + // listref + it('sicp 138.1', () => + expect( + evaluate( + ` +(def (listref items n) (if (eq n 0) (car items) + (listref (cdr items) (- n 1)))) +(def squares (list 1 4 9 16 25)) +(listref squares 3)`, + scope, + ), + ).toEqual(16)); + // length recursive + it('sicp 138.2', () => + expect( + evaluate( + ` + (def (length items) + (if (isnull items) 0 + (+ 1 (length (cdr items))))) + (def odds (list 1 3 5 7)) + (length odds)`, + ), + ).toEqual(4)); + // length iterative + it('sicp 139.1', () => + expect( + evaluate( + ` + (def (length items) + (def (lengthiter a count) + (if (isnull a) count + (lengthiter (cdr a) (+ 1 count)))) + (lengthiter items 0)) + (def odds (list 1 3 5 7)) + (length odds) + `, + scope, + ), + ).toEqual(4)); + // append + it('sicp 139.1', () => + expect( + evaluate( + ` +(def (append list1 list2) +(if (isnull list1) + list2 + (cons (car list1) (append (cdr list1) list2)))) + (append squares odds) + `, + scope, + ), + ).toEqual([1, 4, 9, 16, 25, 1, 3, 5, 7])); + // (define (f x y . z) ⟨body⟩) <- tbd: variable argument count + // Mapping over lists + + it('sicp 143.1', () => + expect( + evaluate( + ` +(def (scalelist items factor) (if (isnull items) nil + (cons (* (car items) factor) + (scalelist (cdr items) factor)))) +(scalelist (list 1 2 3 4 5) 10) + `, + scope, + ), + ).toEqual([10, 20, 30, 40, 50])); + + it('sicp 143.1', () => + expect( + evaluate( + ` + (def (map proc items) (if (isnull items) nil + (cons (proc (car items)) + (map proc (cdr items))))) + (map abs (list -10 2.5 -11.6 17)) + `, + scope, + ), + ).toEqual([10, 2.5, 11.6, 17])); + it('sicp 143.1', () => expect(evaluate(`(map (fn (x) (* x x)) (list 1 2 3 4))`, scope)).toEqual([1, 4, 9, 16])); + it('sicp 143.1', () => + expect( + evaluate( + ` +(def (scalelist items factor) (map (fn (x) (* x factor)) items)) +(scalelist (list 1 2 3 4 5) 10) +`, + scope, + ), + ).toEqual([10, 20, 30, 40, 50])); +}); diff --git a/packages/mondo/vite.config.js b/packages/mondo/vite.config.js new file mode 100644 index 000000000..19e53d7f3 --- /dev/null +++ b/packages/mondo/vite.config.js @@ -0,0 +1,19 @@ +import { defineConfig } from 'vite'; +//import { dependencies } from './package.json'; +import { resolve } from 'path'; + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [], + build: { + lib: { + entry: resolve(__dirname, 'mondo.mjs'), + formats: ['es'], + fileName: (ext) => ({ es: 'mondo.mjs' })[ext], + }, + rollupOptions: { + // external: [...Object.keys(dependencies)], + }, + target: 'esnext', + }, +}); diff --git a/packages/mondough/README.md b/packages/mondough/README.md new file mode 100644 index 000000000..3ae758912 --- /dev/null +++ b/packages/mondough/README.md @@ -0,0 +1,3 @@ +# @strudel/mondough + +connects mondo to strudel. diff --git a/packages/mondough/mondough.mjs b/packages/mondough/mondough.mjs new file mode 100644 index 000000000..6e8278939 --- /dev/null +++ b/packages/mondough/mondough.mjs @@ -0,0 +1,131 @@ +import { + strudelScope, + reify, + fast, + slow, + seq, + stepcat, + extend, + expand, + pace, + chooseIn, + degradeBy, + silence, +} from '@strudel/core'; +import { registerLanguage } from '@strudel/transpiler'; +import { MondoRunner } from 'mondolang'; + +const tail = (friend, pat) => pat.fmap((a) => (b) => (Array.isArray(a) ? [...a, b] : [a, b])).appLeft(friend); + +const arrayRange = (start, stop, step = 1) => + Array.from({ length: Math.abs(stop - start) / step + 1 }, (_, index) => + start < stop ? start + index * step : start - index * step, + ); +const range = (max, min) => min.squeezeBind((a) => max.bind((b) => seq(...arrayRange(a, b)))); + +let nope = (...args) => args[args.length - 1]; + +let lib = {}; +lib['nope'] = nope; +lib['-'] = (a, b) => b.early(a); +lib['+'] = (a, b) => b.late(a); +lib['_'] = silence; +lib['~'] = silence; +lib.curly = stepcat; +lib.square = (...args) => stepcat(...args).setSteps(1); +lib.angle = (...args) => stepcat(...args).pace(1); +lib['*'] = fast; +lib['/'] = slow; +lib['!'] = extend; +lib['@'] = expand; +lib['%'] = pace; +lib['?'] = degradeBy; // todo: default 0.5 not working.. +lib[':'] = tail; +lib['..'] = range; +lib['or'] = (...children) => chooseIn(...children); // always has structure but is cyclewise.. e.g. "s oh*8.dec[.04 | .5]" +//lib['or'] = (...children) => chooseOut(...children); // "s oh*8.dec[.04 | .5]" is better but "dec[.04 | .5].s oh*8" has no struct + +function evaluator(node, scope) { + const { type } = node; + // node is list + if (type === 'list') { + const { children } = node; + const [name, ...args] = children; + // some functions wont be reified to make sure they work (e.g. see extend below) + if (typeof name === 'function') { + return name(...args); + } + if (name.value === 'def') { + return silence; + } + // name is expected to be a pattern of functions! + const first = name.firstCycle(true)[0]; + const type = typeof first?.value; + if (type !== 'function') { + throw new Error(`[mondough] expected function, got "${first?.value}"`); + } + return name + .fmap((fn) => { + if (typeof fn !== 'function') { + throw new Error(`[mondough] "${fn}" is not a function b`); + } + return fn(...args); + }) + .innerJoin(); + } + // node is leaf + let { value } = node; + if (type === 'plain' && scope[value]) { + return reify(scope[value]); // -> local scope has no location + } + const variable = lib[value] ?? strudelScope[value]; + // problem: collisions when we want a string that happens to also be a variable name + // example: "s sine" -> sine is also a variable + let pat; + if (type === 'plain' && typeof variable !== 'undefined') { + // some function names are not patternable, so we skip reification here + if (['!', 'extend', '@', 'expand', 'square', 'angle'].includes(value)) { + return variable; + } + pat = reify(variable); + } else { + pat = reify(value); + } + if (node.loc) { + pat = pat.withLoc(node.loc[0], node.loc[1]); + } + return pat; +} + +let runner = new MondoRunner({ evaluator }); + +export function mondo(code, offset = 0) { + if (Array.isArray(code)) { + code = code.join(''); + } + const pat = runner.run(code, undefined, offset); + return pat.markcss('color: var(--caret,--foreground);text-decoration:underline'); +} + +let getLocations = (code, offset) => runner.parser.get_locations(code, offset); + +export const mondi = (str, offset) => { + const code = `[${str}]`; + return mondo(code, offset); +}; + +// tell transpiler how to get locations for mondo`` calls +registerLanguage('mondo', { + getLocations, +}); + +// this is like mondo, but with a zero offset +export const mondolang = (code) => mondo(code, 0); +registerLanguage('mondolang', { + getLocations: (code) => getLocations(code, 0), +}); +// uncomment the following to use mondo as mini notation language +/* registerLanguage('minilang', { + name: 'mondi', + getLocations, +}); */ diff --git a/packages/mondough/package.json b/packages/mondough/package.json new file mode 100644 index 000000000..a034424c0 --- /dev/null +++ b/packages/mondough/package.json @@ -0,0 +1,44 @@ +{ + "name": "@strudel/mondo", + "version": "1.1.0", + "description": "mondo notation for strudel", + "main": "mondough.mjs", + "type": "module", + "publishConfig": { + "main": "dist/mondough.mjs" + }, + "scripts": { + "test": "vitest run", + "bench": "vitest bench", + "build:parser": "peggy -o krill-parser.js --format es ./krill.pegjs", + "build": "vite build", + "prepublishOnly": "npm run build" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/tidalcycles/strudel.git" + }, + "keywords": [ + "tidalcycles", + "strudel", + "pattern", + "livecoding", + "algorave" + ], + "author": "Felix Roos ", + "license": "AGPL-3.0-or-later", + "bugs": { + "url": "https://github.com/tidalcycles/strudel/issues" + }, + "homepage": "https://github.com/tidalcycles/strudel#readme", + "dependencies": { + "@strudel/core": "workspace:*", + "@strudel/transpiler": "workspace:*", + "mondolang": "workspace:*" + }, + "devDependencies": { + "mondo": "*", + "vite": "^6.0.11", + "vitest": "^3.0.4" + } +} diff --git a/packages/mondough/vite.config.js b/packages/mondough/vite.config.js new file mode 100644 index 000000000..c46972e96 --- /dev/null +++ b/packages/mondough/vite.config.js @@ -0,0 +1,19 @@ +import { defineConfig } from 'vite'; +//import { dependencies } from './package.json'; +import { resolve } from 'path'; + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [], + build: { + lib: { + entry: resolve(__dirname, 'mondough.mjs'), + formats: ['es'], + fileName: (ext) => ({ es: 'mondough.mjs' })[ext], + }, + rollupOptions: { + // external: [...Object.keys(dependencies)], + }, + target: 'esnext', + }, +}); diff --git a/packages/motion/package.json b/packages/motion/package.json index 850a75bd1..a7db05680 100644 --- a/packages/motion/package.json +++ b/packages/motion/package.json @@ -13,7 +13,7 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/tidalcycles/strudel.git" + "url": "git+https://codeberg.org/uzu/strudel.git" }, "keywords": [ "titdalcycles", @@ -25,9 +25,9 @@ "author": "Yuta Nakayama ", "license": "AGPL-3.0-or-later", "bugs": { - "url": "https://github.com/tidalcycles/strudel/issues" + "url": "https://codeberg.org/uzu/strudel/issues" }, - "homepage": "https://github.com/tidalcycles/strudel#readme", + "homepage": "https://codeberg.org/uzu/strudel#readme", "dependencies": { "@strudel/core": "workspace:*" }, diff --git a/packages/mqtt/mqtt.mjs b/packages/mqtt/mqtt.mjs index c2322600c..aef01bd93 100644 --- a/packages/mqtt/mqtt.mjs +++ b/packages/mqtt/mqtt.mjs @@ -1,6 +1,6 @@ /* mqtt.mjs - for patterning the internet of things from strudel -Copyright (C) 2022 Strudel contributors - see +Copyright (C) 2022 Strudel contributors - see 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 . */ diff --git a/packages/mqtt/package.json b/packages/mqtt/package.json index 9b9cdaa3b..f522e3354 100644 --- a/packages/mqtt/package.json +++ b/packages/mqtt/package.json @@ -13,7 +13,7 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/tidalcycles/strudel.git" + "url": "git+https://codeberg.org/uzu/strudel.git" }, "keywords": [ "titdalcycles", @@ -25,9 +25,9 @@ "author": "Alex McLean ", "license": "AGPL-3.0-or-later", "bugs": { - "url": "https://github.com/tidalcycles/strudel/issues" + "url": "https://codeberg.org/uzu/strudel/issues" }, - "homepage": "https://github.com/tidalcycles/strudel#readme", + "homepage": "https://codeberg.org/uzu/strudel#readme", "dependencies": { "@strudel/core": "workspace:*", "paho-mqtt": "^1.1.0" diff --git a/packages/osc/osc.mjs b/packages/osc/osc.mjs index 3c7b92d4e..ac70b9e73 100644 --- a/packages/osc/osc.mjs +++ b/packages/osc/osc.mjs @@ -1,6 +1,6 @@ /* osc.mjs - -Copyright (C) 2022 Strudel contributors - see +Copyright (C) 2022 Strudel contributors - see 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 . */ diff --git a/packages/osc/package.json b/packages/osc/package.json index 7e87c1e1a..7d19fbbfc 100644 --- a/packages/osc/package.json +++ b/packages/osc/package.json @@ -17,7 +17,7 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/tidalcycles/strudel.git" + "url": "git+https://codeberg.org/uzu/strudel.git" }, "keywords": [ "tidalcycles", @@ -32,9 +32,9 @@ ], "license": "AGPL-3.0-or-later", "bugs": { - "url": "https://github.com/tidalcycles/strudel/issues" + "url": "https://codeberg.org/uzu/strudel/issues" }, - "homepage": "https://github.com/tidalcycles/strudel#readme", + "homepage": "https://codeberg.org/uzu/strudel#readme", "dependencies": { "@strudel/core": "workspace:*", "osc-js": "^2.4.1" diff --git a/packages/osc/server.js b/packages/osc/server.js index 7e0b90592..75fc5b1c0 100644 --- a/packages/osc/server.js +++ b/packages/osc/server.js @@ -1,6 +1,6 @@ /* server.js - -Copyright (C) 2022 Strudel contributors - see +Copyright (C) 2022 Strudel contributors - see 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 . */ diff --git a/packages/osc/tidal-sniffer.js b/packages/osc/tidal-sniffer.js index 10bf0ad05..95e1fc329 100644 --- a/packages/osc/tidal-sniffer.js +++ b/packages/osc/tidal-sniffer.js @@ -1,6 +1,6 @@ /* tidal-sniffer.js - -Copyright (C) 2022 Strudel contributors - see +Copyright (C) 2022 Strudel contributors - see 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 . */ diff --git a/packages/reference/package.json b/packages/reference/package.json index fd7edadff..8dc966cc2 100644 --- a/packages/reference/package.json +++ b/packages/reference/package.json @@ -13,7 +13,7 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/tidalcycles/strudel.git" + "url": "git+https://codeberg.org/uzu/strudel.git" }, "keywords": [ "tidalcycles", @@ -28,9 +28,9 @@ ], "license": "AGPL-3.0-or-later", "bugs": { - "url": "https://github.com/tidalcycles/strudel/issues" + "url": "https://codeberg.org/uzu/strudel/issues" }, - "homepage": "https://github.com/tidalcycles/strudel#readme", + "homepage": "https://codeberg.org/uzu/strudel#readme", "devDependencies": { "vite": "^6.0.11" } diff --git a/packages/repl/README.md b/packages/repl/README.md index 46819712c..5b43b5e79 100644 --- a/packages/repl/README.md +++ b/packages/repl/README.md @@ -17,7 +17,7 @@ You can also pin the version like this: ``` This has the advantage that your code will always work, regardless of potential breaking changes in the strudel codebase. -See [releases](https://github.com/tidalcycles/strudel/releases) for the latest versions. +See [releases](https://codeberg.org/uzu/strudel/releases) for the latest versions. ## Use Web Component @@ -91,7 +91,7 @@ or ``` -The `.editor` property on the `strudel-editor` web component gives you the instance of [StrudelMirror](https://github.com/tidalcycles/strudel/blob/a46bd9b36ea7d31c9f1d3fca484297c7da86893f/packages/codemirror/codemirror.mjs#L124) that runs the REPL. +The `.editor` property on the `strudel-editor` web component gives you the instance of [StrudelMirror](https://codeberg.org/uzu/strudel/src/branch/a46bd9b36ea7d31c9f1d3fca484297c7da86893f/packages/codemirror/codemirror.mjs#L124) that runs the REPL. For example, you could use `setCode` to change the code from the outside, `start` / `stop` to toggle playback or `evaluate` to evaluate the code. diff --git a/packages/repl/package.json b/packages/repl/package.json index 5487ce443..bfa404c75 100644 --- a/packages/repl/package.json +++ b/packages/repl/package.json @@ -14,7 +14,7 @@ "type": "module", "repository": { "type": "git", - "url": "git+https://github.com/tidalcycles/strudel.git" + "url": "git+https://codeberg.org/uzu/strudel.git" }, "keywords": [ "tidalcycles", @@ -29,9 +29,9 @@ ], "license": "AGPL-3.0-or-later", "bugs": { - "url": "https://github.com/tidalcycles/strudel/issues" + "url": "https://codeberg.org/uzu/strudel/issues" }, - "homepage": "https://github.com/tidalcycles/strudel#readme", + "homepage": "https://codeberg.org/uzu/strudel#readme", "dependencies": { "@strudel/codemirror": "workspace:*", "@strudel/core": "workspace:*", diff --git a/packages/serial/package.json b/packages/serial/package.json index bf6e0dcab..c04a69cd0 100644 --- a/packages/serial/package.json +++ b/packages/serial/package.json @@ -13,7 +13,7 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/tidalcycles/strudel.git" + "url": "git+https://codeberg.org/uzu/strudel.git" }, "keywords": [ "titdalcycles", @@ -25,9 +25,9 @@ "author": "Alex McLean ", "license": "AGPL-3.0-or-later", "bugs": { - "url": "https://github.com/tidalcycles/strudel/issues" + "url": "https://codeberg.org/uzu/strudel/issues" }, - "homepage": "https://github.com/tidalcycles/strudel#readme", + "homepage": "https://codeberg.org/uzu/strudel#readme", "dependencies": { "@strudel/core": "workspace:*" }, diff --git a/packages/serial/serial.mjs b/packages/serial/serial.mjs index e0eeacedd..692109522 100644 --- a/packages/serial/serial.mjs +++ b/packages/serial/serial.mjs @@ -1,6 +1,6 @@ /* serial.mjs - -Copyright (C) 2022 Strudel contributors - see +Copyright (C) 2022 Strudel contributors - see 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 . */ diff --git a/packages/soundfonts/package.json b/packages/soundfonts/package.json index bf4fa2960..2c87a6e05 100644 --- a/packages/soundfonts/package.json +++ b/packages/soundfonts/package.json @@ -13,7 +13,7 @@ "type": "module", "repository": { "type": "git", - "url": "git+https://github.com/tidalcycles/strudel.git" + "url": "git+https://codeberg.org/uzu/strudel.git" }, "keywords": [ "tidalcycles", @@ -25,9 +25,9 @@ "author": "Felix Roos ", "license": "AGPL-3.0-or-later", "bugs": { - "url": "https://github.com/tidalcycles/strudel/issues" + "url": "https://codeberg.org/uzu/strudel/issues" }, - "homepage": "https://github.com/tidalcycles/strudel#readme", + "homepage": "https://codeberg.org/uzu/strudel#readme", "dependencies": { "@strudel/core": "workspace:*", "@strudel/webaudio": "workspace:*", diff --git a/packages/superdough/index.mjs b/packages/superdough/index.mjs index 3247c5b49..fd49fe338 100644 --- a/packages/superdough/index.mjs +++ b/packages/superdough/index.mjs @@ -1,6 +1,6 @@ /* index.mjs - -Copyright (C) 2022 Strudel contributors - see +Copyright (C) 2022 Strudel contributors - see 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 . */ diff --git a/packages/superdough/package.json b/packages/superdough/package.json index a835f252d..439b83718 100644 --- a/packages/superdough/package.json +++ b/packages/superdough/package.json @@ -16,7 +16,7 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/tidalcycles/strudel.git" + "url": "git+https://codeberg.org/uzu/strudel.git" }, "keywords": [ "tidalcycles", @@ -28,9 +28,9 @@ "author": "Felix Roos ", "license": "AGPL-3.0-or-later", "bugs": { - "url": "https://github.com/tidalcycles/strudel/issues" + "url": "https://codeberg.org/uzu/strudel/issues" }, - "homepage": "https://github.com/tidalcycles/strudel#readme", + "homepage": "https://codeberg.org/uzu/strudel#readme", "devDependencies": { "vite": "^6.0.11", "vite-plugin-bundle-audioworklet": "workspace:*" diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index 1069d4e84..14f46711b 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -1,13 +1,13 @@ /* superdough.mjs - -Copyright (C) 2022 Strudel contributors - see +Copyright (C) 2022 Strudel contributors - see 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 . */ import './feedbackdelay.mjs'; import './reverb.mjs'; import './vowel.mjs'; -import { clamp, nanFallback, _mod } from './util.mjs'; +import { clamp, nanFallback, _mod, cycleToSeconds } from './util.mjs'; import workletsUrl from './worklets.mjs?audioworklet'; import { createFilter, gainNode, getCompressor, getWorklet } from './helpers.mjs'; import { map } from 'nanostores'; @@ -107,6 +107,10 @@ export async function aliasBank(...args) { } export function getSound(s) { + if (typeof s !== 'string') { + console.warn(`getSound: expected string got "${s}". fall back to triangle`); + return soundMap.get().triangle; // is this good? + } return soundMap.get()[s.toLowerCase()]; } @@ -139,7 +143,7 @@ const defaultDefaultValues = { delay: 0, byteBeatExpression: '0', delayfeedback: 0.5, - delaytime: 0.25, + delaysync: 3 / 16, orbit: 1, i: 1, velocity: 1, @@ -446,7 +450,7 @@ function mapChannelNumbers(channels) { return (Array.isArray(channels) ? channels : [channels]).map((ch) => ch - 1); } -export const superdough = async (value, t, hapDuration, cps) => { +export const superdough = async (value, t, hapDuration, cps = 0.5) => { const ac = getAudioContext(); t = typeof t === 'string' && t.startsWith('=') ? Number(t.slice(1)) : ac.currentTime + t; let { stretch } = value; @@ -524,7 +528,8 @@ export const superdough = async (value, t, hapDuration, cps) => { vowel, delay = getDefaultValue('delay'), delayfeedback = getDefaultValue('delayfeedback'), - delaytime = getDefaultValue('delaytime'), + delaysync = getDefaultValue('delaysync'), + delaytime, orbit = getDefaultValue('orbit'), room, roomfade, @@ -543,6 +548,8 @@ export const superdough = async (value, t, hapDuration, cps) => { compressorRelease, } = value; + delaytime = delaytime ?? cycleToSeconds(delaysync, cps); + const orbitChannels = mapChannelNumbers( multiChannelOrbits && orbit > 0 ? [orbit * 2 - 1, orbit * 2] : getDefaultValue('channels'), ); @@ -571,6 +578,9 @@ export const superdough = async (value, t, hapDuration, cps) => { let audioNodes = []; + if (['-', '~', '_'].includes(s)) { + return; + } if (bank && s) { s = `${bank}_${s}`; value.s = s; @@ -718,8 +728,8 @@ export const superdough = async (value, t, hapDuration, cps) => { // delay let delaySend; if (delay > 0 && delaytime > 0 && delayfeedback > 0) { - const delyNode = getDelay(orbit, delaytime, delayfeedback, t, orbitChannels); - delaySend = effectSend(post, delyNode, delay); + const delayNode = getDelay(orbit, delaytime, delayfeedback, t, orbitChannels); + delaySend = effectSend(post, delayNode, delay); audioNodes.push(delaySend); } // reverb diff --git a/packages/superdough/synth.mjs b/packages/superdough/synth.mjs index 834e54c6d..13cfa13a5 100644 --- a/packages/superdough/synth.mjs +++ b/packages/superdough/synth.mjs @@ -1,5 +1,5 @@ import { clamp, midiToFreq, noteToMidi } from './util.mjs'; -import { registerSound, getAudioContext, getLfo } from './superdough.mjs'; +import { registerSound, getAudioContext, soundMap, getLfo } from './superdough.mjs'; import { applyFM, gainNode, @@ -34,6 +34,12 @@ function destroyAudioWorkletNode(node) { } const waveforms = ['triangle', 'square', 'sawtooth', 'sine']; +const waveformAliases = [ + ['tri', 'triangle'], + ['sqr', 'square'], + ['saw', 'sawtooth'], + ['sin', 'sine'], +]; const noises = ['pink', 'white', 'brown', 'crackle']; export function registerSynthSounds() { @@ -342,6 +348,7 @@ export function registerSynthSounds() { { type: 'synth', prebake: true }, ); }); + waveformAliases.forEach(([alias, actual]) => soundMap.set({ ...soundMap.get(), [alias]: soundMap.get()[actual] })); } export function waveformN(partials, type) { diff --git a/packages/superdough/util.mjs b/packages/superdough/util.mjs index 4e3c7e41e..ea63a16f0 100644 --- a/packages/superdough/util.mjs +++ b/packages/superdough/util.mjs @@ -68,3 +68,7 @@ export const _mod = (n, m) => ((n % m) + m) % m; export const getSoundIndex = (n, numSounds) => { return _mod(Math.round(nanFallback(n, 0)), numSounds); }; + +export function cycleToSeconds(cycle, cps) { + return cycle / cps; +} diff --git a/packages/tidal/package.json b/packages/tidal/package.json index 8e4871617..960b09001 100644 --- a/packages/tidal/package.json +++ b/packages/tidal/package.json @@ -6,7 +6,7 @@ "module": "tidal.mjs", "repository": { "type": "git", - "url": "git+https://github.com/tidalcycles/strudel/tree/main/packages/tidal" + "url": "git+https://codeberg.org/uzu/strudel/src/branch/main/packages/tidal" }, "keywords": [ "haskell", @@ -15,9 +15,9 @@ "author": "Felix Roos ", "license": "AGPL-3.0-or-later", "bugs": { - "url": "https://github.com/tidalcycles/strudel/issues" + "url": "https://codeberg.org/uzu/strudel/issues" }, - "homepage": "https://github.com/tidalcycles/strudel/tree/main/packages/hs2js", + "homepage": "https://codeberg.org/uzu/strudel/src/branch/main/packages/hs2js", "dependencies": { "@strudel/core": "workspace:*", "@strudel/mini": "workspace:*", diff --git a/packages/tonal/package.json b/packages/tonal/package.json index 89f02b301..614e86f74 100644 --- a/packages/tonal/package.json +++ b/packages/tonal/package.json @@ -14,7 +14,7 @@ "type": "module", "repository": { "type": "git", - "url": "git+https://github.com/tidalcycles/strudel.git" + "url": "git+https://codeberg.org/uzu/strudel.git" }, "keywords": [ "tidalcycles", @@ -26,9 +26,9 @@ "author": "Felix Roos ", "license": "AGPL-3.0-or-later", "bugs": { - "url": "https://github.com/tidalcycles/strudel/issues" + "url": "https://codeberg.org/uzu/strudel/issues" }, - "homepage": "https://github.com/tidalcycles/strudel#readme", + "homepage": "https://codeberg.org/uzu/strudel#readme", "dependencies": { "@strudel/core": "workspace:*", "@tonaljs/tonal": "^4.10.0", diff --git a/packages/tonal/test/tonal.test.mjs b/packages/tonal/test/tonal.test.mjs index 8dd0e1861..cd8b3c88b 100644 --- a/packages/tonal/test/tonal.test.mjs +++ b/packages/tonal/test/tonal.test.mjs @@ -1,6 +1,6 @@ /* tonal.test.mjs - -Copyright (C) 2022 Strudel contributors - see +Copyright (C) 2022 Strudel contributors - see 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 . */ @@ -25,28 +25,35 @@ describe('tonal', () => { }); it('scale with n values', () => { expect( - n(0, 1, 2) + n(seq(0, 1, 2)) .scale('C major') .firstCycleValues.map((h) => h.note), ).toEqual(['C3', 'D3', 'E3']); }); it('scale with colon', () => { expect( - n(0, 1, 2) + n(seq(0, 1, 2)) .scale('C:major') .firstCycleValues.map((h) => h.note), ).toEqual(['C3', 'D3', 'E3']); }); + it('scale without tonic', () => { + expect( + n(seq(0, 1, 2)) + .scale('major') + .firstCycleValues.map((h) => h.note), + ).toEqual(['C3', 'D3', 'E3']); + }); it('scale with mininotation colon', () => { expect( - n(0, 1, 2) + n(seq(0, 1, 2)) .scale(mini('C:major')) .firstCycleValues.map((h) => h.note), ).toEqual(['C3', 'D3', 'E3']); }); it('transposes note numbers with interval numbers', () => { expect( - note(40, 40, 40) + note(seq(40, 40, 40)) .transpose(0, 1, 2) .firstCycleValues.map((h) => h.note), ).toEqual([40, 41, 42]); @@ -54,7 +61,7 @@ describe('tonal', () => { }); it('transposes note numbers with interval strings', () => { expect( - note(40, 40, 40) + note(seq(40, 40, 40)) .transpose('1P', '2M', '3m') .firstCycleValues.map((h) => h.note), ).toEqual([40, 42, 43]); @@ -62,7 +69,7 @@ describe('tonal', () => { }); it('transposes note strings with interval numbers', () => { expect( - note('c', 'c', 'c') + note(seq('c', 'c', 'c')) .transpose(0, 1, 2) .firstCycleValues.map((h) => h.note), ).toEqual(['C', 'Db', 'D']); @@ -70,7 +77,7 @@ describe('tonal', () => { }); it('transposes note strings with interval strings', () => { expect( - note('c', 'c', 'c') + note(seq('c', 'c', 'c')) .transpose('1P', '2M', '3m') .firstCycleValues.map((h) => h.note), ).toEqual(['C', 'D', 'Eb']); diff --git a/packages/tonal/test/tonleiter.test.mjs b/packages/tonal/test/tonleiter.test.mjs index e1a693579..b53a8f405 100644 --- a/packages/tonal/test/tonleiter.test.mjs +++ b/packages/tonal/test/tonleiter.test.mjs @@ -1,6 +1,6 @@ /* tonleiter.test.mjs - -Copyright (C) 2022 Strudel contributors - see +Copyright (C) 2022 Strudel contributors - see 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 . */ diff --git a/packages/tonal/tonal.mjs b/packages/tonal/tonal.mjs index 78183d228..a425782d2 100644 --- a/packages/tonal/tonal.mjs +++ b/packages/tonal/tonal.mjs @@ -1,6 +1,6 @@ /* tonal.mjs - -Copyright (C) 2022 Strudel contributors - see +Copyright (C) 2022 Strudel contributors - see 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 . */ @@ -14,7 +14,7 @@ function scaleStep(step, scale) { scale = scale.replaceAll(':', ' '); step = Math.ceil(step); let { intervals, tonic, empty } = Scale.get(scale); - if ((empty && isNote(scale)) || (!empty && !tonic)) { + if ((empty && isNote(scale)) || (empty && !tonic)) { throw new Error(`incomplete scale. Make sure to use ":" instead of spaces, example: .scale("C:major")`); } else if (empty) { throw new Error(`invalid scale "${scale}"`); diff --git a/packages/tonal/tonleiter.mjs b/packages/tonal/tonleiter.mjs index 15288fcc8..3814394f6 100644 --- a/packages/tonal/tonleiter.mjs +++ b/packages/tonal/tonleiter.mjs @@ -178,7 +178,7 @@ export function renderVoicing({ chord, dictionary, offset = 0, n, mode = 'below' return notes; } -// https://github.com/tidalcycles/strudel/blob/14184993d0ee7d69c47df57ac864a1a0f99a893f/packages/tonal/tonleiter.mjs +// https://codeberg.org/uzu/strudel/blob/14184993d0ee7d69c47df57ac864a1a0f99a893f/packages/tonal/tonleiter.mjs const steps = [1, 0, 2, 0, 3, 4, 0, 5, 0, 6, 0, 7]; const notes = ['C', '', 'D', '', 'E', 'F', '', 'G', '', 'A', '', 'B']; const noteLetters = ['C', 'D', 'E', 'F', 'G', 'A', 'B']; diff --git a/packages/tonal/voicings.mjs b/packages/tonal/voicings.mjs index 0a08575db..d81911e01 100644 --- a/packages/tonal/voicings.mjs +++ b/packages/tonal/voicings.mjs @@ -1,6 +1,6 @@ /* voicings.mjs - -Copyright (C) 2022 Strudel contributors - see +Copyright (C) 2022 Strudel contributors - see 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 . */ diff --git a/packages/transpiler/package.json b/packages/transpiler/package.json index 93648bbc7..2a5e39776 100644 --- a/packages/transpiler/package.json +++ b/packages/transpiler/package.json @@ -14,7 +14,7 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/tidalcycles/strudel.git" + "url": "git+https://codeberg.org/uzu/strudel.git" }, "keywords": [ "tidalcycles", @@ -26,9 +26,9 @@ "author": "Felix Roos ", "license": "AGPL-3.0-or-later", "bugs": { - "url": "https://github.com/tidalcycles/strudel/issues" + "url": "https://codeberg.org/uzu/strudel/issues" }, - "homepage": "https://github.com/tidalcycles/strudel#readme", + "homepage": "https://codeberg.org/uzu/strudel#readme", "dependencies": { "@strudel/core": "workspace:*", "@strudel/mini": "workspace:*", diff --git a/packages/transpiler/test/transpiler.test.mjs b/packages/transpiler/test/transpiler.test.mjs index 988479b4f..02970cf43 100644 --- a/packages/transpiler/test/transpiler.test.mjs +++ b/packages/transpiler/test/transpiler.test.mjs @@ -1,6 +1,6 @@ /* transpiler.test.mjs - -Copyright (C) 2022 Strudel contributors - see +Copyright (C) 2022 Strudel contributors - see 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 . */ diff --git a/packages/transpiler/transpiler.mjs b/packages/transpiler/transpiler.mjs index 2e566305f..fea6bad4d 100644 --- a/packages/transpiler/transpiler.mjs +++ b/packages/transpiler/transpiler.mjs @@ -8,6 +8,16 @@ export function registerWidgetType(type) { widgetMethods.push(type); } +let languages = new Map(); +// config = { getLocations: (code: string, offset?: number) => number[][] } +// see mondough.mjs for example use +// the language will kick in when the code contains a template literal of type +// example: mondo`...` will use language of type "mondo" +// TODO: refactor tidal.mjs to use this +export function registerLanguage(type, config) { + languages.set(type, config); +} + export function transpiler(input, options = {}) { const { wrapAsync = false, addReturn = true, emitMiniLocations = true, emitWidgets = true } = options; @@ -19,14 +29,33 @@ export function transpiler(input, options = {}) { let miniLocations = []; const collectMiniLocations = (value, node) => { - const leafLocs = getLeafLocations(`"${value}"`, node.start, input); - miniLocations = miniLocations.concat(leafLocs); + const minilang = languages.get('minilang'); + if (minilang) { + const code = `[${value}]`; + const locs = minilang.getLocations(code, node.start); + miniLocations = miniLocations.concat(locs); + } else { + const leafLocs = getLeafLocations(`"${value}"`, node.start, input); + miniLocations = miniLocations.concat(leafLocs); + } }; let widgets = []; walk(ast, { enter(node, parent /* , prop, index */) { - if (isTidalTeplateLiteral(node)) { + if (isLanguageLiteral(node)) { + const { name } = node.tag; + const language = languages.get(name); + const code = node.quasi.quasis[0].value.raw; + const offset = node.quasi.start + 1; + if (emitMiniLocations) { + const locs = language.getLocations(code, offset); + miniLocations = miniLocations.concat(locs); + } + this.skip(); + return this.replace(languageWithLocation(name, code, offset)); + } + if (isTemplateLiteral(node, 'tidal')) { const raw = node.quasi.quasis[0].value.raw; const offset = node.quasi.start + 1; if (emitMiniLocations) { @@ -84,8 +113,18 @@ export function transpiler(input, options = {}) { leave(node, parent, prop, index) {}, }); - const { body } = ast; - if (!body?.[body.length - 1]?.expression) { + let { body } = ast; + + if (!body.length) { + console.warn('empty body -> fallback to silence'); + body.push({ + type: 'ExpressionStatement', + expression: { + type: 'Identifier', + name: 'silence', + }, + }); + } else if (!body?.[body.length - 1]?.expression) { throw new Error('unexpected ast format without body expression'); } @@ -120,11 +159,18 @@ function isBackTickString(node, parent) { function miniWithLocation(value, node) { const { start: fromOffset } = node; + + const minilang = languages.get('minilang'); + let name = 'm'; + if (minilang && minilang.name) { + name = minilang.name; // name is expected to be exported from the package of the minilang + } + return { type: 'CallExpression', callee: { type: 'Identifier', - name: 'm', + name, }, arguments: [ { type: 'Literal', value }, @@ -194,7 +240,7 @@ function isLabelStatement(node) { } // converts label expressions to p calls: "x: y" to "y.p('x')" -// see https://github.com/tidalcycles/strudel/issues/990 +// see https://codeberg.org/uzu/strudel/issues/990 function labelToP(node) { return { type: 'ExpressionStatement', @@ -219,12 +265,16 @@ function labelToP(node) { }; } +function isLanguageLiteral(node) { + return node.type === 'TaggedTemplateExpression' && languages.has(node.tag.name); +} + // tidal highlighting // this feels kind of stupid, when we also know the location inside the string op (tidal.mjs) // but maybe it's the only way -function isTidalTeplateLiteral(node) { - return node.type === 'TaggedTemplateExpression' && node.tag.name === 'tidal'; +function isTemplateLiteral(node, value) { + return node.type === 'TaggedTemplateExpression' && node.tag.name === value; } function collectHaskellMiniLocations(haskellCode, offset) { @@ -262,3 +312,18 @@ function tidalWithLocation(value, offset) { optional: false, }; } + +function languageWithLocation(name, value, offset) { + return { + type: 'CallExpression', + callee: { + type: 'Identifier', + name: name, + }, + arguments: [ + { type: 'Literal', value }, + { type: 'Literal', value: offset }, + ], + optional: false, + }; +} diff --git a/packages/web/package.json b/packages/web/package.json index 652496fee..0feddc82d 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -14,7 +14,7 @@ "type": "module", "repository": { "type": "git", - "url": "git+https://github.com/tidalcycles/strudel.git" + "url": "git+https://codeberg.org/uzu/strudel.git" }, "keywords": [ "tidalcycles", @@ -29,9 +29,9 @@ ], "license": "AGPL-3.0-or-later", "bugs": { - "url": "https://github.com/tidalcycles/strudel/issues" + "url": "https://codeberg.org/uzu/strudel/issues" }, - "homepage": "https://github.com/tidalcycles/strudel#readme", + "homepage": "https://codeberg.org/uzu/strudel#readme", "dependencies": { "@strudel/core": "workspace:*", "@strudel/mini": "workspace:*", diff --git a/packages/webaudio/index.mjs b/packages/webaudio/index.mjs index 59672b617..362e61c44 100644 --- a/packages/webaudio/index.mjs +++ b/packages/webaudio/index.mjs @@ -1,6 +1,6 @@ /* index.mjs - -Copyright (C) 2022 Strudel contributors - see +Copyright (C) 2022 Strudel contributors - see 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 . */ diff --git a/packages/webaudio/package.json b/packages/webaudio/package.json index 5714fddf3..5cc0a5538 100644 --- a/packages/webaudio/package.json +++ b/packages/webaudio/package.json @@ -17,7 +17,7 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/tidalcycles/strudel.git" + "url": "git+https://codeberg.org/uzu/strudel.git" }, "keywords": [ "tidalcycles", @@ -29,9 +29,9 @@ "author": "Felix Roos ", "license": "AGPL-3.0-or-later", "bugs": { - "url": "https://github.com/tidalcycles/strudel/issues" + "url": "https://codeberg.org/uzu/strudel/issues" }, - "homepage": "https://github.com/tidalcycles/strudel#readme", + "homepage": "https://codeberg.org/uzu/strudel#readme", "dependencies": { "@strudel/core": "workspace:*", "@strudel/draw": "workspace:*", diff --git a/packages/webaudio/webaudio.mjs b/packages/webaudio/webaudio.mjs index 44a683480..91e28defd 100644 --- a/packages/webaudio/webaudio.mjs +++ b/packages/webaudio/webaudio.mjs @@ -1,6 +1,6 @@ /* webaudio.mjs - -Copyright (C) 2022 Strudel contributors - see +Copyright (C) 2022 Strudel contributors - see 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 . */ @@ -17,8 +17,9 @@ const hap2value = (hap) => { export const webaudioOutputTrigger = (t, hap, ct, cps) => superdough(hap2value(hap), t - ct, hap.duration / cps, cps); // uses more precise, absolute t if available, see https://github.com/tidalcycles/strudel/pull/1004 -export const webaudioOutput = (hap, deadline, hapDuration, cps, t) => - superdough(hap2value(hap), t ? `=${t}` : deadline, hapDuration); +export const webaudioOutput = (hap, deadline, hapDuration, cps, t) => { + return superdough(hap2value(hap), t ? `=${t}` : deadline, hapDuration, cps); +}; Pattern.prototype.webaudio = function () { return this.onTrigger(webaudioOutputTrigger); diff --git a/packages/xen/package.json b/packages/xen/package.json index 76fecf525..88c2bb082 100644 --- a/packages/xen/package.json +++ b/packages/xen/package.json @@ -14,7 +14,7 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/tidalcycles/strudel.git" + "url": "git+https://codeberg.org/uzu/strudel.git" }, "keywords": [ "tidalcycles", @@ -26,9 +26,9 @@ "author": "Felix Roos ", "license": "AGPL-3.0-or-later", "bugs": { - "url": "https://github.com/tidalcycles/strudel/issues" + "url": "https://codeberg.org/uzu/strudel/issues" }, - "homepage": "https://github.com/tidalcycles/strudel#readme", + "homepage": "https://codeberg.org/uzu/strudel#readme", "dependencies": { "@strudel/core": "workspace:*" }, diff --git a/packages/xen/test/xen.test.mjs b/packages/xen/test/xen.test.mjs index 977b0f694..a0982208a 100644 --- a/packages/xen/test/xen.test.mjs +++ b/packages/xen/test/xen.test.mjs @@ -1,6 +1,6 @@ /* xen.test.mjs - -Copyright (C) 2022 Strudel contributors - see +Copyright (C) 2022 Strudel contributors - see 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 . */ diff --git a/packages/xen/tune.mjs b/packages/xen/tune.mjs index feed38f4f..01303bf52 100644 --- a/packages/xen/tune.mjs +++ b/packages/xen/tune.mjs @@ -1,6 +1,6 @@ /* tune.mjs - -Copyright (C) 2022 Strudel contributors - see +Copyright (C) 2022 Strudel contributors - see 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 . */ diff --git a/packages/xen/tunejs.js b/packages/xen/tunejs.js index 7b1a804a0..6b5e7cb7c 100644 --- a/packages/xen/tunejs.js +++ b/packages/xen/tunejs.js @@ -1,6 +1,6 @@ /* tunejs.js - -Copyright (C) 2022 Strudel contributors - see +Copyright (C) 2022 Strudel contributors - see 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 . */ diff --git a/packages/xen/xen.mjs b/packages/xen/xen.mjs index 4077632a5..cc96f4110 100644 --- a/packages/xen/xen.mjs +++ b/packages/xen/xen.mjs @@ -1,6 +1,6 @@ /* xen.mjs - -Copyright (C) 2022 Strudel contributors - see +Copyright (C) 2022 Strudel contributors - see 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 . */ diff --git a/paper/iclc2023.html b/paper/iclc2023.html deleted file mode 100644 index a83e075a2..000000000 --- a/paper/iclc2023.html +++ /dev/null @@ -1,818 +0,0 @@ - - - - - - - - Strudel: live coding patterns on the Web - - - - - - - -

Abstract

-
-

This paper introduces Strudel, which brings the TidalCycles approach -to live coding algorithmic patterns to native JavaScript and the web. We -begin by giving a little background of the first year of development, -before sharing some detail about its implementation and examples of use. -We go on to outline the wide range of synthesis and other outputs -available in Strudel, including WebAudio, MIDI, OSC (for SuperDirt), -WebSerial and CSound, and introduce Strudel’s REPL live editor, -including its built-in visualisations. We then compare Strudel with -Tidal, the trade-offs involved between JavaScript and Haskell, and the -unique capabilities offered by Strudel for aligning patterns.

-
- -

1 Introduction

-

In the following paper, we introduce Strudel, an alternative -implementation of the TidalCycles (or ‘Tidal’ for short) live coding -system, using the JavaScript programming language. Strudel is an attempt -to make live coding more accessible, by creating a system that runs -entirely in the browser, while opening Tidal’s approach to algorithmic -patterns (Mclean 2020) up to -modern audio/visual web technologies. The Strudel REPL is a live code -editor dedicated to manipulating patterns while they play, with builtin -visual feedback. While Strudel is written in JavaScript, the API is -optimized for simplicity and readability by applying code -transformations on the syntax tree level, allowing language operations -that would otherwise be impossible. The application supports multiple -ways to output sound, including Tone.js, Web Audio Nodes, OSC (Open -Sound Control) messages, Web Serial, Web MIDI and Csound. The project is -split into multiple packages, allowing granular reuse in other -applications. Apart from TidalCycles, Strudel draws inspiration from -many prior existing projects like TidalVortex (McLean et al. 2022), -Gibber (Roberts and Kuchera-morin -2012), Estuary (Ogborn et al. -2017), Hydra (Jack [2022] 2022), Ocarina (Solomon -[2021] 2022) and Feedforward (McLean 2020). This paper -expands the Strudel Demo paper for the Web Audio Conference 2022 (Roos and McLean -2022).

-

The first tentative commit to the Strudel project was on 22nd January -2022 by Alex McLean, with the core representation implemented over the -following few days. Although this was his first attempt at a -JavaScript-based application, by 27th January, Alex had managed to -upload the initial version to the ‘npm’ javascript package database, -sharing with the wider community for comment. By 4th February, Felix -Roos had discovered Strudel and contributed a ‘REPL’ user interface to -it, and then contributed a scheduler the next day, so that Strudel could -already make sound. At this point, Alex and Felix shared ownership to -the repository, and the project has since proved to be a productive -confluence of Felix’s own work into music representation and -visualisation, with Alex’s experience with making Tidal. Felix has since -become the primary contributor to Strudel, with Alex continuing to jump -between developing both Strudel and Tidal. Aspects of Strudel’s -development have therefore fed back into TidalCycles, and both systems -have maintained a shared conceptual underpinning. We plan to continue -working towards feature parity between these systems, although within -the syntactical trade-offs and library ecosystems of JavaScript and -Haskell, some divergence is inevitable and healthy.

-

Over the first year of its life, Strudel is now a fully-fledged live -coding environment, porting Tidal’s core represention of patterns, -pattern transformations, and mininotation for polymetric sequences, -combined with a wealth of features for synthesising and visualising -those patterns.

-

2 From Tidal to Strudel and -back

-

As mentioned above, the original Tidal is implemented as a domain -specific language (DSL) embedded in the Haskell pure functional -programming language, and takes advantage of Haskell’s terse syntax and -advanced, ‘strong’ type system. JavaScript on the other hand, is a -multi-paradigm programming language, with a dynamic type system. Because -Tidal leans heavily on many of Haskell’s more unique features, it was -not always clear that it could meaningfully be ported to a -multi-paradigm scripting language. However, this possibility was already -demonstrated with an earlier port to Python [TidalVortex; McLean et al. -(2022)], and we have now successfully implemented Tidal’s pure -functional representation of patterns in Strudel, including partial -application, currying, and the functor, applicative and monadic -structures that underlie Tidal’s expressive pattern transformations. The -result is a terse and highly composable system, where everything is -either a pattern, or a function for combining and manipulating patterns, -offering a rich creative ground for exploration.

-

This development process has been far from a one-way port, however. -The process of porting Tidal’s concepts has also opened up new -possibilities, some just from revisiting every design decision, and some -from the particular affordances and constraints offered by JavaScript. -This has lead to new features (and indeed bugfixes) that have found -their way back to Tidal where appropriate, and ongoing work that we will -return to in the conclusion of this paper.

-

3 Representing Patterns

-

Patterns are the essence of Tidal. Its patterns are abstract entities -that represent flows of time as functions, adapting a technique called -pure functional reactive programming. Taking a time span as its input, a -Pattern can output a set of events that happen within that time span. It -depends on the structure of the Pattern how the events are located in -time. From now on, this process of generating events from a time span -will be called querying. Example:

-
const pattern = sequence(c3, [e3, g3])
-const events = pattern.queryArc(0, 1)
-console.log(events.map(e => e.show()))
-

In this example, we create a pattern using the sequence -function and query it for the time span from -0 to 1. Those numbers represent units of time -called cycles. The length of one cycle depends on the -tempo, which defaults to one cycle per second. The resulting events -are:

-
[{ value: 'c3', begin: 0, end: 1/2 },
-{ value: 'e3', begin: 1/2, end: 3/4 },
-{ value: 'g3', begin: 3/4, end: 1 }]
-

Each event has a value, a begin time and an end time, where time is -represented as a fraction. In the above case, the events are placed in -sequential order, where c3 takes the first half, and e3 and g3 together -take the second half. This temporal placement is the result of the -sequence function, which divides its arguments equally over -one cycle. If an argument is an array, the same rule applies to that -part of the cycle. In the example, e3 and g3 are divided equally over -the second half of the whole cycle.

-

The above examples do not represent how Strudel is used in practice. -In the live coding editor, the user only has to type in the pattern -itself, the querying will be handled by the scheduler. The scheduler -will repeatedly query the pattern for events, which are then scheduled -as sound synthesis or other event triggers. Also, the above event data -structure has been simplified for readability.

-
- - -
-

4 Making Patterns

-

In practice, the end-user live coder will not deal with constructing -patterns directly, but will rather build patterns using Strudel’s -extensive combinator library to create, combine and transform -patterns.

-

The live coder will rarely use the sequence function as -seen above, as sequencing is implicit in many functions. For example in -the following, the note function constructs a pattern of -notes, sequencing its arguments in the same manner as the previous -example.

-
note(c3, [e3, g3])
-

Perhaps more often, they will use the mini-notation for even terser -notation of rhythmic sequences: [^This last example is also valid Tidal -code, albeit the parenthesis is not required in its Haskell syntax in -this case. Tidal does not support passing sequences as lists directly to -the note function, however.].

-
note("c3 [e3 g3]")
-

Such sequences are often treated only a starting point for -manipulation, where they then undergo pattern transformations such as -repetition, symmetry, interference/combination or randomisation, -potentially at multiple timescales. Because Strudel patterns are -represented as pure functions of time rather than as data structures, -very long and complex generative results can be represented and -manipulated without having to store the resulting sequences in -memory.

-

5 Pattern Example

-

The following example showcases how patterns can be utilized to -create musical complexity from simple parts, using repetition and -interference:

-
"<0 2 [4 6](3,4,1) 3>"
-.off(1/4, add(2))
-.off(1/2, add(6))
-.scale('D minor')
-.legato(.25)
-.note().s("sawtooth square")
-.delay(.8).delaytime(.125)
-

The pattern starts with a rhythm of numbers in mini notation, which -are later interpreted inside the scale of D minor. The first line could -also be expressed without mini notation:

-
cat(0, 2, [4, 6].euclid(3, 4, 1), 3)
-

These numbers then undergo various pattern transformations. Here is a -short description of all the functions used:

-
    -
  • cat: play elements sequentially, where each lasts one -cycle
  • -
  • brackets: elements inside brackets are divided equally -over the time of their parent
  • -
  • .euclid(p, s, o): place p pulses evenly over s steps, -with offset o (Toussaint -2005)
  • -
  • .off(n, f): layers a pattern on top of itself, with the -new layer offset by n cycles, and with function f applied
  • -
  • .legato(n): multiply the duration of all events in a -pattern by a factor of n
  • -
  • .echo(t, n, v): copy each event t times, with n cycles -in between each copy, decreasing velocity by v
  • -
  • .note(): interpretes values as notes
  • -
  • .s(name): play back each event with the given -sound
  • -
  • .delay(wet): add delay
  • -
  • .delaytime(t): set delay time
  • -
-

Much of the above will be familiar to Tidal users.

- -

6 Ways to make Sound (and other -events)

-

To generate sound, Strudel supports bindings for different -outputs:

-
    -
  • Tone.js (deprecated)
  • -
  • Web Audio API
  • -
  • WebDirt, a js recreation of Tidal’s Dirt sample engine -(deprecated)
  • -
  • OSC via osc-js, compatible with superdirt
  • -
  • Csound via the Csound WebAssembly build
  • -
  • MIDI via WebMIDI
  • -
  • Serial via WebSerial
  • -
-

At first, we used Tone.js as sound output, but it proved to be -limited for the use case of Strudel, where each individual event could -potentially have a completely different audio graph. While the Web Audio -API takes a fire-and-forget approach, creating a lot of Tone.js -instruments and effects causes performance issues quickly. For that -reason, we chose to search for alternatives.

-

Strudel’s new default output uses the Web Audio API to create a new -audio graph for each event. It currently supports basic oscillators, -sample playback, various effects and an experimental support for -soundfonts.

-

WebDirt (Ogborn [2016] 2022) was -created as part of the Estuary Live Coding System (Ogborn et al. -2017), and proved to be a solid choice for handling samples in -Strudel as well. We are however focused on working more directly with -the Web Audio API to be able to integrate new features more tightly.

-

Using the OSC protocol via Strudel’s provided Node.js-based OSC proxy -server, it is possible to send network messages to trigger events. This -is mainly used to render sound using SuperDirt (SuperDirt [2015] 2022), -which is the well-developed Supercollider-based synthesis framework that -Tidal live coders generally use as standard.

-

Recently, the experimental integration of Csound proved to bring a -new dimension of sound design capabilities to Strudel. Thanks to the -WebAssembly distribution of this classic system (Yi, Lazzarini, and Costello -2018), Csound ‘orchestra’ synthesisers can be embedded in and -then patterned with Strudel code.

-

MIDI output can also be used to send MIDI messages to either external -instruments or to other programs on the same device. Unlike OSC, Strudel -is able to send MIDI directly without requiring additional proxy -software, but only from web browsers that support it (at the time of -writing, this means Chromium-based browsers).

-

Finally, Strudel supports Serial output, for example to trigger -events via microcontrollers. This has already been explored for robot -choreography by Kate Sicchio and Alex McLean, via a performance -presented at the International Conference on Live Interfaces 2022.

-

7 The Strudel REPL

-

While Strudel can be used as a library in any JavaScript codebase, -its main, reference user interface is the Strudel REPL[^REPL stands for -read, evaluate, print/play, loop. It is friendly jargon for an -interactive programming interface from computing heritage, usually for a -commandline interface but also applied to live coding editors.], which -is a browser-based live coding environment. This live code editor is -dedicated to manipulating Strudel patterns while they play. The REPL -features built-in visual feedback, which highlights which elements in -the patterned (mini-notation) sequences are influencing the event that -is currently being played. This feedback is designed to support both -learning and live use of Strudel.

-

Besides a UI for playback control and meta information, the main part -of the REPL interface is the code editor powered by CodeMirror. In it, -the user can edit and evaluate pattern code live, using one of the -available synthesis outputs to create music and/or sound art. The -control flow of the REPL follows 3 basic steps:

-
    -
  1. The user writes and updates code. Each update transpiles and -evaluates it to create a Pattern instance
  2. -
  3. While the REPL is running, the Scheduler queries the -active Pattern by a regular interval, generating -Events (also known as Haps in Strudel) for the -next time span.
  4. -
  5. For each scheduling tick, all generated Events are -triggered by calling their onTrigger method, which is set -by the output.
  6. -
-
-REPL control flow - -
-

7.1 User Code

-

To create a Pattern from the user code, two steps are -needed:

-
    -
  1. Transpile the JS input code to make it functional
  2. -
  3. Evaluate the transpiled code
  4. -
-

7.1.1 Transpilation & -Evaluation

-

In the JavaScript world, using transpilation is a common practise to -be able to use language features that are not supported by the base -language. Tools like babel will transpile code that -contains unsupported language features into a version of the code -without those features.

-

In the same tradition, Strudel can add a transpilation step to -simplify the user code in the context of live coding. For example, the -Strudel REPL lets the user create mini notation patterns using just -double quoted strings, while single quoted strings remain what they -are:

-
"c3 [e3 g3]*2"
-

is transpiled to:

-
mini("c3 [e3 g3]*2").withMiniLocation([1,0,0],[1,14,14])
-

Here, the string is wrapped in mini, which will create a -pattern from a mini notation string. Additionally, the -withMiniLocation method passes the original source code -location of the string to the pattern, which enables highlighting active -events.

-

Other convenient features like pseudo variables, operator overloading -and top level await are possible with transpilation.

-

After the transpilation, the code is ready to be evaluated into a -Pattern.

-

Behind the scenes, the user code string is parsed with -acorn, turning it into an Abstract Syntax Tree (AST). The -AST allows changing the structure of the code before generating the -transpiled version using escodegen.

-

7.1.2 Mini Notation

-

While the transpilation allows JavaScript to express Patterns in a -less verbose way, it is still preferable to use the Mini Notation as a -more compact way to express rhythm. Strudel aims to provide the same -Mini Notation features and syntax as used in Tidal.

-

The Mini Notation parser is implemented using peggy, -which allows generating performant parsers for Domain Specific Languages -(DSLs) using a concise grammar notation. The generated parser turns the -Mini Notation string into an AST which is used to call the respective -Strudel functions with the given structure. For example, -"c3 [e3 g3]*2" will result in the following calls:

-
seq(
-  reify('c3').withLocation([1,1,1], [1,4,4]),
-  seq(
-    reify('e3').withLocation([1,5,5], [1,8,8]),
-    reify('g3').withLocation([1,8,8], [1,10,10]),
-  ).fast(2)
-)
-

7.1.3 Highlighting Locations

-

As seen in the examples above, both the JS and the Mini Notation -parser add source code locations using withMiniLocation and -withLocation methods. While the JS parser adds locations -relative to the user code as a whole, the Mini Notation adds locations -relative to the position of the mini notation string. The absolute -location of elements within Mini Notation can be calculated by simply -adding both locations together. This absolute location can be used to -highlight active events in real time.

-

7.2 Scheduling Events

-

After an instance of Pattern is obtained from the user -code, it is used by the scheduler to get queried for events. Once -started, the scheduler runs at a fixed interval to query active pattern -for events withing the current interval’s time span. A simplified -implementation looks like this:

-
let pattern = seq('c3', ['e3', 'g3']); // pattern from user
-let interval = 0.5; // query interval in seconds
-let time = 0; // beginning of current time span
-let minLatency = .1; // min time before a hap should trigger
-setInterval(() => {
-  const haps = pattern.queryArc(time, time + interval);
-  time += interval; // increment time
-  haps.forEach((hap) => {
-    const deadline = hap.whole.begin - time + minLatency;
-    onTrigger(hap, deadline, duration);
-  });
-}, interval * 1000); // query each "interval" seconds
-

Note that the above code is simplified for illustrative purposes. The -actual implementation has to work around imprecise callbacks of -setInterval. More about the implementation details can be -read in this -blog post.

-

The fact that Pattern.queryArc is a pure function that -maps a time span to a set of events allows us to choose any interval we -like without changing the resulting output. It also means that when the -pattern is changed from outside, the next scheduling callback will work -with the new pattern, keeping its clock running.

-

The latency between the time the pattern is evaluated and the change -is heard is between minLatency and -interval + minLatency, in our example between 100ms and -600ms. In Strudel, the current query interval is 50ms with a minLatency -of 100ms, meaning the latency is between 50ms and 150ms.

-

7.3 Output

-

The last step is to trigger each event in the chosen output. This is -where the given time and value of each event is used to generate audio -or any other form of time based output. The default output of the -Strudel REPL is the WebAudio output. To understand what an output does, -we first have to understand what control parameters are.

-

7.3.1 Control Parameters

-

To be able to manipulate multiple aspects of sound in parallel, so -called control parameters are used to shape the value of each event. -Example:

-
note("c3 e3").cutoff(1000).s('sawtooth')
-  .queryArc(0, 1).map(hap => hap.value)
-/* [
-  { note: 'c3', cutoff: 1000, s: 'sawtooth' }
-  { note: 'e3', cutoff: 1000, s: 'sawtooth' }
-] */
-

Here, the control parameter functions note, -cutoff and s are used, where each controls a -different property in the value object. Each control parameter function -accepts a primitive value, a list of values to be sequenced into a -Pattern, or a Pattern. In the example, -note gets a Pattern from a Mini Notation -expression (double quoted), while cutoff and s -are given a Number and a (single quoted) -String respectively.

-

Strudel comes with a large default set of control parameter functions -that are based on the ones used by Tidal and SuperDirt, focusing on -music and audio terminology. It is however possible to create custom -control paramters for any purpose:

-
const { x, y } = createParams('x', 'y')
-x(sine.range(0, 200)).y(cosine.range(0,200))
-

This example creates the custom control parameters x and -y which are then used to form a pattern that descibes the -coordinates of a circle.

-

7.3.2 Outputs

-

Now that we know how the value of an event is manipulated using -control parameters, we can look at how outputs can use that value to -generate anything. The scheduler above was calling the -onTrigger function which is used to implement the output. A -very simple version of the web audio output could look like this:

-
function onTrigger(hap, deadline, duration) {
-  const { note } = hap.value;
-  const time = getAudioContext().currentTime + deadline;
-  const o = getAudioContext().createOscillator();
-  o.frequency.value = getFreq(note);
-  o.start(time);
-  o.stop(time + event.duration);
-  o.connect(getAudioContext().destination);
-}
-

The above example will create an OscillatorNode for each -event, where the frequency is controlled by the note param. -In essence, this is how the WebAudio API output of Strudel works, only -with many more parameters to control synths, samples and effects.

-

8 Pattern alignment and -combination

-

One core aspect of Strudel, inherited from Tidal, is the flexible way -that patterns can be combined, irrespective of their structure. Its -declarative approach means a live coder does not have to think about the -details of how this is done, only what is to be -done.

-

As a simple example, consider two number patterns -"0 [1 2] 3", and "10 20". The first has three -contiguous steps of equal lengths, with the second step broken down into -two substeps, giving four events in total. There are a very large number -of ways in which the structure of these two patterns could be combined, -but the default method in both Strudel and Tidal is to line up the -cycles of the two patterns, and then take events from the first pattern -and match them with those in the second pattern. Therefore, the -following two lines are equivalent:

-
"0 [1 2] 3".add("10 20")
-"10 [11 22] 23"
-

Where the events only partially overlap, they are treated as -fragments of the event in the first pattern. This is a little difficult -to conceptualise, but lets start by comparing the two patterns in the -following example:

-
"0 1 2".add("10 20")
-"10 [11 21] 20"
-

They are similar to the previous example in that the number -1 is split in two, with its two halves added to -10 and 20 respectively. However, the -11 ‘remembers’ that it is a fragment of that original -1 event, and so is treated as having a duration of a third -of a cycle, despite only being active for a sixth of a cycle. Likewise, -the 21 is also a fragment of that original 1 -event, but a fragment of its second half. Because the start of its event -is missing, it wouldn’t actually trigger a sound (unless it underwent -further pattern transformations/combinations).

-

In practice, the effect of this default, implicit method for -combining two patterns is that the second pattern is added in -to the first one, and indeed this can be made explicit:

-
"0 1 2".add.in("10 20")
-

This makes way for other ways to align the pattern, and several are -already defined, in particular:

-
    -
  • in - as explained above, aligns cycles, and applies -values from the pattern on the right in to the pattern on the -left.
  • -
  • out - as with in, but values are applied -out of the pattern on the left (i.e. in to the one on -the right).
  • -
  • mix - structures from both patterns are combined, so -that the new events are not fragments but are created at intersections -of events from both sides.
  • -
  • squeeze - cycles from the pattern on the right are -squeezed into events on the left. So that -e.g. "0 1 2".add.squeeze("10 20") is equivalent to -"[10 20] [11 21] [12 22]".
  • -
  • squeezeout - as with squeeze, but cycles -from the left are squeezed into events on the right. So, -"0 1 2".add.squeezeout("10 20") is equivalent to -[10 11 12] [20 21 22].
  • -
  • trig is similar to squeezeout in that -cycles from the right are aligned with events on the left. However those -cycles are not ‘squeezed’, rather they are truncated to fit the event. -So "0 1 2 3 4 5 6 7".add.trig("10 [20 30]") would be -equivalent to 10 11 12 13 20 21 30 31. In effect, events on -the right ‘trigger’ cycles on the left.
  • -
  • trigzero is similar to trig, but the -pattern is ‘triggered’ from its very first cycle, rather than from the -current cycle. trig and trigzero therefore -only give different results where the leftmost pattern differs from one -cycle to the next.
  • -
-

We will save going deeper into the background, design and -practicalities of these alignment functions for future publications. -However in the next section, we take them as a case study for looking at -the different design affordances offered by Haskell to Tidal, and -JavaScript to Strudel.

-

9 Comparing Strudel and Haskell in -use

-

Unlike Haskell, JavaScript lacks the ability to define custom infix -operators, or change the meaning of existing ones. So the above Strudel -example of "0 1 2".add.out("10 20") is equivalent to the -Tidal expression "0 1 2" +| "10 20", where the vertical bar -in the operator +| stands for out (where -a |+ b would be equivalent of -a.add.in(b)).

-

From this we can already see that Tidal tends towards brevity through -mixing infix operators with functions, and Strudel tends towards -spelling out operations which are joined together with the -. operator. This then is the design trade-off of Tidal’s -tersity, versus Strudel’s simplicity.

-

To demonstrate this, consider the following Tidal pattern:

-
iter 4 $ every 3 (||+ n "10 20") $ (n "0 1 3") # s "triangle" # crush 4
-

This can be directly translated to the Strudel equivalent:

-
iter(4, every(3, add.squeeze("10 20"), n("0 1 3").s("triangle").crush(4)))
-

Although for a more canonical Strudel expression, we would reorder it -as:

-
n("0 1 3").every(3, add.squeeze("10 20")).iter(4).s("triangle").crush(4)
-

The Strudel example uses the . method call operator for -all operations and combinations, whereas the Tidal example has -# for the default method for combining patterns and uses -infix operators for other methods. The lack of parenthesis in the Tidal -example is partly due to the way that arguments are applied to Haskell’s -functions, and partly due to the use of the $ operator as -an alternative way to establish precedence and control the order of -evaluation.

-

Considering the above, we argue that the Haskell syntax is a little -cleaner, but that the Strudel syntax is easier to learn. Our informal -observation is that while Haskell’s dollar $ operator is -very useful in making code easier to work with, it is one of the most -difficult aspects of Tidal use for beginners to learn. On the other -hand, the deeper levels of parenthesis in Strudel code can be difficult -to keep track of, especially while coding under pressure of live musical -performance. However this difficulty can be largely be mitigated by -reordering expressions, and further mitigated by supporting editor -features.

-

With Strudel, we have little choice but to embrace the affordances -and constraints offered by JavaScript, and while designing a -domain-specific language entirely based on method calls is a challenge, -through creative adoption of functional programming techniques like -partial application, we are so far very happy with the results. Tidal’s -functional reactive approach to pattern-making has in general translated -well to JavaScript, and opportunities and constraints have overall -traded off to create a very approachable and useable live coding -environment.

-

9.1 The trade-off of flexible -typing

-

We have identified one problem with porting Tidal to JavaScript where -we have missed Haskell’s strict typing and type inference. In both Tidal -and Strudel, time is rational, where any point in time is represented as -the ratio of two integers. This allows representation of musical ratios -such that are impossible to represent accurately using the more common -floating point numbers. However while libraries are available that -support rational numbers in JavaScript, the lack of strict typing means -that it is easy to implement pattern methods where computationally -expensive conversion from floating point to rational numbers are -performed late, and therefore often enough to overload the CPUs, due to -the large number of iterative calculations required to estimate a ratio -for a given floating point number. To mitigate this problem, we might -consider moving to TypeScript in the future.

-

10 Future Outlook

-

The project is still young, with many features on the horizon. As -general guiding principles, Strudel aims to be

-
    -
  1. accessible
  2. -
  3. consistent with Tidal’s approach to pattern
  4. -
  5. modular and extensible
  6. -
-

While Haskell’s type system makes it a great language for the ongoing -development of Tidal’s inner representation of pattern, JavaScript’s -vibrant ecosystem, flexibility and accessibility makes it a great host -for more ad-hoc experiments, including interface design. For the future, -it is planned to integrate additional alternative sound engines such as -Glicol (Lan -[2020] 2022) and Faust (Faust - Programming -Language for Audio Applications and Plugins [2016] 2022). -Strudel is already approaching feature parity with Tidal, but there are -more Tidal functions to be ported, and work to be done to improve -compatibility with Tidal’s mininotation. Tidal version 2.0 is under -development, which brings a new representation for sequences to its -patterns, which will then be brought to Strudel. Besides sound, other -ways to render events are being explored, such as graphical, and -choreographic output. We are also looking into alternative ways of -editing patterns, including multi-user editing for network music, -parsing a novel syntax to escape the constraints of javascript, and -developing hardware/e-textile interfaces. In summary, there is a lot of -fun ahead.

-

11 Links

-

The Strudel REPL is available at https://strudel.cc, including an -interactive tutorial. The repository is at https://github.com/tidalcycles/strudel, all the code is -open source under the AGPL-3.0 License.

-

12 Acknowledgments

-

Thanks to the Strudel and wider Tidal, live coding, WebAudio and -free/open source software communities for inspiration and support. Alex -McLean’s work on this project is supported by a UKRI Future Leaders -Fellowship [grant number MR/V025260/1].

-

References

-
-
-Faust - Programming Language for Audio Applications and -Plugins. (2016) 2022. C++. GRAME. https://github.com/grame-cncm/faust. -
-
-Jack, Olivia. (2022) 2022. Hydra. https://github.com/ojack/hydra. -
-
-Lan, Qichao. (2020) 2022. Chaosprint/Glicol. Rust. https://github.com/chaosprint/glicol. -
-
-Mclean, Alex. 2020. “Algorithmic Pattern.” In -Proceedings of the International Conference on New Interfaces for -Musical Expression, 265--270. Birmingham, UK. https://zenodo.org/record/4813352. -
-
-McLean, Alex. 2020. “Feedforward.” In Proceedings of -New Interfaces for Musical Expression. Birmingham. https://zenodo.org/record/6353969. -
-
-McLean, Alex, Raphaël Forment, Sylvain Le Beux, and Damián Silvani. -2022. “TidalVortex Zero.” In Proceedings of the 7th -International Conference on Live Coding. Limerick, Ireland: Zenodo. -https://doi.org/10.5281/zenodo.6456380. -
-
-Ogborn, David. (2016) 2022. Dktr0/WebDirt. JavaScript. https://github.com/dktr0/WebDirt. -
-
-Ogborn, David, Jamie Beverley, Luis Navarro del Angel, Eldad Tsabary, -and Alex McLean. 2017. “Estuary: Browser-Based Collaborative -Projectional Live Coding of Musical Patterns.” In Proceedings -of the International Conference on Live Coding, 11. Morelia. -
-
-Roberts, Charles, and Joann Kuchera-morin. 2012. “Gibber: Live -Coding Audio in the Browser.” In In Proceedings of the 2012 -International Computer Music Conference. -
-
-Roos, Felix, and Alex McLean. 2022. “Strudel: Algorithmic Patterns -for the Web.” In. Zenodo. https://doi.org/10.5281/zenodo.6768844. -
-
-Solomon, Mike. (2021) 2022. Purescript-Ocarina. PureScript. https://github.com/mikesol/purescript-ocarina. -
-
-SuperDirt. (2015) 2022. SuperCollider. musikinformatik. https://github.com/musikinformatik/SuperDirt. -
-
-Toussaint, Godfried. 2005. “The Euclidean Algorithm Generates -Traditional Musical Rhythms.” In In Proceedings of BRIDGES: -Mathematical Connections in Art, Music and Science, 47–56. http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.62.231. -
-
-Yi, Steven, Victor Lazzarini, and Edward Costello. 2018. -“WebAssembly AudioWorklet Csound.” In. Berlin, Germany. https://mural.maynoothuniversity.ie/16018/. -
-
- - diff --git a/paper/pandoc/iclc.html b/paper/pandoc/iclc.html deleted file mode 100755 index 90406b16e..000000000 --- a/paper/pandoc/iclc.html +++ /dev/null @@ -1,80 +0,0 @@ -$if(false)$ - -This is a pandoc template and should not be edited. - -$endif$ - - - - - - -$for(author-meta)$ - -$endfor$ -$if(date-meta)$ - -$endif$ - $if(title-prefix)$$title-prefix$ - $endif$$pagetitle$ - -$if(quotes)$ - -$endif$ -$if(highlighting-css)$ - -$endif$ - -$for(css)$ - -$endfor$ -$if(math)$ - $math$ -$endif$ -$for(header-includes)$ - $header-includes$ -$endfor$ - - -$for(include-before)$ -$include-before$ -$endfor$ -$if(title)$ -
-

$title$

-$if(subtitle)$ -

$subtitle$

-$endif$ -
    -$for(author)$ -
  • $author$
  • -$endfor$ -
-$if(date)$ -

$date$

-$endif$ -
-$endif$ -$if(toc)$ -
-$toc$ -
-$endif$ - -

Abstract

-
-$if(abstract)$ -$abstract$ -$else$ -Please provide an abstract in the metadata block at the top of your -markdown document. Refer to template.txt for details. -$endif$ -
- -$body$ -$for(include-after)$ -$include-after$ -$endfor$ - - diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a23c80924..7aecd35bd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -347,6 +347,37 @@ importers: specifier: ^3.0.4 version: 3.0.4(@types/debug@4.1.12)(@types/node@22.10.10)(@vitest/ui@3.0.4)(jiti@2.4.2)(lightningcss@1.29.1)(terser@5.37.0)(yaml@2.7.0) + packages/mondo: + devDependencies: + vite: + specifier: ^6.0.11 + version: 6.0.11(@types/node@22.10.10)(jiti@2.4.2)(lightningcss@1.29.1)(terser@5.37.0)(yaml@2.7.0) + vitest: + specifier: ^3.0.4 + version: 3.0.4(@types/debug@4.1.12)(@types/node@22.10.10)(@vitest/ui@3.0.4)(jiti@2.4.2)(lightningcss@1.29.1)(terser@5.37.0)(yaml@2.7.0) + + packages/mondough: + dependencies: + '@strudel/core': + specifier: workspace:* + version: link:../core + '@strudel/transpiler': + specifier: workspace:* + version: link:../transpiler + mondolang: + specifier: workspace:* + version: link:../mondo + devDependencies: + mondo: + specifier: '*' + version: 0.4.4 + vite: + specifier: ^6.0.11 + version: 6.0.11(@types/node@22.10.10)(jiti@2.4.2)(lightningcss@1.29.1)(terser@5.37.0)(yaml@2.7.0) + vitest: + specifier: ^3.0.4 + version: 3.0.4(@types/debug@4.1.12)(@types/node@22.10.10)(@vitest/ui@3.0.4)(jiti@2.4.2)(lightningcss@1.29.1)(terser@5.37.0)(yaml@2.7.0) + packages/motion: dependencies: '@strudel/core': @@ -683,6 +714,9 @@ importers: '@strudel/mini': specifier: workspace:* version: link:../packages/mini + '@strudel/mondo': + specifier: workspace:* + version: link:../packages/mondough '@strudel/motion': specifier: workspace:* version: link:../packages/motion @@ -2975,6 +3009,10 @@ packages: resolution: {integrity: sha512-groO71Fvi5SWpxjI9Ia+chy0QBwT61mg6yxJV27f5YFf+Mw+STT75K6SHySpP8Co5LsCrtsbCH5dJZSRtkSKaQ==} engines: {node: '>= 14.0.0'} + amdefine@1.0.1: + resolution: {integrity: sha512-S2Hw0TtNkMJhIabBwIojKL9YHO5T0n5eNqWJ7Lrlel/zDbftQpxpapi8tZs3X1HWa+u+QeydGmzzNU0m09+Rcg==} + engines: {node: '>=0.4.2'} + ansi-align@3.0.1: resolution: {integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==} @@ -3103,6 +3141,9 @@ packages: resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} engines: {node: '>= 0.4'} + async@0.2.10: + resolution: {integrity: sha512-eAkdoKxU6/LkKDBzLpT+t6Ff5EtfSF4wx1WfJiPEEV7WNLnDaRXk0oVysiEPm262roaachGexwUv94WhSgN5TQ==} + async@3.2.6: resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} @@ -3337,6 +3378,9 @@ packages: claviature@0.1.0: resolution: {integrity: sha512-Ai12axNwQ7x/F9QAj64RYKsgvi5Y33+X3GUSKAC/9s/adEws8TSSc0efeiqhKNGKBo6rT/c+CSCwSXzXxwxZzQ==} + cldr-plurals@1.0.0: + resolution: {integrity: sha512-xGkehDsjj/ng1LtYiAzdiqDgqTQ/qmWafDlB6R8CfXbpXe4Ge2Tl4l7gxiA+yaD1WCTP3EVfwerdmVwfco7vxw==} + clean-stack@2.2.0: resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} engines: {node: '>=6'} @@ -4378,6 +4422,9 @@ packages: resolution: {integrity: sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q==} engines: {node: '>=16 || 14 >=14.17'} + globalize@0.1.1: + resolution: {integrity: sha512-5e01v8eLGfuQSOvx2MsDMOWS0GFtCx1wPzQSmcHw4hkxFzrQDBO3Xwg/m8Hr/7qXMrHeOIE29qWVzyv06u1TZA==} + globals@11.12.0: resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} engines: {node: '>=4'} @@ -4439,6 +4486,11 @@ packages: h3@1.14.0: resolution: {integrity: sha512-ao22eiONdgelqcnknw0iD645qW0s9NnrJHr5OBz4WOMdBdycfSas1EQf1wXRsm+PcB2Yoj43pjBPwqIpJQTeWg==} + handlebars@1.1.2: + resolution: {integrity: sha512-6lekK3aSHE7XnEqEs+JWQJRSRdCqJYHEVjEfWWZv9pjLUYZNeP26EtlgOrpDCSX+k5N1m74urTbztgBOCko1Kg==} + engines: {node: '>=0.4.7'} + hasBin: true + handlebars@4.7.8: resolution: {integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==} engines: {node: '>=0.4.7'} @@ -5586,6 +5638,10 @@ packages: engines: {node: '>=18'} hasBin: true + mondo@0.4.4: + resolution: {integrity: sha512-3ot6iKwC9KZvwmyZ9dgPCnlt0O1GuBnK8QZyhnoJdtkIiRL9X/cmQuu88CbFSznQx6bq19xQdwJHpv8hggH62Q==} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. + mrmime@2.0.0: resolution: {integrity: sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==} engines: {node: '>=10'} @@ -5827,6 +5883,9 @@ packages: resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} engines: {node: '>=12'} + optimist@0.3.7: + resolution: {integrity: sha512-TCx0dXQzVtSCg2OgY/bO9hjM9cV4XYx09TVK+s3+FhkjT6LovsLe+pPMzpWf+6yXK/hUizs2gUoTw3jHM0VaTQ==} + optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} @@ -6725,6 +6784,10 @@ packages: source-map-support@0.5.21: resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + source-map@0.1.43: + resolution: {integrity: sha512-VtCvB9SIQhk3aF6h+N85EaqIaBFIAfZ9Cu+NJHHVvc8BbEcnvDcFw6sqQ2dQrT6SlOrZq3tIvyD9+EGq/lJryQ==} + engines: {node: '>=0.8.0'} + source-map@0.5.7: resolution: {integrity: sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==} engines: {node: '>=0.10.0'} @@ -7155,6 +7218,11 @@ packages: ufo@1.5.4: resolution: {integrity: sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==} + uglify-js@2.3.6: + resolution: {integrity: sha512-T2LWWydxf5+Btpb0S/Gg/yKFmYjnX9jtQ4mdN9YRq73BhN21EhU0Dvw3wYDLqd3TooGUJlCKf3Gfyjjy/RTcWA==} + engines: {node: '>=0.4.0'} + hasBin: true + uglify-js@3.19.3: resolution: {integrity: sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==} engines: {node: '>=0.8.0'} @@ -7173,6 +7241,9 @@ packages: underscore@1.13.7: resolution: {integrity: sha512-GMXzWtsc57XAtguZgaQViUOzs0KTkk8ojr3/xAxXLITqf/3EMwxC0inyETfDFjH/Krbhuep0HNbbjI9i/q3F3g==} + underscore@1.5.2: + resolution: {integrity: sha512-yejOFsRnTJs0N9CK5Apzf6maDO2djxGoLLrlZlvGs2o9ZQuhIhDL18rtFyy4FBIbOkzA6+4hDgXbgz5EvDQCXQ==} + undici-types@6.20.0: resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==} @@ -7555,6 +7626,10 @@ packages: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} + wordwrap@0.0.3: + resolution: {integrity: sha512-1tMA907+V4QmxV7dbRvb4/8MaRALK6q9Abid3ndMYnbyo8piisCmeONVqVSXqQA3KaP4SLt5b7ud6E2sqP8TFw==} + engines: {node: '>=0.4.0'} + wordwrap@1.0.0: resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} @@ -10438,6 +10513,9 @@ snapshots: '@algolia/requester-fetch': 5.20.0 '@algolia/requester-node-http': 5.20.0 + amdefine@1.0.1: + optional: true + ansi-align@3.0.1: dependencies: string-width: 4.2.3 @@ -10647,6 +10725,9 @@ snapshots: async-function@1.0.0: {} + async@0.2.10: + optional: true + async@3.2.6: {} asynckit@0.4.0: {} @@ -10904,6 +10985,8 @@ snapshots: claviature@0.1.0: {} + cldr-plurals@1.0.0: {} + clean-stack@2.2.0: {} cli-boxes@3.0.0: {} @@ -12046,6 +12129,8 @@ snapshots: minipass: 4.2.8 path-scurry: 1.11.1 + globalize@0.1.1: {} + globals@11.12.0: {} globals@14.0.0: {} @@ -12112,6 +12197,12 @@ snapshots: uncrypto: 0.1.3 unenv: 1.10.0 + handlebars@1.1.2: + dependencies: + optimist: 0.3.7 + optionalDependencies: + uglify-js: 2.3.6 + handlebars@4.7.8: dependencies: minimist: 1.2.8 @@ -13695,6 +13786,13 @@ snapshots: requirejs: 2.3.7 requirejs-config-file: 4.0.0 + mondo@0.4.4: + dependencies: + cldr-plurals: 1.0.0 + globalize: 0.1.1 + handlebars: 1.1.2 + underscore: 1.5.2 + mrmime@2.0.0: {} ms@2.0.0: {} @@ -13987,6 +14085,10 @@ snapshots: is-docker: 2.2.1 is-wsl: 2.2.0 + optimist@0.3.7: + dependencies: + wordwrap: 0.0.3 + optionator@0.9.4: dependencies: deep-is: 0.1.4 @@ -15094,6 +15196,11 @@ snapshots: buffer-from: 1.1.2 source-map: 0.6.1 + source-map@0.1.43: + dependencies: + amdefine: 1.0.1 + optional: true + source-map@0.5.7: {} source-map@0.6.1: {} @@ -15561,6 +15668,13 @@ snapshots: ufo@1.5.4: {} + uglify-js@2.3.6: + dependencies: + async: 0.2.10 + optimist: 0.3.7 + source-map: 0.1.43 + optional: true + uglify-js@3.19.3: optional: true @@ -15577,6 +15691,8 @@ snapshots: underscore@1.13.7: {} + underscore@1.5.2: {} + undici-types@6.20.0: {} unenv@1.10.0: @@ -15957,6 +16073,8 @@ snapshots: word-wrap@1.2.5: {} + wordwrap@0.0.3: {} + wordwrap@1.0.0: {} workbox-background-sync@7.0.0: diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index ef8cc99d5..a4fca9ff7 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,6 +1,12 @@ packages: - # all packages in direct subdirs of packages/ - - "packages/*" - - "examples/*" - - "tools/dbpatch" - - "website/" + - packages/* + - examples/* + - tools/dbpatch + - website/ + +onlyBuiltDependencies: + - esbuild + - nx + - sharp + - tree-sitter + - tree-sitter-haskell diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap index 0eacfdfb0..cc45848bf 100644 --- a/test/__snapshots__/examples.test.mjs.snap +++ b/test/__snapshots__/examples.test.mjs.snap @@ -1889,6 +1889,86 @@ exports[`runs examples > example "chunkBack" example index 0 1`] = ` ] `; +exports[`runs examples > example "chunkBackInto" example index 0 1`] = ` +[ + "[ 0/1 → 1/16 | s:bd speed:2 bank:tr909 ]", + "[ 1/16 → 1/8 | s:sd speed:2 bank:tr909 ]", + "[ 1/8 → 3/16 | s:bd speed:2 bank:tr909 ]", + "[ 3/16 → 1/4 | s:sd speed:2 bank:tr909 ]", + "[ 1/4 → 3/8 | s:ht bank:tr909 ]", + "[ 3/8 → 1/2 | s:lt bank:tr909 ]", + "[ 1/2 → 5/8 | s:bd bank:tr909 ]", + "[ 3/4 → 7/8 | s:cp bank:tr909 ]", + "[ 7/8 → 1/1 | s:lt bank:tr909 ]", + "[ 1/1 → 9/8 | s:bd bank:tr909 ]", + "[ 9/8 → 5/4 | s:sd bank:tr909 ]", + "[ 5/4 → 21/16 | s:ht speed:2 bank:tr909 ]", + "[ 21/16 → 11/8 | s:lt speed:2 bank:tr909 ]", + "[ 11/8 → 23/16 | s:ht speed:2 bank:tr909 ]", + "[ 23/16 → 3/2 | s:lt speed:2 bank:tr909 ]", + "[ 3/2 → 13/8 | s:bd bank:tr909 ]", + "[ 7/4 → 15/8 | s:cp bank:tr909 ]", + "[ 15/8 → 2/1 | s:lt bank:tr909 ]", + "[ 2/1 → 17/8 | s:bd bank:tr909 ]", + "[ 17/8 → 9/4 | s:sd bank:tr909 ]", + "[ 9/4 → 19/8 | s:ht bank:tr909 ]", + "[ 19/8 → 5/2 | s:lt bank:tr909 ]", + "[ 5/2 → 41/16 | s:bd speed:2 bank:tr909 ]", + "[ 21/8 → 43/16 | s:bd speed:2 bank:tr909 ]", + "[ 11/4 → 23/8 | s:cp bank:tr909 ]", + "[ 23/8 → 3/1 | s:lt bank:tr909 ]", + "[ 3/1 → 25/8 | s:bd bank:tr909 ]", + "[ 25/8 → 13/4 | s:sd bank:tr909 ]", + "[ 13/4 → 27/8 | s:ht bank:tr909 ]", + "[ 27/8 → 7/2 | s:lt bank:tr909 ]", + "[ 7/2 → 29/8 | s:bd bank:tr909 ]", + "[ 15/4 → 61/16 | s:cp speed:2 bank:tr909 ]", + "[ 61/16 → 31/8 | s:lt speed:2 bank:tr909 ]", + "[ 31/8 → 63/16 | s:cp speed:2 bank:tr909 ]", + "[ 63/16 → 4/1 | s:lt speed:2 bank:tr909 ]", +] +`; + +exports[`runs examples > example "chunkInto" example index 0 1`] = ` +[ + "[ 0/1 → 1/16 | s:bd speed:2 bank:tr909 ]", + "[ 1/16 → 1/8 | s:sd speed:2 bank:tr909 ]", + "[ 1/8 → 3/16 | s:bd speed:2 bank:tr909 ]", + "[ 3/16 → 1/4 | s:sd speed:2 bank:tr909 ]", + "[ 1/4 → 3/8 | s:ht bank:tr909 ]", + "[ 3/8 → 1/2 | s:lt bank:tr909 ]", + "[ 1/2 → 5/8 | s:bd bank:tr909 ]", + "[ 3/4 → 7/8 | s:cp bank:tr909 ]", + "[ 7/8 → 1/1 | s:lt bank:tr909 ]", + "[ 1/1 → 9/8 | s:bd bank:tr909 ]", + "[ 9/8 → 5/4 | s:sd bank:tr909 ]", + "[ 5/4 → 21/16 | s:ht speed:2 bank:tr909 ]", + "[ 21/16 → 11/8 | s:lt speed:2 bank:tr909 ]", + "[ 11/8 → 23/16 | s:ht speed:2 bank:tr909 ]", + "[ 23/16 → 3/2 | s:lt speed:2 bank:tr909 ]", + "[ 3/2 → 13/8 | s:bd bank:tr909 ]", + "[ 7/4 → 15/8 | s:cp bank:tr909 ]", + "[ 15/8 → 2/1 | s:lt bank:tr909 ]", + "[ 2/1 → 17/8 | s:bd bank:tr909 ]", + "[ 17/8 → 9/4 | s:sd bank:tr909 ]", + "[ 9/4 → 19/8 | s:ht bank:tr909 ]", + "[ 19/8 → 5/2 | s:lt bank:tr909 ]", + "[ 5/2 → 41/16 | s:bd speed:2 bank:tr909 ]", + "[ 21/8 → 43/16 | s:bd speed:2 bank:tr909 ]", + "[ 11/4 → 23/8 | s:cp bank:tr909 ]", + "[ 23/8 → 3/1 | s:lt bank:tr909 ]", + "[ 3/1 → 25/8 | s:bd bank:tr909 ]", + "[ 25/8 → 13/4 | s:sd bank:tr909 ]", + "[ 13/4 → 27/8 | s:ht bank:tr909 ]", + "[ 27/8 → 7/2 | s:lt bank:tr909 ]", + "[ 7/2 → 29/8 | s:bd bank:tr909 ]", + "[ 15/4 → 61/16 | s:cp speed:2 bank:tr909 ]", + "[ 61/16 → 31/8 | s:lt speed:2 bank:tr909 ]", + "[ 31/8 → 63/16 | s:cp speed:2 bank:tr909 ]", + "[ 63/16 → 4/1 | s:lt speed:2 bank:tr909 ]", +] +`; + exports[`runs examples > example "clip" example index 0 1`] = ` [ "[ 0/1 → 1/4 | note:c s:piano clip:0.5 ]", @@ -2505,6 +2585,19 @@ exports[`runs examples > example "delayfeedback" example index 0 1`] = ` ] `; +exports[`runs examples > example "delaysync" example index 0 1`] = ` +[ + "[ 0/1 → 1/2 | s:bd delay:0.25 delaysync:0.125 ]", + "[ 1/2 → 1/1 | s:bd delay:0.25 delaysync:0.125 ]", + "[ 1/1 → 3/2 | s:bd delay:0.25 delaysync:0.25 ]", + "[ 3/2 → 2/1 | s:bd delay:0.25 delaysync:0.25 ]", + "[ 2/1 → 5/2 | s:bd delay:0.25 delaysync:0.375 ]", + "[ 5/2 → 3/1 | s:bd delay:0.25 delaysync:0.375 ]", + "[ 3/1 → 7/2 | s:bd delay:0.25 delaysync:0.625 ]", + "[ 7/2 → 4/1 | s:bd delay:0.25 delaysync:0.625 ]", +] +`; + exports[`runs examples > example "delaytime" example index 0 1`] = ` [ "[ 0/1 → 1/2 | s:bd delay:0.25 delaytime:0.125 ]", @@ -3145,18 +3238,19 @@ exports[`runs examples > example "euclidLegato" example index 0 1`] = ` exports[`runs examples > example "euclidLegatoRot" example index 0 1`] = ` [ - "[ 0/1 → 1/4 | note:c3 ]", - "[ 1/4 → 3/4 | note:c3 ]", - "[ 3/4 → 1/1 | note:c3 ]", - "[ 1/1 → 5/4 | note:c3 ]", - "[ 5/4 → 7/4 | note:c3 ]", - "[ 7/4 → 2/1 | note:c3 ]", - "[ 2/1 → 9/4 | note:c3 ]", - "[ 9/4 → 11/4 | note:c3 ]", - "[ 11/4 → 3/1 | note:c3 ]", - "[ 3/1 → 13/4 | note:c3 ]", - "[ 13/4 → 15/4 | note:c3 ]", - "[ 15/4 → 4/1 | note:c3 ]", + "[ -1/5 ⇜ (0/1 → 1/5) | note:c3 ]", + "[ 1/5 → 2/5 | note:c3 ]", + "[ 2/5 → 4/5 | note:c3 ]", + "[ 4/5 → 6/5 | note:c3 ]", + "[ 6/5 → 7/5 | note:c3 ]", + "[ 7/5 → 9/5 | note:c3 ]", + "[ 9/5 → 11/5 | note:c3 ]", + "[ 11/5 → 12/5 | note:c3 ]", + "[ 12/5 → 14/5 | note:c3 ]", + "[ 14/5 → 16/5 | note:c3 ]", + "[ 16/5 → 17/5 | note:c3 ]", + "[ 17/5 → 19/5 | note:c3 ]", + "[ (19/5 → 4/1) ⇝ 21/5 | note:c3 ]", ] `; @@ -4390,6 +4484,35 @@ exports[`runs examples > example "inside" example index 0 1`] = ` ] `; +exports[`runs examples > example "into" example index 0 1`] = ` +[ + "[ 0/1 → 1/8 | s:bd speed:2 ]", + "[ 1/8 → 1/4 | s:sd speed:2 ]", + "[ 1/4 → 3/8 | s:bd speed:2 ]", + "[ 3/8 → 1/2 | s:sd speed:2 ]", + "[ 1/2 → 3/4 | s:ht ]", + "[ 3/4 → 1/1 | s:lt ]", + "[ 1/1 → 9/8 | s:bd speed:2 ]", + "[ 9/8 → 5/4 | s:sd speed:2 ]", + "[ 5/4 → 11/8 | s:bd speed:2 ]", + "[ 11/8 → 3/2 | s:sd speed:2 ]", + "[ 3/2 → 7/4 | s:ht ]", + "[ 7/4 → 2/1 | s:lt ]", + "[ 2/1 → 17/8 | s:bd speed:2 ]", + "[ 17/8 → 9/4 | s:sd speed:2 ]", + "[ 9/4 → 19/8 | s:bd speed:2 ]", + "[ 19/8 → 5/2 | s:sd speed:2 ]", + "[ 5/2 → 11/4 | s:ht ]", + "[ 11/4 → 3/1 | s:lt ]", + "[ 3/1 → 25/8 | s:bd speed:2 ]", + "[ 25/8 → 13/4 | s:sd speed:2 ]", + "[ 13/4 → 27/8 | s:bd speed:2 ]", + "[ 27/8 → 7/2 | s:sd speed:2 ]", + "[ 7/2 → 15/4 | s:ht ]", + "[ 15/4 → 4/1 | s:lt ]", +] +`; + exports[`runs examples > example "invert" example index 0 1`] = ` [ "[ 0/1 → 1/8 | s:bd ]", @@ -5015,6 +5138,15 @@ exports[`runs examples > example "linger" example index 0 1`] = ` ] `; +exports[`runs examples > example "lock" example index 0 1`] = ` +[ + "[ 0/1 → 1/1 | delay:{s:sd} lock:1 ]", + "[ 1/1 → 2/1 | delay:{s:sd} lock:1 ]", + "[ 2/1 → 3/1 | delay:{s:sd} lock:1 ]", + "[ 3/1 → 4/1 | delay:{s:sd} lock:1 ]", +] +`; + exports[`runs examples > example "loop" example index 0 1`] = ` [ "[ 0/1 → 1/1 | s:casio loop:1 ]", @@ -8449,6 +8581,35 @@ exports[`runs examples > example "seqPLoop" example index 0 1`] = ` ] `; +exports[`runs examples > example "setcpm" example index 0 1`] = ` +[ + "[ 0/1 → 1/4 | s:bd bank:tr707 ]", + "[ 1/4 → 1/2 | s:bd bank:tr707 ]", + "[ 1/4 → 1/2 | s:sd bank:tr707 ]", + "[ 1/2 → 3/4 | s:bd bank:tr707 ]", + "[ 3/4 → 1/1 | s:bd bank:tr707 ]", + "[ 3/4 → 1/1 | s:sd bank:tr707 ]", + "[ 1/1 → 5/4 | s:bd bank:tr707 ]", + "[ 5/4 → 3/2 | s:bd bank:tr707 ]", + "[ 5/4 → 3/2 | s:sd bank:tr707 ]", + "[ 3/2 → 7/4 | s:bd bank:tr707 ]", + "[ 7/4 → 2/1 | s:bd bank:tr707 ]", + "[ 7/4 → 2/1 | s:sd bank:tr707 ]", + "[ 2/1 → 9/4 | s:bd bank:tr707 ]", + "[ 9/4 → 5/2 | s:bd bank:tr707 ]", + "[ 9/4 → 5/2 | s:sd bank:tr707 ]", + "[ 5/2 → 11/4 | s:bd bank:tr707 ]", + "[ 11/4 → 3/1 | s:bd bank:tr707 ]", + "[ 11/4 → 3/1 | s:sd bank:tr707 ]", + "[ 3/1 → 13/4 | s:bd bank:tr707 ]", + "[ 13/4 → 7/2 | s:bd bank:tr707 ]", + "[ 13/4 → 7/2 | s:sd bank:tr707 ]", + "[ 7/2 → 15/4 | s:bd bank:tr707 ]", + "[ 15/4 → 4/1 | s:bd bank:tr707 ]", + "[ 15/4 → 4/1 | s:sd bank:tr707 ]", +] +`; + exports[`runs examples > example "shape" example index 0 1`] = ` [ "[ 0/1 → 1/8 | s:hh shape:0 ]", diff --git a/test/__snapshots__/tunes.test.mjs.snap b/test/__snapshots__/tunes.test.mjs.snap index 06267b8f9..4a0ddd539 100644 --- a/test/__snapshots__/tunes.test.mjs.snap +++ b/test/__snapshots__/tunes.test.mjs.snap @@ -7318,12 +7318,12 @@ exports[`renders tunes > tune: randomBells 1`] = ` [ "[ -9/8 ⇜ (0/1 → 3/8) | gain:0.6 note:A3 velocity:0.5989903202280402 s:bell delay:0.2 delaytime:0.3333333333333333 delayfeedback:0.8 ]", "[ -3/4 ⇜ (0/1 → 3/4) | gain:0.6 note:C5 velocity:0.8369929669424891 s:bell delay:0.2 delaytime:0.3333333333333333 delayfeedback:0.8 ]", - "[ 0/1 → 3/2 | note:D2 s:bass clip:1 gain:0.8 ]", + "[ 0/1 → 3/2 | note:F2 s:bass clip:1 gain:0.8 ]", "[ 0/1 → 9/4 | gain:0.6 note:D3 velocity:0.5 s:bell delay:0.2 delaytime:0.3333333333333333 delayfeedback:0.8 ]", "[ 3/8 → 21/8 | gain:0.6 note:F5 velocity:0.9213038925081491 s:bell delay:0.2 delaytime:0.3333333333333333 delayfeedback:0.8 ]", "[ 3/4 → 3/1 | gain:0.6 note:C5 velocity:0.8426077850162983 s:bell delay:0.2 delaytime:0.3333333333333333 delayfeedback:0.8 ]", - "[ 3/2 → 9/4 | note:D2 s:bass clip:1 gain:0.8 ]", - "[ 9/4 → 3/1 | note:D2 s:bass clip:1 gain:0.8 ]", + "[ 3/2 → 9/4 | note:F2 s:bass clip:1 gain:0.8 ]", + "[ 9/4 → 3/1 | note:F2 s:bass clip:1 gain:0.8 ]", "[ 9/4 → 9/2 | gain:0.6 note:D4 velocity:0.7006962578743696 s:bell delay:0.2 delaytime:0.3333333333333333 delayfeedback:0.8 ]", "[ 21/8 → 39/8 | gain:0.6 note:C4 velocity:0.6507943943142891 s:bell delay:0.2 delaytime:0.3333333333333333 delayfeedback:0.8 ]", "[ 3/1 → 9/2 | note:D2 s:bass clip:1 gain:0.8 ]", @@ -7335,12 +7335,12 @@ exports[`renders tunes > tune: randomBells 1`] = ` "[ (21/4 → 6/1) ⇝ 27/4 | gain:0.6 note:D4 velocity:0.6988155404105783 s:bell delay:0.2 delaytime:0.3333333333333333 delayfeedback:0.8 ]", "[ 39/8 ⇜ (6/1 → 51/8) | gain:0.6 note:D5 velocity:0.8758113365620375 s:bell delay:0.2 delaytime:0.3333333333333333 delayfeedback:0.8 ]", "[ 21/4 ⇜ (6/1 → 27/4) | gain:0.6 note:D4 velocity:0.6988155404105783 s:bell delay:0.2 delaytime:0.3333333333333333 delayfeedback:0.8 ]", - "[ 6/1 → 15/2 | note:A2 s:bass clip:1 gain:0.8 ]", + "[ 6/1 → 15/2 | note:D2 s:bass clip:1 gain:0.8 ]", "[ 6/1 → 33/4 | gain:0.6 note:G4 velocity:0.7597710825502872 s:bell delay:0.2 delaytime:0.3333333333333333 delayfeedback:0.8 ]", "[ 51/8 → 69/8 | gain:0.6 note:G4 velocity:0.7743164440616965 s:bell delay:0.2 delaytime:0.3333333333333333 delayfeedback:0.8 ]", "[ 27/4 → 9/1 | gain:0.6 note:C5 velocity:0.8362447572872043 s:bell delay:0.2 delaytime:0.3333333333333333 delayfeedback:0.8 ]", - "[ 15/2 → 33/4 | note:A2 s:bass clip:1 gain:0.8 ]", - "[ 33/4 → 9/1 | note:A2 s:bass clip:1 gain:0.8 ]", + "[ 15/2 → 33/4 | note:D2 s:bass clip:1 gain:0.8 ]", + "[ 33/4 → 9/1 | note:D2 s:bass clip:1 gain:0.8 ]", "[ 33/4 → 21/2 | gain:0.6 note:A3 velocity:0.5914018759503961 s:bell delay:0.2 delaytime:0.3333333333333333 delayfeedback:0.8 ]", "[ 69/8 → 87/8 | gain:0.6 note:G4 velocity:0.754063542932272 s:bell delay:0.2 delaytime:0.3333333333333333 delayfeedback:0.8 ]", "[ 9/1 → 21/2 | note:A2 s:bass clip:1 gain:0.8 ]", @@ -7352,12 +7352,12 @@ exports[`renders tunes > tune: randomBells 1`] = ` "[ (45/4 → 12/1) ⇝ 51/4 | gain:0.6 note:A4 velocity:0.7972785895690322 s:bell delay:0.2 delaytime:0.3333333333333333 delayfeedback:0.8 ]", "[ 87/8 ⇜ (12/1 → 99/8) | gain:0.6 note:F4 velocity:0.7347871446982026 s:bell delay:0.2 delaytime:0.3333333333333333 delayfeedback:0.8 ]", "[ 45/4 ⇜ (12/1 → 51/4) | gain:0.6 note:A4 velocity:0.7972785895690322 s:bell delay:0.2 delaytime:0.3333333333333333 delayfeedback:0.8 ]", - "[ 12/1 → 27/2 | note:G2 s:bass clip:1 gain:0.8 ]", + "[ 12/1 → 27/2 | note:A2 s:bass clip:1 gain:0.8 ]", "[ 12/1 → 57/4 | gain:0.6 note:G5 velocity:0.9797635599970818 s:bell delay:0.2 delaytime:0.3333333333333333 delayfeedback:0.8 ]", "[ 99/8 → 117/8 | gain:0.6 note:C4 velocity:0.6662392104044557 s:bell delay:0.2 delaytime:0.3333333333333333 delayfeedback:0.8 ]", "[ 51/4 → 15/1 | gain:0.6 note:F5 velocity:0.9516951469704509 s:bell delay:0.2 delaytime:0.3333333333333333 delayfeedback:0.8 ]", - "[ 27/2 → 57/4 | note:G2 s:bass clip:1 gain:0.8 ]", - "[ 57/4 → 15/1 | note:G2 s:bass clip:1 gain:0.8 ]", + "[ 27/2 → 57/4 | note:A2 s:bass clip:1 gain:0.8 ]", + "[ 57/4 → 15/1 | note:A2 s:bass clip:1 gain:0.8 ]", "[ 57/4 → 33/2 | gain:0.6 note:F5 velocity:0.9182533202692866 s:bell delay:0.2 delaytime:0.3333333333333333 delayfeedback:0.8 ]", "[ 117/8 → 135/8 | gain:0.6 note:G3 velocity:0.5711571052670479 s:bell delay:0.2 delaytime:0.3333333333333333 delayfeedback:0.8 ]", "[ 15/1 → 33/2 | note:G2 s:bass clip:1 gain:0.8 ]", @@ -7369,12 +7369,12 @@ exports[`renders tunes > tune: randomBells 1`] = ` "[ (69/4 → 18/1) ⇝ 75/4 | gain:0.6 note:F3 velocity:0.5081270858645439 s:bell delay:0.2 delaytime:0.3333333333333333 delayfeedback:0.8 ]", "[ 135/8 ⇜ (18/1 → 147/8) | gain:0.6 note:F5 velocity:0.9456470254808664 s:bell delay:0.2 delaytime:0.3333333333333333 delayfeedback:0.8 ]", "[ 69/4 ⇜ (18/1 → 75/4) | gain:0.6 note:F3 velocity:0.5081270858645439 s:bell delay:0.2 delaytime:0.3333333333333333 delayfeedback:0.8 ]", - "[ 18/1 → 39/2 | note:F2 s:bass clip:1 gain:0.8 ]", + "[ 18/1 → 39/2 | note:G2 s:bass clip:1 gain:0.8 ]", "[ 18/1 → 81/4 | gain:0.6 note:A3 velocity:0.6086445553228259 s:bell delay:0.2 delaytime:0.3333333333333333 delayfeedback:0.8 ]", "[ 147/8 → 165/8 | gain:0.6 note:F3 velocity:0.5062594395130873 s:bell delay:0.2 delaytime:0.3333333333333333 delayfeedback:0.8 ]", "[ 75/4 → 21/1 | gain:0.6 note:D4 velocity:0.6716219391673803 s:bell delay:0.2 delaytime:0.3333333333333333 delayfeedback:0.8 ]", - "[ 39/2 → 81/4 | note:F2 s:bass clip:1 gain:0.8 ]", - "[ 81/4 → 21/1 | note:F2 s:bass clip:1 gain:0.8 ]", + "[ 39/2 → 81/4 | note:G2 s:bass clip:1 gain:0.8 ]", + "[ 81/4 → 21/1 | note:G2 s:bass clip:1 gain:0.8 ]", "[ 81/4 → 45/2 | gain:0.6 note:D4 velocity:0.7043459005653858 s:bell delay:0.2 delaytime:0.3333333333333333 delayfeedback:0.8 ]", "[ 165/8 → 183/8 | gain:0.6 note:D5 velocity:0.8878388572484255 s:bell delay:0.2 delaytime:0.3333333333333333 delayfeedback:0.8 ]", "[ 21/1 → 45/2 | note:F2 s:bass clip:1 gain:0.8 ]", diff --git a/test/runtime.mjs b/test/runtime.mjs index 6b75fb3be..2a29de3f5 100644 --- a/test/runtime.mjs +++ b/test/runtime.mjs @@ -97,6 +97,7 @@ const toneHelpersMocked = { '_pianoroll', '_spectrum', 'markcss', + 'p', ].forEach((mock) => { strudel.Pattern.prototype[mock] = function () { return this; @@ -163,6 +164,7 @@ evalScope( loadCsound, loadcsound, setcps: id, + setcpm: id, Clock: {}, // whatever }, ); diff --git a/website/agpl-header.txt b/website/agpl-header.txt index 6fd0c0fc9..8b7b6d631 100644 --- a/website/agpl-header.txt +++ b/website/agpl-header.txt @@ -1,10 +1,10 @@ /* Strudel - javascript-based environment for live coding algorithmic (musical) patterns -https://strudel.cc / https://github.com/tidalcycles/strudel/ +https://strudel.cc / https://codeberg.org/uzu/strudel/ Copyright (C) Strudel contributors -https://github.com/tidalcycles/strudel/graphs/contributors +https://codeberg.org/uzu/strudel/activity/contributors 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 diff --git a/website/package.json b/website/package.json index 8e980f1ca..499758147 100644 --- a/website/package.json +++ b/website/package.json @@ -42,6 +42,7 @@ "@strudel/tonal": "workspace:*", "@strudel/transpiler": "workspace:*", "@strudel/webaudio": "workspace:*", + "@strudel/mondo": "workspace:*", "@strudel/xen": "workspace:*", "@supabase/supabase-js": "^2.48.1", "@tailwindcss/forms": "^0.5.10", diff --git a/website/src/components/Footer/AvatarList.astro b/website/src/components/Footer/AvatarList.astro deleted file mode 100644 index 86bbcc875..000000000 --- a/website/src/components/Footer/AvatarList.astro +++ /dev/null @@ -1,169 +0,0 @@ ---- -// fetch all commits for just this page's path -type Props = { - path: string; -}; -const { path } = Astro.props as Props; -const resolvedPath = `website/src/pages${path}.mdx`; -const url = `https://api.github.com/repos/tidalcycles/strudel/commits?path=${resolvedPath}`; -const commitsURL = `https://github.com/tidalcycles/strudel/commits/main/${resolvedPath}`; - -type Commit = { - author: { - id: string; - login: string; - }; -}; - -async function getCommits(url: string) { - try { - const token = import.meta.env.SNOWPACK_PUBLIC_GITHUB_TOKEN ?? 'hello'; - if (!token) { - throw new Error('Cannot find "SNOWPACK_PUBLIC_GITHUB_TOKEN" used for escaping rate-limiting.'); - } - - const auth = `Basic ${Buffer.from(token, 'binary').toString('base64')}`; - - const res = await fetch(url, { - method: 'GET', - headers: { - Authorization: auth, - 'User-Agent': 'astro-docs/1.0', - }, - }); - - const data = await res.json(); - - if (!res.ok) { - throw new Error( - `Request to fetch commits failed. Reason: ${res.statusText} - Message: ${data.message}`, - ); - } - - return data as Commit[]; - } catch (e) { - console.warn(`[error] /src/components/AvatarList.astro - ${(e as any)?.message ?? e}`); - return [] as Commit[]; - } -} - -function removeDups(arr: Commit[]) { - const map = new Map(); - for (let item of arr) { - const author = item.author; - // Deduplicate based on author.id - //map.set(author.id, { login: author.login, id: author.id }); - author && map.set(author.id, { login: author.login, id: author.id }); - } - - return [...map.values()]; -} - -const data = await getCommits(url); -const unique = removeDups(data); -const recentContributors = unique.slice(0, 3); // only show avatars for the 3 most recent contributors -const additionalContributors = unique.length - recentContributors.length; // list the rest of them as # of extra contributors ---- - - -
-
    - { - recentContributors.map((item) => ( -
  • - - {`Contributor - -
  • - )) - } -
- { - additionalContributors > 0 && ( - - {`and ${additionalContributors} additional contributor${ - additionalContributors > 1 ? 's' : '' - }.`} - - ) - } - {unique.length === 0 && Contributors} -
- - diff --git a/website/src/components/Header/Search.tsx b/website/src/components/Header/Search.tsx index c982d80b6..f200dc79f 100644 --- a/website/src/components/Header/Search.tsx +++ b/website/src/components/Header/Search.tsx @@ -82,7 +82,7 @@ export default function Search() { appId={ALGOLIA.appId} apiKey={ALGOLIA.apiKey} getMissingResultsUrl={({ query }) => { - return `https://github.com/tidalcycles/strudel/issues/new?title=Missing doc for ${query}`; + return `https://codeberg.org/uzu/strudel/issues/new?title=Missing%20doc%20for${encodeURIComponent(query)}`; }} transformItems={(items) => { return items.map((item) => { diff --git a/website/src/components/RightSidebar/RightSidebar.astro b/website/src/components/RightSidebar/RightSidebar.astro index cd501b1a2..28c713022 100644 --- a/website/src/components/RightSidebar/RightSidebar.astro +++ b/website/src/components/RightSidebar/RightSidebar.astro @@ -18,5 +18,4 @@ currentPage = currentPage.endsWith('/') ? currentPage.slice(0, -1) : currentPage diff --git a/website/src/config.ts b/website/src/config.ts index 2f499d55f..2e2e264da 100644 --- a/website/src/config.ts +++ b/website/src/config.ts @@ -28,7 +28,7 @@ export const KNOWN_LANGUAGES = { } as const; export const KNOWN_LANGUAGE_CODES = Object.values(KNOWN_LANGUAGES); -export const GITHUB_EDIT_URL = `https://github.com/tidalcycles/strudel/tree/main/website`; +export const GITHUB_EDIT_URL = `https://codeberg.org/uzu/strudel/src/branch/main/website`; export const COMMUNITY_INVITE_URL = `https://discord.com/invite/HGEdXmRkzT`; @@ -58,7 +58,7 @@ export const SIDEBAR: Sidebar = { { text: 'What is Strudel?', link: 'workshop/getting-started' }, { text: 'Showcase', link: 'intro/showcase' }, { text: 'Blog', link: 'blog' }, - { text: 'Community Bakery', link: 'bakery' }, + /* { text: 'Community Bakery', link: 'bakery' }, */ ], Workshop: [ // { text: 'Getting Started', link: 'workshop/getting-started' }, @@ -81,6 +81,7 @@ export const SIDEBAR: Sidebar = { { text: 'Visual Feedback', link: 'learn/visual-feedback' }, { text: 'Offline', link: 'learn/pwa' }, { text: 'Patterns', link: 'technical-manual/patterns' }, + { text: 'Mondo Notation', link: 'learn/mondo-notation' }, { text: 'Music metadata', link: 'learn/metadata' }, { text: 'CSound', link: 'learn/csound' }, { text: 'Hydra', link: 'learn/hydra' }, diff --git a/website/src/content/blog/release-0.0.2-schwindlig.mdx b/website/src/content/blog/release-0.0.2-schwindlig.mdx index cf4bc19d6..8dbff4928 100644 --- a/website/src/content/blog/release-0.0.2-schwindlig.mdx +++ b/website/src/content/blog/release-0.0.2-schwindlig.mdx @@ -8,33 +8,33 @@ author: froos ## What's Changed -- Most work done as [commits to main](https://github.com/tidalcycles/strudel/commits/2a0d8c3f77ff7b34e82602e2d02400707f367316) -- repl + reify functions by @felixroos in https://github.com/tidalcycles/strudel/pull/2 -- Fix path by @yaxu in https://github.com/tidalcycles/strudel/pull/3 -- update readme for local dev by @kindohm in https://github.com/tidalcycles/strudel/pull/4 -- Patternify all the things by @yaxu in https://github.com/tidalcycles/strudel/pull/5 -- krill parser + improved repl by @felixroos in https://github.com/tidalcycles/strudel/pull/6 -- fixed editor crash by @felixroos in https://github.com/tidalcycles/strudel/pull/7 -- timeCat by @yaxu in https://github.com/tidalcycles/strudel/pull/8 -- Bugfix every, and create more top level functions by @yaxu in https://github.com/tidalcycles/strudel/pull/9 -- Failing test for `when` WIP by @yaxu in https://github.com/tidalcycles/strudel/pull/10 -- Added mask() and struct() by @yaxu in https://github.com/tidalcycles/strudel/pull/11 -- Add continuous signals (sine, cosine, saw, etc) by @yaxu in https://github.com/tidalcycles/strudel/pull/13 -- add apply and layer, and missing div/mul methods by @yaxu in https://github.com/tidalcycles/strudel/pull/15 -- higher latencyHint by @felixroos in https://github.com/tidalcycles/strudel/pull/16 -- test: 📦 Add missing dependency and a CI check, to prevent oversights ;p by @puria in https://github.com/tidalcycles/strudel/pull/17 -- fix: 💄 Enhance visualisation of the Tutorial on mobile by @puria in https://github.com/tidalcycles/strudel/pull/19 -- Stateful queries and events (WIP) by @yaxu in https://github.com/tidalcycles/strudel/pull/14 -- Fix resolveState by @yaxu in https://github.com/tidalcycles/strudel/pull/22 -- added \_asNumber + interpret numbers as midi by @felixroos in https://github.com/tidalcycles/strudel/pull/21 -- Update package.json by @ChiakiUehira in https://github.com/tidalcycles/strudel/pull/23 -- packaging by @felixroos in https://github.com/tidalcycles/strudel/pull/24 +- Most work done as [commits to main](https://codeberg.org/uzu/strudel/commit/2a0d8c3f77ff7b34e82602e2d02400707f367316) +- repl + reify functions by @felixroos in https://codeberg.org/uzu/strudel/pulls/2 +- Fix path by @yaxu in https://codeberg.org/uzu/strudel/pulls/3 +- update readme for local dev by @kindohm in https://codeberg.org/uzu/strudel/pulls/4 +- Patternify all the things by @yaxu in https://codeberg.org/uzu/strudel/pulls/5 +- krill parser + improved repl by @felixroos in https://codeberg.org/uzu/strudel/pulls/6 +- fixed editor crash by @felixroos in https://codeberg.org/uzu/strudel/pulls/7 +- timeCat by @yaxu in https://codeberg.org/uzu/strudel/pulls/8 +- Bugfix every, and create more top level functions by @yaxu in https://codeberg.org/uzu/strudel/pulls/9 +- Failing test for `when` WIP by @yaxu in https://codeberg.org/uzu/strudel/pulls/10 +- Added mask() and struct() by @yaxu in https://codeberg.org/uzu/strudel/pulls/11 +- Add continuous signals (sine, cosine, saw, etc) by @yaxu in https://codeberg.org/uzu/strudel/pulls/13 +- add apply and layer, and missing div/mul methods by @yaxu in https://codeberg.org/uzu/strudel/pulls/15 +- higher latencyHint by @felixroos in https://codeberg.org/uzu/strudel/pulls/16 +- test: 📦 Add missing dependency and a CI check, to prevent oversights ;p by @puria in https://codeberg.org/uzu/strudel/pulls/17 +- fix: 💄 Enhance visualisation of the Tutorial on mobile by @puria in https://codeberg.org/uzu/strudel/pulls/19 +- Stateful queries and events (WIP) by @yaxu in https://codeberg.org/uzu/strudel/pulls/14 +- Fix resolveState by @yaxu in https://codeberg.org/uzu/strudel/pulls/22 +- added \_asNumber + interpret numbers as midi by @felixroos in https://codeberg.org/uzu/strudel/pulls/21 +- Update package.json by @ChiakiUehira in https://codeberg.org/uzu/strudel/pulls/23 +- packaging by @felixroos in https://codeberg.org/uzu/strudel/pulls/24 ## New Contributors -- @felixroos made their first contribution in https://github.com/tidalcycles/strudel/pull/2 -- @kindohm made their first contribution in https://github.com/tidalcycles/strudel/pull/4 -- @puria made their first contribution in https://github.com/tidalcycles/strudel/pull/17 -- @ChiakiUehira made their first contribution in https://github.com/tidalcycles/strudel/pull/23 +- @felixroos made their first contribution in https://codeberg.org/uzu/strudel/pulls/2 +- @kindohm made their first contribution in https://codeberg.org/uzu/strudel/pulls/4 +- @puria made their first contribution in https://codeberg.org/uzu/strudel/pulls/17 +- @ChiakiUehira made their first contribution in https://codeberg.org/uzu/strudel/pulls/23 -**Full Changelog**: https://github.com/tidalcycles/strudel/commits/2a0d8c3f77ff7b34e82602e2d02400707f367316 +**Full Changelog**: https://codeberg.org/uzu/strudel/commit/2a0d8c3f77ff7b34e82602e2d02400707f367316 diff --git a/website/src/content/blog/release-0.0.2.1-stuermisch.mdx b/website/src/content/blog/release-0.0.2.1-stuermisch.mdx index edefd2b09..441d99f76 100644 --- a/website/src/content/blog/release-0.0.2.1-stuermisch.mdx +++ b/website/src/content/blog/release-0.0.2.1-stuermisch.mdx @@ -7,47 +7,47 @@ author: froos ## What's Changed -- Add chunk, chunkBack and iterBack by @yaxu in https://github.com/tidalcycles/strudel/pull/25 -- Update tutorial.mdx by @bwagner in https://github.com/tidalcycles/strudel/pull/37 -- Update tutorial.mdx by @bwagner in https://github.com/tidalcycles/strudel/pull/38 -- Compose by @felixroos in https://github.com/tidalcycles/strudel/pull/40 -- Fix polymeter by @yaxu in https://github.com/tidalcycles/strudel/pull/44 -- First run at squeezeBind, ref #32 by @yaxu in https://github.com/tidalcycles/strudel/pull/48 -- Implement `chop()` by @yaxu in https://github.com/tidalcycles/strudel/pull/50 -- OSC and SuperDirt support by @yaxu in https://github.com/tidalcycles/strudel/pull/27 -- More functions by @yaxu in https://github.com/tidalcycles/strudel/pull/56 -- More functions by @yaxu in https://github.com/tidalcycles/strudel/pull/61 -- Separate out strudel.mjs, make index.mjs aggregate module by @yaxu in https://github.com/tidalcycles/strudel/pull/62 -- Speech output by @felixroos in https://github.com/tidalcycles/strudel/pull/67 -- use new fixed version of osc-js package by @felixroos in https://github.com/tidalcycles/strudel/pull/68 -- First effort at rand() by @yaxu in https://github.com/tidalcycles/strudel/pull/69 -- More randomness, fix `rand`, and add `brand`, `irand` and `choose` by @yaxu in https://github.com/tidalcycles/strudel/pull/70 -- webaudio package by @felixroos in https://github.com/tidalcycles/strudel/pull/26 -- Port `perlin` noise, `rangex`, and `palindrome` by @yaxu in https://github.com/tidalcycles/strudel/pull/73 -- More random functions by @yaxu in https://github.com/tidalcycles/strudel/pull/74 -- Try to fix appLeft / appRight by @yaxu in https://github.com/tidalcycles/strudel/pull/75 -- Basic webserial support by @yaxu in https://github.com/tidalcycles/strudel/pull/80 -- Webaudio in REPL by @felixroos in https://github.com/tidalcycles/strudel/pull/77 -- add `striate()` by @yaxu in https://github.com/tidalcycles/strudel/pull/76 -- Tidy up a couple of old files by @mindofmatthew in https://github.com/tidalcycles/strudel/pull/84 -- Add pattern composers, implements #82 by @yaxu in https://github.com/tidalcycles/strudel/pull/83 -- Fiddles with cat/stack by @yaxu in https://github.com/tidalcycles/strudel/pull/90 -- Paper by @felixroos in https://github.com/tidalcycles/strudel/pull/98 -- Change to Affero GPL by @yaxu in https://github.com/tidalcycles/strudel/pull/101 -- Work on Codemirror 6 highlighting by @mindofmatthew in https://github.com/tidalcycles/strudel/pull/102 -- Codemirror 6 by @felixroos in https://github.com/tidalcycles/strudel/pull/97 -- Tune tests by @felixroos in https://github.com/tidalcycles/strudel/pull/104 -- /embed package: web component for repl by @felixroos in https://github.com/tidalcycles/strudel/pull/106 -- Reset, Restart and other composers by @felixroos in https://github.com/tidalcycles/strudel/pull/88 -- Embed style by @felixroos in https://github.com/tidalcycles/strudel/pull/109 -- In source doc by @yaxu in https://github.com/tidalcycles/strudel/pull/105 -- `.brak()`, `.inside()` and `.outside()` by @yaxu in https://github.com/tidalcycles/strudel/pull/112 -- loopAt by @yaxu in https://github.com/tidalcycles/strudel/pull/114 -- Osc timing improvements by @yaxu in https://github.com/tidalcycles/strudel/pull/113 +- Add chunk, chunkBack and iterBack by @yaxu in https://codeberg.org/uzu/strudel/pulls/25 +- Update tutorial.mdx by @bwagner in https://codeberg.org/uzu/strudel/pulls/37 +- Update tutorial.mdx by @bwagner in https://codeberg.org/uzu/strudel/pulls/38 +- Compose by @felixroos in https://codeberg.org/uzu/strudel/pulls/40 +- Fix polymeter by @yaxu in https://codeberg.org/uzu/strudel/pulls/44 +- First run at squeezeBind, ref #32 by @yaxu in https://codeberg.org/uzu/strudel/pulls/48 +- Implement `chop()` by @yaxu in https://codeberg.org/uzu/strudel/pulls/50 +- OSC and SuperDirt support by @yaxu in https://codeberg.org/uzu/strudel/pulls/27 +- More functions by @yaxu in https://codeberg.org/uzu/strudel/pulls/56 +- More functions by @yaxu in https://codeberg.org/uzu/strudel/pulls/61 +- Separate out strudel.mjs, make index.mjs aggregate module by @yaxu in https://codeberg.org/uzu/strudel/pulls/62 +- Speech output by @felixroos in https://codeberg.org/uzu/strudel/pulls/67 +- use new fixed version of osc-js package by @felixroos in https://codeberg.org/uzu/strudel/pulls/68 +- First effort at rand() by @yaxu in https://codeberg.org/uzu/strudel/pulls/69 +- More randomness, fix `rand`, and add `brand`, `irand` and `choose` by @yaxu in https://codeberg.org/uzu/strudel/pulls/70 +- webaudio package by @felixroos in https://codeberg.org/uzu/strudel/pulls/26 +- Port `perlin` noise, `rangex`, and `palindrome` by @yaxu in https://codeberg.org/uzu/strudel/pulls/73 +- More random functions by @yaxu in https://codeberg.org/uzu/strudel/pulls/74 +- Try to fix appLeft / appRight by @yaxu in https://codeberg.org/uzu/strudel/pulls/75 +- Basic webserial support by @yaxu in https://codeberg.org/uzu/strudel/pulls/80 +- Webaudio in REPL by @felixroos in https://codeberg.org/uzu/strudel/pulls/77 +- add `striate()` by @yaxu in https://codeberg.org/uzu/strudel/pulls/76 +- Tidy up a couple of old files by @mindofmatthew in https://codeberg.org/uzu/strudel/pulls/84 +- Add pattern composers, implements #82 by @yaxu in https://codeberg.org/uzu/strudel/pulls/83 +- Fiddles with cat/stack by @yaxu in https://codeberg.org/uzu/strudel/pulls/90 +- Paper by @felixroos in https://codeberg.org/uzu/strudel/pulls/98 +- Change to Affero GPL by @yaxu in https://codeberg.org/uzu/strudel/pulls/101 +- Work on Codemirror 6 highlighting by @mindofmatthew in https://codeberg.org/uzu/strudel/pulls/102 +- Codemirror 6 by @felixroos in https://codeberg.org/uzu/strudel/pulls/97 +- Tune tests by @felixroos in https://codeberg.org/uzu/strudel/pulls/104 +- /embed package: web component for repl by @felixroos in https://codeberg.org/uzu/strudel/pulls/106 +- Reset, Restart and other composers by @felixroos in https://codeberg.org/uzu/strudel/pulls/88 +- Embed style by @felixroos in https://codeberg.org/uzu/strudel/pulls/109 +- In source doc by @yaxu in https://codeberg.org/uzu/strudel/pulls/105 +- `.brak()`, `.inside()` and `.outside()` by @yaxu in https://codeberg.org/uzu/strudel/pulls/112 +- loopAt by @yaxu in https://codeberg.org/uzu/strudel/pulls/114 +- Osc timing improvements by @yaxu in https://codeberg.org/uzu/strudel/pulls/113 ## New Contributors -- @bwagner made their first contribution in https://github.com/tidalcycles/strudel/pull/37 -- @mindofmatthew made their first contribution in https://github.com/tidalcycles/strudel/pull/84 +- @bwagner made their first contribution in https://codeberg.org/uzu/strudel/pulls/37 +- @mindofmatthew made their first contribution in https://codeberg.org/uzu/strudel/pulls/84 -**Full Changelog**: https://github.com/tidalcycles/strudel/compare/v0.0.2...@strudel.cycles/core@0.1.0 +**Full Changelog**: https://codeberg.org/uzu/strudel/compare/v0.0.2...@strudel.cycles/core@0.1.0 diff --git a/website/src/content/blog/release-0.0.3-maelstrom.mdx b/website/src/content/blog/release-0.0.3-maelstrom.mdx index 3710a9ce5..5141b34a7 100644 --- a/website/src/content/blog/release-0.0.3-maelstrom.mdx +++ b/website/src/content/blog/release-0.0.3-maelstrom.mdx @@ -8,59 +8,59 @@ author: froos ## What's Changed -- Add chunk, chunkBack and iterBack by @yaxu in https://github.com/tidalcycles/strudel/pull/25 -- Update tutorial.mdx by @bwagner in https://github.com/tidalcycles/strudel/pull/37 -- Update tutorial.mdx by @bwagner in https://github.com/tidalcycles/strudel/pull/38 -- Compose by @felixroos in https://github.com/tidalcycles/strudel/pull/40 -- Fix polymeter by @yaxu in https://github.com/tidalcycles/strudel/pull/44 -- First run at squeezeBind, ref #32 by @yaxu in https://github.com/tidalcycles/strudel/pull/48 -- Implement `chop()` by @yaxu in https://github.com/tidalcycles/strudel/pull/50 -- OSC and SuperDirt support by @yaxu in https://github.com/tidalcycles/strudel/pull/27 -- More functions by @yaxu in https://github.com/tidalcycles/strudel/pull/56 -- More functions by @yaxu in https://github.com/tidalcycles/strudel/pull/61 -- Separate out strudel.mjs, make index.mjs aggregate module by @yaxu in https://github.com/tidalcycles/strudel/pull/62 -- Speech output by @felixroos in https://github.com/tidalcycles/strudel/pull/67 -- use new fixed version of osc-js package by @felixroos in https://github.com/tidalcycles/strudel/pull/68 -- First effort at rand() by @yaxu in https://github.com/tidalcycles/strudel/pull/69 -- More randomness, fix `rand`, and add `brand`, `irand` and `choose` by @yaxu in https://github.com/tidalcycles/strudel/pull/70 -- webaudio package by @felixroos in https://github.com/tidalcycles/strudel/pull/26 -- Port `perlin` noise, `rangex`, and `palindrome` by @yaxu in https://github.com/tidalcycles/strudel/pull/73 -- More random functions by @yaxu in https://github.com/tidalcycles/strudel/pull/74 -- Try to fix appLeft / appRight by @yaxu in https://github.com/tidalcycles/strudel/pull/75 -- Basic webserial support by @yaxu in https://github.com/tidalcycles/strudel/pull/80 -- Webaudio in REPL by @felixroos in https://github.com/tidalcycles/strudel/pull/77 -- add `striate()` by @yaxu in https://github.com/tidalcycles/strudel/pull/76 -- Tidy up a couple of old files by @mindofmatthew in https://github.com/tidalcycles/strudel/pull/84 -- Add pattern composers, implements #82 by @yaxu in https://github.com/tidalcycles/strudel/pull/83 -- Fiddles with cat/stack by @yaxu in https://github.com/tidalcycles/strudel/pull/90 -- Paper by @felixroos in https://github.com/tidalcycles/strudel/pull/98 -- Change to Affero GPL by @yaxu in https://github.com/tidalcycles/strudel/pull/101 -- Work on Codemirror 6 highlighting by @mindofmatthew in https://github.com/tidalcycles/strudel/pull/102 -- Codemirror 6 by @felixroos in https://github.com/tidalcycles/strudel/pull/97 -- Tune tests by @felixroos in https://github.com/tidalcycles/strudel/pull/104 -- /embed package: web component for repl by @felixroos in https://github.com/tidalcycles/strudel/pull/106 -- Reset, Restart and other composers by @felixroos in https://github.com/tidalcycles/strudel/pull/88 -- Embed style by @felixroos in https://github.com/tidalcycles/strudel/pull/109 -- In source doc by @yaxu in https://github.com/tidalcycles/strudel/pull/105 -- `.brak()`, `.inside()` and `.outside()` by @yaxu in https://github.com/tidalcycles/strudel/pull/112 -- loopAt by @yaxu in https://github.com/tidalcycles/strudel/pull/114 -- Osc timing improvements by @yaxu in https://github.com/tidalcycles/strudel/pull/113 -- react package + vite build by @felixroos in https://github.com/tidalcycles/strudel/pull/116 -- In source doc by @felixroos in https://github.com/tidalcycles/strudel/pull/117 -- fix: #108 by @felixroos in https://github.com/tidalcycles/strudel/pull/123 -- fix: #122 ctrl enter would add newline by @felixroos in https://github.com/tidalcycles/strudel/pull/124 -- Webdirt by @felixroos in https://github.com/tidalcycles/strudel/pull/121 -- Fix link to contributing to tutorial docs by @stephendwolff in https://github.com/tidalcycles/strudel/pull/129 -- Pianoroll enhancements by @felixroos in https://github.com/tidalcycles/strudel/pull/131 -- add createParam + createParams by @felixroos in https://github.com/tidalcycles/strudel/pull/110 -- remove cycle + delta from onTrigger by @felixroos in https://github.com/tidalcycles/strudel/pull/135 -- Scheduler improvements by @felixroos in https://github.com/tidalcycles/strudel/pull/134 -- add onTrigger helper by @felixroos in https://github.com/tidalcycles/strudel/pull/136 +- Add chunk, chunkBack and iterBack by @yaxu in https://codeberg.org/uzu/strudel/pulls/25 +- Update tutorial.mdx by @bwagner in https://codeberg.org/uzu/strudel/pulls/37 +- Update tutorial.mdx by @bwagner in https://codeberg.org/uzu/strudel/pulls/38 +- Compose by @felixroos in https://codeberg.org/uzu/strudel/pulls/40 +- Fix polymeter by @yaxu in https://codeberg.org/uzu/strudel/pulls/44 +- First run at squeezeBind, ref #32 by @yaxu in https://codeberg.org/uzu/strudel/pulls/48 +- Implement `chop()` by @yaxu in https://codeberg.org/uzu/strudel/pulls/50 +- OSC and SuperDirt support by @yaxu in https://codeberg.org/uzu/strudel/pulls/27 +- More functions by @yaxu in https://codeberg.org/uzu/strudel/pulls/56 +- More functions by @yaxu in https://codeberg.org/uzu/strudel/pulls/61 +- Separate out strudel.mjs, make index.mjs aggregate module by @yaxu in https://codeberg.org/uzu/strudel/pulls/62 +- Speech output by @felixroos in https://codeberg.org/uzu/strudel/pulls/67 +- use new fixed version of osc-js package by @felixroos in https://codeberg.org/uzu/strudel/pulls/68 +- First effort at rand() by @yaxu in https://codeberg.org/uzu/strudel/pulls/69 +- More randomness, fix `rand`, and add `brand`, `irand` and `choose` by @yaxu in https://codeberg.org/uzu/strudel/pulls/70 +- webaudio package by @felixroos in https://codeberg.org/uzu/strudel/pulls/26 +- Port `perlin` noise, `rangex`, and `palindrome` by @yaxu in https://codeberg.org/uzu/strudel/pulls/73 +- More random functions by @yaxu in https://codeberg.org/uzu/strudel/pulls/74 +- Try to fix appLeft / appRight by @yaxu in https://codeberg.org/uzu/strudel/pulls/75 +- Basic webserial support by @yaxu in https://codeberg.org/uzu/strudel/pulls/80 +- Webaudio in REPL by @felixroos in https://codeberg.org/uzu/strudel/pulls/77 +- add `striate()` by @yaxu in https://codeberg.org/uzu/strudel/pulls/76 +- Tidy up a couple of old files by @mindofmatthew in https://codeberg.org/uzu/strudel/pulls/84 +- Add pattern composers, implements #82 by @yaxu in https://codeberg.org/uzu/strudel/pulls/83 +- Fiddles with cat/stack by @yaxu in https://codeberg.org/uzu/strudel/pulls/90 +- Paper by @felixroos in https://codeberg.org/uzu/strudel/pulls/98 +- Change to Affero GPL by @yaxu in https://codeberg.org/uzu/strudel/pulls/101 +- Work on Codemirror 6 highlighting by @mindofmatthew in https://codeberg.org/uzu/strudel/pulls/102 +- Codemirror 6 by @felixroos in https://codeberg.org/uzu/strudel/pulls/97 +- Tune tests by @felixroos in https://codeberg.org/uzu/strudel/pulls/104 +- /embed package: web component for repl by @felixroos in https://codeberg.org/uzu/strudel/pulls/106 +- Reset, Restart and other composers by @felixroos in https://codeberg.org/uzu/strudel/pulls/88 +- Embed style by @felixroos in https://codeberg.org/uzu/strudel/pulls/109 +- In source doc by @yaxu in https://codeberg.org/uzu/strudel/pulls/105 +- `.brak()`, `.inside()` and `.outside()` by @yaxu in https://codeberg.org/uzu/strudel/pulls/112 +- loopAt by @yaxu in https://codeberg.org/uzu/strudel/pulls/114 +- Osc timing improvements by @yaxu in https://codeberg.org/uzu/strudel/pulls/113 +- react package + vite build by @felixroos in https://codeberg.org/uzu/strudel/pulls/116 +- In source doc by @felixroos in https://codeberg.org/uzu/strudel/pulls/117 +- fix: #108 by @felixroos in https://codeberg.org/uzu/strudel/pulls/123 +- fix: #122 ctrl enter would add newline by @felixroos in https://codeberg.org/uzu/strudel/pulls/124 +- Webdirt by @felixroos in https://codeberg.org/uzu/strudel/pulls/121 +- Fix link to contributing to tutorial docs by @stephendwolff in https://codeberg.org/uzu/strudel/pulls/129 +- Pianoroll enhancements by @felixroos in https://codeberg.org/uzu/strudel/pulls/131 +- add createParam + createParams by @felixroos in https://codeberg.org/uzu/strudel/pulls/110 +- remove cycle + delta from onTrigger by @felixroos in https://codeberg.org/uzu/strudel/pulls/135 +- Scheduler improvements by @felixroos in https://codeberg.org/uzu/strudel/pulls/134 +- add onTrigger helper by @felixroos in https://codeberg.org/uzu/strudel/pulls/136 ## New Contributors -- @bwagner made their first contribution in https://github.com/tidalcycles/strudel/pull/37 -- @mindofmatthew made their first contribution in https://github.com/tidalcycles/strudel/pull/84 -- @stephendwolff made their first contribution in https://github.com/tidalcycles/strudel/pull/129 +- @bwagner made their first contribution in https://codeberg.org/uzu/strudel/pulls/37 +- @mindofmatthew made their first contribution in https://codeberg.org/uzu/strudel/pulls/84 +- @stephendwolff made their first contribution in https://codeberg.org/uzu/strudel/pulls/129 -**Full Changelog**: https://github.com/tidalcycles/strudel/compare/v0.0.2...v0.0.3 +**Full Changelog**: https://codeberg.org/uzu/strudel/compare/v0.0.2...v0.0.3 diff --git a/website/src/content/blog/release-0.0.4-gischt.mdx b/website/src/content/blog/release-0.0.4-gischt.mdx index 9149137ca..f06dd38e8 100644 --- a/website/src/content/blog/release-0.0.4-gischt.mdx +++ b/website/src/content/blog/release-0.0.4-gischt.mdx @@ -8,38 +8,38 @@ author: froos ## What's Changed -- Webaudio rewrite by @felixroos in https://github.com/tidalcycles/strudel/pull/138 -- Fix createParam() by @yaxu in https://github.com/tidalcycles/strudel/pull/140 -- Soundfont Support by @felixroos in https://github.com/tidalcycles/strudel/pull/139 -- Serial twiddles by @yaxu in https://github.com/tidalcycles/strudel/pull/141 -- Pianoroll Object Support by @felixroos in https://github.com/tidalcycles/strudel/pull/142 -- flash effect on ctrl enter by @felixroos in https://github.com/tidalcycles/strudel/pull/144 -- can now generate short link for sharing by @felixroos in https://github.com/tidalcycles/strudel/pull/146 -- Sampler optimizations and more by @felixroos in https://github.com/tidalcycles/strudel/pull/148 -- Final update to demo.pdf by @yaxu in https://github.com/tidalcycles/strudel/pull/151 -- add webdirt drum samples to prebake for general availability by @larkob in https://github.com/tidalcycles/strudel/pull/150 -- update to tutorial documentation by @larkob in https://github.com/tidalcycles/strudel/pull/162 -- add chooseInWith/chooseCycles by @yaxu in https://github.com/tidalcycles/strudel/pull/166 -- fix: jsdoc comments by @felixroos in https://github.com/tidalcycles/strudel/pull/169 -- Pianoroll fixes by @felixroos in https://github.com/tidalcycles/strudel/pull/163 -- Talk fixes by @felixroos in https://github.com/tidalcycles/strudel/pull/164 -- Amend shapeshifter to allow use of dynamic import by @debrisapron in https://github.com/tidalcycles/strudel/pull/171 -- add more shapeshifter flags by @felixroos in https://github.com/tidalcycles/strudel/pull/99 -- Replace react-codemirror6 with @uiw/react-codemirror by @felixroos in https://github.com/tidalcycles/strudel/pull/173 -- fix some annoying bugs by @felixroos in https://github.com/tidalcycles/strudel/pull/177 -- incorporate elements of randomness to the mini notation by @bpow in https://github.com/tidalcycles/strudel/pull/165 -- replace mocha with vitest by @felixroos in https://github.com/tidalcycles/strudel/pull/175 -- scheduler improvements by @felixroos in https://github.com/tidalcycles/strudel/pull/181 -- Fix codemirror bug by @felixroos in https://github.com/tidalcycles/strudel/pull/186 -- wait for prebake to finish before evaluating by @felixroos in https://github.com/tidalcycles/strudel/pull/189 -- fix regression: old way of setting frequencies was broken by @felixroos in https://github.com/tidalcycles/strudel/pull/190 -- Soundfont file support by @felixroos in https://github.com/tidalcycles/strudel/pull/183 -- change "stride"/"offset" of successive degradeBy/chooseIn by @bpow in https://github.com/tidalcycles/strudel/pull/185 +- Webaudio rewrite by @felixroos in https://codeberg.org/uzu/strudel/pulls/138 +- Fix createParam() by @yaxu in https://codeberg.org/uzu/strudel/pulls/140 +- Soundfont Support by @felixroos in https://codeberg.org/uzu/strudel/pulls/139 +- Serial twiddles by @yaxu in https://codeberg.org/uzu/strudel/pulls/141 +- Pianoroll Object Support by @felixroos in https://codeberg.org/uzu/strudel/pulls/142 +- flash effect on ctrl enter by @felixroos in https://codeberg.org/uzu/strudel/pulls/144 +- can now generate short link for sharing by @felixroos in https://codeberg.org/uzu/strudel/pulls/146 +- Sampler optimizations and more by @felixroos in https://codeberg.org/uzu/strudel/pulls/148 +- Final update to demo.pdf by @yaxu in https://codeberg.org/uzu/strudel/pulls/151 +- add webdirt drum samples to prebake for general availability by @larkob in https://codeberg.org/uzu/strudel/pulls/150 +- update to tutorial documentation by @larkob in https://codeberg.org/uzu/strudel/pulls/162 +- add chooseInWith/chooseCycles by @yaxu in https://codeberg.org/uzu/strudel/pulls/166 +- fix: jsdoc comments by @felixroos in https://codeberg.org/uzu/strudel/pulls/169 +- Pianoroll fixes by @felixroos in https://codeberg.org/uzu/strudel/pulls/163 +- Talk fixes by @felixroos in https://codeberg.org/uzu/strudel/pulls/164 +- Amend shapeshifter to allow use of dynamic import by @debrisapron in https://codeberg.org/uzu/strudel/pulls/171 +- add more shapeshifter flags by @felixroos in https://codeberg.org/uzu/strudel/pulls/99 +- Replace react-codemirror6 with @uiw/react-codemirror by @felixroos in https://codeberg.org/uzu/strudel/pulls/173 +- fix some annoying bugs by @felixroos in https://codeberg.org/uzu/strudel/pulls/177 +- incorporate elements of randomness to the mini notation by @bpow in https://codeberg.org/uzu/strudel/pulls/165 +- replace mocha with vitest by @felixroos in https://codeberg.org/uzu/strudel/pulls/175 +- scheduler improvements by @felixroos in https://codeberg.org/uzu/strudel/pulls/181 +- Fix codemirror bug by @felixroos in https://codeberg.org/uzu/strudel/pulls/186 +- wait for prebake to finish before evaluating by @felixroos in https://codeberg.org/uzu/strudel/pulls/189 +- fix regression: old way of setting frequencies was broken by @felixroos in https://codeberg.org/uzu/strudel/pulls/190 +- Soundfont file support by @felixroos in https://codeberg.org/uzu/strudel/pulls/183 +- change "stride"/"offset" of successive degradeBy/chooseIn by @bpow in https://codeberg.org/uzu/strudel/pulls/185 ## New Contributors -- @larkob made their first contribution in https://github.com/tidalcycles/strudel/pull/150 -- @debrisapron made their first contribution in https://github.com/tidalcycles/strudel/pull/171 -- @bpow made their first contribution in https://github.com/tidalcycles/strudel/pull/165 +- @larkob made their first contribution in https://codeberg.org/uzu/strudel/pulls/150 +- @debrisapron made their first contribution in https://codeberg.org/uzu/strudel/pulls/171 +- @bpow made their first contribution in https://codeberg.org/uzu/strudel/pulls/165 -**Full Changelog**: https://github.com/tidalcycles/strudel/compare/v0.0.3...v0.0.4 +**Full Changelog**: https://codeberg.org/uzu/strudel/compare/v0.0.3...v0.0.4 diff --git a/website/src/content/blog/release-0.3.0-donauwelle.mdx b/website/src/content/blog/release-0.3.0-donauwelle.mdx index 054b36b31..a551dc53b 100644 --- a/website/src/content/blog/release-0.3.0-donauwelle.mdx +++ b/website/src/content/blog/release-0.3.0-donauwelle.mdx @@ -22,44 +22,44 @@ author: froos ## What's Changed -- Fix numbers in sampler by @felixroos in https://github.com/tidalcycles/strudel/pull/196 -- document random functions by @felixroos in https://github.com/tidalcycles/strudel/pull/199 -- add rollup-plugin-visualizer to build by @felixroos in https://github.com/tidalcycles/strudel/pull/200 -- add vowel to .out by @felixroos in https://github.com/tidalcycles/strudel/pull/201 -- Coarse crush shape by @felixroos in https://github.com/tidalcycles/strudel/pull/205 -- Webaudio guide by @felixroos in https://github.com/tidalcycles/strudel/pull/207 -- Even more docs by @felixroos in https://github.com/tidalcycles/strudel/pull/212 -- Just another docs PR by @felixroos in https://github.com/tidalcycles/strudel/pull/215 -- sampler features + fixes by @felixroos in https://github.com/tidalcycles/strudel/pull/217 -- samples now have envelopes by @felixroos in https://github.com/tidalcycles/strudel/pull/218 -- encapsulate webaudio output by @felixroos in https://github.com/tidalcycles/strudel/pull/219 -- Fix squeeze join by @yaxu in https://github.com/tidalcycles/strudel/pull/220 -- Feedback Delay by @felixroos in https://github.com/tidalcycles/strudel/pull/213 -- support negative speeds by @felixroos in https://github.com/tidalcycles/strudel/pull/222 -- focus tweak for squeezeJoin - another go at fixing #216 by @yaxu in https://github.com/tidalcycles/strudel/pull/221 -- Reverb by @felixroos in https://github.com/tidalcycles/strudel/pull/224 -- fix fastgap for events that go across cycle boundaries by @yaxu in https://github.com/tidalcycles/strudel/pull/225 -- Core util tests by @mystery-house in https://github.com/tidalcycles/strudel/pull/226 -- Refactor tunes away from tone by @felixroos in https://github.com/tidalcycles/strudel/pull/230 -- Just another docs branch by @felixroos in https://github.com/tidalcycles/strudel/pull/228 -- Patternify range by @yaxu in https://github.com/tidalcycles/strudel/pull/231 -- Out by default by @felixroos in https://github.com/tidalcycles/strudel/pull/232 -- Fix zero length queries WIP by @yaxu in https://github.com/tidalcycles/strudel/pull/234 -- add vcsl sample library by @felixroos in https://github.com/tidalcycles/strudel/pull/235 -- fx on stereo speakers by @felixroos in https://github.com/tidalcycles/strudel/pull/236 -- Tidal drum machines by @felixroos in https://github.com/tidalcycles/strudel/pull/237 -- Object arithmetic by @felixroos in https://github.com/tidalcycles/strudel/pull/238 -- Load samples from url by @felixroos in https://github.com/tidalcycles/strudel/pull/239 -- feat: support github: links by @felixroos in https://github.com/tidalcycles/strudel/pull/240 -- in source example tests by @felixroos in https://github.com/tidalcycles/strudel/pull/242 -- Readme + TLC by @felixroos in https://github.com/tidalcycles/strudel/pull/244 -- patchday by @felixroos in https://github.com/tidalcycles/strudel/pull/246 -- Some tunes by @felixroos in https://github.com/tidalcycles/strudel/pull/247 -- snapshot tests on shared snippets by @felixroos in https://github.com/tidalcycles/strudel/pull/243 -- General purpose scheduler by @felixroos in https://github.com/tidalcycles/strudel/pull/248 +- Fix numbers in sampler by @felixroos in https://codeberg.org/uzu/strudel/pulls/196 +- document random functions by @felixroos in https://codeberg.org/uzu/strudel/pulls/199 +- add rollup-plugin-visualizer to build by @felixroos in https://codeberg.org/uzu/strudel/pulls/200 +- add vowel to .out by @felixroos in https://codeberg.org/uzu/strudel/pulls/201 +- Coarse crush shape by @felixroos in https://codeberg.org/uzu/strudel/pulls/205 +- Webaudio guide by @felixroos in https://codeberg.org/uzu/strudel/pulls/207 +- Even more docs by @felixroos in https://codeberg.org/uzu/strudel/pulls/212 +- Just another docs PR by @felixroos in https://codeberg.org/uzu/strudel/pulls/215 +- sampler features + fixes by @felixroos in https://codeberg.org/uzu/strudel/pulls/217 +- samples now have envelopes by @felixroos in https://codeberg.org/uzu/strudel/pulls/218 +- encapsulate webaudio output by @felixroos in https://codeberg.org/uzu/strudel/pulls/219 +- Fix squeeze join by @yaxu in https://codeberg.org/uzu/strudel/pulls/220 +- Feedback Delay by @felixroos in https://codeberg.org/uzu/strudel/pulls/213 +- support negative speeds by @felixroos in https://codeberg.org/uzu/strudel/pulls/222 +- focus tweak for squeezeJoin - another go at fixing #216 by @yaxu in https://codeberg.org/uzu/strudel/pulls/221 +- Reverb by @felixroos in https://codeberg.org/uzu/strudel/pulls/224 +- fix fastgap for events that go across cycle boundaries by @yaxu in https://codeberg.org/uzu/strudel/pulls/225 +- Core util tests by @mystery-house in https://codeberg.org/uzu/strudel/pulls/226 +- Refactor tunes away from tone by @felixroos in https://codeberg.org/uzu/strudel/pulls/230 +- Just another docs branch by @felixroos in https://codeberg.org/uzu/strudel/pulls/228 +- Patternify range by @yaxu in https://codeberg.org/uzu/strudel/pulls/231 +- Out by default by @felixroos in https://codeberg.org/uzu/strudel/pulls/232 +- Fix zero length queries WIP by @yaxu in https://codeberg.org/uzu/strudel/pulls/234 +- add vcsl sample library by @felixroos in https://codeberg.org/uzu/strudel/pulls/235 +- fx on stereo speakers by @felixroos in https://codeberg.org/uzu/strudel/pulls/236 +- Tidal drum machines by @felixroos in https://codeberg.org/uzu/strudel/pulls/237 +- Object arithmetic by @felixroos in https://codeberg.org/uzu/strudel/pulls/238 +- Load samples from url by @felixroos in https://codeberg.org/uzu/strudel/pulls/239 +- feat: support github: links by @felixroos in https://codeberg.org/uzu/strudel/pulls/240 +- in source example tests by @felixroos in https://codeberg.org/uzu/strudel/pulls/242 +- Readme + TLC by @felixroos in https://codeberg.org/uzu/strudel/pulls/244 +- patchday by @felixroos in https://codeberg.org/uzu/strudel/pulls/246 +- Some tunes by @felixroos in https://codeberg.org/uzu/strudel/pulls/247 +- snapshot tests on shared snippets by @felixroos in https://codeberg.org/uzu/strudel/pulls/243 +- General purpose scheduler by @felixroos in https://codeberg.org/uzu/strudel/pulls/248 ## New Contributors -- @mystery-house made their first contribution in https://github.com/tidalcycles/strudel/pull/226 +- @mystery-house made their first contribution in https://codeberg.org/uzu/strudel/pulls/226 -**Full Changelog**: https://github.com/tidalcycles/strudel/compare/v0.0.4...v0.3.0 +**Full Changelog**: https://codeberg.org/uzu/strudel/compare/v0.0.4...v0.3.0 diff --git a/website/src/content/blog/release-0.4.0-brandung.mdx b/website/src/content/blog/release-0.4.0-brandung.mdx index 06023662b..01e2f9b89 100644 --- a/website/src/content/blog/release-0.4.0-brandung.mdx +++ b/website/src/content/blog/release-0.4.0-brandung.mdx @@ -8,8 +8,8 @@ author: froos ## What's Changed -- new transpiler based on acorn by @felixroos in https://github.com/tidalcycles/strudel/pull/249 -- Webaudio build by @felixroos in https://github.com/tidalcycles/strudel/pull/250 -- Repl refactoring by @felixroos in https://github.com/tidalcycles/strudel/pull/255 +- new transpiler based on acorn by @felixroos in https://codeberg.org/uzu/strudel/pulls/249 +- Webaudio build by @felixroos in https://codeberg.org/uzu/strudel/pulls/250 +- Repl refactoring by @felixroos in https://codeberg.org/uzu/strudel/pulls/255 -**Full Changelog**: https://github.com/tidalcycles/strudel/compare/v0.3.0...v0.4.0 +**Full Changelog**: https://codeberg.org/uzu/strudel/compare/v0.3.0...v0.4.0 diff --git a/website/src/content/blog/release-0.5.0-wirbel.mdx b/website/src/content/blog/release-0.5.0-wirbel.mdx index 032639fd9..3c1e1bfe2 100644 --- a/website/src/content/blog/release-0.5.0-wirbel.mdx +++ b/website/src/content/blog/release-0.5.0-wirbel.mdx @@ -26,34 +26,34 @@ author: froos ## What's Changed -- Binaries by @felixroos in https://github.com/tidalcycles/strudel/pull/254 -- fix tutorial bugs by @felixroos in https://github.com/tidalcycles/strudel/pull/263 -- fix performance bottleneck by @felixroos in https://github.com/tidalcycles/strudel/pull/266 -- Tidying up core by @yaxu in https://github.com/tidalcycles/strudel/pull/256 -- tonal update with fixed memory leak by @felixroos in https://github.com/tidalcycles/strudel/pull/272 -- add eslint by @felixroos in https://github.com/tidalcycles/strudel/pull/271 -- release version bumps by @felixroos in https://github.com/tidalcycles/strudel/pull/273 -- Support sending CRC16 bytes with serial messages by @yaxu in https://github.com/tidalcycles/strudel/pull/276 -- add licenses / credits to all tunes + remove some by @felixroos in https://github.com/tidalcycles/strudel/pull/277 -- add basic csound output by @felixroos in https://github.com/tidalcycles/strudel/pull/275 -- do not recompile orc by @felixroos in https://github.com/tidalcycles/strudel/pull/278 -- implement collect + arp function by @felixroos in https://github.com/tidalcycles/strudel/pull/281 -- Switch 'operators' from .whatHow to .what.how by @yaxu in https://github.com/tidalcycles/strudel/pull/285 -- Fancy hap show, include part in snapshots by @yaxu in https://github.com/tidalcycles/strudel/pull/291 -- Reorganise pattern.mjs with a 'toplevel first' regime by @yaxu in https://github.com/tidalcycles/strudel/pull/286 -- add prettier task by @felixroos in https://github.com/tidalcycles/strudel/pull/296 -- Move stuff to new register function by @felixroos in https://github.com/tidalcycles/strudel/pull/295 -- can now add bare numbers to numeral object props by @felixroos in https://github.com/tidalcycles/strudel/pull/287 -- update vitest by @felixroos in https://github.com/tidalcycles/strudel/pull/297 -- remove whitespace from highlighted region by @felixroos in https://github.com/tidalcycles/strudel/pull/298 -- .defragmentHaps() for merging touching haps that share a whole and value by @yaxu in https://github.com/tidalcycles/strudel/pull/299 -- fix whitespace trimming by @felixroos in https://github.com/tidalcycles/strudel/pull/300 -- add freq support to sampler by @felixroos in https://github.com/tidalcycles/strudel/pull/301 -- add lint + prettier check before test by @felixroos in https://github.com/tidalcycles/strudel/pull/305 -- Updated csoundm to use the register facility . by @gogins in https://github.com/tidalcycles/strudel/pull/303 +- Binaries by @felixroos in https://codeberg.org/uzu/strudel/pulls/254 +- fix tutorial bugs by @felixroos in https://codeberg.org/uzu/strudel/pulls/263 +- fix performance bottleneck by @felixroos in https://codeberg.org/uzu/strudel/pulls/266 +- Tidying up core by @yaxu in https://codeberg.org/uzu/strudel/pulls/256 +- tonal update with fixed memory leak by @felixroos in https://codeberg.org/uzu/strudel/pulls/272 +- add eslint by @felixroos in https://codeberg.org/uzu/strudel/pulls/271 +- release version bumps by @felixroos in https://codeberg.org/uzu/strudel/pulls/273 +- Support sending CRC16 bytes with serial messages by @yaxu in https://codeberg.org/uzu/strudel/pulls/276 +- add licenses / credits to all tunes + remove some by @felixroos in https://codeberg.org/uzu/strudel/pulls/277 +- add basic csound output by @felixroos in https://codeberg.org/uzu/strudel/pulls/275 +- do not recompile orc by @felixroos in https://codeberg.org/uzu/strudel/pulls/278 +- implement collect + arp function by @felixroos in https://codeberg.org/uzu/strudel/pulls/281 +- Switch 'operators' from .whatHow to .what.how by @yaxu in https://codeberg.org/uzu/strudel/pulls/285 +- Fancy hap show, include part in snapshots by @yaxu in https://codeberg.org/uzu/strudel/pulls/291 +- Reorganise pattern.mjs with a 'toplevel first' regime by @yaxu in https://codeberg.org/uzu/strudel/pulls/286 +- add prettier task by @felixroos in https://codeberg.org/uzu/strudel/pulls/296 +- Move stuff to new register function by @felixroos in https://codeberg.org/uzu/strudel/pulls/295 +- can now add bare numbers to numeral object props by @felixroos in https://codeberg.org/uzu/strudel/pulls/287 +- update vitest by @felixroos in https://codeberg.org/uzu/strudel/pulls/297 +- remove whitespace from highlighted region by @felixroos in https://codeberg.org/uzu/strudel/pulls/298 +- .defragmentHaps() for merging touching haps that share a whole and value by @yaxu in https://codeberg.org/uzu/strudel/pulls/299 +- fix whitespace trimming by @felixroos in https://codeberg.org/uzu/strudel/pulls/300 +- add freq support to sampler by @felixroos in https://codeberg.org/uzu/strudel/pulls/301 +- add lint + prettier check before test by @felixroos in https://codeberg.org/uzu/strudel/pulls/305 +- Updated csoundm to use the register facility . by @gogins in https://codeberg.org/uzu/strudel/pulls/303 ## New Contributors -- @gogins made their first contribution in https://github.com/tidalcycles/strudel/pull/303 +- @gogins made their first contribution in https://codeberg.org/uzu/strudel/pulls/303 -**Full Changelog**: https://github.com/tidalcycles/strudel/compare/v0.4.0...v0.5.0 +**Full Changelog**: https://codeberg.org/uzu/strudel/compare/v0.4.0...v0.5.0 diff --git a/website/src/content/blog/release-0.6.0-zimtschnecke.mdx b/website/src/content/blog/release-0.6.0-zimtschnecke.mdx index 2736974bf..19536581d 100644 --- a/website/src/content/blog/release-0.6.0-zimtschnecke.mdx +++ b/website/src/content/blog/release-0.6.0-zimtschnecke.mdx @@ -26,59 +26,59 @@ author: froos ## What's Changed -- support freq in pianoroll by @felixroos in https://github.com/tidalcycles/strudel/pull/308 -- ICLC2023 paper WIP by @yaxu in https://github.com/tidalcycles/strudel/pull/306 -- fix: copy share link to clipboard was broken for some browsers by @felixroos in https://github.com/tidalcycles/strudel/pull/311 -- Jsdoc component by @felixroos in https://github.com/tidalcycles/strudel/pull/312 -- object support for .scale by @felixroos in https://github.com/tidalcycles/strudel/pull/307 -- Astro build by @felixroos in https://github.com/tidalcycles/strudel/pull/315 -- Reference tab sort by @felixroos in https://github.com/tidalcycles/strudel/pull/318 -- tutorial updates by @jarmitage in https://github.com/tidalcycles/strudel/pull/320 -- support notes without octave by @felixroos in https://github.com/tidalcycles/strudel/pull/323 -- mini repl improvements by @felixroos in https://github.com/tidalcycles/strudel/pull/324 -- fix: workaround Object.assign globalThis by @felixroos in https://github.com/tidalcycles/strudel/pull/326 -- add examples route by @felixroos in https://github.com/tidalcycles/strudel/pull/327 -- add my-patterns by @felixroos in https://github.com/tidalcycles/strudel/pull/328 -- my-patterns build + deploy by @felixroos in https://github.com/tidalcycles/strudel/pull/329 -- my-patterns: fix paths + update readme by @felixroos in https://github.com/tidalcycles/strudel/pull/330 -- improve displaying 's' in pianoroll by @felixroos in https://github.com/tidalcycles/strudel/pull/331 -- fix: can now multiply floats in mini notation by @felixroos in https://github.com/tidalcycles/strudel/pull/332 -- Embed mode improvements by @felixroos in https://github.com/tidalcycles/strudel/pull/333 -- testing + docs docs by @felixroos in https://github.com/tidalcycles/strudel/pull/334 -- animate mvp by @felixroos in https://github.com/tidalcycles/strudel/pull/335 -- Tidy parser, implement polymeters by @yaxu in https://github.com/tidalcycles/strudel/pull/336 -- animation options by @felixroos in https://github.com/tidalcycles/strudel/pull/337 -- move /my-patterns to /swatch by @yaxu in https://github.com/tidalcycles/strudel/pull/338 -- more animate functions + mini repl fix by @felixroos in https://github.com/tidalcycles/strudel/pull/340 -- Patternify euclid, fast, slow and polymeter step parameters in mininotation by @yaxu in https://github.com/tidalcycles/strudel/pull/341 -- fixes #346 by @felixroos in https://github.com/tidalcycles/strudel/pull/347 -- Fix prebake base path by @felixroos in https://github.com/tidalcycles/strudel/pull/345 -- Fix Bjorklund by @yaxu in https://github.com/tidalcycles/strudel/pull/343 -- docs: tidal comparison + add global fx + add missing sampler fx by @felixroos in https://github.com/tidalcycles/strudel/pull/356 -- Fix .out(), renaming webaudio's out() to webaudio() by @yaxu in https://github.com/tidalcycles/strudel/pull/361 -- Support for multiple mininotation operators by @yaxu in https://github.com/tidalcycles/strudel/pull/350 -- doc structuring by @felixroos in https://github.com/tidalcycles/strudel/pull/360 -- add https to url by @urswilke in https://github.com/tidalcycles/strudel/pull/364 -- document more functions + change arp join by @felixroos in https://github.com/tidalcycles/strudel/pull/369 -- improve new draw logic by @felixroos in https://github.com/tidalcycles/strudel/pull/372 -- Draw fixes by @felixroos in https://github.com/tidalcycles/strudel/pull/377 -- update my-patterns instructions by @felixroos in https://github.com/tidalcycles/strudel/pull/384 -- docs: use note instead of n to mitigate confusion by @felixroos in https://github.com/tidalcycles/strudel/pull/385 -- add run + test + docs by @felixroos in https://github.com/tidalcycles/strudel/pull/386 -- Rename a to angle by @felixroos in https://github.com/tidalcycles/strudel/pull/387 -- document csound by @felixroos in https://github.com/tidalcycles/strudel/pull/391 -- Notes are not essential :) by @yaxu in https://github.com/tidalcycles/strudel/pull/393 -- add ribbon + test + docs by @felixroos in https://github.com/tidalcycles/strudel/pull/388 -- Add tidal-drum-patterns to examples by @urswilke in https://github.com/tidalcycles/strudel/pull/379 -- add pattern methods hurry, press and pressBy by @yaxu in https://github.com/tidalcycles/strudel/pull/397 -- proper builds + use pnpm workspaces by @felixroos in https://github.com/tidalcycles/strudel/pull/396 -- fix: minirepl styles by @felixroos in https://github.com/tidalcycles/strudel/pull/398 -- can now await initAudio + initAudioOnFirstClick by @felixroos in https://github.com/tidalcycles/strudel/pull/399 -- release webaudio by @felixroos in https://github.com/tidalcycles/strudel/pull/400 +- support freq in pianoroll by @felixroos in https://codeberg.org/uzu/strudel/pulls/308 +- ICLC2023 paper WIP by @yaxu in https://codeberg.org/uzu/strudel/pulls/306 +- fix: copy share link to clipboard was broken for some browsers by @felixroos in https://codeberg.org/uzu/strudel/pulls/311 +- Jsdoc component by @felixroos in https://codeberg.org/uzu/strudel/pulls/312 +- object support for .scale by @felixroos in https://codeberg.org/uzu/strudel/pulls/307 +- Astro build by @felixroos in https://codeberg.org/uzu/strudel/pulls/315 +- Reference tab sort by @felixroos in https://codeberg.org/uzu/strudel/pulls/318 +- tutorial updates by @jarmitage in https://codeberg.org/uzu/strudel/pulls/320 +- support notes without octave by @felixroos in https://codeberg.org/uzu/strudel/pulls/323 +- mini repl improvements by @felixroos in https://codeberg.org/uzu/strudel/pulls/324 +- fix: workaround Object.assign globalThis by @felixroos in https://codeberg.org/uzu/strudel/pulls/326 +- add examples route by @felixroos in https://codeberg.org/uzu/strudel/pulls/327 +- add my-patterns by @felixroos in https://codeberg.org/uzu/strudel/pulls/328 +- my-patterns build + deploy by @felixroos in https://codeberg.org/uzu/strudel/pulls/329 +- my-patterns: fix paths + update readme by @felixroos in https://codeberg.org/uzu/strudel/pulls/330 +- improve displaying 's' in pianoroll by @felixroos in https://codeberg.org/uzu/strudel/pulls/331 +- fix: can now multiply floats in mini notation by @felixroos in https://codeberg.org/uzu/strudel/pulls/332 +- Embed mode improvements by @felixroos in https://codeberg.org/uzu/strudel/pulls/333 +- testing + docs docs by @felixroos in https://codeberg.org/uzu/strudel/pulls/334 +- animate mvp by @felixroos in https://codeberg.org/uzu/strudel/pulls/335 +- Tidy parser, implement polymeters by @yaxu in https://codeberg.org/uzu/strudel/pulls/336 +- animation options by @felixroos in https://codeberg.org/uzu/strudel/pulls/337 +- move /my-patterns to /swatch by @yaxu in https://codeberg.org/uzu/strudel/pulls/338 +- more animate functions + mini repl fix by @felixroos in https://codeberg.org/uzu/strudel/pulls/340 +- Patternify euclid, fast, slow and polymeter step parameters in mininotation by @yaxu in https://codeberg.org/uzu/strudel/pulls/341 +- fixes #346 by @felixroos in https://codeberg.org/uzu/strudel/pulls/347 +- Fix prebake base path by @felixroos in https://codeberg.org/uzu/strudel/pulls/345 +- Fix Bjorklund by @yaxu in https://codeberg.org/uzu/strudel/pulls/343 +- docs: tidal comparison + add global fx + add missing sampler fx by @felixroos in https://codeberg.org/uzu/strudel/pulls/356 +- Fix .out(), renaming webaudio's out() to webaudio() by @yaxu in https://codeberg.org/uzu/strudel/pulls/361 +- Support for multiple mininotation operators by @yaxu in https://codeberg.org/uzu/strudel/pulls/350 +- doc structuring by @felixroos in https://codeberg.org/uzu/strudel/pulls/360 +- add https to url by @urswilke in https://codeberg.org/uzu/strudel/pulls/364 +- document more functions + change arp join by @felixroos in https://codeberg.org/uzu/strudel/pulls/369 +- improve new draw logic by @felixroos in https://codeberg.org/uzu/strudel/pulls/372 +- Draw fixes by @felixroos in https://codeberg.org/uzu/strudel/pulls/377 +- update my-patterns instructions by @felixroos in https://codeberg.org/uzu/strudel/pulls/384 +- docs: use note instead of n to mitigate confusion by @felixroos in https://codeberg.org/uzu/strudel/pulls/385 +- add run + test + docs by @felixroos in https://codeberg.org/uzu/strudel/pulls/386 +- Rename a to angle by @felixroos in https://codeberg.org/uzu/strudel/pulls/387 +- document csound by @felixroos in https://codeberg.org/uzu/strudel/pulls/391 +- Notes are not essential :) by @yaxu in https://codeberg.org/uzu/strudel/pulls/393 +- add ribbon + test + docs by @felixroos in https://codeberg.org/uzu/strudel/pulls/388 +- Add tidal-drum-patterns to examples by @urswilke in https://codeberg.org/uzu/strudel/pulls/379 +- add pattern methods hurry, press and pressBy by @yaxu in https://codeberg.org/uzu/strudel/pulls/397 +- proper builds + use pnpm workspaces by @felixroos in https://codeberg.org/uzu/strudel/pulls/396 +- fix: minirepl styles by @felixroos in https://codeberg.org/uzu/strudel/pulls/398 +- can now await initAudio + initAudioOnFirstClick by @felixroos in https://codeberg.org/uzu/strudel/pulls/399 +- release webaudio by @felixroos in https://codeberg.org/uzu/strudel/pulls/400 ## New Contributors -- @jarmitage made their first contribution in https://github.com/tidalcycles/strudel/pull/320 -- @urswilke made their first contribution in https://github.com/tidalcycles/strudel/pull/364 +- @jarmitage made their first contribution in https://codeberg.org/uzu/strudel/pulls/320 +- @urswilke made their first contribution in https://codeberg.org/uzu/strudel/pulls/364 -**Full Changelog**: https://github.com/tidalcycles/strudel/compare/v0.5.0...v0.6.0 +**Full Changelog**: https://codeberg.org/uzu/strudel/compare/v0.5.0...v0.6.0 diff --git a/website/src/content/blog/release-0.7.0-zuckerguss.mdx b/website/src/content/blog/release-0.7.0-zuckerguss.mdx index 0a3a4649b..f67deee73 100644 --- a/website/src/content/blog/release-0.7.0-zuckerguss.mdx +++ b/website/src/content/blog/release-0.7.0-zuckerguss.mdx @@ -23,66 +23,66 @@ author: froos ## What's Changed -- pin @csound/browser to 6.18.3 + bump by @felixroos in https://github.com/tidalcycles/strudel/pull/403 -- update csound + fix sound output by @felixroos in https://github.com/tidalcycles/strudel/pull/404 -- fix: share url on subpath by @felixroos in https://github.com/tidalcycles/strudel/pull/405 -- add shabda doc by @felixroos in https://github.com/tidalcycles/strudel/pull/407 -- Update effects.mdx by @bwagner in https://github.com/tidalcycles/strudel/pull/410 -- improve effects doc by @felixroos in https://github.com/tidalcycles/strudel/pull/409 -- google gtfo by @felixroos in https://github.com/tidalcycles/strudel/pull/413 -- improve samples doc by @felixroos in https://github.com/tidalcycles/strudel/pull/411 -- PWA with offline support by @felixroos in https://github.com/tidalcycles/strudel/pull/417 -- add caching strategy for missing file types + cache all samples loaded from github by @felixroos in https://github.com/tidalcycles/strudel/pull/419 -- add more offline caching by @felixroos in https://github.com/tidalcycles/strudel/pull/421 -- add cdn.freesound to cache list by @felixroos in https://github.com/tidalcycles/strudel/pull/425 -- minirepl: add keyboard shortcuts by @felixroos in https://github.com/tidalcycles/strudel/pull/429 -- Themes by @felixroos in https://github.com/tidalcycles/strudel/pull/431 -- autocomplete preparations by @felixroos in https://github.com/tidalcycles/strudel/pull/427 -- Fix anchors by @felixroos in https://github.com/tidalcycles/strudel/pull/433 -- Update code.mdx by @bwagner in https://github.com/tidalcycles/strudel/pull/436 -- Update mini-notation.mdx by @bwagner in https://github.com/tidalcycles/strudel/pull/437 -- Update synths.mdx by @bwagner in https://github.com/tidalcycles/strudel/pull/438 -- FIXES: Warning about jsxBracketSameLine deprecation by @bwagner in https://github.com/tidalcycles/strudel/pull/461 -- Composable functions by @yaxu in https://github.com/tidalcycles/strudel/pull/390 -- weave and weaveWith by @yaxu in https://github.com/tidalcycles/strudel/pull/465 -- slice and splice by @yaxu in https://github.com/tidalcycles/strudel/pull/466 -- fix: osc should not return a promise by @felixroos in https://github.com/tidalcycles/strudel/pull/472 -- FIXES: freqs instead of pitches by @bwagner in https://github.com/tidalcycles/strudel/pull/464 -- Update input-output.mdx by @bwagner in https://github.com/tidalcycles/strudel/pull/471 -- settings tab with vim / emacs modes + additional themes and fonts by @felixroos in https://github.com/tidalcycles/strudel/pull/467 -- fix: hash links by @felixroos in https://github.com/tidalcycles/strudel/pull/473 -- midi cc support by @felixroos in https://github.com/tidalcycles/strudel/pull/478 -- Fix array args by @felixroos in https://github.com/tidalcycles/strudel/pull/480 -- docs: packages + offline by @felixroos in https://github.com/tidalcycles/strudel/pull/482 -- Update mini-notation.mdx by @yaxu in https://github.com/tidalcycles/strudel/pull/365 -- Revert "Another attempt at composable functions - WIP (#390)" by @felixroos in https://github.com/tidalcycles/strudel/pull/484 -- fix app height by @felixroos in https://github.com/tidalcycles/strudel/pull/485 -- add algolia creds + optimize sidebar for crawling by @felixroos in https://github.com/tidalcycles/strudel/pull/488 -- refactor react package by @felixroos in https://github.com/tidalcycles/strudel/pull/490 -- react style fixes by @felixroos in https://github.com/tidalcycles/strudel/pull/491 -- implement cps in scheduler by @felixroos in https://github.com/tidalcycles/strudel/pull/493 -- Add control aliases by @yaxu in https://github.com/tidalcycles/strudel/pull/497 -- fix: nano-repl highlighting by @felixroos in https://github.com/tidalcycles/strudel/pull/501 -- Reinstate slice and splice by @yaxu in https://github.com/tidalcycles/strudel/pull/500 -- can now use : as a replacement for space in scales by @felixroos in https://github.com/tidalcycles/strudel/pull/502 -- Support list syntax in mininotation by @yaxu in https://github.com/tidalcycles/strudel/pull/512 -- update react to 18 by @felixroos in https://github.com/tidalcycles/strudel/pull/514 -- add arrange function by @felixroos in https://github.com/tidalcycles/strudel/pull/508 -- Update README.md by @bwagner in https://github.com/tidalcycles/strudel/pull/474 -- add 2 illegible fonts by @felixroos in https://github.com/tidalcycles/strudel/pull/518 -- registerSound API + improved sounds tab + regroup soundfonts by @felixroos in https://github.com/tidalcycles/strudel/pull/516 -- fix: envelopes in chrome by @felixroos in https://github.com/tidalcycles/strudel/pull/521 -- Update samples.mdx by @bwagner in https://github.com/tidalcycles/strudel/pull/524 -- Update intro.mdx by @bwagner in https://github.com/tidalcycles/strudel/pull/525 -- fix(footer): fix link to tidalcycles by @revolunet in https://github.com/tidalcycles/strudel/pull/529 -- FIXES: alias pm for polymeter by @bwagner in https://github.com/tidalcycles/strudel/pull/527 -- Maintain random seed state in parser, not globally by @ijc8 in https://github.com/tidalcycles/strudel/pull/531 -- feat: add freq support to gm soundfonts by @felixroos in https://github.com/tidalcycles/strudel/pull/534 -- Update lerna by @felixroos in https://github.com/tidalcycles/strudel/pull/535 +- pin @csound/browser to 6.18.3 + bump by @felixroos in https://codeberg.org/uzu/strudel/pulls/403 +- update csound + fix sound output by @felixroos in https://codeberg.org/uzu/strudel/pulls/404 +- fix: share url on subpath by @felixroos in https://codeberg.org/uzu/strudel/pulls/405 +- add shabda doc by @felixroos in https://codeberg.org/uzu/strudel/pulls/407 +- Update effects.mdx by @bwagner in https://codeberg.org/uzu/strudel/pulls/410 +- improve effects doc by @felixroos in https://codeberg.org/uzu/strudel/pulls/409 +- google gtfo by @felixroos in https://codeberg.org/uzu/strudel/pulls/413 +- improve samples doc by @felixroos in https://codeberg.org/uzu/strudel/pulls/411 +- PWA with offline support by @felixroos in https://codeberg.org/uzu/strudel/pulls/417 +- add caching strategy for missing file types + cache all samples loaded from github by @felixroos in https://codeberg.org/uzu/strudel/pulls/419 +- add more offline caching by @felixroos in https://codeberg.org/uzu/strudel/pulls/421 +- add cdn.freesound to cache list by @felixroos in https://codeberg.org/uzu/strudel/pulls/425 +- minirepl: add keyboard shortcuts by @felixroos in https://codeberg.org/uzu/strudel/pulls/429 +- Themes by @felixroos in https://codeberg.org/uzu/strudel/pulls/431 +- autocomplete preparations by @felixroos in https://codeberg.org/uzu/strudel/pulls/427 +- Fix anchors by @felixroos in https://codeberg.org/uzu/strudel/pulls/433 +- Update code.mdx by @bwagner in https://codeberg.org/uzu/strudel/pulls/436 +- Update mini-notation.mdx by @bwagner in https://codeberg.org/uzu/strudel/pulls/437 +- Update synths.mdx by @bwagner in https://codeberg.org/uzu/strudel/pulls/438 +- FIXES: Warning about jsxBracketSameLine deprecation by @bwagner in https://codeberg.org/uzu/strudel/pulls/461 +- Composable functions by @yaxu in https://codeberg.org/uzu/strudel/pulls/390 +- weave and weaveWith by @yaxu in https://codeberg.org/uzu/strudel/pulls/465 +- slice and splice by @yaxu in https://codeberg.org/uzu/strudel/pulls/466 +- fix: osc should not return a promise by @felixroos in https://codeberg.org/uzu/strudel/pulls/472 +- FIXES: freqs instead of pitches by @bwagner in https://codeberg.org/uzu/strudel/pulls/464 +- Update input-output.mdx by @bwagner in https://codeberg.org/uzu/strudel/pulls/471 +- settings tab with vim / emacs modes + additional themes and fonts by @felixroos in https://codeberg.org/uzu/strudel/pulls/467 +- fix: hash links by @felixroos in https://codeberg.org/uzu/strudel/pulls/473 +- midi cc support by @felixroos in https://codeberg.org/uzu/strudel/pulls/478 +- Fix array args by @felixroos in https://codeberg.org/uzu/strudel/pulls/480 +- docs: packages + offline by @felixroos in https://codeberg.org/uzu/strudel/pulls/482 +- Update mini-notation.mdx by @yaxu in https://codeberg.org/uzu/strudel/pulls/365 +- Revert "Another attempt at composable functions - WIP (#390)" by @felixroos in https://codeberg.org/uzu/strudel/pulls/484 +- fix app height by @felixroos in https://codeberg.org/uzu/strudel/pulls/485 +- add algolia creds + optimize sidebar for crawling by @felixroos in https://codeberg.org/uzu/strudel/pulls/488 +- refactor react package by @felixroos in https://codeberg.org/uzu/strudel/pulls/490 +- react style fixes by @felixroos in https://codeberg.org/uzu/strudel/pulls/491 +- implement cps in scheduler by @felixroos in https://codeberg.org/uzu/strudel/pulls/493 +- Add control aliases by @yaxu in https://codeberg.org/uzu/strudel/pulls/497 +- fix: nano-repl highlighting by @felixroos in https://codeberg.org/uzu/strudel/pulls/501 +- Reinstate slice and splice by @yaxu in https://codeberg.org/uzu/strudel/pulls/500 +- can now use : as a replacement for space in scales by @felixroos in https://codeberg.org/uzu/strudel/pulls/502 +- Support list syntax in mininotation by @yaxu in https://codeberg.org/uzu/strudel/pulls/512 +- update react to 18 by @felixroos in https://codeberg.org/uzu/strudel/pulls/514 +- add arrange function by @felixroos in https://codeberg.org/uzu/strudel/pulls/508 +- Update README.md by @bwagner in https://codeberg.org/uzu/strudel/pulls/474 +- add 2 illegible fonts by @felixroos in https://codeberg.org/uzu/strudel/pulls/518 +- registerSound API + improved sounds tab + regroup soundfonts by @felixroos in https://codeberg.org/uzu/strudel/pulls/516 +- fix: envelopes in chrome by @felixroos in https://codeberg.org/uzu/strudel/pulls/521 +- Update samples.mdx by @bwagner in https://codeberg.org/uzu/strudel/pulls/524 +- Update intro.mdx by @bwagner in https://codeberg.org/uzu/strudel/pulls/525 +- fix(footer): fix link to tidalcycles by @revolunet in https://codeberg.org/uzu/strudel/pulls/529 +- FIXES: alias pm for polymeter by @bwagner in https://codeberg.org/uzu/strudel/pulls/527 +- Maintain random seed state in parser, not globally by @ijc8 in https://codeberg.org/uzu/strudel/pulls/531 +- feat: add freq support to gm soundfonts by @felixroos in https://codeberg.org/uzu/strudel/pulls/534 +- Update lerna by @felixroos in https://codeberg.org/uzu/strudel/pulls/535 ## New Contributors -- @revolunet made their first contribution in https://github.com/tidalcycles/strudel/pull/529 -- @ijc8 made their first contribution in https://github.com/tidalcycles/strudel/pull/531 +- @revolunet made their first contribution in https://codeberg.org/uzu/strudel/pulls/529 +- @ijc8 made their first contribution in https://codeberg.org/uzu/strudel/pulls/531 -**Full Changelog**: https://github.com/tidalcycles/strudel/compare/v0.6.0...v0.7.0 +**Full Changelog**: https://codeberg.org/uzu/strudel/compare/v0.6.0...v0.7.0 diff --git a/website/src/content/blog/release-0.8.0-himbeermuffin.mdx b/website/src/content/blog/release-0.8.0-himbeermuffin.mdx index 7620140b5..1eb1888a7 100644 --- a/website/src/content/blog/release-0.8.0-himbeermuffin.mdx +++ b/website/src/content/blog/release-0.8.0-himbeermuffin.mdx @@ -6,7 +6,7 @@ tags: ['meta'] author: froos --- -import BlogVideo from '../../components/BlogVideo.astro'; +import { Youtube } from '@src/components/Youtube'; These are the release notes for Strudel 0.8.0 aka "Himbeermuffin"! @@ -18,11 +18,11 @@ Let me write up some of the highlights: Besides the REPL (https://strudel.tidalcycles.org/), Strudel is now also distributed as a Desktop App via https://tauri.app/! Thanks to [vasilymilovidov](https://github.com/vasilymilovidov)! -- [Linux: Debian based](https://github.com/tidalcycles/strudel/releases/download/v0.8.0/strudel_0.1.0_amd64.deb) -- [Linux: AppImage](https://github.com/tidalcycles/strudel/releases/download/v0.8.0/strudel_0.1.0_amd64.AppImage) -- [MacOS](https://github.com/tidalcycles/strudel/releases/download/v0.8.0/Strudel_0.1.0_x64.dmg) -- [Windows .exe](https://github.com/tidalcycles/strudel/releases/download/v0.8.0/Strudel_0.1.0_x64-setup.exe) -- [Windows .msi](https://github.com/tidalcycles/strudel/releases/download/v0.8.0/Strudel_0.1.0_x64_en-US.msi) +- [Linux: Debian based](https://codeberg.org/uzu/strudel/releases/download/v0.8.0/strudel_0.1.0_amd64.deb) +- [Linux: AppImage](https://codeberg.org/uzu/strudel/releases/download/v0.8.0/strudel_0.1.0_amd64.AppImage) +- [MacOS](https://codeberg.org/uzu/strudel/releases/download/v0.8.0/Strudel_0.1.0_x64.dmg) +- [Windows .exe](https://codeberg.org/uzu/strudel/releases/download/v0.8.0/Strudel_0.1.0_x64-setup.exe) +- [Windows .msi](https://codeberg.org/uzu/strudel/releases/download/v0.8.0/Strudel_0.1.0_x64_en-US.msi) edit: the desktop app performance on linux is currently not that great.. the web REPL runs much smoother (using firefox or chromium) @@ -43,7 +43,7 @@ I would be very happy to collect some feedback on how it works across different Also still undocumented, but you can now visualize patterns as a spiral via `.spiral()`: - + This is especially nice because strudel is not only the name of a dessert but also the german word for vortex! The spiral is very fitting to visualize cycles because you can align cycles vertically, while surfing along an infinite twisted timeline. @@ -82,49 +82,49 @@ A big thanks to all the contributors! ## What's Changed -- fix period key for dvorak + remove duplicated code by @felixroos in https://github.com/tidalcycles/strudel/pull/537 -- improve initial loading + wait before eval by @felixroos in https://github.com/tidalcycles/strudel/pull/538 -- do not reset cps before eval #517 by @felixroos in https://github.com/tidalcycles/strudel/pull/539 -- feat: add loader bar to animate loading state by @felixroos in https://github.com/tidalcycles/strudel/pull/542 -- add firacode font by @felixroos in https://github.com/tidalcycles/strudel/pull/544 -- fix: allow whitespace at the end of a mini pattern by @felixroos in https://github.com/tidalcycles/strudel/pull/547 -- fix: reset time on stop by @felixroos in https://github.com/tidalcycles/strudel/pull/548 -- fix: load soundfonts in prebake by @felixroos in https://github.com/tidalcycles/strudel/pull/550 -- fix: colorable highlighting by @felixroos in https://github.com/tidalcycles/strudel/pull/553 -- fix: make soundfonts import dynamic by @felixroos in https://github.com/tidalcycles/strudel/pull/556 -- add basic triads and guidetone voicings by @felixroos in https://github.com/tidalcycles/strudel/pull/557 -- Patchday by @felixroos in https://github.com/tidalcycles/strudel/pull/559 -- Vanilla JS Refactoring by @felixroos in https://github.com/tidalcycles/strudel/pull/563 -- repl: add option to display line numbers by @roipoussiere in https://github.com/tidalcycles/strudel/pull/582 -- learn/tonal: fix typo in "scaleTran[s]pose" by @srenatus in https://github.com/tidalcycles/strudel/pull/585 -- Music metadata by @roipoussiere in https://github.com/tidalcycles/strudel/pull/580 -- New Workshop by @felixroos in https://github.com/tidalcycles/strudel/pull/587 -- Fix option dot by @felixroos in https://github.com/tidalcycles/strudel/pull/596 -- fix: allow f for flat notes like tidal by @felixroos in https://github.com/tidalcycles/strudel/pull/593 -- fix: division by zero by @felixroos in https://github.com/tidalcycles/strudel/pull/591 -- Solmization added by @dariacotocu in https://github.com/tidalcycles/strudel/pull/570 -- improve cursor by @felixroos in https://github.com/tidalcycles/strudel/pull/597 -- enable auto-completion by @roipoussiere in https://github.com/tidalcycles/strudel/pull/588 -- add ratio function by @felixroos in https://github.com/tidalcycles/strudel/pull/602 -- editor: enable line wrapping by @roipoussiere in https://github.com/tidalcycles/strudel/pull/581 -- tonal fixes by @felixroos in https://github.com/tidalcycles/strudel/pull/607 -- fix: flatten scale lists by @felixroos in https://github.com/tidalcycles/strudel/pull/605 -- clip now works like legato in tidal by @felixroos in https://github.com/tidalcycles/strudel/pull/598 -- fix: doc links by @felixroos in https://github.com/tidalcycles/strudel/pull/612 -- tauri desktop app by @vasilymilovidov in https://github.com/tidalcycles/strudel/pull/613 -- add spiral viz by @felixroos in https://github.com/tidalcycles/strudel/pull/614 -- patterning ui settings by @felixroos in https://github.com/tidalcycles/strudel/pull/606 -- Fix typo on packages.mdx by @paikwiki in https://github.com/tidalcycles/strudel/pull/520 -- cps dependent functions by @felixroos in https://github.com/tidalcycles/strudel/pull/620 -- desktop: play samples from disk by @felixroos in https://github.com/tidalcycles/strudel/pull/621 -- fix: midi clock drift by @felixroos in https://github.com/tidalcycles/strudel/pull/627 +- fix period key for dvorak + remove duplicated code by @felixroos in https://codeberg.org/uzu/strudel/pulls/537 +- improve initial loading + wait before eval by @felixroos in https://codeberg.org/uzu/strudel/pulls/538 +- do not reset cps before eval #517 by @felixroos in https://codeberg.org/uzu/strudel/pulls/539 +- feat: add loader bar to animate loading state by @felixroos in https://codeberg.org/uzu/strudel/pulls/542 +- add firacode font by @felixroos in https://codeberg.org/uzu/strudel/pulls/544 +- fix: allow whitespace at the end of a mini pattern by @felixroos in https://codeberg.org/uzu/strudel/pulls/547 +- fix: reset time on stop by @felixroos in https://codeberg.org/uzu/strudel/pulls/548 +- fix: load soundfonts in prebake by @felixroos in https://codeberg.org/uzu/strudel/pulls/550 +- fix: colorable highlighting by @felixroos in https://codeberg.org/uzu/strudel/pulls/553 +- fix: make soundfonts import dynamic by @felixroos in https://codeberg.org/uzu/strudel/pulls/556 +- add basic triads and guidetone voicings by @felixroos in https://codeberg.org/uzu/strudel/pulls/557 +- Patchday by @felixroos in https://codeberg.org/uzu/strudel/pulls/559 +- Vanilla JS Refactoring by @felixroos in https://codeberg.org/uzu/strudel/pulls/563 +- repl: add option to display line numbers by @roipoussiere in https://codeberg.org/uzu/strudel/pulls/582 +- learn/tonal: fix typo in "scaleTran[s]pose" by @srenatus in https://codeberg.org/uzu/strudel/pulls/585 +- Music metadata by @roipoussiere in https://codeberg.org/uzu/strudel/pulls/580 +- New Workshop by @felixroos in https://codeberg.org/uzu/strudel/pulls/587 +- Fix option dot by @felixroos in https://codeberg.org/uzu/strudel/pulls/596 +- fix: allow f for flat notes like tidal by @felixroos in https://codeberg.org/uzu/strudel/pulls/593 +- fix: division by zero by @felixroos in https://codeberg.org/uzu/strudel/pulls/591 +- Solmization added by @dariacotocu in https://codeberg.org/uzu/strudel/pulls/570 +- improve cursor by @felixroos in https://codeberg.org/uzu/strudel/pulls/597 +- enable auto-completion by @roipoussiere in https://codeberg.org/uzu/strudel/pulls/588 +- add ratio function by @felixroos in https://codeberg.org/uzu/strudel/pulls/602 +- editor: enable line wrapping by @roipoussiere in https://codeberg.org/uzu/strudel/pulls/581 +- tonal fixes by @felixroos in https://codeberg.org/uzu/strudel/pulls/607 +- fix: flatten scale lists by @felixroos in https://codeberg.org/uzu/strudel/pulls/605 +- clip now works like legato in tidal by @felixroos in https://codeberg.org/uzu/strudel/pulls/598 +- fix: doc links by @felixroos in https://codeberg.org/uzu/strudel/pulls/612 +- tauri desktop app by @vasilymilovidov in https://codeberg.org/uzu/strudel/pulls/613 +- add spiral viz by @felixroos in https://codeberg.org/uzu/strudel/pulls/614 +- patterning ui settings by @felixroos in https://codeberg.org/uzu/strudel/pulls/606 +- Fix typo on packages.mdx by @paikwiki in https://codeberg.org/uzu/strudel/pulls/520 +- cps dependent functions by @felixroos in https://codeberg.org/uzu/strudel/pulls/620 +- desktop: play samples from disk by @felixroos in https://codeberg.org/uzu/strudel/pulls/621 +- fix: midi clock drift by @felixroos in https://codeberg.org/uzu/strudel/pulls/627 ## New Contributors -- @roipoussiere made their first contribution in https://github.com/tidalcycles/strudel/pull/582 -- @srenatus made their first contribution in https://github.com/tidalcycles/strudel/pull/585 -- @dariacotocu made their first contribution in https://github.com/tidalcycles/strudel/pull/570 -- @vasilymilovidov made their first contribution in https://github.com/tidalcycles/strudel/pull/613 -- @paikwiki made their first contribution in https://github.com/tidalcycles/strudel/pull/520 +- @roipoussiere made their first contribution in https://codeberg.org/uzu/strudel/pulls/582 +- @srenatus made their first contribution in https://codeberg.org/uzu/strudel/pulls/585 +- @dariacotocu made their first contribution in https://codeberg.org/uzu/strudel/pulls/570 +- @vasilymilovidov made their first contribution in https://codeberg.org/uzu/strudel/pulls/613 +- @paikwiki made their first contribution in https://codeberg.org/uzu/strudel/pulls/520 -**Full Changelog**: https://github.com/tidalcycles/strudel/compare/v0.7.0...v0.8.0 +**Full Changelog**: https://codeberg.org/uzu/strudel/compare/v0.7.0...v0.8.0 diff --git a/website/src/content/blog/release-0.9.0-bananenbrot.mdx b/website/src/content/blog/release-0.9.0-bananenbrot.mdx index ae77b4247..eb6894f69 100644 --- a/website/src/content/blog/release-0.9.0-bananenbrot.mdx +++ b/website/src/content/blog/release-0.9.0-bananenbrot.mdx @@ -6,8 +6,6 @@ tags: ['meta'] author: froos --- -import BlogVideo from '../../components/BlogVideo.astro'; - These are the release notes for Strudel 0.9.0 aka "Bananenbrot"! The last release was over 11 weeks ago, so a lot of things have happened! @@ -25,18 +23,20 @@ Main new features include: - [vibrato](https://strudel.tidalcycles.org/learn/synths#vibrato) - an integration of [ZZFX](https://strudel.tidalcycles.org/learn/synths#zzfx) - +import { Youtube } from '@src/components/Youtube'; + + Related PRs: -- superdough: encapsulates web audio output by @felixroos in https://github.com/tidalcycles/strudel/pull/664 -- basic fm by @felixroos in https://github.com/tidalcycles/strudel/pull/669 -- Wave Selection and Global Envelope on the FM Synth Modulator by @Bubobubobubobubo in https://github.com/tidalcycles/strudel/pull/683 -- control osc partial count with n by @felixroos in https://github.com/tidalcycles/strudel/pull/674 -- ZZFX Synth support by @Bubobubobubobubo in https://github.com/tidalcycles/strudel/pull/684 -- Adding filter envelopes and filter order selection by @Bubobubobubobubo in https://github.com/tidalcycles/strudel/pull/692 -- Adding loop points and thus wavetable synthesis by @Bubobubobubobubo in https://github.com/tidalcycles/strudel/pull/698 -- Adding vibrato to base oscillators by @Bubobubobubobubo in https://github.com/tidalcycles/strudel/pull/693 +- superdough: encapsulates web audio output by @felixroos in https://codeberg.org/uzu/strudel/pulls/664 +- basic fm by @felixroos in https://codeberg.org/uzu/strudel/pulls/669 +- Wave Selection and Global Envelope on the FM Synth Modulator by @Bubobubobubobubo in https://codeberg.org/uzu/strudel/pulls/683 +- control osc partial count with n by @felixroos in https://codeberg.org/uzu/strudel/pulls/674 +- ZZFX Synth support by @Bubobubobubobubo in https://codeberg.org/uzu/strudel/pulls/684 +- Adding filter envelopes and filter order selection by @Bubobubobubobubo in https://codeberg.org/uzu/strudel/pulls/692 +- Adding loop points and thus wavetable synthesis by @Bubobubobubobubo in https://codeberg.org/uzu/strudel/pulls/698 +- Adding vibrato to base oscillators by @Bubobubobubobubo in https://codeberg.org/uzu/strudel/pulls/693 ## Desktop App Improvements @@ -45,70 +45,70 @@ which do not depend on browser APIs! You can see superdough, superdirt via OSC + hardware synths via MIDI all together playing in harmony in this [awesome video](https://www.youtube.com/watch?v=lxQgBeLQBgk). These are the related PRs: -- Create Midi Integration for Tauri Desktop app by @daslyfe in https://github.com/tidalcycles/strudel/pull/685 -- add sleep timer + improve message iterating by @daslyfe in https://github.com/tidalcycles/strudel/pull/688 -- fix MIDI CC messages by @vasilymilovidov in https://github.com/tidalcycles/strudel/pull/690 -- Direct OSC Support in Tauri by @daslyfe in https://github.com/tidalcycles/strudel/pull/694 -- Add logging from tauri by @daslyfe in https://github.com/tidalcycles/strudel/pull/697 -- fix osc bundle timestamp glitches caused by drifting clock by @daslyfe in https://github.com/tidalcycles/strudel/pull/666 -- Midi time fixes by @daslyfe in https://github.com/tidalcycles/strudel/pull/668 -- [Bug Fix] Account for numeral notation when converting to midi by @daslyfe in https://github.com/tidalcycles/strudel/pull/656 -- [Bug Fix] Midi: Don't treat note 0 as false by @daslyfe in https://github.com/tidalcycles/strudel/pull/657 +- Create Midi Integration for Tauri Desktop app by @daslyfe in https://codeberg.org/uzu/strudel/pulls/685 +- add sleep timer + improve message iterating by @daslyfe in https://codeberg.org/uzu/strudel/pulls/688 +- fix MIDI CC messages by @vasilymilovidov in https://codeberg.org/uzu/strudel/pulls/690 +- Direct OSC Support in Tauri by @daslyfe in https://codeberg.org/uzu/strudel/pulls/694 +- Add logging from tauri by @daslyfe in https://codeberg.org/uzu/strudel/pulls/697 +- fix osc bundle timestamp glitches caused by drifting clock by @daslyfe in https://codeberg.org/uzu/strudel/pulls/666 +- Midi time fixes by @daslyfe in https://codeberg.org/uzu/strudel/pulls/668 +- [Bug Fix] Account for numeral notation when converting to midi by @daslyfe in https://codeberg.org/uzu/strudel/pulls/656 +- [Bug Fix] Midi: Don't treat note 0 as false by @daslyfe in https://codeberg.org/uzu/strudel/pulls/657 ## Visuals -- 2 new FFT based vizualisations have now landed: [scope and fscope](https://github.com/tidalcycles/strudel/pull/677) (featured in the video at the top). -- pianoroll has new options, see [PR](https://github.com/tidalcycles/strudel/pull/679) +- 2 new FFT based vizualisations have now landed: [scope and fscope](https://codeberg.org/uzu/strudel/pulls/677) (featured in the video at the top). +- pianoroll has new options, see [PR](https://codeberg.org/uzu/strudel/pulls/679) Related PRs: -- Scope by @felixroos in https://github.com/tidalcycles/strudel/pull/677 ([demo](https://strudel.tidalcycles.org/?hXVQF-KxMI8p)) -- Pianoroll improvements by @felixroos in https://github.com/tidalcycles/strudel/pull/679 ([demo](https://strudel.tidalcycles.org/?aPMKqXGVMgSM)) +- Scope by @felixroos in https://codeberg.org/uzu/strudel/pulls/677 ([demo](https://strudel.tidalcycles.org/?hXVQF-KxMI8p)) +- Pianoroll improvements by @felixroos in https://codeberg.org/uzu/strudel/pulls/679 ([demo](https://strudel.tidalcycles.org/?aPMKqXGVMgSM)) ## Voicings There is now a new way to play chord voicings + a huge selection of chord voicings available. Find out more in these PRs: -- stateless voicings + tonleiter lib by @felixroos in https://github.com/tidalcycles/strudel/pull/647 ([demo](https://strudel.tidalcycles.org/?FoILM0Hs9y9f)) -- ireal voicings by @felixroos in https://github.com/tidalcycles/strudel/pull/653 ([demo](https://strudel.tidalcycles.org/?bv_TjY9hOC28)) +- stateless voicings + tonleiter lib by @felixroos in https://codeberg.org/uzu/strudel/pulls/647 ([demo](https://strudel.tidalcycles.org/?FoILM0Hs9y9f)) +- ireal voicings by @felixroos in https://codeberg.org/uzu/strudel/pulls/653 ([demo](https://strudel.tidalcycles.org/?bv_TjY9hOC28)) ## Adaptive Highlighting Thanks to @mindofmatthew , the highlighting will adapt to edits instantly! Related PRs: -- More work on highlight IDs by @mindofmatthew in https://github.com/tidalcycles/strudel/pull/636 -- Adaptive Highlighting by @felixroos in https://github.com/tidalcycles/strudel/pull/634 +- More work on highlight IDs by @mindofmatthew in https://codeberg.org/uzu/strudel/pulls/636 +- Adaptive Highlighting by @felixroos in https://codeberg.org/uzu/strudel/pulls/634 ## UI Changes -- teletext theme + fonts by @felixroos in https://github.com/tidalcycles/strudel/pull/681 (featured in video at the top) -- togglable panel position by @felixroos in https://github.com/tidalcycles/strudel/pull/667 +- teletext theme + fonts by @felixroos in https://codeberg.org/uzu/strudel/pulls/681 (featured in video at the top) +- togglable panel position by @felixroos in https://codeberg.org/uzu/strudel/pulls/667 ## Other New Features -- slice: list mode by @felixroos in https://github.com/tidalcycles/strudel/pull/645 ([demo](https://strudel.tidalcycles.org/?bAYIqz5NLjRr)) -- add emoji support by @felixroos in https://github.com/tidalcycles/strudel/pull/680 ([demo](https://strudel.tidalcycles.org/?a6FgLz475gN9)) +- slice: list mode by @felixroos in https://codeberg.org/uzu/strudel/pulls/645 ([demo](https://strudel.tidalcycles.org/?bAYIqz5NLjRr)) +- add emoji support by @felixroos in https://codeberg.org/uzu/strudel/pulls/680 ([demo](https://strudel.tidalcycles.org/?a6FgLz475gN9)) ## Articles -- Understand pitch by @felixroos in https://github.com/tidalcycles/strudel/pull/652 +- Understand pitch by @felixroos in https://codeberg.org/uzu/strudel/pulls/652 ## Other Fixes & Enhancements -- fix: out of range error by @felixroos in https://github.com/tidalcycles/strudel/pull/630 -- fix: update canvas size on window resize by @felixroos in https://github.com/tidalcycles/strudel/pull/631 -- FIXES: TODO in rotateChroma by @bwagner in https://github.com/tidalcycles/strudel/pull/650 -- snapshot tests: sort haps by part by @felixroos in https://github.com/tidalcycles/strudel/pull/637 -- Delete old packages by @felixroos in https://github.com/tidalcycles/strudel/pull/639 -- update vitest by @felixroos in https://github.com/tidalcycles/strudel/pull/651 -- fix: welcome message for latestCode by @felixroos in https://github.com/tidalcycles/strudel/pull/659 -- fix: always run previous trigger by @felixroos in https://github.com/tidalcycles/strudel/pull/660 +- fix: out of range error by @felixroos in https://codeberg.org/uzu/strudel/pulls/630 +- fix: update canvas size on window resize by @felixroos in https://codeberg.org/uzu/strudel/pulls/631 +- FIXES: TODO in rotateChroma by @bwagner in https://codeberg.org/uzu/strudel/pulls/650 +- snapshot tests: sort haps by part by @felixroos in https://codeberg.org/uzu/strudel/pulls/637 +- Delete old packages by @felixroos in https://codeberg.org/uzu/strudel/pulls/639 +- update vitest by @felixroos in https://codeberg.org/uzu/strudel/pulls/651 +- fix: welcome message for latestCode by @felixroos in https://codeberg.org/uzu/strudel/pulls/659 +- fix: always run previous trigger by @felixroos in https://codeberg.org/uzu/strudel/pulls/660 ## New Contributors -- @daslyfe made their first contribution in https://github.com/tidalcycles/strudel/pull/656 -- @Bubobubobubobubo made their first contribution in https://github.com/tidalcycles/strudel/pull/683 +- @daslyfe made their first contribution in https://codeberg.org/uzu/strudel/pulls/656 +- @Bubobubobubobubo made their first contribution in https://codeberg.org/uzu/strudel/pulls/683 -**Full Changelog**: https://github.com/tidalcycles/strudel/compare/v0.8.0...v0.9.0 +**Full Changelog**: https://codeberg.org/uzu/strudel/compare/v0.8.0...v0.9.0 A big thanks to all the contributors! diff --git a/website/src/content/blog/release-1.0.0-geburtstagskuchen.mdx b/website/src/content/blog/release-1.0.0-geburtstagskuchen.mdx index 5784fe52d..bfbf24a20 100644 --- a/website/src/content/blog/release-1.0.0-geburtstagskuchen.mdx +++ b/website/src/content/blog/release-1.0.0-geburtstagskuchen.mdx @@ -6,8 +6,6 @@ tags: ['meta'] author: froos --- -import BlogVideo from '../../components/BlogVideo.astro'; - These are the release notes for Strudel 1.0.0 aka "Geburtstagskuchen" This release marks the 2 year anniversary of the project, the first commit was on the 22nd January 2022 by Alex McLean. @@ -27,13 +25,13 @@ Let me write up some of the highlights: This version changes the default cps value from 1 to 0.5 to give patterns a little bit more time by default. If you find your existing patterns to be suddenly half the speed, just add a `setcps(1)` to the top and it should sound as it did before! -- make 0.5hz cps the default by @yaxu in https://github.com/tidalcycles/strudel/pull/931 +- make 0.5hz cps the default by @yaxu in https://codeberg.org/uzu/strudel/pulls/931 ## New Domain Strudel is now available under [strudel.cc](https://strudel.cc/). The old domain still works but you might not get the most recent version. -- replace strudel.tidalcycles.org with strudel.cc by @felixroos in https://github.com/tidalcycles/strudel/pull/768 +- replace strudel.tidalcycles.org with strudel.cc by @felixroos in https://codeberg.org/uzu/strudel/pulls/768 ## Strudel on Mastodon @@ -43,52 +41,52 @@ Strudel now has a mastodon presence: https://social.toplap.org/@strudel superdough, the audio engine of strudel has gotten some new features: -- Create phaser effect by @daslyfe in https://github.com/tidalcycles/strudel/pull/798 -- Multichannel audio by @daslyfe in https://github.com/tidalcycles/strudel/pull/820 -- Audio device selection by @daslyfe in https://github.com/tidalcycles/strudel/pull/854 -- Better convolution reverb by generating impulse responses by @Bubobubobubobubo and @felixroos in https://github.com/tidalcycles/strudel/pull/718 -- Add 'white', 'pink' and 'brown' oscillators + refactor synth by @Bubobubobubobubo and @felixroos in https://github.com/tidalcycles/strudel/pull/713 -- New noise type: "crackle" by @Bubobubobubobubo in https://github.com/tidalcycles/strudel/pull/806 -- Add support for using samples as impulse response buffers for the reverb by @vasilymilovidov in https://github.com/tidalcycles/strudel/pull/717 -- Compressor by @felixroos in https://github.com/tidalcycles/strudel/pull/729 -- Adding vibrato to Superdough sampler by @Bubobubobubobubo and @felixroos in https://github.com/tidalcycles/strudel/pull/706 -- Further Envelope improvements by @daslyfe in https://github.com/tidalcycles/strudel/pull/868 -- Add more vowel qualities for the vowels function by @fnordomat in https://github.com/tidalcycles/strudel/pull/907 -- pitch envelope by @felixroos in https://github.com/tidalcycles/strudel/pull/913 +- Create phaser effect by @daslyfe in https://codeberg.org/uzu/strudel/pulls/798 +- Multichannel audio by @daslyfe in https://codeberg.org/uzu/strudel/pulls/820 +- Audio device selection by @daslyfe in https://codeberg.org/uzu/strudel/pulls/854 +- Better convolution reverb by generating impulse responses by @Bubobubobubobubo and @felixroos in https://codeberg.org/uzu/strudel/pulls/718 +- Add 'white', 'pink' and 'brown' oscillators + refactor synth by @Bubobubobubobubo and @felixroos in https://codeberg.org/uzu/strudel/pulls/713 +- New noise type: "crackle" by @Bubobubobubobubo in https://codeberg.org/uzu/strudel/pulls/806 +- Add support for using samples as impulse response buffers for the reverb by @vasilymilovidov in https://codeberg.org/uzu/strudel/pulls/717 +- Compressor by @felixroos in https://codeberg.org/uzu/strudel/pulls/729 +- Adding vibrato to Superdough sampler by @Bubobubobubobubo and @felixroos in https://codeberg.org/uzu/strudel/pulls/706 +- Further Envelope improvements by @daslyfe in https://codeberg.org/uzu/strudel/pulls/868 +- Add more vowel qualities for the vowels function by @fnordomat in https://codeberg.org/uzu/strudel/pulls/907 +- pitch envelope by @felixroos in https://codeberg.org/uzu/strudel/pulls/913 ## Slider Controls The new `slider` function inlines a draggable slider element into the code, bridging the gap between code and GUI. -- widgets by @felixroos in https://github.com/tidalcycles/strudel/pull/714 -- Slider afterthoughts by @felixroos in https://github.com/tidalcycles/strudel/pull/723 -- add xfade by @felixroos in https://github.com/tidalcycles/strudel/pull/780 +- widgets by @felixroos in https://codeberg.org/uzu/strudel/pulls/714 +- Slider afterthoughts by @felixroos in https://codeberg.org/uzu/strudel/pulls/723 +- add xfade by @felixroos in https://codeberg.org/uzu/strudel/pulls/780 ## Improved MIDI integration Pattern params [can now be controlled with cc messages](https://www.youtube.com/watch?v=e2-Sv_jjDQk) + you can now send a MIDI clock to sync your DAW with strudel. -- Midi in by @felixroos in https://github.com/tidalcycles/strudel/pull/699 -- add midi clock support by @felixroos in https://github.com/tidalcycles/strudel/pull/710 +- Midi in by @felixroos in https://codeberg.org/uzu/strudel/pulls/699 +- add midi clock support by @felixroos in https://codeberg.org/uzu/strudel/pulls/710 ## hydra [hydra](https://hydra.ojack.xyz), the live coding video synth [can now be used directly inside the strudel REPL](https://strudel.cc/learn/hydra/). -- Hydra integration by @felixroos in https://github.com/tidalcycles/strudel/pull/759 -- add options param to initHydra by @kasparsj in https://github.com/tidalcycles/strudel/pull/808 -- Hydra fixes and improvements by @atfornes in https://github.com/tidalcycles/strudel/pull/818 +- Hydra integration by @felixroos in https://codeberg.org/uzu/strudel/pulls/759 +- add options param to initHydra by @kasparsj in https://codeberg.org/uzu/strudel/pulls/808 +- Hydra fixes and improvements by @atfornes in https://codeberg.org/uzu/strudel/pulls/818 ## Vanilla REPL The codemirror editor and the repl abstraction have been refactored from react to vanilla JS! This should give some performance improvements and less dependency / maintenance burden: -- Vanilla repl 2 by @felixroos in https://github.com/tidalcycles/strudel/pull/863 -- Vanilla repl 3 by @felixroos in https://github.com/tidalcycles/strudel/pull/865 -- more work on vanilla repl: repl web component + package + MicroRepl by @felixroos in https://github.com/tidalcycles/strudel/pull/866 -- main repl vanillification by @felixroos in https://github.com/tidalcycles/strudel/pull/873 -- final vanillification by @felixroos in https://github.com/tidalcycles/strudel/pull/876 +- Vanilla repl 2 by @felixroos in https://codeberg.org/uzu/strudel/pulls/863 +- Vanilla repl 3 by @felixroos in https://codeberg.org/uzu/strudel/pulls/865 +- more work on vanilla repl: repl web component + package + MicroRepl by @felixroos in https://codeberg.org/uzu/strudel/pulls/866 +- main repl vanillification by @felixroos in https://codeberg.org/uzu/strudel/pulls/873 +- final vanillification by @felixroos in https://codeberg.org/uzu/strudel/pulls/876 ## Doc Changes @@ -97,25 +95,25 @@ Plenty of things have been added to the docs, including a [showcase of what peop
show PRs -- Showcase by @felixroos in https://github.com/tidalcycles/strudel/pull/885 -- Recipes by @felixroos in https://github.com/tidalcycles/strudel/pull/742 -- Document striate function by @ilesinge in https://github.com/tidalcycles/strudel/pull/766 -- Document adsr function by @ilesinge in https://github.com/tidalcycles/strudel/pull/767 -- Add function params in reference tab by @ilesinge in https://github.com/tidalcycles/strudel/pull/785 -- Update first-sounds.mdx by @bwagner in https://github.com/tidalcycles/strudel/pull/794 -- Update recap.mdx by @bwagner in https://github.com/tidalcycles/strudel/pull/797 -- Update pattern-effects.mdx by @bwagner in https://github.com/tidalcycles/strudel/pull/796 -- Update first-effects.mdx by @bwagner in https://github.com/tidalcycles/strudel/pull/795 -- Document pianoroll by @ilesinge in https://github.com/tidalcycles/strudel/pull/784 -- Add doc for euclidLegatoRot, wordfall and slider by @ilesinge in https://github.com/tidalcycles/strudel/pull/801 -- Improve documentation for synonym functions by @ilesinge in https://github.com/tidalcycles/strudel/pull/800 -- Add and style algolia search by @ilesinge in https://github.com/tidalcycles/strudel/pull/827 -- Fix a typo by @drewgbarnes in https://github.com/tidalcycles/strudel/pull/830 -- add mastodon link by @felixroos in https://github.com/tidalcycles/strudel/pull/884 -- adds a blog by @felixroos in https://github.com/tidalcycles/strudel/pull/911 -- community bakery by @felixroos in https://github.com/tidalcycles/strudel/pull/923 -- Blog improvements by @felixroos in https://github.com/tidalcycles/strudel/pull/919 -- 2 years blog post by @felixroos in https://github.com/tidalcycles/strudel/pull/929 +- Showcase by @felixroos in https://codeberg.org/uzu/strudel/pulls/885 +- Recipes by @felixroos in https://codeberg.org/uzu/strudel/pulls/742 +- Document striate function by @ilesinge in https://codeberg.org/uzu/strudel/pulls/766 +- Document adsr function by @ilesinge in https://codeberg.org/uzu/strudel/pulls/767 +- Add function params in reference tab by @ilesinge in https://codeberg.org/uzu/strudel/pulls/785 +- Update first-sounds.mdx by @bwagner in https://codeberg.org/uzu/strudel/pulls/794 +- Update recap.mdx by @bwagner in https://codeberg.org/uzu/strudel/pulls/797 +- Update pattern-effects.mdx by @bwagner in https://codeberg.org/uzu/strudel/pulls/796 +- Update first-effects.mdx by @bwagner in https://codeberg.org/uzu/strudel/pulls/795 +- Document pianoroll by @ilesinge in https://codeberg.org/uzu/strudel/pulls/784 +- Add doc for euclidLegatoRot, wordfall and slider by @ilesinge in https://codeberg.org/uzu/strudel/pulls/801 +- Improve documentation for synonym functions by @ilesinge in https://codeberg.org/uzu/strudel/pulls/800 +- Add and style algolia search by @ilesinge in https://codeberg.org/uzu/strudel/pulls/827 +- Fix a typo by @drewgbarnes in https://codeberg.org/uzu/strudel/pulls/830 +- add mastodon link by @felixroos in https://codeberg.org/uzu/strudel/pulls/884 +- adds a blog by @felixroos in https://codeberg.org/uzu/strudel/pulls/911 +- community bakery by @felixroos in https://codeberg.org/uzu/strudel/pulls/923 +- Blog improvements by @felixroos in https://codeberg.org/uzu/strudel/pulls/919 +- 2 years blog post by @felixroos in https://codeberg.org/uzu/strudel/pulls/929
@@ -124,34 +122,34 @@ Plenty of things have been added to the docs, including a [showcase of what peop
There is a lot more -- mini notation: international alphabets support by @ilesinge in https://github.com/tidalcycles/strudel/pull/751 -- Add shabda shortcut by @ilesinge in https://github.com/tidalcycles/strudel/pull/740 -- add play function by @felixroos in https://github.com/tidalcycles/strudel/pull/758 (superseded by next) -- tidal style d1 ... d9 functions + more by @felixroos in https://github.com/tidalcycles/strudel/pull/805 -- add vscode bindings by @Dsm0 in https://github.com/tidalcycles/strudel/pull/773 -- Implement optional hover tooltip with function documentation by @ilesinge in https://github.com/tidalcycles/strudel/pull/783 -- samples loading shortcuts: by @felixroos in https://github.com/tidalcycles/strudel/pull/788 -- add option to disable active line highlighting in Code Settings by @kasparsj in https://github.com/tidalcycles/strudel/pull/804 -- Color hsl by @felixroos in https://github.com/tidalcycles/strudel/pull/815 -- Patterns tab + Refactor Panel by @felixroos in https://github.com/tidalcycles/strudel/pull/769 -- patterns tab: import patterns + style by @felixroos in https://github.com/tidalcycles/strudel/pull/852 -- Export patterns + ui tweaks by @felixroos in https://github.com/tidalcycles/strudel/pull/855 -- Pattern organization by @felixroos in https://github.com/tidalcycles/strudel/pull/858 -- Sound Import from local file system by @daslyfe in https://github.com/tidalcycles/strudel/pull/839 -- bugfix: suspend and close existing audio context when changing interface by @daslyfe in https://github.com/tidalcycles/strudel/pull/882 -- add root mode for voicings by @felixroos in https://github.com/tidalcycles/strudel/pull/887 -- scales can now be anchored by @felixroos in https://github.com/tidalcycles/strudel/pull/888 -- add dough function for raw dsp by @felixroos in https://github.com/tidalcycles/strudel/pull/707 (experimental) -- support mininotation '..' range operator, fixes #715 by @yaxu in https://github.com/tidalcycles/strudel/pull/716 -- Add pick and squeeze functions by @daslyfe in https://github.com/tidalcycles/strudel/pull/771 -- support , in < > by @felixroos in https://github.com/tidalcycles/strudel/pull/886 -- public sharing by @felixroos in https://github.com/tidalcycles/strudel/pull/910 -- pick, pickmod, inhabit, inhabitmod by @yaxu in https://github.com/tidalcycles/strudel/pull/921 -- Mini-notation additions towards tidal compatibility by @yaxu in https://github.com/tidalcycles/strudel/pull/926 -- add pickF and pickmodF by @geikha in https://github.com/tidalcycles/strudel/pull/924 -- Make splice cps-aware by @yaxu in https://github.com/tidalcycles/strudel/pull/932 -- Refactor cps functions by @felixroos in https://github.com/tidalcycles/strudel/pull/933 -- Add useful pattern selection behavior for performing. by @daslyfe in https://github.com/tidalcycles/strudel/pull/897 +- mini notation: international alphabets support by @ilesinge in https://codeberg.org/uzu/strudel/pulls/751 +- Add shabda shortcut by @ilesinge in https://codeberg.org/uzu/strudel/pulls/740 +- add play function by @felixroos in https://codeberg.org/uzu/strudel/pulls/758 (superseded by next) +- tidal style d1 ... d9 functions + more by @felixroos in https://codeberg.org/uzu/strudel/pulls/805 +- add vscode bindings by @Dsm0 in https://codeberg.org/uzu/strudel/pulls/773 +- Implement optional hover tooltip with function documentation by @ilesinge in https://codeberg.org/uzu/strudel/pulls/783 +- samples loading shortcuts: by @felixroos in https://codeberg.org/uzu/strudel/pulls/788 +- add option to disable active line highlighting in Code Settings by @kasparsj in https://codeberg.org/uzu/strudel/pulls/804 +- Color hsl by @felixroos in https://codeberg.org/uzu/strudel/pulls/815 +- Patterns tab + Refactor Panel by @felixroos in https://codeberg.org/uzu/strudel/pulls/769 +- patterns tab: import patterns + style by @felixroos in https://codeberg.org/uzu/strudel/pulls/852 +- Export patterns + ui tweaks by @felixroos in https://codeberg.org/uzu/strudel/pulls/855 +- Pattern organization by @felixroos in https://codeberg.org/uzu/strudel/pulls/858 +- Sound Import from local file system by @daslyfe in https://codeberg.org/uzu/strudel/pulls/839 +- bugfix: suspend and close existing audio context when changing interface by @daslyfe in https://codeberg.org/uzu/strudel/pulls/882 +- add root mode for voicings by @felixroos in https://codeberg.org/uzu/strudel/pulls/887 +- scales can now be anchored by @felixroos in https://codeberg.org/uzu/strudel/pulls/888 +- add dough function for raw dsp by @felixroos in https://codeberg.org/uzu/strudel/pulls/707 (experimental) +- support mininotation '..' range operator, fixes #715 by @yaxu in https://codeberg.org/uzu/strudel/pulls/716 +- Add pick and squeeze functions by @daslyfe in https://codeberg.org/uzu/strudel/pulls/771 +- support , in < > by @felixroos in https://codeberg.org/uzu/strudel/pulls/886 +- public sharing by @felixroos in https://codeberg.org/uzu/strudel/pulls/910 +- pick, pickmod, inhabit, inhabitmod by @yaxu in https://codeberg.org/uzu/strudel/pulls/921 +- Mini-notation additions towards tidal compatibility by @yaxu in https://codeberg.org/uzu/strudel/pulls/926 +- add pickF and pickmodF by @geikha in https://codeberg.org/uzu/strudel/pulls/924 +- Make splice cps-aware by @yaxu in https://codeberg.org/uzu/strudel/pulls/932 +- Refactor cps functions by @felixroos in https://codeberg.org/uzu/strudel/pulls/933 +- Add useful pattern selection behavior for performing. by @daslyfe in https://codeberg.org/uzu/strudel/pulls/897
@@ -160,79 +158,79 @@ Plenty of things have been added to the docs, including a [showcase of what peop
show -- fix: finally repair envelopes by @felixroos in https://github.com/tidalcycles/strudel/pull/861 -- fix: reverb regenerate loophole by @felixroos in https://github.com/tidalcycles/strudel/pull/726 -- fix: reverb roomsize not required by @felixroos in https://github.com/tidalcycles/strudel/pull/731 -- fix: reverb sampleRate by @felixroos in https://github.com/tidalcycles/strudel/pull/732 -- consume n with scale by @felixroos in https://github.com/tidalcycles/strudel/pull/727 -- fix: hashes in urls by @felixroos in https://github.com/tidalcycles/strudel/pull/728 -- [Bug Fix] chooseWith: prevent pattern from stopping audio when selection is >= 1 or < 0 by @daslyfe in https://github.com/tidalcycles/strudel/pull/741 -- Fix addivite synthesis phases by @felixroos in https://github.com/tidalcycles/strudel/pull/762 -- fix: scale offset by @felixroos in https://github.com/tidalcycles/strudel/pull/764 -- fix zen mode logo overlap by @felixroos in https://github.com/tidalcycles/strudel/pull/760 -- fix: share copy to clipboard + alert by @felixroos in https://github.com/tidalcycles/strudel/pull/774 -- fix: style issues by @felixroos in https://github.com/tidalcycles/strudel/pull/781 -- Fix scope pos + document by @felixroos in https://github.com/tidalcycles/strudel/pull/786 -- don't use anchor links for reference by @felixroos in https://github.com/tidalcycles/strudel/pull/791 -- remove unwanted cm6 outline for strudelTheme by @kasparsj in https://github.com/tidalcycles/strudel/pull/802 -- FIXES: palindrome abc -> abccba by @bwagner in https://github.com/tidalcycles/strudel/pull/831 -- Bug Fix #119: Clock drift by @daslyfe in https://github.com/tidalcycles/strudel/pull/874 -- bugfix: sound select indexes out of bounds by @daslyfe in https://github.com/tidalcycles/strudel/pull/871 -- Error tolerance by @felixroos in https://github.com/tidalcycles/strudel/pull/880 -- fix: make sure n is never undefined before nanFallback by @felixroos in https://github.com/tidalcycles/strudel/pull/881 -- fix: invisible selection on vim + emacs mode by @felixroos in https://github.com/tidalcycles/strudel/pull/889 -- fix: autocomplete / tooltip code example bug by @felixroos in https://github.com/tidalcycles/strudel/pull/898 -- Fix examples page, piano() and a few workshop imgs by @shiyouganai in https://github.com/tidalcycles/strudel/pull/848 -- fix: trailing slash confusion by @felixroos in https://github.com/tidalcycles/strudel/pull/743 -- fix: try different trailing slash behavior by @felixroos in https://github.com/tidalcycles/strudel/pull/744 -- Fix krill build command in README by @ilesinge in https://github.com/tidalcycles/strudel/pull/748 -- Fix for #1. Enables named instruments for csoundm. by @gogins in https://github.com/tidalcycles/strudel/pull/662 -- fix: missing hash for links starting with / by @felixroos in https://github.com/tidalcycles/strudel/pull/845 -- fix: swatch png src by @felixroos in https://github.com/tidalcycles/strudel/pull/846 -- Fix edge case with rehype-urls and trailing slashes in image file paths by @shiyouganai in https://github.com/tidalcycles/strudel/pull/849 -- fix: multiple repls by @felixroos in https://github.com/tidalcycles/strudel/pull/813 -- Fix chunk, add fastChunk and repeatCycles by @yaxu in https://github.com/tidalcycles/strudel/pull/712 -- Update tauri.yml workflow file by @vasilymilovidov in https://github.com/tidalcycles/strudel/pull/705 -- vite-vanilla-repl readme fix by @felixroos in https://github.com/tidalcycles/strudel/pull/737 -- completely revert config mess by @felixroos in https://github.com/tidalcycles/strudel/pull/745 -- hopefully fix trailing slashes bug by @felixroos in https://github.com/tidalcycles/strudel/pull/753 -- Update vite pwa by @felixroos in https://github.com/tidalcycles/strudel/pull/772 -- Update to Astro 3 by @felixroos in https://github.com/tidalcycles/strudel/pull/775 -- support multiple named serial connections, change default baudrate by @yaxu in https://github.com/tidalcycles/strudel/pull/551 -- CHANGES: github action checkout v2 -> v4 by @bwagner in https://github.com/tidalcycles/strudel/pull/837 -- CHANGES: pin pnpm to version 8.3.1 by @bwagner in https://github.com/tidalcycles/strudel/pull/834 -- CHANGES: github action pnpm version from 7 to 8.3.1 by @bwagner in https://github.com/tidalcycles/strudel/pull/835 -- ADDS: JetBrains IDE files and directories to .gitignore by @bwagner in https://github.com/tidalcycles/strudel/pull/840 -- Prevent 404 on Algolia crawls by @ilesinge in https://github.com/tidalcycles/strudel/pull/838 -- Add in fixes from my fork to slashocalypse branch by @shiyouganai in https://github.com/tidalcycles/strudel/pull/843 -- improve slashing + base href behavior by @felixroos in https://github.com/tidalcycles/strudel/pull/842 -- CHANGES: pnpm 8.1.3 to 8.11.0 by @bwagner in https://github.com/tidalcycles/strudel/pull/850 -- add missing trailing slashes by @felixroos in https://github.com/tidalcycles/strudel/pull/860 -- move all examples to separate examples folder by @felixroos in https://github.com/tidalcycles/strudel/pull/878 -- Dependency update by @felixroos in https://github.com/tidalcycles/strudel/pull/879 -- Update Vite version so hot reload works properly with newest pnpm version by @daslyfe in https://github.com/tidalcycles/strudel/pull/892 -- prevent vite from complaining about additional exports in jsx files by @daslyfe in https://github.com/tidalcycles/strudel/pull/891 -- fix some build warnings by @felixroos in https://github.com/tidalcycles/strudel/pull/902 -- Remove hideHeader for better mobile UI and consistency by @rjulian in https://github.com/tidalcycles/strudel/pull/894 -- Fix: swatch/[name].png.js static path by @oscarbyrne in https://github.com/tidalcycles/strudel/pull/916 -- rename @strudel.cycles/_ packages to @strudel/_ by @felixroos in https://github.com/tidalcycles/strudel/pull/917 -- `pick` now accepts lookup tables, with alternate cycle squeezing behaviour as new `inhabit` function by @yaxu in https://github.com/tidalcycles/strudel/pull/918 -- Revert "`pick` now accepts lookup tables, with alternate cycle squeezing behaviour as new `inhabit` function" by @yaxu in https://github.com/tidalcycles/strudel/pull/920 -- Fix pattern tab not showing patterns without created date by @daslyfe in https://github.com/tidalcycles/strudel/pull/934 +- fix: finally repair envelopes by @felixroos in https://codeberg.org/uzu/strudel/pulls/861 +- fix: reverb regenerate loophole by @felixroos in https://codeberg.org/uzu/strudel/pulls/726 +- fix: reverb roomsize not required by @felixroos in https://codeberg.org/uzu/strudel/pulls/731 +- fix: reverb sampleRate by @felixroos in https://codeberg.org/uzu/strudel/pulls/732 +- consume n with scale by @felixroos in https://codeberg.org/uzu/strudel/pulls/727 +- fix: hashes in urls by @felixroos in https://codeberg.org/uzu/strudel/pulls/728 +- [Bug Fix] chooseWith: prevent pattern from stopping audio when selection is >= 1 or < 0 by @daslyfe in https://codeberg.org/uzu/strudel/pulls/741 +- Fix addivite synthesis phases by @felixroos in https://codeberg.org/uzu/strudel/pulls/762 +- fix: scale offset by @felixroos in https://codeberg.org/uzu/strudel/pulls/764 +- fix zen mode logo overlap by @felixroos in https://codeberg.org/uzu/strudel/pulls/760 +- fix: share copy to clipboard + alert by @felixroos in https://codeberg.org/uzu/strudel/pulls/774 +- fix: style issues by @felixroos in https://codeberg.org/uzu/strudel/pulls/781 +- Fix scope pos + document by @felixroos in https://codeberg.org/uzu/strudel/pulls/786 +- don't use anchor links for reference by @felixroos in https://codeberg.org/uzu/strudel/pulls/791 +- remove unwanted cm6 outline for strudelTheme by @kasparsj in https://codeberg.org/uzu/strudel/pulls/802 +- FIXES: palindrome abc -> abccba by @bwagner in https://codeberg.org/uzu/strudel/pulls/831 +- Bug Fix #119: Clock drift by @daslyfe in https://codeberg.org/uzu/strudel/pulls/874 +- bugfix: sound select indexes out of bounds by @daslyfe in https://codeberg.org/uzu/strudel/pulls/871 +- Error tolerance by @felixroos in https://codeberg.org/uzu/strudel/pulls/880 +- fix: make sure n is never undefined before nanFallback by @felixroos in https://codeberg.org/uzu/strudel/pulls/881 +- fix: invisible selection on vim + emacs mode by @felixroos in https://codeberg.org/uzu/strudel/pulls/889 +- fix: autocomplete / tooltip code example bug by @felixroos in https://codeberg.org/uzu/strudel/pulls/898 +- Fix examples page, piano() and a few workshop imgs by @shiyouganai in https://codeberg.org/uzu/strudel/pulls/848 +- fix: trailing slash confusion by @felixroos in https://codeberg.org/uzu/strudel/pulls/743 +- fix: try different trailing slash behavior by @felixroos in https://codeberg.org/uzu/strudel/pulls/744 +- Fix krill build command in README by @ilesinge in https://codeberg.org/uzu/strudel/pulls/748 +- Fix for #1. Enables named instruments for csoundm. by @gogins in https://codeberg.org/uzu/strudel/pulls/662 +- fix: missing hash for links starting with / by @felixroos in https://codeberg.org/uzu/strudel/pulls/845 +- fix: swatch png src by @felixroos in https://codeberg.org/uzu/strudel/pulls/846 +- Fix edge case with rehype-urls and trailing slashes in image file paths by @shiyouganai in https://codeberg.org/uzu/strudel/pulls/849 +- fix: multiple repls by @felixroos in https://codeberg.org/uzu/strudel/pulls/813 +- Fix chunk, add fastChunk and repeatCycles by @yaxu in https://codeberg.org/uzu/strudel/pulls/712 +- Update tauri.yml workflow file by @vasilymilovidov in https://codeberg.org/uzu/strudel/pulls/705 +- vite-vanilla-repl readme fix by @felixroos in https://codeberg.org/uzu/strudel/pulls/737 +- completely revert config mess by @felixroos in https://codeberg.org/uzu/strudel/pulls/745 +- hopefully fix trailing slashes bug by @felixroos in https://codeberg.org/uzu/strudel/pulls/753 +- Update vite pwa by @felixroos in https://codeberg.org/uzu/strudel/pulls/772 +- Update to Astro 3 by @felixroos in https://codeberg.org/uzu/strudel/pulls/775 +- support multiple named serial connections, change default baudrate by @yaxu in https://codeberg.org/uzu/strudel/pulls/551 +- CHANGES: github action checkout v2 -> v4 by @bwagner in https://codeberg.org/uzu/strudel/pulls/837 +- CHANGES: pin pnpm to version 8.3.1 by @bwagner in https://codeberg.org/uzu/strudel/pulls/834 +- CHANGES: github action pnpm version from 7 to 8.3.1 by @bwagner in https://codeberg.org/uzu/strudel/pulls/835 +- ADDS: JetBrains IDE files and directories to .gitignore by @bwagner in https://codeberg.org/uzu/strudel/pulls/840 +- Prevent 404 on Algolia crawls by @ilesinge in https://codeberg.org/uzu/strudel/pulls/838 +- Add in fixes from my fork to slashocalypse branch by @shiyouganai in https://codeberg.org/uzu/strudel/pulls/843 +- improve slashing + base href behavior by @felixroos in https://codeberg.org/uzu/strudel/pulls/842 +- CHANGES: pnpm 8.1.3 to 8.11.0 by @bwagner in https://codeberg.org/uzu/strudel/pulls/850 +- add missing trailing slashes by @felixroos in https://codeberg.org/uzu/strudel/pulls/860 +- move all examples to separate examples folder by @felixroos in https://codeberg.org/uzu/strudel/pulls/878 +- Dependency update by @felixroos in https://codeberg.org/uzu/strudel/pulls/879 +- Update Vite version so hot reload works properly with newest pnpm version by @daslyfe in https://codeberg.org/uzu/strudel/pulls/892 +- prevent vite from complaining about additional exports in jsx files by @daslyfe in https://codeberg.org/uzu/strudel/pulls/891 +- fix some build warnings by @felixroos in https://codeberg.org/uzu/strudel/pulls/902 +- Remove hideHeader for better mobile UI and consistency by @rjulian in https://codeberg.org/uzu/strudel/pulls/894 +- Fix: swatch/[name].png.js static path by @oscarbyrne in https://codeberg.org/uzu/strudel/pulls/916 +- rename @strudel.cycles/_ packages to @strudel/_ by @felixroos in https://codeberg.org/uzu/strudel/pulls/917 +- `pick` now accepts lookup tables, with alternate cycle squeezing behaviour as new `inhabit` function by @yaxu in https://codeberg.org/uzu/strudel/pulls/918 +- Revert "`pick` now accepts lookup tables, with alternate cycle squeezing behaviour as new `inhabit` function" by @yaxu in https://codeberg.org/uzu/strudel/pulls/920 +- Fix pattern tab not showing patterns without created date by @daslyfe in https://codeberg.org/uzu/strudel/pulls/934
## New Contributors -- @ilesinge made their first contribution in https://github.com/tidalcycles/strudel/pull/748 -- @Dsm0 made their first contribution in https://github.com/tidalcycles/strudel/pull/773 -- @kasparsj made their first contribution in https://github.com/tidalcycles/strudel/pull/802 -- @atfornes made their first contribution in https://github.com/tidalcycles/strudel/pull/818 -- @drewgbarnes made their first contribution in https://github.com/tidalcycles/strudel/pull/830 -- @shiyouganai made their first contribution in https://github.com/tidalcycles/strudel/pull/843 -- @rjulian made their first contribution in https://github.com/tidalcycles/strudel/pull/894 -- @fnordomat made their first contribution in https://github.com/tidalcycles/strudel/pull/907 -- @oscarbyrne made their first contribution in https://github.com/tidalcycles/strudel/pull/916 -- @geikha made their first contribution in https://github.com/tidalcycles/strudel/pull/924 +- @ilesinge made their first contribution in https://codeberg.org/uzu/strudel/pulls/748 +- @Dsm0 made their first contribution in https://codeberg.org/uzu/strudel/pulls/773 +- @kasparsj made their first contribution in https://codeberg.org/uzu/strudel/pulls/802 +- @atfornes made their first contribution in https://codeberg.org/uzu/strudel/pulls/818 +- @drewgbarnes made their first contribution in https://codeberg.org/uzu/strudel/pulls/830 +- @shiyouganai made their first contribution in https://codeberg.org/uzu/strudel/pulls/843 +- @rjulian made their first contribution in https://codeberg.org/uzu/strudel/pulls/894 +- @fnordomat made their first contribution in https://codeberg.org/uzu/strudel/pulls/907 +- @oscarbyrne made their first contribution in https://codeberg.org/uzu/strudel/pulls/916 +- @geikha made their first contribution in https://codeberg.org/uzu/strudel/pulls/924 -**Full Changelog**: https://github.com/tidalcycles/strudel/compare/v0.9.0...v1.0.0 +**Full Changelog**: https://codeberg.org/uzu/strudel/compare/v0.9.0...v1.0.0 diff --git a/website/src/content/blog/release-1.1.0-bananensplit.mdx b/website/src/content/blog/release-1.1.0-bananensplit.mdx new file mode 100644 index 000000000..dcc547682 --- /dev/null +++ b/website/src/content/blog/release-1.1.0-bananensplit.mdx @@ -0,0 +1,317 @@ +--- +title: 'Release Notes v1.1.0' +description: '' +date: '2024-06-02' +tags: ['meta'] +author: froos +--- + +These are the release notes for Strudel 1.1.0 aka "Bananensplit". + +The last release was over 19 weeks ago, so a lot of things have happened! + +First, here's a little demo, teasing some of the new features: + +import { Youtube } from '@src/components/Youtube'; + + + +Let's write up some of the highlights: + +## New DSP Features + +### Stereo Supersaw + +with spread, unison, and detune parameters + +```plaintext +note("d f a a# a d3").fast(2) +.s("supersaw").spread(".8").detune(.3).unison("2 7") +``` + +### Analog "ladder" filter type + +works great for acid basslines and vibey tones + +```plaintext +note("{d d d a a# d3 f4}%16".sub(12)).gain(1).s("sawtooth") +.lpf(200).lpenv(slider(1.36,0,8)).lpq(7).distort("1.5:.7")` +.ftype('ladder') +``` + +### stereo distortion effect + +```plaintext +note("{g g a# g g4}%8".add("{0 7 12 0}%8")).lpf(500) +.s("supersaw").dist("4:.2") +``` + +## Editor Features + +### inline viz + +The editor now supports multiple visuals within the code, using the `_` prefix for viz functions: + +Screenshot 2024-06-01 at 01 23 51 + +- `._pianoroll()`: inline pianoroll +- `._punchcard()`: inline punchcard +- `._scope()`: inline scope +- `._pitchwheel()`: inline pitchwheel + +For more info, check out the new [Visual Feedback Page](https://strudel.cc/learn/visual-feedback/) + +### label notation + +This new notation simplifies writing patterns at the top level: + +```plaintext +d1: s("bd*4") +d2: s("[- hh]*4") +``` + +This is equivalent to: + +```plaintext +stack( + s("bd*4"), + s("[- hh]*4") +) +``` + +The labels you choose are arbitrary, the above `d1` and `d2` are a typical thing you'd write in tidal, for example `d1 $ s "bd*4"`. +If the same label is used multiple times, the last one wins: + +```plaintext +d1: s("bd*4") +d1: s("[- hh]*4") // <-- only this plays +``` + +There is a special label anonymous label `$`, which can appear multiple times without overriding itself: + +```plaintext +// both of these will play: +$: s("bd*4") +$: s("[- hh]*4") +``` + +You can mute a pattern by prefixing `_`: + +```plaintext +_$: s("bd*4") // <-- this one is muted +$: s("[- hh]*4") +``` + +To run a transformation on all patterns, you can use `all`: + +```plaintext +$: s("bd*4") +$: s("[- hh]*4") +all(x=>x.room(.5)) +``` + +This notation is now the recommended way to [play patterns in parallel](https://strudel.cc/workshop/first-notes/#playing-multiple-patterns) + +### Clock sync between multiple instances + +timing has received a major overhaul, and is now much more accurate on all browsers. Additionally, you can now sync timing across multiple windows. +![Screenshot 2024-06-02 at 11 24 40 PM](https://codeberg.org/uzu/strudel/assets/47068718/840be744-a13e-4d7b-ab09-50d3a70b1f85) + +### Better sample upload support + +you can now upload large amounts of samples much faster across all browsers including on IOS devices. supported filetypes now include: ogg flac mp3 wav aac m4a + +### experimental tidal syntax + +The new `tidal` function allows you to write strudel patterns in tidal syntax: + +```plaintext +await initTidal() + +tidal` +d1 $ s "bd*4" + +d2 $ s "[- hh]*4" +` +``` + +As we're looking to improve compatibility with tidal, we're happy to hear feedback. + +## breaking changes + +This release comes with a bunch of breaking changes. If you find your patterns to sound different, check out the PRs below for guidance on how to update them. Most of these changes shouldn't affect a lot of patterns. +In case of doubt, add the line `// @version 1.0` to your old pattern. +If you're having problems, please let us know! + +- remove legacy legato + duration implementations by @felixroos in https://codeberg.org/uzu/strudel/pull/965 +- Velocity in value by @felixroos in https://codeberg.org/uzu/strudel/pull/974 +- use ireal as default voicing dict by @felixroos https://codeberg.org/uzu/strudel/pull/967 +- Color in hap value by @felixroos https://codeberg.org/uzu/strudel/pull/1007 +- rename trig -> reset, trigzero -> restart by @felixroos https://codeberg.org/uzu/strudel/pull/1010 +- remove dangerous arithmetic feature by @felixroos https://codeberg.org/uzu/strudel/pull/1030 +- change fanchor to 0 by @daslyfe https://codeberg.org/uzu/strudel/pull/1107 + +## superdough features + +- replace shape with distort in learn doc by @daslyfe https://codeberg.org/uzu/strudel/pull/982 +- Worklet Improvents / fixes by @daslyfe https://codeberg.org/uzu/strudel/pull/963 +- supersaw oscillator by @daslyfe https://codeberg.org/uzu/strudel/pull/978 +- Add analog-style ladder filter by @daslyfe https://codeberg.org/uzu/strudel/pull/1103 +- Calculate phaser modulation phase based on time by @daslyfe https://codeberg.org/uzu/strudel/pull/1110 +- rollback phaser by @daslyfe https://codeberg.org/uzu/strudel/pull/1113 + +## editor / ui features + +- 'Enable Bracket Matching' option in Codemirror by @eefano https://codeberg.org/uzu/strudel/pull/956 +- REPL sync between windows by @daslyfe https://codeberg.org/uzu/strudel/pull/900 +- inline viz / widgets package by @felixroos https://codeberg.org/uzu/strudel/pull/989 +- Inline punchcard + spiral by @felixroos https://codeberg.org/uzu/strudel/pull/1008 +- More fonts by @felixroos https://codeberg.org/uzu/strudel/pull/1023 +- better theme integration for visuals + various fixes by @felixroos https://codeberg.org/uzu/strudel/pull/1024 +- add setting for sync flag by @felixroos https://codeberg.org/uzu/strudel/pull/1025 +- add closeBrackets setting by @felixroos https://codeberg.org/uzu/strudel/pull/1031 +- add font file types to offline cache by @felixroos https://codeberg.org/uzu/strudel/pull/1032 +- pitchwheel visual by @felixroos https://codeberg.org/uzu/strudel/pull/1041 +- repl: set document.title from @title by @kasparsj https://codeberg.org/uzu/strudel/pull/1090 +- Samples tab improvements by @daslyfe https://codeberg.org/uzu/strudel/pull/1102 + +## language features + +- pickOut(), pickRestart(), pickReset() by @eefano https://codeberg.org/uzu/strudel/pull/950 +- Auto await samples by @felixroos https://codeberg.org/uzu/strudel/pull/955 +- feat: can now invert euclid pulses with negative numbers by @felixroos https://codeberg.org/uzu/strudel/pull/959 +- Nested controls by @felixroos https://codeberg.org/uzu/strudel/pull/973 +- alias - for ~ by @yaxu https://codeberg.org/uzu/strudel/pull/981 +- Beat-oriented functionality by @yaxu https://codeberg.org/uzu/strudel/pull/976 +- Labeled statements by @felixroos https://codeberg.org/uzu/strudel/pull/991 +- accidentals in scale degrees by @eefano https://codeberg.org/uzu/strudel/pull/1000 +- Feature: tactus marking by @yaxu https://codeberg.org/uzu/strudel/pull/1021 +- Tactus tidy by @yaxu https://codeberg.org/uzu/strudel/pull/1027 +- Wax, wane, taper and taperlist by @yaxu https://codeberg.org/uzu/strudel/pull/1042 +- transpose: support all combinations of numbers and strings for notes and intervals by @felixroos https://codeberg.org/uzu/strudel/pull/1048 +- anonymous patterns + muting by @felixroos https://codeberg.org/uzu/strudel/pull/1059 +- add swing + swingBy by @felixroos https://codeberg.org/uzu/strudel/pull/1038 +- Stepwise functions from Tidal by @yaxu https://codeberg.org/uzu/strudel/pull/1060 +- Tactus tweaks - fixes for maintaining tactus and highlight locations by @yaxu https://codeberg.org/uzu/strudel/pull/1065 +- Fix stepjoin by @yaxu https://codeberg.org/uzu/strudel/pull/1067 +- More tactus tidying by @yaxu https://codeberg.org/uzu/strudel/pull/1071 +- Tactus calculation toggle and breaking change to tactus calculation in fast/slow/hurry by @yaxu https://codeberg.org/uzu/strudel/pull/1081 +- hs2js package / tidal parser by @felixroos https://codeberg.org/uzu/strudel/pull/870 +- Add the mousex and mousey signal by @Enelg52 https://codeberg.org/uzu/strudel/pull/1112 +- can now access strudelMirror from repl by @felixroos https://codeberg.org/uzu/strudel/pull/1117 + +## sampler + +If you have nodejs installed on your system, you can now use [@strudel/sampler](https://www.npmjs.com/package/@strudel/sampler) to serve samples from disk to the REPL or flok. + +- local sample server cli by @felixroos https://codeberg.org/uzu/strudel/pull/1033 +- Fix sampler paths by @felixroos https://codeberg.org/uzu/strudel/pull/1034 +- Fix sampler windows by @felixroos https://codeberg.org/uzu/strudel/pull/1108 +- fix sampler on windows by @geikha https://codeberg.org/uzu/strudel/pull/1109 + +## docs + +- V1 release notes by @felixroos https://codeberg.org/uzu/strudel/pull/935 +- Minor documentation error: Update first-sounds.mdx by @mhetrick https://codeberg.org/uzu/strudel/pull/941 +- Update synths.mdx by @andresgottlieb https://codeberg.org/uzu/strudel/pull/984 +- using strudel in your project guide + cleanup examples by @felixroos https://codeberg.org/uzu/strudel/pull/1006 +- Document signals by @ilesinge https://codeberg.org/uzu/strudel/pull/1015 +- improve tutorial + custom samples doc by @felixroos https://codeberg.org/uzu/strudel/pull/1053 +- fix cr typo on first-sounds.mdx by @cleary https://codeberg.org/uzu/strudel/pull/1068 +- fix first sounds typo by @cleary https://codeberg.org/uzu/strudel/pull/1069 +- add `<...>` to first-sounds.mdx recap by @cleary https://codeberg.org/uzu/strudel/pull/1070 +- add nesting to `off` example variation in pattern-effects.mdx by @cleary https://codeberg.org/uzu/strudel/pull/1075 +- fix translation issue in first-effects.mdx by @cleary https://codeberg.org/uzu/strudel/pull/1072 +- add signals to recap in first-effects.mdx by @cleary https://codeberg.org/uzu/strudel/pull/1073 +- fix docs on alignment.mdx by @diegodorado https://codeberg.org/uzu/strudel/pull/1076 +- fix little dub tune example by @lukad https://codeberg.org/uzu/strudel/pull/1104 +- clarify `off` in pattern-effects.mdx by @cleary https://codeberg.org/uzu/strudel/pull/1074 +- Fixes drawPianoroll import in codemirror example by @giohappy https://codeberg.org/uzu/strudel/pull/1116 +- Migrate tutorial fanchor by @felixroos https://codeberg.org/uzu/strudel/pull/1122 + +## internals + +- remove cjs builds by @felixroos https://codeberg.org/uzu/strudel/pull/945 +- controls refactoring: simplify exports by @felixroos https://codeberg.org/uzu/strudel/pull/962 +- move canvas related helpers from core to new draw package by @felixroos https://codeberg.org/uzu/strudel/pull/971 +- remove canvas, externalize samples, delete junk by @felixroos https://codeberg.org/uzu/strudel/pull/1003 +- Improve performance of ! (replicate) by @yaxu https://codeberg.org/uzu/strudel/pull/1084 +- Benchmarks by @yaxu https://codeberg.org/uzu/strudel/pull/1079 + +## fixes + +- fix midi issue on firefox and added quote error by @Enelg52 https://codeberg.org/uzu/strudel/pull/936 +- fix: pianoroll sorting by @felixroos https://codeberg.org/uzu/strudel/pull/938 +- account for cps in midi time duration by @daslyfe https://codeberg.org/uzu/strudel/pull/954 +- fix script importable packages (web + repl) by @felixroos https://codeberg.org/uzu/strudel/pull/957 +- fix: reset global fx on pattern change by @felixroos https://codeberg.org/uzu/strudel/pull/960 +- add debounce to logger by @felixroos https://codeberg.org/uzu/strudel/pull/968 +- fix for transpose(): preserve hap value object structure by @eefano https://codeberg.org/uzu/strudel/pull/966 +- fix: clear hydra on reset by @felixroos https://codeberg.org/uzu/strudel/pull/983 +- little fix for withVal by @eefano https://codeberg.org/uzu/strudel/pull/980 +- fix: share now shares what's visible instead of active by @felixroos https://codeberg.org/uzu/strudel/pull/985 +- Fix pure mini highlight by @yaxu https://codeberg.org/uzu/strudel/pull/994 +- fix: await injectPatternMethods by @felixroos https://codeberg.org/uzu/strudel/pull/1012 +- update undocumented script by @felixroos https://codeberg.org/uzu/strudel/pull/1013 +- eliminate chromium clock jitter by @felixroos https://codeberg.org/uzu/strudel/pull/1004 +- Repl sync fixes by @daslyfe https://codeberg.org/uzu/strudel/pull/1014 +- hotfix for 1017 by @daslyfe https://codeberg.org/uzu/strudel/pull/1020 +- fix cyclist fizzling out by @felixroos https://codeberg.org/uzu/strudel/pull/1046 +- Midi Time hotfix for scheduler updates by @daslyfe https://codeberg.org/uzu/strudel/pull/1047 +- fix: do not reset cc input values on each eval by @felixroos https://codeberg.org/uzu/strudel/pull/1054 +- Fix wchooseCycles not picking the whole pattern by @ilesinge https://codeberg.org/uzu/strudel/pull/1061 +- fix OSC timing for recent scheduler updates by @daslyfe https://codeberg.org/uzu/strudel/pull/1062 +- clarify license by @yaxu https://codeberg.org/uzu/strudel/pull/1064 +- fix failing format test by @daslyfe https://codeberg.org/uzu/strudel/pull/1077 +- fix: url parsing with extra params by @felixroos https://codeberg.org/uzu/strudel/pull/1083 +- fix: csound + dough timing by @felixroos https://codeberg.org/uzu/strudel/pull/1086 +- fix: missing events due to premature worklet cleanup by @felixroos https://codeberg.org/uzu/strudel/pull/1089 +- Use sessionStorage for viewingPatternData and activePattern by @kasparsj https://codeberg.org/uzu/strudel/pull/1091 +- osc: couple of fixes by @kasparsj https://codeberg.org/uzu/strudel/pull/1093 +- web package fixes by @felixroos https://codeberg.org/uzu/strudel/pull/1044 +- Fix audio worklets by @daslyfe https://codeberg.org/uzu/strudel/pull/1114 +- fix: use full repl in web package by @felixroos https://codeberg.org/uzu/strudel/pull/1119 +- [BUG FIX] Audio worklets sometimes dont load by @daslyfe https://codeberg.org/uzu/strudel/pull/1121 + +## New Contributors + +- @mhetrick made their first contribution https://codeberg.org/uzu/strudel/pull/941 +- @eefano made their first contribution https://codeberg.org/uzu/strudel/pull/956 +- @Enelg52 made their first contribution https://codeberg.org/uzu/strudel/pull/936 +- @andresgottlieb made their first contribution https://codeberg.org/uzu/strudel/pull/984 +- @cleary made their first contribution https://codeberg.org/uzu/strudel/pull/1068 +- @diegodorado made their first contribution https://codeberg.org/uzu/strudel/pull/1076 +- @lukad made their first contribution https://codeberg.org/uzu/strudel/pull/1104 +- @giohappy made their first contribution https://codeberg.org/uzu/strudel/pull/1116 + +A huge thanks to all contributors!!! + +## Packages + +- @strudel/codemirror@1.1.0 +- @strudel/core@1.1.0 +- @strudel/csound@1.1.0 +- @strudel/draw@1.1.0 +- @strudel/embed@1.1.0 +- hs2js@0.1.0 +- @strudel/hydra@1.1.0 +- @strudel/midi@1.1.0 +- @strudel/mini@1.1.0 +- @strudel/osc@1.1.0 +- @strudel/repl@1.1.0 +- @strudel/sampler@0.1.0 +- @strudel/serial@1.1.0 +- @strudel/soundfonts@1.1.0 +- superdough@1.1.0 +- @strudel/tidal@0.1.0 +- @strudel/tonal@1.1.0 +- @strudel/transpiler@1.1.0 +- @strudel/web@1.1.0 +- @strudel/webaudio@1.1.0 +- @strudel/xen@1.1.0 + +**Full Changelog**: https://codeberg.org/uzu/strudel/compare/v1.0.0...v1.1.0 diff --git a/website/src/content/blog/release-1.2.0-kardinalschnitten.mdx b/website/src/content/blog/release-1.2.0-kardinalschnitten.mdx new file mode 100644 index 000000000..e215d4532 --- /dev/null +++ b/website/src/content/blog/release-1.2.0-kardinalschnitten.mdx @@ -0,0 +1,186 @@ +--- +title: 'Release Notes v1.2.0' +description: '' +date: '2025-05-01' +tags: ['meta'] +author: froos +--- + +## What's Changed + +## highlights + +- [stepwise functions](https://strudel.cc/learn/stepwise/) ([PR](https://github.com/tidalcycles/strudel/pull/1262)) +- [midimaps](https://strudel.cc/learn/input-output/#midimaps) ([PR](https://github.com/tidalcycles/strudel/pull/1274)) +- [spectrum](https://strudel.cc/learn/visual-feedback/#spectrum) ([PR](https://github.com/tidalcycles/strudel/pull/1213)) +- [mqtt](https://strudel.cc/learn/input-output/#mqtt) ([PR](https://github.com/tidalcycles/strudel/pull/1224)) +- pulse oscillator (todo: https://github.com/tidalcycles/strudel/issues/1336) ([PR](https://github.com/tidalcycles/strudel/pull/1304)) +- theme improvements + +## breaking changes + +- [breaking change] Sample signals from query onset, rather than midpoint by @yaxu in https://github.com/tidalcycles/strudel/pull/1278 +- change behaviour of polymeter, and remove polymeterSteps by @yaxu in https://github.com/tidalcycles/strudel/pull/1302 +- Polish, rename, and document stepwise functions by @yaxu in https://github.com/tidalcycles/strudel/pull/1262 + +### superdough + +- feat: Create Pulse Oscillator with variable PWM by @daslyfe in https://github.com/tidalcycles/strudel/pull/1304 +- add num samples (edited numbers) by @yaxu in https://github.com/tidalcycles/strudel/pull/1309 +- Add num samples from 0 up to 20 by @yaxu in https://github.com/tidalcycles/strudel/pull/1310 +- feat: add max polyphony feature for superdough by @daslyfe in https://github.com/tidalcycles/strudel/pull/1317 + +### docs + +- doc: visual functions + refactor onPaint by @felixroos in https://github.com/tidalcycles/strudel/pull/1125 +- Labeled statements doc by @felixroos in https://github.com/tidalcycles/strudel/pull/1126 +- Correct spelling mistakes by @EdwardBetts in https://github.com/tidalcycles/strudel/pull/1183 +- remove redundant example for cat, update snapshot by @kdiab in https://github.com/tidalcycles/strudel/pull/1189 +- chore: Edit run locally instructions in README.md by @ChinoUkaegbu in https://github.com/tidalcycles/strudel/pull/1206 +- suggested changes to voicings.mdx by @bwagner in https://github.com/tidalcycles/strudel/pull/1231 +- Documentation for all/each, and bugfix for each by @yaxu in https://github.com/tidalcycles/strudel/pull/1233 +- Update documentation for param value modification by @gillespi314 in https://github.com/tidalcycles/strudel/pull/1238 +- fix docs for beat function by @daslyfe in https://github.com/tidalcycles/strudel/pull/1248 +- understand voicings page by @felixroos in https://github.com/tidalcycles/strudel/pull/1230 +- add reference package by @felixroos in https://github.com/tidalcycles/strudel/pull/1252 +- Stepwise documentation tweaks, with mridangam samples by @yaxu in https://github.com/tidalcycles/strudel/pull/1275 +- showcase tweaks by @yaxu in https://github.com/tidalcycles/strudel/pull/1291 +- Signpost licenses for source code and samples a bit more, ref #1277 by @yaxu in https://github.com/tidalcycles/strudel/pull/1289 +- Fix misplaced ending sentence by @makmanalp in https://github.com/tidalcycles/strudel/pull/1296 +- Fix typo pattnr by @ReneNyffenegger in https://github.com/tidalcycles/strudel/pull/1316 +- update docs to reflect import sounds tab change by @hpunq in https://github.com/tidalcycles/strudel/pull/1332 + +### ui improvements + +- Udels (MultiFrame Strudel) Revisited by @daslyfe in https://github.com/tidalcycles/strudel/pull/1132 +- Create audio target selector for OSC/Superdirt by @daslyfe in https://github.com/tidalcycles/strudel/pull/1160 +- Add a search bar to the REPL Reference tab by @netux in https://github.com/tidalcycles/strudel/pull/1165 +- Adding search bar (soundtab.jsx) by @Bubobubobubobubo in https://github.com/tidalcycles/strudel/pull/1185 +- add 2 new ui settings by @felixroos in https://github.com/tidalcycles/strudel/pull/1200 +- Theme glowup by @felixroos in https://github.com/tidalcycles/strudel/pull/1268 +- Create Pattern Page Pagination by @daslyfe in https://github.com/tidalcycles/strudel/pull/1287 +- feat: Theme improvements by @daslyfe in https://github.com/tidalcycles/strudel/pull/1295 +- feat: new themes + theme improvements by @daslyfe in https://github.com/tidalcycles/strudel/pull/1326 +- Add new "import-sounds" tab with explanation on folder import by @hpunq in https://github.com/tidalcycles/strudel/pull/1329 +- Add Icon to import sample button by @daslyfe in https://github.com/tidalcycles/strudel/pull/1331 +- better spacing in zen mode by @felixroos in https://github.com/tidalcycles/strudel/pull/1147 +- Screenreader improvements by @yaxu in https://github.com/tidalcycles/strudel/pull/1158 +- colorize console + tweak header by @felixroos in https://github.com/tidalcycles/strudel/pull/1203 +- Menu Panel Improvements! by @daslyfe in https://github.com/tidalcycles/strudel/pull/1193 +- Make panel hover behavior optional by @daslyfe in https://github.com/tidalcycles/strudel/pull/1199 +- REPL: solo and sync configuration by @bthj in https://github.com/tidalcycles/strudel/pull/1214 +- enhancement: make error messages easier to read by @daslyfe in https://github.com/tidalcycles/strudel/pull/1315 + +### mqtt + +- MQTT support by @yaxu in https://github.com/tidalcycles/strudel/pull/1224 +- MQTT - if password isn't provided, prompt for one by @yaxu in https://github.com/tidalcycles/strudel/pull/1249 +- MQTT - support adding hap duration and cps metadata to JSON messages by @yaxu in https://github.com/tidalcycles/strudel/pull/1279 +- make mqtt topic patternable by @yaxu in https://github.com/tidalcycles/strudel/pull/1280 +- Bugfix: update mqtt connections dictionary by @yaxu in https://github.com/tidalcycles/strudel/pull/1281 +- mqtt bugfix - connection check by @yaxu in https://github.com/tidalcycles/strudel/pull/1282 + +### new functions + +- Add scramble and shuffle by @yaxu in https://github.com/tidalcycles/strudel/pull/1167 +- polyJoin by @yaxu in https://github.com/tidalcycles/strudel/pull/1168 +- Add seqPLoop from Tidal by @yaxu in https://github.com/tidalcycles/strudel/pull/1182 +- add filter + filterWhen + within by @felixroos in https://github.com/tidalcycles/strudel/pull/1039 +- Add bite function by @yaxu in https://github.com/tidalcycles/strudel/pull/1187 +- markcss by @felixroos in https://github.com/tidalcycles/strudel/pull/1202 +- "beat" function for "step sequencer" style rhythm notation by @daslyfe in https://github.com/tidalcycles/strudel/pull/1237 +- Add s_zip for 'cat'-ing patterns together step-by-step, bugfix `steps` by @yaxu in https://github.com/tidalcycles/strudel/pull/1208 +- "stretch" function (phase vocoder) by @daslyfe in https://github.com/tidalcycles/strudel/pull/1130 +- add basic spectrum function by @felixroos in https://github.com/tidalcycles/strudel/pull/1213 +- Add onKey function for custom key commands for patterns by @daslyfe in https://github.com/tidalcycles/strudel/pull/1235 +- Add binary and binaryN by @heerman in https://github.com/tidalcycles/strudel/pull/1226 +- midimaps by @felixroos in https://github.com/tidalcycles/strudel/pull/1274 +- small feat: Add alias for segment and ribbon by @daslyfe in https://github.com/tidalcycles/strudel/pull/1314 +- feat: Create scrub function for scrubbing an audio file by @daslyfe in https://github.com/tidalcycles/strudel/pull/1321 +- feat: Improve gain curve by @daslyfe in https://github.com/tidalcycles/strudel/pull/1318 +- Chop chop by @yaxu in https://github.com/tidalcycles/strudel/pull/1078 + +### more + +- Make `all()` post-stack again, and add `each()` for pre-stack by @yaxu in https://github.com/tidalcycles/strudel/pull/1229 +- Add stepBind, and some toplevel aliases for binds and withValue by @yaxu in https://github.com/tidalcycles/strudel/pull/1241 +- Make cps patternable by @eefano in https://github.com/tidalcycles/strudel/pull/1001 +- Allow wchooseCycles probabilities to be patterned by @yaxu in https://github.com/tidalcycles/strudel/pull/1292 +- @strudel/sampler improvements by @felixroos in https://github.com/tidalcycles/strudel/pull/1288 + +### refactor + +- expose comment commands by @felixroos in https://github.com/tidalcycles/strudel/pull/1136 +- containerize/seperate out boolean checks for repl types/Repl logic into bespoke components. by @daslyfe in https://github.com/tidalcycles/strudel/pull/1163 +- Improve + simplify neocyclist timing by @daslyfe in https://github.com/tidalcycles/strudel/pull/1164 +- Make phaser control consistent with superdirt by @daslyfe in https://github.com/tidalcycles/strudel/pull/1178 +- Revert "Make phaser control consistent with superdirt" by @daslyfe in https://github.com/tidalcycles/strudel/pull/1179 +- make phaser control match superdirt by @daslyfe in https://github.com/tidalcycles/strudel/pull/1180 +- refactor sampler by @felixroos in https://github.com/tidalcycles/strudel/pull/1101 +- update lockfile + minor versions by @felixroos in https://github.com/tidalcycles/strudel/pull/1198 +- Preserve tactus for 'degrade' and friends, and tidy up 'pick' and friends by @yaxu in https://github.com/tidalcycles/strudel/pull/1205 +- Apply `all` function to individual patterns rather than final stack by @yaxu in https://github.com/tidalcycles/strudel/pull/1209 +- Revert "Fix sometimes" by @yaxu in https://github.com/tidalcycles/strudel/pull/1267 +- patchday by @felixroos in https://github.com/tidalcycles/strudel/pull/1264 +- Rename repeat back to extend by @yaxu in https://github.com/tidalcycles/strudel/pull/1285 +- Send delta in OSC message in seconds, to match tidal/superdirt by @yaxu in https://github.com/tidalcycles/strudel/pull/1323 + +### fixes + +- Fix clock worker dependency path in module builds by @matthewkaney in https://github.com/tidalcycles/strudel/pull/1129 +- Fix bug in Fraction.lcm by @yaxu in https://github.com/tidalcycles/strudel/pull/1133 +- Fix tactus marking in mininotation by @yaxu in https://github.com/tidalcycles/strudel/pull/1144 +- Fix loopAt tactus by @yaxu in https://github.com/tidalcycles/strudel/pull/1145 +- Fix OSC clock jitter by @daslyfe in https://github.com/tidalcycles/strudel/pull/1157 +- [CORS HOTFIX] by @daslyfe in https://github.com/tidalcycles/strudel/pull/1162 +- Fixes fit so it works after a chop or slice by @yaxu in https://github.com/tidalcycles/strudel/pull/1171 +- fix sample speed when using splice and fit with superdirt by @daslyfe in https://github.com/tidalcycles/strudel/pull/1172 +- handle midin device not found error by @felixroos in https://github.com/tidalcycles/strudel/pull/1146 +- Fix serial timing by @yaxu in https://github.com/tidalcycles/strudel/pull/1188 +- Fix regression for d1, p1, p(n) by @yaxu in https://github.com/tidalcycles/strudel/pull/1227 +- Fix sometimes by @yaxu in https://github.com/tidalcycles/strudel/pull/1243 +- Fix sf2 timing by @felixroos in https://github.com/tidalcycles/strudel/pull/1272 +- Fix "squeezejoin" and functions using it, including "bite" by @yaxu in https://github.com/tidalcycles/strudel/pull/1286 +- Fixes inverted triangle wave by renaming it to "itri", making non-inverted "tri" by @yaxu in https://github.com/tidalcycles/strudel/pull/1283 +- Hotfix: prevent undefined pattern code from crashing strudel on load by @daslyfe in https://github.com/tidalcycles/strudel/pull/1297 +- Fix test error #1297 by @nkymut in https://github.com/tidalcycles/strudel/pull/1298 +- bugfix zoom stepcount by @yaxu in https://github.com/tidalcycles/strudel/pull/1301 +- bugfix: Allow single param to be used in the as function by @daslyfe in https://github.com/tidalcycles/strudel/pull/1312 +- fix: replace empty spaces in registered sound keys by @daslyfe in https://github.com/tidalcycles/strudel/pull/1319 +- FIX: Multichannel Audio by @daslyfe in https://github.com/tidalcycles/strudel/pull/1322 +- fix: udels header by @daslyfe in https://github.com/tidalcycles/strudel/pull/1325 +- fix: disable astro toolbar by default by @daslyfe in https://github.com/tidalcycles/strudel/pull/1324 +- FIX: sound import order by @daslyfe in https://github.com/tidalcycles/strudel/pull/1333` + +## New Contributors + +- @EdwardBetts made their first contribution in https://github.com/tidalcycles/strudel/pull/1183 +- @netux made their first contribution in https://github.com/tidalcycles/strudel/pull/1165 +- @kdiab made their first contribution in https://github.com/tidalcycles/strudel/pull/1189 + +**Full Changelog**: https://github.com/tidalcycles/strudel/compare/v1.1.0...v1.1.1 + +## packages + +- @strudel/codemirror@1.2.0 +- @strudel/core@1.2.0 +- @strudel/csound@1.2.0 +- @strudel/draw@1.2.0 +- @strudel/gamepad@1.2.0 +- @strudel/hydra@1.2.0 +- @strudel/midi@1.2.0 +- @strudel/mini@1.2.0 +- @strudel/motion@1.2.0 +- @strudel/mqtt@1.2.0 +- @strudel/osc@1.2.0 +- @strudel/reference@1.2.0 +- @strudel/repl@1.2.0 +- @strudel/sampler@0.2.0 +- @strudel/serial@1.2.0 +- @strudel/soundfonts@1.2.0 +- superdough@1.2.0 +- @strudel/tonal@1.2.0 +- @strudel/transpiler@1.2.0 +- @strudel/web@1.2.0 +- @strudel/webaudio@1.2.0 +- @strudel/xen@1.2.0 diff --git a/website/src/content/blog/year-2.mdx b/website/src/content/blog/year-2.mdx index 38e39bbfd..1a33c673b 100644 --- a/website/src/content/blog/year-2.mdx +++ b/website/src/content/blog/year-2.mdx @@ -216,9 +216,9 @@ Here is a recording of the first session we organized over the discord server: The midi integration has gotten a few new features: -- [clock out](https://github.com/tidalcycles/strudel/pull/710) to sync your midi devices / DAW to the strudel clock (better doc coming soon) +- [clock out](https://codeberg.org/uzu/strudel/pulls/710) to sync your midi devices / DAW to the strudel clock (better doc coming soon) - [cc output](https://strudel.cc/learn/input-output/#ccn--ccv) to send cc values to your gear -- [cc input](https://github.com/tidalcycles/strudel/pull/699) to control strudel via MIDI (better doc coming soon) +- [cc input](https://codeberg.org/uzu/strudel/pulls/699) to control strudel via MIDI (better doc coming soon) Here is a little demo of me fiddling with a midi controller, changing a piano pattern: @@ -228,7 +228,7 @@ Here is a little demo of me fiddling with a midi controller, changing a piano pa - You can now run [hydra inside strudel](https://strudel.cc/learn/hydra/) + you can even run strudel in [hydra](https://hydra.ojack.xyz), thanks to [Olivia Jack](https://ojack.xyz/) and [Ámbar Tenorio Fornés](https://atenor.io/)! Read more [here](https://alpaca.pubpub.org/pub/b7hwrjfk/release/2?readingCollection=1def0192) - There is now a [VSCode Plugin](https://marketplace.visualstudio.com/items?itemName=roipoussiere.tidal-strudel) thanks to [roipoussiere](https://github.com/roipoussiere)! It allows you run patterns from a `.strudel` file inside VSCode. -- Strudel can now be downloaded as a desktop app, thanks to [vasilymilovidov](https://github.com/vasilymilovidov) who has wrapped the REPL with [tauri](https://tauri.app/). You can download it [on the releases page](https://github.com/tidalcycles/strudel/releases) (scroll down to Assets). The performance is not optimal on MacOS and Linux, which is why it is still considered experimental +- Strudel can now be downloaded as a desktop app, thanks to [vasilymilovidov](https://github.com/vasilymilovidov) who has wrapped the REPL with [tauri](https://tauri.app/). You can download it [on the releases page](https://codeberg.org/uzu/strudel/releases) (scroll down to Assets). The performance is not optimal on MacOS and Linux, which is why it is still considered experimental ## Stats diff --git a/website/src/cx.mjs b/website/src/cx.mjs index 4e4aea08d..d8ff9eda1 100644 --- a/website/src/cx.mjs +++ b/website/src/cx.mjs @@ -1,6 +1,6 @@ /* cx.js - -Copyright (C) 2022 Strudel contributors - see +Copyright (C) 2022 Strudel contributors - see 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 . */ diff --git a/website/src/docs/MiniRepl.jsx b/website/src/docs/MiniRepl.jsx index 0f3d3ce10..09ebb7692 100644 --- a/website/src/docs/MiniRepl.jsx +++ b/website/src/docs/MiniRepl.jsx @@ -30,6 +30,7 @@ export function MiniRepl({ maxHeight, autodraw, drawTime, + mondo = false, }) { const code = tunes ? tunes[0] : tune; const id = useMemo(() => s4(), []); @@ -85,6 +86,7 @@ export function MiniRepl({ }, beforeStart: () => audioReady, afterEval: ({ code }) => setVersionDefaultsFrom(code), + mondo, }); // init settings editor.setCode(code); diff --git a/website/src/pages/de/workshop/first-sounds.mdx b/website/src/pages/de/workshop/first-sounds.mdx index e289e7248..3695843b0 100644 --- a/website/src/pages/de/workshop/first-sounds.mdx +++ b/website/src/pages/de/workshop/first-sounds.mdx @@ -168,9 +168,14 @@ Korrekt, der echte Vorteil dieser Schreibweise zeigt sich wenn du Elemente entfe -**Tempo ändern mit `cpm`** +**Tempo ändern mit `setcpm`** -*8").cpm(90/4)`} punchcard /> +*8")`} + punchcard +/> @@ -233,8 +238,9 @@ Bolero: @@ -313,18 +319,23 @@ Das haben wir bisher gelernt: Die mit Apostrophen umgebene Mini-Notation benutzt man normalerweise in einer sogenannten Funktion. Die folgenden Funktionen haben wir bereits gesehen: -| Name | Description | Example | -| ----- | -------------------------------------- | ----------------------------------------------------------------------- | -| sound | Spielt den Sound mit dem Namen | | -| bank | Wählt die Soundbank / Drum Machine | | -| cpm | Tempo in **C**ycles **p**ro **M**inute | | -| n | Sample Nummer | | +| Name | Description | Example | +| ------ | -------------------------------------- | ----------------------------------------------------------------------- | +| sound | Spielt den Sound mit dem Namen | | +| bank | Wählt die Soundbank / Drum Machine | | +| setcpm | Tempo in **C**ycles **p**ro **M**inute | | +| n | Sample Nummer | | ## Beispiele **Einfacher Rock Beat** - + **Klassischer House** @@ -339,7 +350,12 @@ Bestimmte Drum Patterns werden oft genreübergreifend wiederverwendet. We Will Rock you - + **Yellow Magic Orchestra - Firecracker** @@ -354,12 +370,13 @@ We Will Rock you @@ -367,12 +384,13 @@ We Will Rock you @@ -380,11 +398,11 @@ We Will Rock you diff --git a/website/src/pages/de/workshop/recap.mdx b/website/src/pages/de/workshop/recap.mdx index 14ef6b252..ec279c084 100644 --- a/website/src/pages/de/workshop/recap.mdx +++ b/website/src/pages/de/workshop/recap.mdx @@ -56,13 +56,13 @@ Diese Seite ist eine Auflistung aller im Workshop vorgestellten Funktionen. ## Pattern-Effekte -| Name | Beschreibung | Beispiel | -| ---- | --------------------------------- | ----------------------------------------------------------------------------------- | -| cpm | Tempo in Cycles pro Minute | | -| fast | schneller | | -| slow | langsamer | | -| rev | rückwärts | | -| jux | einen Stereo-Kanal modifizieren | | -| add | addiert Zahlen oder Noten | ")).scale("C:minor")`} /> | -| ply | jedes Element schneller machen | ")`} /> | -| off | verzögert eine modifizierte Kopie | x.speed(2))`} /> | +| Name | Beschreibung | Beispiel | +| ------ | --------------------------------- | ----------------------------------------------------------------------------------- | +| setcpm | Tempo in Cycles pro Minute | | +| fast | schneller | | +| slow | langsamer | | +| rev | rückwärts | | +| jux | einen Stereo-Kanal modifizieren | | +| add | addiert Zahlen oder Noten | ")).scale("C:minor")`} /> | +| ply | jedes Element schneller machen | ")`} /> | +| off | verzögert eine modifizierte Kopie | x.speed(2))`} /> | diff --git a/website/src/pages/learn/getting-started.mdx b/website/src/pages/learn/getting-started.mdx index d18a05607..b3348ccda 100644 --- a/website/src/pages/learn/getting-started.mdx +++ b/website/src/pages/learn/getting-started.mdx @@ -14,7 +14,7 @@ These pages will introduce you to [Strudel](https://strudel.cc/), a web-based [l # What is Strudel? -[Strudel](https://strudel.cc/) is a version of [Tidal Cycles](https://tidalcycles.org) written in [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript), initiated by [Alex McLean](https://slab.org) and [Felix Roos](https://github.com/felixroos) in 2022. +[Strudel](https://strudel.cc/) is a version of [Tidal Cycles](https://tidalcycles.org) written in [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript), initiated by [Alex McLean](https://slab.org) and [Felix Roos](https://froos.cc/) in 2022. Tidal Cycles, also known as Tidal, is a language for [algorithmic pattern](https://algorithmicpattern.org), and though it is most commonly used for [making music](https://tidalcycles.org/docs/showcase), it can be used for any kind of pattern making activity, including [weaving](https://www.youtube.com/watch?v=TfEmEsusXjU). Tidal was first implemented as a library written in the [Haskell](https://www.haskell.org/) functional programming language, and by itself it does not make any sound. @@ -81,7 +81,7 @@ s("bd,[~ ],hh*8") // drums Please note that this project is still in its experimental state. In the future, parts of it might change significantly. This tutorial is also far from complete. -You can contribute to it clicking 'Edit this page' in the top right, or by visiting the [Strudel GitHub page](https://github.com/tidalcycles/strudel/). +You can contribute to it clicking 'Edit this page' in the top right, or by visiting the [Strudel GitHub page](https://codeberg.org/uzu/strudel/). # What's next? diff --git a/website/src/pages/learn/input-output.mdx b/website/src/pages/learn/input-output.mdx index 92379aa6f..0dd48b7de 100644 --- a/website/src/pages/learn/input-output.mdx +++ b/website/src/pages/learn/input-output.mdx @@ -8,7 +8,7 @@ import { JsDoc } from '../../docs/JsDoc'; # MIDI, OSC and MQTT -Normally, Strudel is used to pattern sound, using its own '[web audio](https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API)'-based synthesiser called [SuperDough](https://github.com/tidalcycles/strudel/tree/main/packages/superdough). +Normally, Strudel is used to pattern sound, using its own '[web audio](https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API)'-based synthesiser called [SuperDough](https://codeberg.org/uzu/strudel/src/branch/main/packages/superdough). It is also possible to pattern other things with Strudel, such as software and hardware synthesisers with MIDI, other software using Open Sound Control/OSC (including the [SuperDirt](https://github.com/musikinformatik/SuperDirt/) synthesiser commonly used with Strudel's sibling [TidalCycles](https://tidalcycles.org/)), or the MQTT 'internet of things' protocol. @@ -184,7 +184,7 @@ To get SuperDirt to work with Strudel, you need to 1. install SuperCollider + sc3 plugins, see [Tidal Docs](https://tidalcycles.org/docs/) (Install Tidal) for more info. 2. install SuperDirt, or the [StrudelDirt](https://github.com/daslyfe/StrudelDirt) fork which is optimised for use with Strudel 3. install [node.js](https://nodejs.org/en/) -4. download [Strudel Repo](https://github.com/tidalcycles/strudel/) (or git clone, if you have git installed) +4. download [Strudel Repo](https://codeberg.org/uzu/strudel/) (or git clone, if you have git installed) 5. run `pnpm i` in the strudel directory 6. run `pnpm run osc` to start the osc server, which forwards OSC messages from Strudel REPL to SuperCollider diff --git a/website/src/pages/learn/metadata.mdx b/website/src/pages/learn/metadata.mdx index e8bb86dfc..61db16f0e 100644 --- a/website/src/pages/learn/metadata.mdx +++ b/website/src/pages/learn/metadata.mdx @@ -18,8 +18,6 @@ You can optionally add some music metadata in your Strudel code, by using tags i Like other comments, those are ignored by Strudel, but it can be used by other tools to retrieve some information about the music. -It is for instance used by the [swatch tool](https://github.com/tidalcycles/strudel/tree/main/my-patterns) to display pattern titles in the [examples page](https://strudel.cc/examples/). - ## Alternative syntax You can also use comment blocks: diff --git a/website/src/pages/learn/mondo-notation.mdx b/website/src/pages/learn/mondo-notation.mdx new file mode 100644 index 000000000..3b00b9902 --- /dev/null +++ b/website/src/pages/learn/mondo-notation.mdx @@ -0,0 +1,178 @@ +--- +title: Mondo Notation +layout: ../../layouts/MainLayout.astro +--- + +import { MiniRepl } from '../../docs/MiniRepl'; +import { JsDoc } from '../../docs/JsDoc'; + +# Mondo Notation + +"Mondo Notation" is a new kind of notation that is similar to [Mini Notation](/learn/mini-notation/), but with enough abilities to make it work as a standalone pattern language. +Here's an example: + + <8 16>) # *2 + # s "sine" # add (note [0 <12 24>]*2) + # dec(sine # range .2 2) + # room .5 + # lpf (sine/3 # range 120 400) + # lpenv (rand # range .5 4) + # lpq (perlin # range 5 12 # * 2) + # dist 1 # fm 4 # fmh 5.01 # fmdecay <.1 .2> + # postgain .6 # delay .1 # clip 5 + +$ s [bd bd bd bd] # bank tr909 # clip .5 + +# ply <1 [1 [2 4]]> + +$ s oh\*4 # press # bank tr909 # speed.8 + +# dec (<.02 .05>\*2 # add (saw/8 # range 0 1)) + +`} +/> + +## Mondo in the REPL + +For now, you can only use mondo in the repl like this: + + + +The rest of this site will only use the mondo notation itself. +In the future, the REPL might get a way to use mondo notation directly. + +## Calling Functions + +Compared to Mini Notation, the most notable feature of Mondo Notation is the ability to call functions using round brackets: + + + +The first element inside the brackets is the function name. In JS, this would look like: + + + +The outermost parens are not needed, so we can drop them: + + + +## Mini Notation Features + +Besides function calling with round parens, Mondo Notation has a lot in common with Mini Notation: + +### Brackets + +- `[]` for 1-cycle sequences +- `<>` for multi-cycle sequences +- `{}` for stepped sequences (more on that later) + +### Infix Operators + +- \* => [fast](/learn/time-modifiers/#fast) +- / => [slow](/learn/time-modifiers/#slow) +- ! => [extend](/learn/stepwise/#extend) +- @ => [expand](/learn/stepwise/#expand) +- % => [pace](/learn/stepwise/#pace) +- ? => [degradeBy](/learn/random-modifiers/#degradeby) (currently requires right operand) +- : => tail (creates a list) +- .. => range (between numbers) +- , => [stack](/learn/factories/#stack) +- | => [chooseIn](/learn/random-modifiers/#choose) + +### Example + +`} +/> + +## Chaining Functions + +Similar to how "." works in javascript (JS), we can chain functions calls with the "#" operator: + +*4 + # scale C4:minor + # jux rev + # dec .2 + # delay .5`} +/> + +Here's the same written in JS: + +*4") + .scale("C4:minor") + .jux(rev) + .dec(.2) + .delay(.5)`} +/> + +### Chaining Functions Locally + +A function can be applied to a single element by wrapping it in round parens: + + + +in this case, `delay .6` will only be applied to `cp`. compare this with the JS version: + + + +here we can see how much we can save when there's no boundary between mini notation and function calls! + +### Chaining Infix Operators + +Infix operators exist as regular functions, so they can be chained as well: + + + +In this case, the \*2 will be applied to the whole pattern. + +### Lambda Functions + +Some functions in strudel expect a function as input, for example: + +x.dec(.1))`} /> + +in mondo, the `x=>x.` can be shortened to: + + + +chaining works as expected: + + + +## Strings + +You can use "double quotes" and 'single quotes' to get a string: + + + +## Multiple Patterns + +The `$` sign can be used to separate multiple patterns: + + # voicing + # struct[x ~ ~ x ~ x ~ ~] # delay .5`} +/> + +The `$` sign is an alias for `,` so it will create a stack behind the scenes. diff --git a/website/src/pages/learn/samples.mdx b/website/src/pages/learn/samples.mdx index daaecd06c..bbe650cf5 100644 --- a/website/src/pages/learn/samples.mdx +++ b/website/src/pages/learn/samples.mdx @@ -62,7 +62,7 @@ To see which sample names are available, open the `sounds` tab in the [REPL](htt Note that only the sample maps (mapping names to URLs) are loaded initially, while the audio samples themselves are not loaded until they are actually played. This behaviour of loading things only when they are needed is also called `lazy loading`. While it saves resources, it can also lead to sounds not being audible the first time they are triggered, because the sound is still loading. -[This might be fixed in the future](https://github.com/tidalcycles/strudel/issues/187) +[This might be fixed in the future](https://codeberg.org/uzu/strudel/issues/187) # Sound Banks @@ -262,15 +262,17 @@ We can also declare different samples for different regions of the keyboard: !2, g4 f4]>") - .s('moog').clip(1) - .gain(.5).cpm(60)`} +.s('moog').clip(1) +.gain(.5)`} /> The sampler will always pick the closest matching sample for the current note! diff --git a/website/src/pages/learn/strudel-vs-tidal.mdx b/website/src/pages/learn/strudel-vs-tidal.mdx index 40ea188fd..9c1cc6b73 100644 --- a/website/src/pages/learn/strudel-vs-tidal.mdx +++ b/website/src/pages/learn/strudel-vs-tidal.mdx @@ -79,7 +79,7 @@ Instead of `+` / `add`, you can use any of the available operators of the first ## Function Compatibility -[This issue](https://github.com/tidalcycles/strudel/issues/31) tracks which Tidal functions are implemented in Strudel. +[This issue](https://codeberg.org/uzu/strudel/issues/31) tracks which Tidal functions are implemented in Strudel. The list might not be 100% up to date and probably also misses some functions completely.. Feel encouraged to search the source code for a function you're looking for. If you find a function that's not on the list, please tell! @@ -89,7 +89,7 @@ If you find a function that's not on the list, please tell! As seen in the example, the `#` operator (shorthand for `|>`) is also just a function call in strudel. So `note "c5" # s "gtr"` becomes `note("c5").s('gtr')`. -[This file](https://github.com/tidalcycles/strudel/blob/main/packages/core/controls.mjs) lists all available control params. +[This file](https://codeberg.org/uzu/strudel/src/branch/main/packages/core/controls.mjs) lists all available control params. Note that not all of those work in the Webaudio Output of Strudel. If you find a tidal control that's not on the list, please tell! @@ -107,11 +107,11 @@ You can find a [list of available effects here](/learn/effects/). ### Sampler Strudel's sampler supports [a subset](/learn/samples) of Superdirt's sampler. -Also, samples are always loaded from a URL rather than from the disk, although [that might be possible in the future](https://github.com/tidalcycles/strudel/issues/118). +Also, samples are always loaded from a URL rather than from the disk, although [that might be possible in the future](https://codeberg.org/uzu/strudel/issues/118). ## Evaluation -The Strudel REPL does not support [block based evaluation](https://github.com/tidalcycles/strudel/issues/34) yet. +The Strudel REPL does not support [block based evaluation](https://codeberg.org/uzu/strudel/issues/34) yet. You can use labeled statements and `_` to mute: *8").scale('G4 minor') This will load the strudel website in an iframe, using the code provided within the HTML comments ``. The HTML comments are needed to make sure the browser won't interpret it as HTML. -For alternative ways to load this package, see the [@strudel/embed README](https://github.com/tidalcycles/strudel/tree/main/packages/embed#strudelembed). +For alternative ways to load this package, see the [@strudel/embed README](https://codeberg.org/uzu/strudel/src/branch/main/packages/embed#strudel-embed). ### @strudel/repl @@ -99,7 +99,7 @@ The upside of using the repl without an iframe is that you can pin the strudel v This will guarantee your pattern wont break due to changes to the strudel project in the future. -For more info on this package, see the [@strudel/repl README](https://github.com/tidalcycles/strudel/tree/main/packages/repl#strudelrepl). +For more info on this package, see the [@strudel/repl README](https://codeberg.org/uzu/strudel/src/branch/main/packages/repl#strudel-repl). ## With your own UI @@ -118,7 +118,7 @@ If you'd rather use your own UI, you can use the `@strudel/web` package: ``` -For more info on this package, see the [@strudel/web README](https://github.com/tidalcycles/strudel/tree/main/packages/web#strudelweb). +For more info on this package, see the [@strudel/web README]https://codeberg.org/uzu/strudel/src/branch/main/packages/web#strudel-web). ## Via npm diff --git a/website/src/pages/technical-manual/repl.mdx b/website/src/pages/technical-manual/repl.mdx index f336ce361..f53efac41 100644 --- a/website/src/pages/technical-manual/repl.mdx +++ b/website/src/pages/technical-manual/repl.mdx @@ -17,7 +17,7 @@ Besides a UI for playback control and meta information, the main part of the REP 2. While the REPL is running, the `Scheduler` queries the active `Pattern` by a regular interval, generating `Events` (also known as `Haps` in Strudel) for the next time span. 3. For each scheduling tick, all generated `Events` are triggered by calling their `onTrigger` method, which is set by the output. - + ## User Code @@ -75,7 +75,7 @@ The sum of both is passed to `withLoc` to tell each element its location, which Another important part of the user code is the mini notation, which allows to express rhythms in a short manner. -- the mini notation is [implemented as a PEG grammar](https://github.com/tidalcycles/strudel/blob/main/packages/mini/krill.pegjs), living in the [mini package](https://github.com/tidalcycles/strudel/tree/main/packages/mini) +- the mini notation is [implemented as a PEG grammar](https://codeberg.org/uzu/strudel/src/branch/talk/packages/mini/krill.pegjs), living in the [mini package](https://codeberg.org/uzu/strudel/src/branch/main/packages/mini) - it is based on [krill](https://github.com/Mdashdotdashn/krill) by Mdashdotdashn - the peg grammar is used to generate a parser with [peggyjs](https://peggyjs.org/) - the generated parser takes a mini notation string and outputs an AST diff --git a/website/src/pages/understand/cycles.mdx b/website/src/pages/understand/cycles.mdx index 4694850c3..1c0a778a6 100644 --- a/website/src/pages/understand/cycles.mdx +++ b/website/src/pages/understand/cycles.mdx @@ -43,21 +43,33 @@ This is why the same CPS can produce different perceived tempos. ## Setting CPM -If you're familiar with BPM, you can use the `cpm` method to set the tempo in cycles per minute: +If you're familiar with BPM, you can use the `setcpm` method to set the global tempo in cycles per minute: - + If you want to add more beats per cycle, you might want to divide the cpm: - + Or using 2 beats per cycle: - + -To set a specific bpm, use `.cpm(bpm/bpc)` +To set a specific bpm, use `setcpm(bpm/bpc)` - bpm: the target beats per minute - bpc: the number of perceived beats per cycle @@ -74,22 +86,31 @@ Many music programs use it as a default. Strudel does not a have concept of bars or measures, there are only cycles. How you use them is up to you. Above, we've had this example: - + This could be interpreted as 4/4 time with a tempo of 110bpm. We could write out multiple bars like this: \`).cpm(110/4)`} +>\`)`} /> Instead of writing out each bar separately, we could express this much shorter: ->,hh*4").cpm(110/2)`} /> +>,hh*4")`} +/> Here we can see that thinking in cycles rather than bars simplifies things a lot! These types of simplifications work because of the repetitive nature of rhythm. @@ -109,10 +130,11 @@ We could also write multiple bars with different time signatures: \`).cpm(110*2)`} +>\`)`} /> Here we switch between 3/4 and 4/4, keeping the same tempo. @@ -121,10 +143,11 @@ If we don't specify the length, we get what's called a metric modulation: \`).cpm(110/2)`} +>\`)`} /> Now the 3 elements get the same time as the 4 elements, which is why the tempo changes. diff --git a/website/src/pages/workshop/first-notes.mdx b/website/src/pages/workshop/first-notes.mdx index f3d74ddce..56312a355 100644 --- a/website/src/pages/workshop/first-notes.mdx +++ b/website/src/pages/workshop/first-notes.mdx @@ -216,8 +216,9 @@ Finding the right notes can be difficult.. Scales are here to help: ") -.scale("C:minor").sound("piano").cpm(60)`} + tune={`setcpm(60) +n("0 2 4 <[6,8] [7,9]>") +.scale("C:minor").sound("piano")`} punchcard /> @@ -242,9 +243,10 @@ Just like anything, we can automate the scale with a pattern: , 2 4 <[6,8] [7,9]>") + tune={`setcpm(60) +n("<0 -3>, 2 4 <[6,8] [7,9]>") .scale("/4") -.sound("piano").cpm(60)`} +.sound("piano")`} punchcard /> @@ -275,9 +277,10 @@ Try changing that number! *2") + tune={`setcpm(60) +n("<[4@2 4] [5@2 5] [6@2 6] [5@2 5]>*2") .scale("/4") -.sound("gm_acoustic_bass").cpm(60)`} +.sound("gm_acoustic_bass")`} punchcard /> @@ -291,7 +294,12 @@ This is also sometimes called triplet swing. You'll often find it in blues and j **Replicate** -]").sound("piano").cpm(60)`} punchcard /> +]").sound("piano")`} + punchcard +/> diff --git a/website/src/pages/workshop/first-sounds.mdx b/website/src/pages/workshop/first-sounds.mdx index 9c33cdc3c..74daf4bab 100644 --- a/website/src/pages/workshop/first-sounds.mdx +++ b/website/src/pages/workshop/first-sounds.mdx @@ -166,9 +166,14 @@ Try also changing the number at the end to change the tempo! -**changing the tempo with cpm** +**changing the tempo with setcpm** -*8").cpm(90/4)`} punchcard /> +*8")`} + punchcard +/> @@ -283,12 +288,12 @@ This is what we've learned so far: The Mini-Notation is usually used inside some function. These are the functions we've seen so far: -| Name | Description | Example | -| ----- | ----------------------------------- | --------------------------------------------------------------------------------- | -| sound | plays the sound of the given name | | -| bank | selects the sound bank | | -| cpm | sets the tempo in cycles per minute | | -| n | select sample number | | +| Name | Description | Example | +| ------ | ----------------------------------- | --------------------------------------------------------------------------------- | +| sound | plays the sound of the given name | | +| bank | selects the sound bank | | +| setcpm | sets the tempo in cycles per minute | | +| n | select sample number | | ## Examples @@ -296,8 +301,8 @@ The Mini-Notation is usually used inside some function. These are the functions @@ -314,14 +319,20 @@ Certain drum patterns are reused across genres. We Will Rock you - + **Yellow Magic Orchestra - Firecracker** @@ -329,12 +340,14 @@ We Will Rock you @@ -342,12 +355,13 @@ We Will Rock you @@ -355,11 +369,11 @@ We Will Rock you diff --git a/website/src/pages/workshop/pattern-effects.mdx b/website/src/pages/workshop/pattern-effects.mdx index c9cb7b462..ad84a9ab3 100644 --- a/website/src/pages/workshop/pattern-effects.mdx +++ b/website/src/pages/workshop/pattern-effects.mdx @@ -68,9 +68,10 @@ Try commenting out one or more by adding `//` before a line >")) + tune={`setcpm(60) +note("c2 [eb3,g3] ".add("<0 <1 -1>>")) .color(">").adsr("[.1 0]:.2:[1 0]") -.sound("gm_acoustic_bass").room(.5).cpm(60)`} +.sound("gm_acoustic_bass").room(.5)`} punchcard /> @@ -84,9 +85,10 @@ We can add as often as we like: >").add("0,7")) + tune={`setcpm(60) +note("c2 [eb3,g3]".add("<0 <1 -1>>").add("0,7")) .color(">").adsr("[.1 0]:.2:[1 0]") -.sound("gm_acoustic_bass").room(.5).cpm(60)`} +.sound("gm_acoustic_bass").room(.5)`} punchcard /> diff --git a/website/src/pages/workshop/recap.mdx b/website/src/pages/workshop/recap.mdx index e2b661f56..08e243cef 100644 --- a/website/src/pages/workshop/recap.mdx +++ b/website/src/pages/workshop/recap.mdx @@ -56,13 +56,13 @@ This page is just a listing of all functions covered in the workshop! ## Pattern Effects -| name | description | example | -| ---- | ----------------------------------- | ----------------------------------------------------------------------------------- | -| cpm | sets the tempo in cycles per minute | | -| fast | speed up | | -| slow | slow down | | -| rev | reverse | | -| jux | split left/right, modify right | | -| add | add numbers / notes | ")).scale("C:minor")`} /> | -| ply | speed up each event n times | ")`} /> | -| off | copy, shift time & modify | x.speed(2))`} /> | +| name | description | example | +| ------ | ----------------------------------- | ----------------------------------------------------------------------------------- | +| setcpm | sets the tempo in cycles per minute | | +| fast | speed up | | +| slow | slow down | | +| rev | reverse | | +| jux | split left/right, modify right | | +| add | add numbers / notes | ")).scale("C:minor")`} /> | +| ply | speed up each event n times | ")`} /> | +| off | copy, shift time & modify | x.speed(2))`} /> | diff --git a/website/src/repl/Repl.jsx b/website/src/repl/Repl.jsx index e46eb7171..813f63897 100644 --- a/website/src/repl/Repl.jsx +++ b/website/src/repl/Repl.jsx @@ -1,6 +1,6 @@ /* Repl.jsx - -Copyright (C) 2022 Strudel contributors - see +Copyright (C) 2022 Strudel contributors - see 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 . */ diff --git a/website/src/repl/components/Header.jsx b/website/src/repl/components/Header.jsx index 2963a5f97..ca4e1ecf9 100644 --- a/website/src/repl/components/Header.jsx +++ b/website/src/repl/components/Header.jsx @@ -93,7 +93,7 @@ export function Header({ context, embedded = false }) { > {!isEmbedded && update} - {!isEmbedded && ( + {/* !isEmbedded && ( - )} + ) */} {!isEmbedded && (
); @@ -250,6 +250,11 @@ export function PatternsTab({ context }) { const { patternFilter } = useSettings(); return ( +
+ +
+ ); + /* return (
)}
- ); + ); */ } diff --git a/website/src/repl/components/panel/Reference.jsx b/website/src/repl/components/panel/Reference.jsx index 18721eb43..1b617341b 100644 --- a/website/src/repl/components/panel/Reference.jsx +++ b/website/src/repl/components/panel/Reference.jsx @@ -21,7 +21,11 @@ export function Reference() { return true; } - return entry.name.includes(search) || (entry.synonyms?.some((s) => s.includes(search)) ?? false); + const lowCaseSearch = search.toLowerCase(); + return ( + entry.name.toLowerCase().includes(lowCaseSearch) || + (entry.synonyms?.some((s) => s.includes(lowCaseSearch)) ?? false) + ); }); }, [search]); @@ -70,7 +74,7 @@ export function Reference() {
    {entry.params?.map(({ name, type, description }, i) => (
  • - {name} : {type.names?.join(' | ')} {description ? <> - {getInnerText(description)} : ''} + {name} : {type?.names?.join(' | ')} {description ? <> - {getInnerText(description)} : ''}
  • ))}
diff --git a/website/src/repl/components/panel/WelcomeTab.jsx b/website/src/repl/components/panel/WelcomeTab.jsx index 1049a581d..7f0ee70ab 100644 --- a/website/src/repl/components/panel/WelcomeTab.jsx +++ b/website/src/repl/components/panel/WelcomeTab.jsx @@ -15,17 +15,18 @@ export function WelcomeTab({ context }) {
1. hit play - 2. change something -{' '} 3. hit update -
- If you don't like what you hear, try shuffle! + {/*
+ If you don't like what you hear, try shuffle! */}

- To learn more about what this all means, check out the{' '} + {/* To learn more about what this all means, check out the{' '} */} + To get started, check out the{' '} interactive tutorial . Also feel free to join the{' '} - tidalcycles discord channel + discord channel {' '} to ask any questions, give feedback or just say hello.

@@ -37,12 +38,12 @@ export function WelcomeTab({ context }) { , which is a popular live coding language for music, written in Haskell. Strudel is free/open source software: you can redistribute and/or modify it under the terms of the{' '} - + GNU Affero General Public License . You can find the source code at{' '} - - github + + codeberg . You can also find licensing info{' '} for the default sound banks there. Please consider to{' '} diff --git a/website/src/repl/tunes.mjs b/website/src/repl/tunes.mjs index cffe0c0eb..f1c72b12d 100644 --- a/website/src/repl/tunes.mjs +++ b/website/src/repl/tunes.mjs @@ -1,6 +1,6 @@ /* tunes.mjs - -Copyright (C) 2022 Strudel contributors - see +Copyright (C) 2022 Strudel contributors - see 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 . */ @@ -69,32 +69,32 @@ stack( export const giantSteps = `// John Coltrane - Giant Steps -let melody = note( +let melody = seq( "[F#5 D5] [B4 G4] Bb4 [B4 A4]", "[D5 Bb4] [G4 Eb4] F#4 [G4 F4]", "Bb4 [B4 A4] D5 [D#5 C#5]", "F#5 [G5 F5] Bb5 [F#5 F#5]", -) +).note() stack( // melody melody.color('#F8E71C'), // chords - chord( + seq( "[B^7 D7] [G^7 Bb7] Eb^7 [Am7 D7]", "[G^7 Bb7] [Eb^7 F#7] B^7 [Fm7 Bb7]", "Eb^7 [Am7 D7] G^7 [C#m7 F#7]", "B^7 [Fm7 Bb7] Eb^7 [C#m7 F#7]" - ).dict('lefthand') + ).chord().dict('lefthand') .anchor(melody).mode('duck') .voicing().color('#7ED321'), // bass - note( + seq( "[B2 D2] [G2 Bb2] [Eb2 Bb3] [A2 D2]", "[G2 Bb2] [Eb2 F#2] [B2 F#2] [F2 Bb2]", "[Eb2 Bb2] [A2 D2] [G2 D2] [C#2 F#2]", "[B2 F#2] [F2 Bb2] [Eb2 Bb3] [C#2 F#2]" - ).color('#00B8D4') + ).note().color('#00B8D4') ).slow(20) .pianoroll({fold:1})`; @@ -313,26 +313,6 @@ stack( ) .fast(2/3) .pianoroll()`; -/* -export const bridgeIsOver = `// "Bridge is over" -// @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ -// @by Felix Roos, bassline by BDP - The Bridge Is Over - -samples({mad:'https://freesound.org/data/previews/22/22274_109943-lq.mp3'}) -stack( - stack( - note("c3*2 [[c3@1.4 bb2] ab2] gb2*2 <[[gb2@1.4 ab2] bb2] gb2>") - .gain(.8).clip("[.5 1]*2"), - n("<0 1 2 3 4 3 2 1>") - .clip(.5) - .echoWith(8, 1/32, (x,i)=>x.add(n(i)).velocity(Math.pow(.7,i))) - .scale('c4 whole tone') - .echo(3, 1/8, .5) - ).piano(), - s("mad").slow(2) -).cpm(78).slow(4) - .pianoroll() -`; */ export const goodTimes = `// "Good times" // @license CC BY-NC-SA 4.0 https://creativecommons.org/licenses/by-nc-sa/4.0/ diff --git a/website/src/repl/useReplContext.jsx b/website/src/repl/useReplContext.jsx index f88e5a6e2..12621c50a 100644 --- a/website/src/repl/useReplContext.jsx +++ b/website/src/repl/useReplContext.jsx @@ -1,6 +1,6 @@ /* Repl.jsx - -Copyright (C) 2022 Strudel contributors - see +Copyright (C) 2022 Strudel contributors - see 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 . */ @@ -59,6 +59,8 @@ async function getModule(name) { return modules.find((m) => m.packageName === name); } +const initialCode = `// LOADING`; + export function useReplContext() { const { isSyncEnabled, audioEngineTarget } = useSettings(); const shouldUseWebaudio = audioEngineTarget !== audioEngineTargets.osc; @@ -77,7 +79,7 @@ export function useReplContext() { transpiler, autodraw: false, root: containerRef.current, - initialCode: '// LOADING', + initialCode, pattern: silence, drawTime, drawContext, @@ -133,9 +135,10 @@ export function useReplContext() { code = latestCode; msg = `Your last session has been loaded!`; } else { - const { code: randomTune, name } = await getRandomTune(); - code = randomTune; - msg = `A random code snippet named "${name}" has been loaded!`; + /* const { code: randomTune, name } = await getRandomTune(); + code = randomTune; */ + code = '$: s("[bd ]*2").bank("tr909").dec(.4)'; + msg = `Default code has been loaded`; } editor.setCode(code); setDocumentTitle(code); diff --git a/website/src/repl/util.mjs b/website/src/repl/util.mjs index f49dd568f..ac27158f4 100644 --- a/website/src/repl/util.mjs +++ b/website/src/repl/util.mjs @@ -7,7 +7,7 @@ import './Repl.css'; import { createClient } from '@supabase/supabase-js'; import { nanoid } from 'nanoid'; import { writeText } from '@tauri-apps/plugin-clipboard-manager'; -import { $featuredPatterns, loadDBPatterns } from '@src/user_pattern_utils.mjs'; +import { $featuredPatterns /* , loadDBPatterns */ } from '@src/user_pattern_utils.mjs'; // Create a single supabase client for interacting with your database export const supabase = createClient( @@ -16,9 +16,9 @@ export const supabase = createClient( ); let dbLoaded; -if (typeof window !== 'undefined') { +/* if (typeof window !== 'undefined') { dbLoaded = loadDBPatterns(); -} +} */ export async function initCode() { // load code from url hash (either short hash from database or decode long hash) @@ -84,6 +84,7 @@ export function loadModules() { import('@strudel/gamepad'), import('@strudel/motion'), import('@strudel/mqtt'), + import('@strudel/mondo'), ]; if (isTauri()) { modules = modules.concat([ @@ -109,38 +110,56 @@ export function confirmDialog(msg) { } let lastShared; -export async function shareCode(codeToShare) { - // const codeToShare = activeCode || code; - if (lastShared === codeToShare) { - logger(`Link already generated!`, 'error'); - return; - } - confirmDialog( - 'Do you want your pattern to be public? If no, press cancel and you will get just a private link.', - ).then(async (isPublic) => { - const hash = nanoid(12); - const shareUrl = window.location.origin + window.location.pathname + '?' + hash; - const { error } = await supabase.from('code_v1').insert([{ code: codeToShare, hash, ['public']: isPublic }]); - if (!error) { - lastShared = codeToShare; - // copy shareUrl to clipboard - if (isTauri()) { - await writeText(shareUrl); - } else { - await navigator.clipboard.writeText(shareUrl); - } - const message = `Link copied to clipboard: ${shareUrl}`; - alert(message); - // alert(message); - logger(message, 'highlight'); +//RIP due to SPAM +// export async function shareCode(codeToShare) { +// // const codeToShare = activeCode || code; +// if (lastShared === codeToShare) { +// logger(`Link already generated!`, 'error'); +// return; +// } + +// confirmDialog( +// 'Do you want your pattern to be public? If no, press cancel and you will get just a private link.', +// ).then(async (isPublic) => { +// const hash = nanoid(12); +// const shareUrl = window.location.origin + window.location.pathname + '?' + hash; +// const { error } = await supabase.from('code_v1').insert([{ code: codeToShare, hash, ['public']: isPublic }]); +// if (!error) { +// lastShared = codeToShare; +// // copy shareUrl to clipboard +// if (isTauri()) { +// await writeText(shareUrl); +// } else { +// await navigator.clipboard.writeText(shareUrl); +// } +// const message = `Link copied to clipboard: ${shareUrl}`; +// alert(message); +// // alert(message); +// logger(message, 'highlight'); +// } else { +// console.log('error', error); +// const message = `Error: ${error.message}`; +// // alert(message); +// logger(message); +// } +// }); +// } + +export async function shareCode() { + try { + const shareUrl = window.location.href; + if (isTauri()) { + await writeText(shareUrl); } else { - console.log('error', error); - const message = `Error: ${error.message}`; - // alert(message); - logger(message); + await navigator.clipboard.writeText(shareUrl); } - }); + const message = `Link copied to clipboard!`; + alert(message); + logger(message, 'highlight'); + } catch (e) { + console.error(e); + } } export const isIframe = () => window.location !== window.parent.location; diff --git a/website/src/user_pattern_utils.mjs b/website/src/user_pattern_utils.mjs index 18442cb4c..791c6a8f9 100644 --- a/website/src/user_pattern_utils.mjs +++ b/website/src/user_pattern_utils.mjs @@ -96,7 +96,7 @@ export async function loadDBPatterns() { } } -// reason: https://github.com/tidalcycles/strudel/issues/857 +// reason: https://codeberg.org/uzu/strudel/issues/857 const $activePattern = sessionAtom('activePattern', ''); export function setActivePattern(key) {