at.atmosynth.module

atmosynth.at

Schema Diff

+28 -6

From

CID
bafyreibishg657q...
Indexed At
2026-09-09 12:09 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 +
            "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.",
162 163
            "items": {
163 164
              "ref": "#port",
164 165
              "type": "ref"
165 166
            },
166 167
            "type": "array"
167 168
          },
168 169
          "moduleId": {
169 170
            "description": "Stable identity shared by every published version of this module.",
170 171
            "maxLength": 128,
171 172
            "type": "string"
172 173
          },
173 174
          "name": {
174 175
            "maxLength": 200,
175 176
            "minLength": 1,
176 177
            "type": "string"
177 178
          },
178 179
          "nodes": {
179 180
            "items": {
180 181
              "ref": "#node",
181 182
              "type": "ref"
182 183
            },
183 184
            "type": "array"
184 185
          },
185 186
          "noteOutputs": {
186 -
            "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 MIDI input 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.",
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.",
187 188
            "items": {
188 189
              "ref": "#port",
189 190
              "type": "ref"
190 191
            },
191 192
            "type": "array"
192 193
          },
193 194
          "noteSource": {
194 -
            "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.",
195 196
            "knownValues": [
196 197
              "keyboard",
197 -
              "sequencer"
198 +
              "sequencer",
199 +
              "range",
200 +
              "transpose",
201 +
              "assign",
202 +
              "oneshot"
198 203
            ],
199 204
            "maxLength": 64,
200 205
            "type": "string"
201 206
          },
202 207
          "parameters": {
203 208
            "items": {
204 209
              "ref": "#parameter",
205 210
              "type": "ref"
206 211
            },
207 212
            "type": "array"
208 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 +
          },
209 223
          "sampleSlots": {
210 224
            "items": {
211 225
              "ref": "#sampleSlot",
212 226
              "type": "ref"
213 227
            },
214 228
            "type": "array"
215 229
          },
216 230
          "versionKey": {
217 -
            "description": "This record's own key within the series. Informational; the record key is authoritative.",
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.",
218 232
            "maxLength": 128,
219 233
            "type": "string"
220 234
          }
221 235
        },
222 236
        "required": [
223 237
          "descriptionVersion",
224 238
          "moduleId",
225 239
          "name",
226 240
          "description",
227 241
          "audioInputs",
228 242
          "audioOutputs",
229 243
          "midiInputs",
230 244
          "nodes",
231 245
          "connections",
232 246
          "createdAt"
233 247
        ],
234 248
        "type": "object"
235 249
      },
236 250
      "type": "record"
237 251
    },
238 252
    "node": {
239 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.",
240 254
      "properties": {
241 255
        "id": {
242 256
          "maxLength": 64,
243 257
          "minLength": 1,
244 258
          "type": "string"
245 259
        },
246 260
        "kind": {
247 261
          "maxLength": 64,
248 262
          "minLength": 1,
249 263
          "type": "string"
250 264
        },
251 265
        "options": {
252 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.",
253 267
          "type": "unknown"
254 268
        }
255 269
      },
256 270
      "required": [
257 271
        "id",
258 272
        "kind"
259 273
      ],
260 274
      "type": "object"
261 275
    },
262 276
    "paramTarget": {
263 277
      "properties": {
264 278
        "node": {
265 279
          "maxLength": 64,
266 280
          "minLength": 1,
267 281
          "type": "string"
268 282
        },
269 283
        "param": {
270 284
          "maxLength": 64,
271 285
          "minLength": 1,
272 286
          "type": "string"
273 287
        }
274 288
      },
275 289
      "required": [
276 290
        "node",
277 291
        "param"
278 292
      ],
279 293
      "type": "object"
280 294
    },
281 295
    "parameter": {
282 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.",
283 297
      "properties": {
284 298
        "default": {
285 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.",
286 300
          "maxLength": 200,
287 301
          "type": "string"
288 302
        },
289 303
        "id": {
290 304
          "maxLength": 64,
291 305
          "minLength": 1,
292 306
          "type": "string"
293 307
        },
294 308
        "label": {
295 309
          "maxLength": 200,
296 310
          "minLength": 1,
297 311
          "type": "string"
298 312
        },
299 313
        "range": {
300 314
          "ref": "#range",
301 315
          "type": "ref"
302 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 +
        },
303 325
        "targets": {
304 326
          "items": {
305 327
            "ref": "#parameterTarget",
306 328
            "type": "ref"
307 329
          },
308 330
          "minLength": 1,
309 331
          "type": "array"
310 332
        },
311 333
        "values": {
312 -
          "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.",
313 335
          "items": {
314 336
            "maxLength": 200,
315 337
            "type": "string"
316 338
          },
317 339
          "type": "array"
318 340
        }
319 341
      },
320 342
      "required": [
321 343
        "id",
322 344
        "label",
323 345
        "targets"
324 346
      ],
325 347
      "type": "object"
326 348
    },
327 349
    "parameterTarget": {
328 350
      "description": "One of: {node, param}, {node, option}, or {automation, breakpoint, field}.",
329 351
      "properties": {
330 352
        "automation": {
331 353
          "maxLength": 64,
332 354
          "type": "string"
333 355
        },
334 356
        "breakpoint": {
335 357
          "minimum": 0,
336 358
          "type": "integer"
337 359
        },
338 360
        "field": {
339 361
          "knownValues": [
340 362
            "time",
341 363
            "value"
342 364
          ],
343 365
          "type": "string"
344 366
        },
345 367
        "node": {
346 368
          "maxLength": 64,
347 369
          "type": "string"
348 370
        },
349 371
        "option": {
350 372
          "maxLength": 64,
351 373
          "type": "string"
352 374
        },
353 375
        "param": {
354 376
          "maxLength": 64,
355 377
          "type": "string"
356 378
        }
357 379
      },
358 380
      "type": "object"
359 381
    },
360 382
    "port": {
361 383
      "description": "One individually identified audio or MIDI boundary of the module.",
362 384
      "properties": {
363 385
        "id": {
364 386
          "maxLength": 64,
365 387
          "minLength": 1,
366 388
          "type": "string"
367 389
        },
368 390
        "label": {
369 391
          "maxLength": 200,
370 392
          "type": "string"
371 393
        }
372 394
      },
373 395
      "required": [
374 396
        "id"
375 397
      ],
376 398
      "type": "object"
377 399
    },
378 400
    "range": {
379 401
      "properties": {
380 402
        "max": {
381 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.",
382 404
          "maxLength": 200,
383 405
          "type": "string"
384 406
        },
385 407
        "min": {
386 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.",
387 409
          "maxLength": 200,
388 410
          "type": "string"
389 411
        }
390 412
      },
391 413
      "required": [
392 414
        "min",
393 415
        "max"
394 416
      ],
395 417
      "type": "object"
396 418
    },
397 419
    "sampleRef": {
398 420
      "description": "A sample blob and the account that holds it.",
399 421
      "properties": {
400 422
        "blob": {
401 423
          "accept": [
402 424
            "audio/wav"
403 425
          ],
404 -
          "maxSize": 2097152,
426 +
          "maxSize": 10485760,
405 427
          "type": "blob"
406 428
        },
407 429
        "ownerDid": {
408 430
          "format": "did",
409 431
          "type": "string"
410 432
        }
411 433
      },
412 434
      "required": [
413 435
        "blob",
414 436
        "ownerDid"
415 437
      ],
416 438
      "type": "object"
417 439
    },
418 440
    "sampleSlot": {
419 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.",
420 442
      "properties": {
421 443
        "default": {
422 444
          "ref": "#sampleRef",
423 445
          "type": "ref"
424 446
        },
425 447
        "id": {
426 448
          "maxLength": 64,
427 449
          "minLength": 1,
428 450
          "type": "string"
429 451
        },
430 452
        "label": {
431 453
          "maxLength": 200,
432 454
          "type": "string"
433 455
        },
434 456
        "node": {
435 457
          "maxLength": 64,
436 458
          "minLength": 1,
437 459
          "type": "string"
438 460
        },
439 461
        "property": {
440 462
          "knownValues": [
441 463
            "buffer"
442 464
          ],
443 465
          "type": "string"
444 466
        }
445 467
      },
446 468
      "required": [
447 469
        "id",
448 470
        "node",
449 471
        "property"
450 472
      ],
451 473
      "type": "object"
452 474
    },
453 475
    "source": {
454 476
      "properties": {
455 477
        "node": {
456 478
          "maxLength": 64,
457 479
          "minLength": 1,
458 480
          "type": "string"
459 481
        },
460 482
        "output": {
461 483
          "minimum": 0,
462 484
          "type": "integer"
463 485
        }
464 486
      },
465 487
      "required": [
466 488
        "node"
467 489
      ],
468 490
      "type": "object"
469 491
    },
470 492
    "target": {
471 493
      "description": "Exactly one of `input` or `param` is present. `param` is modulation: a node output reaching a named AudioParam.",
472 494
      "properties": {
473 495
        "input": {
474 496
          "minimum": 0,
475 497
          "type": "integer"
476 498
        },
477 499
        "node": {
478 500
          "maxLength": 64,
479 501
          "minLength": 1,
480 502
          "type": "string"
481 503
        },
482 504
        "param": {
483 505
          "maxLength": 64,
484 506
          "type": "string"
485 507
        }
486 508
      },
487 509
      "required": [
488 510
        "node"
489 511
      ],
490 512
      "type": "object"
491 513
    }
492 514
  },
493 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.",
494 516
  "id": "at.atmosynth.module",
495 517
  "lexicon": 1
496 518
}

Compare Other Versions

Lexicon Garden

@