at.atmosynth.module
Schema Diff
+41 -15
Compatibility Analysis
Breaking Changes Detected
9 breaking changes, 4 non-breaking changes.
Breaking Changes (9)
- KindChanged KindChanged { vertex_id: "at.atmosynth.module#breakpoint.value", old_kind: "unknown", new_kind: "string" }
- KindChanged KindChanged { vertex_id: "at.atmosynth.module#parameter.default", old_kind: "unknown", new_kind: "string" }
- KindChanged KindChanged { vertex_id: "at.atmosynth.module#range.max", old_kind: "unknown", new_kind: "string" }
- KindChanged KindChanged { vertex_id: "at.atmosynth.module#range.min", old_kind: "unknown", new_kind: "string" }
- ConstraintAdded ConstraintAdded { vertex_id: "at.atmosynth.module#breakpoint.value", sort: "maxLength", value: "200" }
- ConstraintAdded ConstraintAdded { vertex_id: "at.atmosynth.module#range.max", sort: "maxLength", value: "200" }
- ConstraintTightened ConstraintTightened { vertex_id: "at.atmosynth.module:body.noteSource", sort: "knownValues", old_value: "[\"keyboard\",\"sequencer\"]", new_value: "[\"keyboard\",\"sequencer\",\"range\",\"transpose\",\"assign\",\"oneshot\"]" }
- ConstraintAdded ConstraintAdded { vertex_id: "at.atmosynth.module#range.min", sort: "maxLength", value: "200" }
- ConstraintAdded ConstraintAdded { vertex_id: "at.atmosynth.module#parameter.default", sort: "maxLength", value: "200" }
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
- ConstraintAdded ConstraintAdded { vertex_id: "at.atmosynth.module#parameter.default", sort: "maxLength", value: "200" }
- ConstraintTightened ConstraintTightened { vertex_id: "at.atmosynth.module:body.noteSource", sort: "knownValues", old_value: "[\"keyboard\",\"sequencer\"]", new_value: "[\"keyboard\",\"sequencer\",\"range\",\"transpose\",\"assign\",\"oneshot\"]" }
- ConstraintAdded ConstraintAdded { vertex_id: "at.atmosynth.module#breakpoint.value", sort: "maxLength", value: "200" }
- ConstraintAdded ConstraintAdded { vertex_id: "at.atmosynth.module#range.min", sort: "maxLength", value: "200" }
- ConstraintAdded ConstraintAdded { vertex_id: "at.atmosynth.module#range.max", sort: "maxLength", value: "200" }
Additional Notes
- Breaking: KindChanged { vertex_id: "at.atmosynth.module#breakpoint.value", old_kind: "unknown", new_kind: "string" }
- Breaking: KindChanged { vertex_id: "at.atmosynth.module#parameter.default", old_kind: "unknown", new_kind: "string" }
- Breaking: KindChanged { vertex_id: "at.atmosynth.module#range.max", old_kind: "unknown", new_kind: "string" }
- Breaking: KindChanged { vertex_id: "at.atmosynth.module#range.min", old_kind: "unknown", new_kind: "string" }
- 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
-
"description": "The param's value at this instant. A number that is not a whole number is carried as its decimal string — \"0.5\", not 0.5 — because the ATproto data model has no floating-point type and a PDS refuses one. A whole number is an ordinary integer, and a reader turns such a string back into a number.",
59
-
"type": "unknown"
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
+
"maxLength": 200,
60
+
"type": "string"
60
61
}
61
62
},
62
63
"required": [
63
64
"time",
64
65
"value"
65
66
],
66
67
"type": "object"
67
68
},
68
69
"connection": {
69
70
"description": "An audio connection from a node output to either an audio input or a named AudioParam.",
70
71
"properties": {
71
72
"from": {
72
73
"ref": "#source",
73
74
"type": "ref"
74
75
},
75
76
"to": {
76
77
"ref": "#target",
77
78
"type": "ref"
78
79
}
79
80
},
80
81
"required": [
81
82
"from",
82
83
"to"
83
84
],
84
85
"type": "object"
85
86
},
86
87
"lineage": {
87
88
"description": "The module this one was forked from, and its author.",
88
89
"properties": {
89
90
"authorDid": {
90
91
"format": "did",
91
92
"type": "string"
92
93
},
93
94
"moduleId": {
94
95
"maxLength": 128,
95
96
"type": "string"
96
97
},
97
98
"uri": {
98
99
"format": "at-uri",
99
100
"type": "string"
100
101
}
101
102
},
102
103
"required": [
103
104
"uri",
104
105
"authorDid"
105
106
],
106
107
"type": "object"
107
108
},
108
109
"main": {
109
110
"description": "A module, held in its author's own repository.",
110
111
"key": "tid",
111
112
"record": {
112
113
"properties": {
113
114
"audioInputs": {
114
115
"description": "Zero or more individually identified audio inputs.",
115
116
"items": {
116
117
"ref": "#port",
117
118
"type": "ref"
118
119
},
119
120
"type": "array"
120
121
},
121
122
"audioOutputs": {
122
123
"items": {
123
124
"ref": "#port",
124
125
"type": "ref"
125
126
},
126
127
"type": "array"
127
128
},
128
129
"automation": {
129
130
"description": "Scheduled AudioParam envelopes. Web Audio has no envelope node, so these are their own structure.",
130
131
"items": {
131
132
"ref": "#automation",
132
133
"type": "ref"
133
134
},
134
135
"type": "array"
135
136
},
136
137
"connections": {
137
138
"items": {
138
139
"ref": "#connection",
139
140
"type": "ref"
140
141
},
141
142
"type": "array"
142
143
},
143
144
"createdAt": {
144
145
"format": "datetime",
145
146
"type": "string"
146
147
},
147
148
"description": {
148
149
"maxLength": 3000,
149
150
"type": "string"
150
151
},
151
152
"descriptionVersion": {
152
153
"description": "Version of the module description this record follows. A reader accepts any version at or below its own.",
153
154
"minimum": 1,
154
155
"type": "integer"
155
156
},
156
157
"forkedFrom": {
157
158
"ref": "#lineage",
158
159
"type": "ref"
159
160
},
160
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.",
161
163
"items": {
162
164
"ref": "#port",
163
165
"type": "ref"
164
166
},
165
167
"type": "array"
166
168
},
167
169
"moduleId": {
168
170
"description": "Stable identity shared by every published version of this module.",
169
171
"maxLength": 128,
170
172
"type": "string"
171
173
},
172
174
"name": {
173
175
"maxLength": 200,
174
176
"minLength": 1,
175
177
"type": "string"
176
178
},
177
179
"nodes": {
178
180
"items": {
179
181
"ref": "#node",
180
182
"type": "ref"
181
183
},
182
184
"type": "array"
183
185
},
184
186
"noteOutputs": {
185
-
"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.",
186
188
"items": {
187
189
"ref": "#port",
188
190
"type": "ref"
189
191
},
190
192
"type": "array"
191
193
},
192
194
"noteSource": {
193
-
"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.",
194
196
"knownValues": [
195
197
"keyboard",
196
-
"sequencer"
198
+
"sequencer",
199
+
"range",
200
+
"transpose",
201
+
"assign",
202
+
"oneshot"
197
203
],
198
204
"maxLength": 64,
199
205
"type": "string"
200
206
},
201
207
"parameters": {
202
208
"items": {
203
209
"ref": "#parameter",
204
210
"type": "ref"
205
211
},
206
212
"type": "array"
207
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
+
},
208
223
"sampleSlots": {
209
224
"items": {
210
225
"ref": "#sampleSlot",
211
226
"type": "ref"
212
227
},
213
228
"type": "array"
214
229
},
215
230
"versionKey": {
216
-
"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.",
217
232
"maxLength": 128,
218
233
"type": "string"
219
234
}
220
235
},
221
236
"required": [
222
237
"descriptionVersion",
223
238
"moduleId",
224
239
"name",
225
240
"description",
226
241
"audioInputs",
227
242
"audioOutputs",
228
243
"midiInputs",
229
244
"nodes",
230
245
"connections",
231
246
"createdAt"
232
247
],
233
248
"type": "object"
234
249
},
235
250
"type": "record"
236
251
},
237
252
"node": {
238
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.",
239
254
"properties": {
240
255
"id": {
241
256
"maxLength": 64,
242
257
"minLength": 1,
243
258
"type": "string"
244
259
},
245
260
"kind": {
246
261
"maxLength": 64,
247
262
"minLength": 1,
248
263
"type": "string"
249
264
},
250
265
"options": {
251
-
"description": "The node's settings, as its kind defines them. A number that is not a whole number is carried as its decimal string — \"0.5\", not 0.5 — because the ATproto data model has no floating-point type and a PDS refuses one. A whole number is an ordinary integer, and a reader turns such a string back into a number.",
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.",
252
267
"type": "unknown"
253
268
}
254
269
},
255
270
"required": [
256
271
"id",
257
272
"kind"
258
273
],
259
274
"type": "object"
260
275
},
261
276
"paramTarget": {
262
277
"properties": {
263
278
"node": {
264
279
"maxLength": 64,
265
280
"minLength": 1,
266
281
"type": "string"
267
282
},
268
283
"param": {
269
284
"maxLength": 64,
270
285
"minLength": 1,
271
286
"type": "string"
272
287
}
273
288
},
274
289
"required": [
275
290
"node",
276
291
"param"
277
292
],
278
293
"type": "object"
279
294
},
280
295
"parameter": {
281
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.",
282
297
"properties": {
283
298
"default": {
284
-
"description": "The value used where a patch sets none. A number that is not a whole number is carried as its decimal string — \"0.5\", not 0.5 — because the ATproto data model has no floating-point type and a PDS refuses one. A whole number is an ordinary integer, and a reader turns such a string back into a number. Where this parameter declares a closed value set, it is one of those strings.",
285
-
"type": "unknown"
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.",
300
+
"maxLength": 200,
301
+
"type": "string"
286
302
},
287
303
"id": {
288
304
"maxLength": 64,
289
305
"minLength": 1,
290
306
"type": "string"
291
307
},
292
308
"label": {
293
309
"maxLength": 200,
294
310
"minLength": 1,
295
311
"type": "string"
296
312
},
297
313
"range": {
298
314
"ref": "#range",
299
315
"type": "ref"
300
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
+
},
301
325
"targets": {
302
326
"items": {
303
327
"ref": "#parameterTarget",
304
328
"type": "ref"
305
329
},
306
330
"minLength": 1,
307
331
"type": "array"
308
332
},
309
333
"values": {
310
-
"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.",
311
335
"items": {
312
336
"maxLength": 200,
313
337
"type": "string"
314
338
},
315
339
"type": "array"
316
340
}
317
341
},
318
342
"required": [
319
343
"id",
320
344
"label",
321
345
"targets"
322
346
],
323
347
"type": "object"
324
348
},
325
349
"parameterTarget": {
326
350
"description": "One of: {node, param}, {node, option}, or {automation, breakpoint, field}.",
327
351
"properties": {
328
352
"automation": {
329
353
"maxLength": 64,
330
354
"type": "string"
331
355
},
332
356
"breakpoint": {
333
357
"minimum": 0,
334
358
"type": "integer"
335
359
},
336
360
"field": {
337
361
"knownValues": [
338
362
"time",
339
363
"value"
340
364
],
341
365
"type": "string"
342
366
},
343
367
"node": {
344
368
"maxLength": 64,
345
369
"type": "string"
346
370
},
347
371
"option": {
348
372
"maxLength": 64,
349
373
"type": "string"
350
374
},
351
375
"param": {
352
376
"maxLength": 64,
353
377
"type": "string"
354
378
}
355
379
},
356
380
"type": "object"
357
381
},
358
382
"port": {
359
383
"description": "One individually identified audio or MIDI boundary of the module.",
360
384
"properties": {
361
385
"id": {
362
386
"maxLength": 64,
363
387
"minLength": 1,
364
388
"type": "string"
365
389
},
366
390
"label": {
367
391
"maxLength": 200,
368
392
"type": "string"
369
393
}
370
394
},
371
395
"required": [
372
396
"id"
373
397
],
374
398
"type": "object"
375
399
},
376
400
"range": {
377
401
"properties": {
378
402
"max": {
379
-
"description": "The high bound. A number that is not a whole number is carried as its decimal string — \"0.5\", not 0.5 — because the ATproto data model has no floating-point type and a PDS refuses one. A whole number is an ordinary integer, and a reader turns such a string back into a number.",
380
-
"type": "unknown"
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.",
404
+
"maxLength": 200,
405
+
"type": "string"
381
406
},
382
407
"min": {
383
-
"description": "The low bound. A number that is not a whole number is carried as its decimal string — \"0.5\", not 0.5 — because the ATproto data model has no floating-point type and a PDS refuses one. A whole number is an ordinary integer, and a reader turns such a string back into a number.",
384
-
"type": "unknown"
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.",
409
+
"maxLength": 200,
410
+
"type": "string"
385
411
}
386
412
},
387
413
"required": [
388
414
"min",
389
415
"max"
390
416
],
391
417
"type": "object"
392
418
},
393
419
"sampleRef": {
394
420
"description": "A sample blob and the account that holds it.",
395
421
"properties": {
396
422
"blob": {
397
423
"accept": [
398
424
"audio/wav"
399
425
],
400
-
"maxSize": 2097152,
426
+
"maxSize": 10485760,
401
427
"type": "blob"
402
428
},
403
429
"ownerDid": {
404
430
"format": "did",
405
431
"type": "string"
406
432
}
407
433
},
408
434
"required": [
409
435
"blob",
410
436
"ownerDid"
411
437
],
412
438
"type": "object"
413
439
},
414
440
"sampleSlot": {
415
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.",
416
442
"properties": {
417
443
"default": {
418
444
"ref": "#sampleRef",
419
445
"type": "ref"
420
446
},
421
447
"id": {
422
448
"maxLength": 64,
423
449
"minLength": 1,
424
450
"type": "string"
425
451
},
426
452
"label": {
427
453
"maxLength": 200,
428
454
"type": "string"
429
455
},
430
456
"node": {
431
457
"maxLength": 64,
432
458
"minLength": 1,
433
459
"type": "string"
434
460
},
435
461
"property": {
436
462
"knownValues": [
437
463
"buffer"
438
464
],
439
465
"type": "string"
440
466
}
441
467
},
442
468
"required": [
443
469
"id",
444
470
"node",
445
471
"property"
446
472
],
447
473
"type": "object"
448
474
},
449
475
"source": {
450
476
"properties": {
451
477
"node": {
452
478
"maxLength": 64,
453
479
"minLength": 1,
454
480
"type": "string"
455
481
},
456
482
"output": {
457
483
"minimum": 0,
458
484
"type": "integer"
459
485
}
460
486
},
461
487
"required": [
462
488
"node"
463
489
],
464
490
"type": "object"
465
491
},
466
492
"target": {
467
493
"description": "Exactly one of `input` or `param` is present. `param` is modulation: a node output reaching a named AudioParam.",
468
494
"properties": {
469
495
"input": {
470
496
"minimum": 0,
471
497
"type": "integer"
472
498
},
473
499
"node": {
474
500
"maxLength": 64,
475
501
"minLength": 1,
476
502
"type": "string"
477
503
},
478
504
"param": {
479
505
"maxLength": 64,
480
506
"type": "string"
481
507
}
482
508
},
483
509
"required": [
484
510
"node"
485
511
],
486
512
"type": "object"
487
513
}
488
514
},
489
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.",
490
516
"id": "at.atmosynth.module",
491
517
"lexicon": 1
492
518
}