diff --git a/Instruments.md b/Instruments.md index ebe1050..b7ebffa 100644 --- a/Instruments.md +++ b/Instruments.md @@ -1,7 +1,7 @@ An **instrument** (**+ → Instrument**, #331) makes a note a sound. It is a node graph like a [sound FX](Sounds#sound-fx): **Notes** nodes bring the note in, a **Sample** plays a sound for it -or a **Chip** makes one the way old game consoles did (#329), an **Acid** plays a bass line as the -Roland TB-303 does (#421), the effects of a sound FX shape it +or a **Chip** makes one the way old game consoles did (#329), an **Acid** plays an acid bass line as +nodeseq.com does (#421), the effects of a sound FX shape it and the **Output** is what is heard. It is the first piece of the music tools planned in #330: a [pattern](Patterns) (#333) plays one, and a [song](Songs) (#334) lays patterns out. A behavior plays one with **Play note**, and the tab plays it from its keys. @@ -23,7 +23,7 @@ A at 440 Hz, 127 the highest. A note is named with its octave after it and no da | **Notes** | Where the notes come in. `any` triggers what it is connected to for every note from **From** to **To**. `note` is the number of the note played, `frequency` its pitch in Hz (`440 * 2^((note - 69) / 12)`), `velocity` how hard it is struck, 0 to 1. With **Single notes** there is one trigger output for every note in the range as well, and each can have a **name** of its own (*Name of C2* in the inspector): the output shows it, and so do the keys of the [piano roll](Patterns#the-pattern-tab). | | **Sample** | Plays a sound asset when its `trigger` is on. With **Follow note** (on by default) it plays higher or lower by the distance from **Root**, the note it sounds as it is (C4 by default), and **keeps its length** — it is played by the same worklet as a Pitch (#317). Off, every note plays it as it is, as for a drum. **Play** says how it is played while the note is held and once it is let go (table below). **Loop start** and **Loop end** are in seconds, 0 at the end being the end of the sound, counted in the stretch of the [clip](Sounds#clips) when it plays one. The volume of the sound asset counts. | | **Chip** | Makes a raw wave for every note that triggers it, the way sfxr makes its sounds: square, triangle, saw, sine or noise, with a slide, vibrato, an arpeggio and repeat. It needs no sound file. See [Chips](#chips). | -| **Acid** | A bass line in the way of the Roland TB-303: a saw or a square through a resonant low-pass that saturates, swept open on every note, with accent and slide. It needs no sound file. See [Acid](#acid). | +| **Acid** | An acid bass line as nodeseq.com plays it: a saw or a square through a low-pass with a sharp resonance, one ADSR on volume and filter, a glide from the note before. It needs no sound file. See [Acid](#acid). | | **Parameters** | The instrument's own parameters, each a number output with a default — like the parameters of a sound FX's Input. One per instrument. | | **Sound FX** | Sends the sound through a sound FX asset, as if it were the sound at the FX's Input. Every parameter of that FX is an input here; one left open is the FX's default. The node is titled after the FX, and a double click opens it. | | **Envelope** | Shapes a note as it is played: **Attack**, **Decay** and **Release** in seconds, **Sustain** from 0 to 1. On the way of the sound it is its volume; its output `value` moves a number with the same curve, from **From** to **To**. See [Envelopes](#envelopes). | @@ -264,66 +264,71 @@ from 0 to 1, the Chip counts in Hz, semitones and seconds. ## Acid -An **Acid** (#421) plays a bass line the way the Roland TB-303 does: one oscillator, a resonant -low-pass that squeals and saturates, a sweep that opens it on every note, accent and slide. Notes -`any` into its `trigger`, its `out` to the Output. It always plays the pitch of the note. +An **Acid** (#421) plays an acid bass line the way [nodeseq.com](https://nodeseq.com/studio) +plays its *Acid* bass: one oscillator, a low-pass with a sharp resonance, and one ADSR that shapes +the volume and sweeps the filter alike. Its defaults are nodeseq's acid. Notes `any` into its +`trigger`, its `out` to the Output. It always plays the pitch of the note. | Input | Default | What it does | | --- | --- | --- | | **Wave** (setting) | Saw | *Saw* or *Square*, band-limited (PolyBLEP), so a high note does not alias. | -| **Tuning (st)** | 0 | Semitones off the note, -12 to 12. | -| **Cutoff (Hz)** | 400 | Where the filter rests, 30 Hz to 12 kHz. | -| **Resonance** | 0.7 | How sharply it rings at the cutoff, 0 to 1. At 1 it is just short of whistling by itself. It takes some of the bass, as the 303's does. | -| **Env mod** | 0.5 | How far every note opens the filter: up to four octaves above the cutoff at 1. | -| **Decay (s)** | 0.5 | How long the filter takes to fall back, 0.2 to 2 s (to 1 % of the sweep). Grey while *Env mod* is 0. | -| **Accent** | 0.6 | How much an accented note stands out, 0 to 1. | -| **Slide time (s)** | 0.06 | How long a slide takes to all but reach its note (95 %). The 303's is about 60 ms. | +| **Cutoff (Hz)** | 250 | Where the filter rests, 40 Hz to 18 kHz. nodeseq's Cutoff 0 to 100 is 40 Hz × 2^(8.8 × value/100), so its 30 is 250 Hz. | +| **Resonance** | 0.78 | How sharply it rings at the cutoff, 0 to 1: a Q of 0.5 dB to 17.5 dB, as nodeseq's Reso 0 to 100. | +| **Env mod** | 0.62 | How far the envelope opens the filter: up to five octaves above the cutoff at 1, never above 18 kHz. At 0.62 that is 3.1 octaves, from 250 Hz up to 2.14 kHz. | +| **Attack (s)** | 0.003 | How long volume and filter take to climb to the top, in a straight line. | +| **Decay (s)** | 0.16 | How long they take to fall back to where the sustain holds them. Grey at *Sustain* 1 while *Env mod* is 0. | +| **Sustain** | 0.15 | Where the volume stays while the note is held, 0 to 1. The filter stays at 60 % of that up its sweep: at the defaults 420 Hz. | +| **Release (s)** | 0.08 | How long volume and filter take to close after the note ends. | +| **Glide (s)** | 0.05 | How long the pitch takes to glide from the note before. 0 does not glide. | -- **Accent comes from the velocity:** a note at **0.9 or more** is accented. The piano roll strikes - at 0.8 unless told otherwise, so a plain note is not. An accented note is up to twice as loud - (+6 dB), its sweep falls in 0.2 s whatever *Decay* says, and the filter opens further, up to two - octaves more with *Accent* and *Resonance* at full. That extra goes through a "capacitor" that - charges with a time constant of 20 ms and empties with one of 0.25 s, as the 303's accent - circuit does: accents a sixteenth apart start each one from what the last left, so a run of - them climbs. -- **Slide:** a note that starts while the note before it on the same Acid still plays, or right - where it ends (within 2 ms), **slides**: the pitch glides there in *Slide time* and nothing is - struck anew, so the sweep and the volume run on. The note before is silent from the first sample - of the new one. Notes that start at the same moment do not slide, they all play. In the piano - roll a slide is two notes that touch or overlap. On the keys it is a key struck while the one - before is still held. -- **Its own volume:** the sound rises in 3 ms and dies away slowly (a time constant of 1.6 s) while - the note is held, and ends with the note, faded out in 10 ms. An **Envelope** behind it starts over - with every note, a slide too, so it is better left out. -- **On the audio thread** (AudioParams): *Cutoff*, *Resonance*, *Env mod*, *Decay* and *Accent*. An - Envelope can move *Cutoff*, `Set sound FX parameters` reaches any of them fed by a parameter. A - **Pitch** behind it moves its frequency. *Wave*, *Tuning* and *Slide time* are fixed when the note - starts. -- **How it is made:** a worklet per note, in the same module as the Chip. The oscillator and the - filter run at twice the rate and are averaged down. The filter is four one-pole low-passes in a - row with feedback, solved without a delay in the loop (Zavalishin's topology-preserving - transform), the signal into it saturated by `tanh`. The notes of one Acid that slide into each - other share one synth on the audio thread: it runs once per block, and each note hears the part of - the block that is its own, so it makes no difference which note the audio thread runs first. -- **The node is a device** (#421), 520x280, in the brushed silver of the 303. The inputs are on the +- **Why it squelches:** the filter falls back **in Hz, not in octaves**, as a Web Audio + `setTargetAtTime` on the cutoff does. So it drops fast from the top and lingers low. With the + sharp two-pole resonance on top, that makes the "yow". Attack, decay and release work as + nodeseq automates them: the attack is a straight ramp, decay and release are `setTargetAtTime` + with a time constant of a third and a quarter of their time. +- **Every note strikes anew** and glides from the pitch of the note before on the same Acid, also + after a gap of up to 2 s. A note that starts while the one before still sounds **cuts it short**, + in a fall with a time constant of 6 ms, gone after 60 ms. Notes that start at the same moment do + not glide and do not cut, they all play. +- **Its own volume:** it plays out its *Release* when the note ends and stops after 1.6 releases + and 50 ms, as nodeseq stops its oscillator. So it **needs no Envelope behind it.** An Envelope + there would shape it a second time. +- **Velocity** makes it louder or softer. A note at velocity 1 is as loud as nodeseq's bass at its + volume 0.8. +- **On the audio thread** (AudioParams): *Cutoff*, *Resonance* and *Env mod*. An Envelope can move + *Cutoff*, `Set sound FX parameters` reaches any of them fed by a parameter. A **Pitch** behind + it moves its frequency. *Wave*, the ADSR and *Glide* are fixed when the note starts. The note's + end and the cut are set on the audio clock, to the sample. +- **How it is made:** a worklet per note, in the same module as the Chip. The filter is a + state-variable filter solved without a delay in the loop (Simper's). It has the same curve as the + cookbook biquad that Chrome's `BiquadFilterNode` runs, and it stays steady however fast the cutoff + moves. Like that node it takes its Q in dB. +- **Grit** comes from behind it: nodeseq runs its whole mix through a `tanh` drive, a compressor and + a limiter. Here that is a **Saturation**, a **Compressor** and a **Limiter** after the Acid or on + the master. +- **The node is a device** (#421), 580x280, in the brushed silver of the 303. The inputs are on the left, `out` on the right: - a **button for each wave** at the top, the one playing lit in orange; - - a dark screen, **Filter over a note**: the filter where it rests in dull green, at the top of a - plain note's sweep in acid green, the room between them lit faintly, 20 Hz to 20 kHz, +24 to - -48 dB. It says where it rests and how far a note sweeps it; the bezel says how far a full - accent takes it; - - a **chrome knob** for every number, grouped *Filter* (cutoff, resonance, env mod, decay) and - *Note* (tuning, accent, slide). A drag is one undo step, a double-click goes back to the - default. The tuning has 0 straight up. + - a dark screen, **Filter over a note**: the filter where it rests in dull green, at the top of + its sweep in acid green, the room between them lit faintly, 20 Hz to 20 kHz, +24 to -48 dB. It + says where it rests and how far a note sweeps it; the bezel says where the sustain holds it; + - a **chrome knob** for every number, grouped *Filter* (cutoff, resonance, env), *Envelope* + (attack, decay, sustain, release) and *Note* (glide). A drag is one undo step, a double-click + goes back to the default. The screen is drawn from the settings, not listened to. The [presets](#presets-randomize-and-mutate) give an Acid three columns of room; Randomize and Mutate leave it as it is. -- **Measured** (2026-10-13, e2e 34, Chrome offline on the Windows runner): two notes on one line, - the second sliding in at 0.1 s. The first was heard from sample 0 to 4799, the second from 4800 - to the end. Where it took over, the sound of both together moved by 0.0012 from one sample to - the next, the largest step anywhere else was 0.10. Live, of three notes at 120 BPM the second, - struck where the first ended, slid, the third after half a second's gap did not. +- **Measured against nodeseq** (2026-10-13, Chrome offline): five sixteenths at 132 BPM, played by + the worklet and by nodeseq's voice built from Web Audio nodes as its studio builds it. In every + 10 ms window the two were no more than 0.14 dB apart in loudness, and 95 to 97 % of the windows + were within a tenth of an octave in brightness (the others are dark windows where only the place + of the saw's jump differs). That held at the defaults, at full resonance and sweep, without + resonance, and with a slow ADSR. e2e 34 checks it. +- **A Chrome quirk found on the way:** an `OscillatorNode` started inside a block of 128 samples, + with its frequency automated, runs its first samples at the frequency it had before it started + (440 Hz unless told otherwise). So in nodeseq every note but those on a block's start begins at + a phase at random. Nobody hears that, and the Acid starts every note at the same phase. ## The Instrument tab @@ -398,7 +403,7 @@ The instruments, all on every note unless the table says otherwise: | *Pluck* | Square at duty 0.3 → Envelope → Low-pass that snaps shut from 6 kHz to 600 Hz | dies away | | *Triangle Bass* | The triangle of the NES → Envelope at full sustain | held | | *Pluck Bass* | Saw → Envelope → resonant Low-pass that snaps open to 2.4 kHz and falls back towards 180 Hz | held | -| *Acid Bass* | [Acid](#acid), a saw at cutoff 300 Hz, resonance 0.8, env mod 0.6, decay 0.4 s, accent 0.7 → Delay, a dotted eighth, a little of it | dies away slowly, slides | +| *Acid Bass* | [Acid](#acid) with nodeseq's acid: a saw at cutoff 250 Hz, resonance 0.78, env mod 0.62, attack 3 ms, decay 0.16 s, sustain 0.15, release 80 ms, glide 50 ms → Delay, a dotted eighth, a little of it | squelches, glides | | *Drum Kit* | *Single notes* 36 to 46: Kick on C2, Snare on D2, Closed hat on F#2, Open hat on A#2, as in General MIDI | each ends by itself | The kick is a sine that drops from 160 Hz until it is below 30 Hz, the snare a falling triangle and