Tonejs / Tone.js

A Web Audio framework for making interactive music in the browser.
https://tonejs.github.io
MIT License
13.37k stars 975 forks source link

Way to obtain control limitations for each control on each module #988

Open indignant opened 2 years ago

indignant commented 2 years ago

The feature you'd like Hey there! First let me say that I've used Tone.js on a few projects now and it's always loads of fun - thank you for maintaining the project.

Right now I'm working on a UI that will let users control each Tone effect. So for example, I'm making a React component that maps to the controls of Chebyshev. I've never heard of a Chebyshev, so I'm just looking at the API docs and I see that order is a Positive and that's A number greater than or equal to 0. Great! But what does that really mean? A number between 0-1000000? Like I'm making a range element that takes a min, max, and step. So I try 0-10 with a step of 0.01, but that causes the app to crash. So I look through the code and see it's 1-100 and must be an integer (step 1). Perfect! So I plug that in and get a Unhandled Rejection (InternalError): too much recursion at a setting of 1. By now I can't tell if this is a Tone bug or my misunderstanding of the range 😂

This is just one control on one effect and I imagine anyone making a UI for Tone or making sound procedurally must go through this process. Even with TypeScript and marking order with Positive wouldn't really be enough because order is a specific type of Positive.

tl;dr: I think for each component/effect, it would be useful to have min/max/step data on each control similar to how there's initial data for modules. UI developers can plug/play for their control elements and programmatic Tonesters can use it in their generative functions.

Any alternatives you've considered Honestly a giant control tree would be boss:

{
  'Chebyshev': {
    module: Chebyshev,
    controls: {
      'order': {
        min: 1,
        max: 100,
        step: 1
      },
      'oversampling': [
        'none',
        '2x',
        '4x',
      ]
    }
  },
  'Auto Wah': { /* ...etc... */ }
}

Probably too UI-focused though. I feel like just having this data on the individual modules would be more than enough.

tambien commented 2 years ago

Take a look at the data.json which is distributed as part of the npm package. A lot of that information is already in there. You'll see in the code for Chebyshev for example that there is a min and max field in the documentation which is parsed into that json file. I think that you could parse the data.json file and probably generate a simplified description without the rest of the documentation data like you're describing.

A lot of the code also has an assertRange attribute which throws an error if the value is not within the given range (for values which are listed as Positive for example). I guess the value should be set to 2-100 instead of 1-100 if you're getting an error with Chebyshev.

indignant commented 2 years ago

Oh, that is interesting; data.json does have a lot of this stuff. It also highlights a problem with my original feature request: since individual elements inherit from other elements they can end up with a ton of configurable pieces. I imagine trying to manage all of that manually would be a nightmare (although at the same time, I guess if it's inheriting bits, it could inherit the min/max/step if they were set in code).

Just looking at the order though, it's clear there would be an issue with just parsing the JSON:

{
  "id": 15450,
  "name": "__set",
  "kind": 1048576,
  "kindString": "Set signature",
  "flags": {
    "isExported": true
  },
  "comment": {
    "shortText": "The order of the Chebyshev polynomial which creates the equation which is applied to the incoming\nsignal through a Tone.WaveShaper. The equations are in the form:\n```\norder 2: 2x^2 + 1\norder 3: 4x^3 + 3x\n```",
    "tags": [
      {
        "tag": "min",
        "text": "1"
      },
      {
        "tag": "max",
        "text": "100\n"
      }
    ]
  },
  "parameters": [
    {
      "id": 15451,
      "name": "order",
      "kind": 32768,
      "kindString": "Parameter",
      "flags": {
        "isExported": true
      },
      "type": {
        "type": "intrinsic",
        "name": "number"
      }
    }
  ],
  "type": {
    "type": "intrinsic",
    "name": "void"
  }
}

There's no step (and this control requires an int) and the min/max values are formatted for text output (the max includes a \n). Is this file made by typedoc? Makes me wonder if there's a way to autogenerate the file I'm suggesting in a similar way, but rather than outputting JSON for docs outputting JSON for devs to use. Just seems like it would be cool to have a way to instantly bootstrap a modular system without ever having to look at the docs for control constraints; Create-React-App for new web audio nerds. Speaking of which, do you know of an OS project using Tone that's a full modular system using the pre-baked Tone modules? Maybe I could see how they do things.

Really though, the more I think about it it seems like there really should be a way to autogenerate this data: for most of these inputs the type matches the expectation (unlike Chebyshev's order which is a "Positive" but constrained to 1-100 in steps of 1, Chorus' depth is a Normal which can accept anything between 0-1) so it would just be a matter of overwriting the type's base expectation or using a more accurate type. Then ??? and profit. I'm not familiar with TypeScript, so I'm not sure how to grab those types into something more basic for people to use.