at.atmosynth.module

atmosynth.at

Schema Diff

+25 -4

From

CID
bafyreifgfuhftmi...
Indexed At
2026-09-18 11:56 UTC
View this version

To

CID
bafyreigkd32vga2...
Indexed At
2026-10-06 13:10 UTC
View this version

Compatibility Analysis

Breaking Changes Detected

1 breaking change, 4 non-breaking changes.

Breaking Changes (1)
  • ConstraintTightened ConstraintTightened { vertex_id: "at.atmosynth.module:body.noteSource", sort: "knownValues", old_value: "[\"keyboard\",\"sequencer\"]", new_value: "[\"keyboard\",\"sequencer\",\"range\",\"transpose\",\"assign\",\"oneshot\"]" }
Non-Breaking Changes (4)
  • AddedVertex AddedVertex { vertex_id: "at.atmosynth.module#parameter.scale" }
  • AddedVertex AddedVertex { vertex_id: "at.atmosynth.module:body.patternSource" }
  • AddedEdge AddedEdge { src: "at.atmosynth.module#parameter", tgt: "at.atmosynth.module#parameter.scale", kind: "prop", name: Some("scale") }
  • AddedEdge AddedEdge { src: "at.atmosynth.module:body", tgt: "at.atmosynth.module:body.patternSource", kind: "prop", name: Some("patternSource") }

Migration Guidance

Added Elements

  • AddedVertex { vertex_id: "at.atmosynth.module#parameter.scale" }
  • AddedVertex { vertex_id: "at.atmosynth.module:body.patternSource" }

Constraint Changes

  • ConstraintTightened ConstraintTightened { vertex_id: "at.atmosynth.module:body.noteSource", sort: "knownValues", old_value: "[\"keyboard\",\"sequencer\"]", new_value: "[\"keyboard\",\"sequencer\",\"range\",\"transpose\",\"assign\",\"oneshot\"]" }

Additional Notes

  • Non-breaking: AddedEdge { src: "at.atmosynth.module#parameter", tgt: "at.atmosynth.module#parameter.scale", kind: "prop", name: Some("scale") }
  • Non-breaking: AddedEdge { src: "at.atmosynth.module:body", tgt: "at.atmosynth.module:body.patternSource", kind: "prop", name: Some("patternSource") }
1 1
{
2 2
  "$type": "com.atproto.lexicon.schema",
3 3
  "defs": {
4 4
    "automation": {
5 5
      "description": "A breakpoint envelope scheduled onto an AudioParam. Breakpoints up to and including sustainAt are scheduled on note-on; those after it on note-off.",
6 6
      "properties": {
7 7
        "breakpoints": {
8 8
          "items": {
9 9
            "ref": "#breakpoint",
10 10
            "type": "ref"
11 11
          },
12 12
          "minLength": 1,
13 13
          "type": "array"
14 14
        },
15 15
        "id": {
16 16
          "maxLength": 64,
17 17
          "minLength": 1,
18 18
          "type": "string"
19 19
        },
20 20
        "label": {
21 21
          "maxLength": 200,
22 22
          "type": "string"
23 23
        },
24 24
        "sustainAt": {
25 25
          "description": "Index into breakpoints where the value holds while a note is held. Absent means a one-shot.",
26 26
          "minimum": 0,
27 27
          "type": "integer"
28 28
        },
29 29
        "target": {
30 30
          "ref": "#paramTarget",
31 31
          "type": "ref"
32 32
        }
33 33
      },
34 34
      "required": [
35 35
        "id",
36 36
        "target",
37 37
        "breakpoints"
38 38
      ],
39 39
      "type": "object"
40 40
    },
41 41
    "breakpoint": {
42 42
      "properties": {
43 43
        "curve": {
44 44
          "description": "How the value travels from the previous breakpoint.",
45 45
          "knownValues": [
46 46
            "step",
47 47
            "linear",
48 48
            "exponential"
49 49
          ],
50 50
          "type": "string"
51 51
        },
52 52
        "time": {
53 53
          "description": "Milliseconds from the trigger.",
54 54
          "minimum": 0,
55 55
          "type": "integer"
56 56
        },
57 57
        "value": {
58 58
          "description": "The param's value at this instant, as its decimal form — \"0.25\", \"1\" — because the ATproto data model has no floating-point type and a PDS refuses one. An automation is numeric by definition, so a reader turns it back into a number without asking anything else.",
59 59
          "maxLength": 200,
60 60
          "type": "string"
61 61
        }
62 62
      },
63 63
      "required": [
64 64
        "time",
65 65
        "value"
66 66
      ],
67 67
      "type": "object"
68 68
    },
69 69
    "connection": {
70 70
      "description": "An audio connection from a node output to either an audio input or a named AudioParam.",
71 71
      "properties": {
72 72
        "from": {
73 73
          "ref": "#source",
74 74
          "type": "ref"
75 75
        },
76 76
        "to": {
77 77
          "ref": "#target",
78 78
          "type": "ref"
79 79
        }
80 80
      },
81 81
      "required": [
82 82
        "from",
83 83
        "to"
84 84
      ],
85 85
      "type": "object"
86 86
    },
87 87
    "lineage": {
88 88
      "description": "The module this one was forked from, and its author.",
89 89
      "properties": {
90 90
        "authorDid": {
91 91
          "format": "did",
92 92
          "type": "string"
93 93
        },
94 94
        "moduleId": {
95 95
          "maxLength": 128,
96 96
          "type": "string"
97 97
        },
98 98
        "uri": {
99 99
          "format": "at-uri",
100 100
          "type": "string"
101 101
        }
102 102
      },
103 103
      "required": [
104 104
        "uri",
105 105
        "authorDid"
106 106
      ],
107 107
      "type": "object"
108 108
    },
109 109
    "main": {
110 110
      "description": "A module, held in its author's own repository.",
111 111
      "key": "tid",
112 112
      "record": {
113 113
        "properties": {
114 114
          "audioInputs": {
115 115
            "description": "Zero or more individually identified audio inputs.",
116 116
            "items": {
117 117
              "ref": "#port",
118 118
              "type": "ref"
119 119
            },
120 120
            "type": "array"
121 121
          },
122 122
          "audioOutputs": {
123 123
            "items": {
124 124
              "ref": "#port",
125 125
              "type": "ref"
126 126
            },
127 127
            "type": "array"
128 128
          },
129 129
          "automation": {
130 130
            "description": "Scheduled AudioParam envelopes. Web Audio has no envelope node, so these are their own structure.",
131 131
            "items": {
132 132
              "ref": "#automation",
133 133
              "type": "ref"
134 134
            },
135 135
            "type": "array"
136 136
          },
137 137
          "connections": {
138 138
            "items": {
139 139
              "ref": "#connection",
140 140
              "type": "ref"
141 141
            },
142 142
            "type": "array"
143 143
          },
144 144
          "createdAt": {
145 145
            "format": "datetime",
146 146
            "type": "string"
147 147
          },
148 148
          "description": {
149 149
            "maxLength": 3000,
150 150
            "type": "string"
151 151
          },
152 152
          "descriptionVersion": {
153 153
            "description": "Version of the module description this record follows. A reader accepts any version at or below its own.",
154 154
            "minimum": 1,
155 155
            "type": "integer"
156 156
          },
157 157
          "forkedFrom": {
158 158
            "ref": "#lineage",
159 159
            "type": "ref"
160 160
          },
161 161
          "midiInputs": {
162 162
            "description": "Zero or more note inputs. A note input carries note events — what is played, and when — from any note source: a sequencer's note output, or the player, which is the on-screen keys, the computer keyboard and every MIDI device at once. \"MIDI\" is the historical name; nothing arriving here need have come from MIDI.",
163 163
            "items": {
164 164
              "ref": "#port",
165 165
              "type": "ref"
166 166
            },
167 167
            "type": "array"
168 168
          },
169 169
          "moduleId": {
170 170
            "description": "Stable identity shared by every published version of this module.",
171 171
            "maxLength": 128,
172 172
            "type": "string"
173 173
          },
174 174
          "name": {
175 175
            "maxLength": 200,
176 176
            "minLength": 1,
177 177
            "type": "string"
178 178
          },
179 179
          "nodes": {
180 180
            "items": {
181 181
              "ref": "#node",
182 182
              "type": "ref"
183 183
            },
184 184
            "type": "array"
185 185
          },
186 186
          "noteOutputs": {
187 187
            "description": "Zero or more note outputs. A note output carries note events — what is played, and when — rather than a signal, so it is wired to another instance's note input (`midiInputs`) rather than to an audio input. A module that declares one is a source of what is played; a module that declares none is what plays it.",
188 188
            "items": {
189 189
              "ref": "#port",
190 190
              "type": "ref"
191 191
            },
192 192
            "type": "array"
193 193
          },
194 194
          "noteSource": {
195 -
            "description": "What this module emits notes by, where it is not made of Web Audio nodes at all. A note source has no `nodes` and no `connections`: it produces note events, and the behaviour that produces them is named here rather than described, because there is no vocabulary of nodes for a thing that makes no sound. A reader that does not know the named behaviour cannot run the module, and says so rather than guessing.",
195 +
            "description": "What this module emits notes by, where it is not made of Web Audio nodes at all. A note source has no `nodes` and no `connections`: it produces note events, and the behaviour that produces them is named here rather than described, because there is no vocabulary of nodes for a thing that makes no sound. `range` emits none of its own: it passes on the notes wired into its input that fall inside the range its instance names (`noteRange` on the patch's instance), each still the note of whatever played it. `transpose` and `assign` emit none of their own either: they pass on every note wired into their input at another pitch — moved by the semitones the instance names (`transpose`), or at the one note it names (`assignedNote`) — each still the note of whatever played it, and starting and stopping when it did. `oneshot` emits none of its own either: it passes on every note wired into its input lasting as many milliseconds as the instance names (`noteLength`), however long whatever played it held it — each still the note of whatever played it, at its own pitch, and starting when it did. A reader that does not know the named behaviour cannot run the module, and says so rather than guessing.",
196 196
            "knownValues": [
197 197
              "keyboard",
198 -
              "sequencer"
198 +
              "sequencer",
199 +
              "range",
200 +
              "transpose",
201 +
              "assign",
202 +
              "oneshot"
199 203
            ],
200 204
            "maxLength": 64,
201 205
            "type": "string"
202 206
          },
203 207
          "parameters": {
204 208
            "items": {
205 209
              "ref": "#parameter",
206 210
              "type": "ref"
207 211
            },
208 212
            "type": "array"
209 213
          },
214 +
          "patternSource": {
215 +
            "description": "What this module chooses a sequencer's loops by, where it is not made of Web Audio nodes at all. A pattern source emits neither sound nor notes: it is wired to a sequencer's pattern input and decides which of that sequencer's loops is played, and when. Like a note source it has no `nodes` and no `connections`, and its behaviour is named here rather than described. A module is a note source or a pattern source, never both. A reader that does not know the named behaviour cannot run the module, and says so rather than guessing.",
216 +
            "knownValues": [
217 +
              "arranger",
218 +
              "scenes"
219 +
            ],
220 +
            "maxLength": 64,
221 +
            "type": "string"
222 +
          },
210 223
          "sampleSlots": {
211 224
            "items": {
212 225
              "ref": "#sampleSlot",
213 226
              "type": "ref"
214 227
            },
215 228
            "type": "array"
216 229
          },
217 230
          "versionKey": {
218 231
            "description": "The record key this version is published under, repeated in the record so a reader holding only the record knows which version it is. A patch's `moduleRef.versionKey` is this value, and it is the record key a reader fetches: a record whose `versionKey` differs from its own key is pinned by the key and misdescribed by this field.",
219 232
            "maxLength": 128,
220 233
            "type": "string"
221 234
          }
222 235
        },
223 236
        "required": [
224 237
          "descriptionVersion",
225 238
          "moduleId",
226 239
          "name",
227 240
          "description",
228 241
          "audioInputs",
229 242
          "audioOutputs",
230 243
          "midiInputs",
231 244
          "nodes",
232 245
          "connections",
233 246
          "createdAt"
234 247
        ],
235 248
        "type": "object"
236 249
      },
237 250
      "type": "record"
238 251
    },
239 252
    "node": {
240 253
      "description": "One Web Audio node. `kind` is drawn from a closed vocabulary; `options` are its construction-time settings, which are not AudioParams and cannot be modulated.",
241 254
      "properties": {
242 255
        "id": {
243 256
          "maxLength": 64,
244 257
          "minLength": 1,
245 258
          "type": "string"
246 259
        },
247 260
        "kind": {
248 261
          "maxLength": 64,
249 262
          "minLength": 1,
250 263
          "type": "string"
251 264
        },
252 265
        "options": {
253 266
          "description": "The node's settings, as its kind defines them. This is an object — which is the whole of what `unknown` means here — so the data model applies inside it: a whole number is an ordinary integer, and a number that is not a whole number is carried as its decimal string, \"0.5\" rather than 0.5, because the ATproto data model has no floating-point type and a PDS refuses one. That is the one difference from the fields this description types as strings, which have nowhere to put an integer.",
254 267
          "type": "unknown"
255 268
        }
256 269
      },
257 270
      "required": [
258 271
        "id",
259 272
        "kind"
260 273
      ],
261 274
      "type": "object"
262 275
    },
263 276
    "paramTarget": {
264 277
      "properties": {
265 278
        "node": {
266 279
          "maxLength": 64,
267 280
          "minLength": 1,
268 281
          "type": "string"
269 282
        },
270 283
        "param": {
271 284
          "maxLength": 64,
272 285
          "minLength": 1,
273 286
          "type": "string"
274 287
        }
275 288
      },
276 289
      "required": [
277 290
        "node",
278 291
        "param"
279 292
      ],
280 293
      "type": "object"
281 294
    },
282 295
    "parameter": {
283 296
      "description": "A parameter the module exposes for a patch to set. Each target reaches an AudioParam, a construction option, or one field of an automation breakpoint.",
284 297
      "properties": {
285 298
        "default": {
286 299
          "description": "The value used where a patch sets none, always as text. A number is its decimal form — \"0.5\", and \"2\" for a whole one — because the ATproto data model has no floating-point type and a string is the only type that carries both a number and a word. Where this parameter declares a closed value set, it is one of those strings instead, and a label that looks like a number stays the label it is.",
287 300
          "maxLength": 200,
288 301
          "type": "string"
289 302
        },
290 303
        "id": {
291 304
          "maxLength": 64,
292 305
          "minLength": 1,
293 306
          "type": "string"
294 307
        },
295 308
        "label": {
296 309
          "maxLength": 200,
297 310
          "minLength": 1,
298 311
          "type": "string"
299 312
        },
300 313
        "range": {
301 314
          "ref": "#range",
302 315
          "type": "ref"
303 316
        },
317 +
        "scale": {
318 +
          "description": "How a slider or a MIDI control moves through the range. linear moves by equal differences; log moves by equal ratios, which is how a frequency is heard, so the middle of a 20 to 20000 sweep is about 632 rather than about 10000. log needs a range above zero at both ends. Absent is linear. A parameter with a closed value set has no scale.",
319 +
          "knownValues": [
320 +
            "linear",
321 +
            "log"
322 +
          ],
323 +
          "type": "string"
324 +
        },
304 325
        "targets": {
305 326
          "items": {
306 327
            "ref": "#parameterTarget",
307 328
            "type": "ref"
308 329
          },
309 330
          "minLength": 1,
310 331
          "type": "array"
311 332
        },
312 333
        "values": {
313 -
          "description": "A closed value set, where the parameter is not numeric.",
334 +
          "description": "A closed value set, where the parameter is not numeric. A construction option the parameter targets is given the chosen value itself; an AudioParam it targets is given the chosen value's position in this set, counting from zero, which is how a worklet offers a choice its own code interprets.",
314 335
          "items": {
315 336
            "maxLength": 200,
316 337
            "type": "string"
317 338
          },
318 339
          "type": "array"
319 340
        }
320 341
      },
321 342
      "required": [
322 343
        "id",
323 344
        "label",
324 345
        "targets"
325 346
      ],
326 347
      "type": "object"
327 348
    },
328 349
    "parameterTarget": {
329 350
      "description": "One of: {node, param}, {node, option}, or {automation, breakpoint, field}.",
330 351
      "properties": {
331 352
        "automation": {
332 353
          "maxLength": 64,
333 354
          "type": "string"
334 355
        },
335 356
        "breakpoint": {
336 357
          "minimum": 0,
337 358
          "type": "integer"
338 359
        },
339 360
        "field": {
340 361
          "knownValues": [
341 362
            "time",
342 363
            "value"
343 364
          ],
344 365
          "type": "string"
345 366
        },
346 367
        "node": {
347 368
          "maxLength": 64,
348 369
          "type": "string"
349 370
        },
350 371
        "option": {
351 372
          "maxLength": 64,
352 373
          "type": "string"
353 374
        },
354 375
        "param": {
355 376
          "maxLength": 64,
356 377
          "type": "string"
357 378
        }
358 379
      },
359 380
      "type": "object"
360 381
    },
361 382
    "port": {
362 383
      "description": "One individually identified audio or MIDI boundary of the module.",
363 384
      "properties": {
364 385
        "id": {
365 386
          "maxLength": 64,
366 387
          "minLength": 1,
367 388
          "type": "string"
368 389
        },
369 390
        "label": {
370 391
          "maxLength": 200,
371 392
          "type": "string"
372 393
        }
373 394
      },
374 395
      "required": [
375 396
        "id"
376 397
      ],
377 398
      "type": "object"
378 399
    },
379 400
    "range": {
380 401
      "properties": {
381 402
        "max": {
382 403
          "description": "The high bound, as its decimal form — \"20000\", \"0.5\" — because the ATproto data model has no floating-point type and a PDS refuses one. A range is numeric by definition, so a reader turns it back into a number without asking anything else.",
383 404
          "maxLength": 200,
384 405
          "type": "string"
385 406
        },
386 407
        "min": {
387 408
          "description": "The low bound, as its decimal form — \"0.0001\", \"-1\" — because the ATproto data model has no floating-point type and a PDS refuses one. A range is numeric by definition, so a reader turns it back into a number without asking anything else.",
388 409
          "maxLength": 200,
389 410
          "type": "string"
390 411
        }
391 412
      },
392 413
      "required": [
393 414
        "min",
394 415
        "max"
395 416
      ],
396 417
      "type": "object"
397 418
    },
398 419
    "sampleRef": {
399 420
      "description": "A sample blob and the account that holds it.",
400 421
      "properties": {
401 422
        "blob": {
402 423
          "accept": [
403 424
            "audio/wav"
404 425
          ],
405 -
          "maxSize": 2097152,
426 +
          "maxSize": 10485760,
406 427
          "type": "blob"
407 428
        },
408 429
        "ownerDid": {
409 430
          "format": "did",
410 431
          "type": "string"
411 432
        }
412 433
      },
413 434
      "required": [
414 435
        "blob",
415 436
        "ownerDid"
416 437
      ],
417 438
      "type": "object"
418 439
    },
419 440
    "sampleSlot": {
420 441
      "description": "A named slot a patch instance may fill with its own sample. `node` names a node in this graph and `property` the thing the sample feeds.",
421 442
      "properties": {
422 443
        "default": {
423 444
          "ref": "#sampleRef",
424 445
          "type": "ref"
425 446
        },
426 447
        "id": {
427 448
          "maxLength": 64,
428 449
          "minLength": 1,
429 450
          "type": "string"
430 451
        },
431 452
        "label": {
432 453
          "maxLength": 200,
433 454
          "type": "string"
434 455
        },
435 456
        "node": {
436 457
          "maxLength": 64,
437 458
          "minLength": 1,
438 459
          "type": "string"
439 460
        },
440 461
        "property": {
441 462
          "knownValues": [
442 463
            "buffer"
443 464
          ],
444 465
          "type": "string"
445 466
        }
446 467
      },
447 468
      "required": [
448 469
        "id",
449 470
        "node",
450 471
        "property"
451 472
      ],
452 473
      "type": "object"
453 474
    },
454 475
    "source": {
455 476
      "properties": {
456 477
        "node": {
457 478
          "maxLength": 64,
458 479
          "minLength": 1,
459 480
          "type": "string"
460 481
        },
461 482
        "output": {
462 483
          "minimum": 0,
463 484
          "type": "integer"
464 485
        }
465 486
      },
466 487
      "required": [
467 488
        "node"
468 489
      ],
469 490
      "type": "object"
470 491
    },
471 492
    "target": {
472 493
      "description": "Exactly one of `input` or `param` is present. `param` is modulation: a node output reaching a named AudioParam.",
473 494
      "properties": {
474 495
        "input": {
475 496
          "minimum": 0,
476 497
          "type": "integer"
477 498
        },
478 499
        "node": {
479 500
          "maxLength": 64,
480 501
          "minLength": 1,
481 502
          "type": "string"
482 503
        },
483 504
        "param": {
484 505
          "maxLength": 64,
485 506
          "type": "string"
486 507
        }
487 508
      },
488 509
      "required": [
489 510
        "node"
490 511
      ],
491 512
      "type": "object"
492 513
    }
493 514
  },
494 515
  "description": "A reusable synth module: Web Audio nodes wired together, with declared audio and MIDI boundaries, exposed parameters, automation and sample slots. Each published version is its own record; records sharing a moduleId form a series.",
495 516
  "id": "at.atmosynth.module",
496 517
  "lexicon": 1
497 518
}

Compare Other Versions

Lexicon Garden

@