at.atmosynth.module

atmosynth.at

Schema Diff

+17 -4

From

CID
bafyreie7vbl2bxf...
Indexed At
2026-09-22 18:36 UTC
View this version

To

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

Compatibility Analysis

Breaking Changes Detected

2 breaking changes, 2 non-breaking changes.

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

Migration Guidance

Added Elements

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

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\"]" }
  • ConstraintTightened ConstraintTightened { vertex_id: "at.atmosynth.module:body.patternSource", sort: "knownValues", old_value: "[\"arranger\"]", new_value: "[\"arranger\",\"scenes\"]" }

Additional Notes

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

Compare Other Versions

Lexicon Garden

@