Sprites.Media.Sequencer
The sequencer editor: its document helpers, its tune and cell logic, and the Editor that lays out the four stacked areas. The tune area of block slots and its controls (Sprites.Media.Sequencer.Tune) sit at the top, the block-definition grid of beats by channels (Sprites.Media.Sequencer.Grid) fills the height, and the piano keyboard (Sprites.Media.Sequencer.Keyboard) is fixed at the foot, with the always-open Info and Note windows over it. The channels and their capabilities come from a platform's Sprites.Platform.<name>.Sequencer descriptor.
- Sprites.Media.Sequencer.Editor
- Sprites.Media.Sequencer.NewDocument
- Sprites.Media.Sequencer.BlockSize
- Sprites.Media.Sequencer.EmptyBlock
- Sprites.Media.Sequencer.CurrentBlock
- Sprites.Media.Sequencer.SlotFilled
- Sprites.Media.Sequencer.CellValue
- Sprites.Media.Sequencer.CellLens
- Sprites.Media.Sequencer.SetSelectedCell
- Sprites.Media.Sequencer.Tune (slot operations)
- Sprites.Media.Sequencer.NoteRange
- Sprites.Media.Sequencer.Summary
- Sprites.Media.Sequencer.ChannelCount
- Sprites.Media.Sequencer.CurrentGrid
- Sprites.Media.Sequencer.DefaultBarsPerBlock
- Sprites.Media.Sequencer.DefaultBeatsPerBar
- Sprites.Media.Sequencer.DefaultBpm
- Sprites.Media.Sequencer.EmptyCell
- Sprites.Media.Sequencer.NoteNames
- Sprites.Media.Sequencer.NoteNamesValue
- Sprites.Media.Sequencer.NoteText
- Sprites.Media.Sequencer.PhantomGrid
- Sprites.Media.Sequencer.ReshapeTempo
Sprites.Media.Sequencer.Editor Void
Sprites.Media.Sequencer.Editor(data, thumb, info, model)
The sequencer editor. Fills the editor stage with four stacked areas: the tune strip of block slots, the block controls under it, the block-definition grid that takes the rest of the height, and the piano keyboard fixed at the foot. The always-open Info and Note windows float over it. The host page renders the standard editor header and the Name: row above the stage, exactly as the other editors do, so this builds only the stage. One cell is selected at a time, and the keyboard and Note window edit it, creating the slot's block on the first edit. The channel layout, note range and effect superset all come from model.
| Input | Type | Description |
|---|---|---|
data | Sprites.Reactive.Value | The tune, a two way value of { format, platform, tempo, blocks, tune }. See the sequencer storage format. |
thumb | Sprites.Reactive.Value | A value the editor writes a small preview into for the library listing. |
info | Sprites.Reactive.Value | The item's metadata. |
model | Object | The platform's Sequencer descriptor: channelTypes, channels, shared and effects. |
Returns nothing.
const open = Sprites.Reactive.Value(false);
Sprites.Ui.Button.SetValue(open, true, 'Open the editor');
Sprites.Reactive.If(open, () => {
Sprites.Ui.Dom.Tag('div', () => {
Sprites.Ui.Dom.Style('height', '320px');
const fresh = Sprites.Reactive.read(Sprites.Media.Sequencer.NewDocument(Sprites.Platform.C64.Sequencer));
const data = Sprites.Reactive.Value(fresh);
const thumb = Sprites.Reactive.Value(undefined);
const info = Sprites.Reactive.Value(undefined);
Sprites.Media.Sequencer.Editor(data, thumb, info, Sprites.Platform.C64.Sequencer);
});
});
Sprites.Media.Sequencer.NewDocument Object
Sprites.Media.Sequencer.NewDocument(model)
A new tune for a platform model, as plain data for the store: one empty block sized from the default tempo, one slot pointing at it, and a trailing empty slot. A document factory: it composes the block and the platform name as reactive values, snapshots them once with Sprites.Reactive.read, and assembles the plain record, the same shape the sprite editor's NewDocument follows.
| Input | Type | Description |
|---|---|---|
model | Object | The platform's Sequencer descriptor, which fixes the channel count. |
Returns a { format, platform, tempo, blocks, tune } data object.
const data = Sprites.Media.Sequencer.NewDocument(Sprites.Platform.C64.Sequencer);
const tune = Sprites.Object.Field(data, 'tune');
const count = Sprites.Array.Length(tune);
Sprites.Ui.Dom.Text('channels = ', count);
Sprites.Media.Sequencer.BlockSize Number
Sprites.Media.Sequencer.BlockSize(tempo)
The number of beats in a block, the tempo's beatsPerBar times its barsPerBlock. Every block shares this size.
| Input | Type | Description |
|---|---|---|
tempo | Object or Sprites.Reactive.Value | A { bpm, beatsPerBar, barsPerBlock } tempo. |
Returns the block size in beats.
const size = Sprites.Media.Sequencer.BlockSize({ bpm: 125, beatsPerBar: 4, barsPerBlock: 4 });
Sprites.Ui.Dom.Text('block = ', size, ' beats');
Sprites.Media.Sequencer.EmptyBlock Object
Sprites.Media.Sequencer.EmptyBlock(id, beatsPerBar, barsPerBlock, channels)
A fresh block value: an id and a Sprites.Grid.Three cell volume of channels columns by beatsPerBar rows by barsPerBlock bars, every cell undefined. A composite over Sprites.Grid.Three.Blank and Sprites.Reactive.ReadOnly, so it is a live value the document factory snapshots and a slot edit appends.
| Input | Type | Description |
|---|---|---|
id | Number | The block's stable id. |
beatsPerBar | Number | The beats in a bar (the volume's height). |
barsPerBlock | Number | The bars in a block (the volume's depth). |
channels | Number | The channel count (the volume's width). |
Returns a { id, grid } block object, where grid is a { w, h, d, cells } volume.
const block = Sprites.Media.Sequencer.EmptyBlock(1, 4, 4, 3);
const grid = Sprites.Object.Field(block, 'grid');
const beats = Sprites.Grid.Three.Height(grid);
Sprites.Ui.Dom.Text('beats = ', beats);
Sprites.Media.Sequencer.CurrentBlock Object
Sprites.Media.Sequencer.CurrentBlock(data, slot)
The block a tune slot points at, or undefined when the slot is empty. A composite over Sprites.Array.At, which tolerates a missing index, so it never throws for an empty slot.
| Input | Type | Description |
|---|---|---|
data | Sprites.Reactive.Value | The tune data. |
slot | Number or Sprites.Reactive.Value | The tune slot. |
Returns the slot's block, or undefined.
const document = Sprites.Media.Sequencer.NewDocument(Sprites.Platform.C64.Sequencer);
const data = Sprites.Reactive.Value(document);
const block = Sprites.Media.Sequencer.CurrentBlock(data, 0);
const id = Sprites.Object.Field(block, 'id');
Sprites.Ui.Dom.Text('block id = ', id);
Sprites.Media.Sequencer.SlotFilled Boolean
Sprites.Media.Sequencer.SlotFilled(data, slot)
Whether a tune slot holds a block, that is its block index is not undefined. The grid reads this to decide whether to show the real block or an empty, fillable stand in.
| Input | Type | Description |
|---|---|---|
data | Sprites.Reactive.Value | The tune data. |
slot | Number or Sprites.Reactive.Value | The tune slot. |
Returns a boolean value, true when the slot holds a block.
const document = Sprites.Media.Sequencer.NewDocument(Sprites.Platform.C64.Sequencer);
const data = Sprites.Reactive.Value(document);
const slot0Filled = Sprites.Media.Sequencer.SlotFilled(data, 0);
const slot1Filled = Sprites.Media.Sequencer.SlotFilled(data, 1);
Sprites.Ui.Dom.Text('slot 0 filled = ', slot0Filled, ', slot 1 filled = ', slot1Filled);
Sprites.Media.Sequencer.CellValue Object
Sprites.Media.Sequencer.CellValue(data, slot, channel, beatInBar, bar, model)
The value of one cell as a read only reactive value, safe when the slot is empty or the position is blank: the stored cell if present, else the empty cell ({ effects: [] }). The position is a channel, a beat within its bar, and a bar, the three axes of the block volume. A composite over CurrentGrid, Sprites.Grid.Three.At and Sprites.Logic.Select. This is the read half that CellLens wraps.
| Input | Type | Description |
|---|---|---|
data | Sprites.Reactive.Value | The tune data. |
slot | Number or Sprites.Reactive.Value | The tune slot. |
channel | Number or Sprites.Reactive.Value | The channel (the volume's x). |
beatInBar | Number or Sprites.Reactive.Value | The beat within its bar (the volume's y). |
bar | Number or Sprites.Reactive.Value | The bar (the volume's z). |
model | Object | The platform descriptor, used to size the phantom volume of an empty slot. |
Returns the cell, or the empty cell.
const document = Sprites.Media.Sequencer.NewDocument(Sprites.Platform.C64.Sequencer);
const data = Sprites.Reactive.Value(document);
const cell = Sprites.Media.Sequencer.CellValue(data, 0, 0, 0, 0, Sprites.Platform.C64.Sequencer);
const effects = Sprites.Object.Field(cell, 'effects');
const effectCount = Sprites.Array.Length(effects);
Sprites.Ui.Dom.Text('blank cell effects = ', effectCount);
Sprites.Media.Sequencer.CellLens Sprites.Reactive.Value
Sprites.Media.Sequencer.CellLens(data, slot, channel, beatInBar, bar, model)
A two way value onto one cell of the block: reading gives the cell, or the empty cell when the slot is empty or the position is blank, and setting writes it back into the slot's volume, creating the block if the slot was empty. A cell left with no note and no effects is stored as undefined, so clearing a note empties the cell. Every unrolled grid cell is one of these, and the editor derives the selected one for the keyboard and note window, so a write from any of them updates them all. Because a block can appear in many slots, editing a cell changes every slot that uses that block.
| Input | Type | Description |
|---|---|---|
data | Sprites.Reactive.Value | The tune data. |
slot | Number or Sprites.Reactive.Value | The tune slot. |
channel | Number or Sprites.Reactive.Value | The channel (the volume's x). |
beatInBar | Number or Sprites.Reactive.Value | The beat within its bar (the volume's y). |
bar | Number or Sprites.Reactive.Value | The bar (the volume's z). |
model | Object | The platform descriptor, used to size a block created on the first edit of an empty slot. |
Returns a Sprites.Reactive.Value lens onto the cell.
const document = Sprites.Media.Sequencer.NewDocument(Sprites.Platform.C64.Sequencer);
const data = Sprites.Reactive.Value(document);
const cell = Sprites.Media.Sequencer.CellLens(data, 0, 0, 0, 0, Sprites.Platform.C64.Sequencer);
const note = Sprites.Object.Field(cell, 'optionalNote');
Sprites.Ui.Input.Number(note);
const label = Sprites.Media.Sequencer.NoteText(note);
Sprites.Ui.Dom.Text(' label = ', label);
Sprites.Media.Sequencer.SetSelectedCell Object
Sprites.Media.Sequencer.SetSelectedCell(data, slot, channel, beatInBar, bar, cell, model)
New tune data with one cell set, creating the slot's block if the slot is empty. When the slot is empty a fresh block sized from the tempo is appended and the slot is pointed at it; when it is filled the existing block's volume is set with Sprites.Grid.Three.Set. The position is a channel, a beat within its bar, and a bar. A composite, so it composes inside a lens and the whole change commits at once. This is the write half that CellLens wraps.
| Input | Type | Description |
|---|---|---|
data | Sprites.Reactive.Value | The tune data. |
slot, channel, beatInBar, bar | Number or Sprites.Reactive.Value | The position to set: the slot, and the channel, beat within its bar, and bar of the volume. |
cell | Object or undefined | The new cell, { optionalNote, effects }, or undefined to clear the position. |
model | Object | The platform descriptor, used to size a block created for an empty slot. |
Returns new tune data with the cell set.
const document = Sprites.Media.Sequencer.NewDocument(Sprites.Platform.C64.Sequencer);
const data = Sprites.Reactive.Value(document);
const setNote = Sprites.Reactive.Bindingundefined, () => {
const cell = { optionalNote: 60, effects: [] };
const next = Sprites.Media.Sequencer.SetSelectedCell(data, 1, 0, 0, 0, cell, Sprites.Platform.C64.Sequencer);
Sprites.Reactive.Set(data, next);
});
Sprites.Ui.Button.Act(setNote, 'Set note in empty slot 1');
const cell = Sprites.Media.Sequencer.CellValue(data, 1, 0, 0, 0, Sprites.Platform.C64.Sequencer);
const note = Sprites.Object.Field(cell, 'optionalNote');
Sprites.Ui.Dom.Text(' note = ', note);
Sprites.Media.Sequencer.NoteRange Object
Sprites.Media.Sequencer.NoteRange(model)
The { low, high } semitone range a platform plays, read straight from the descriptor's declared range. A composite: a field of the model (Sprites.Object.Field). The descriptor declares the range, so the editor never derives it.
| Input | Type | Description |
|---|---|---|
model | Object | The platform Sequencer descriptor. |
Returns a { low, high } semitone range.
const range = Sprites.Media.Sequencer.NoteRange(Sprites.Platform.C64.Sequencer);
const low = Sprites.Object.Field(range, 'low');
const high = Sprites.Object.Field(range, 'high');
Sprites.Ui.Dom.Text('low = ', low, ', high = ', high);
Sprites.Media.Sequencer.Summary String
Sprites.Media.Sequencer.Summary(data)
A short listing summary of a tune: the platform, the block and slot counts, and the tempo.
| Input | Type | Description |
|---|---|---|
data | Sprites.Reactive.Value | The tune data. |
Returns the summary string.
const document = Sprites.Media.Sequencer.NewDocument(Sprites.Platform.C64.Sequencer);
const data = Sprites.Reactive.Value(document);
const summary = Sprites.Media.Sequencer.Summary(data);
Sprites.Ui.Dom.Text(summary);
Sprites.Media.Sequencer.ChannelCount Sprites.Reactive.Value
Sprites.Media.Sequencer.ChannelCount(model)
The number of channels a model has. A composite over the descriptor's channel list, so the editor sizes every block volume to the platform's own voice count.
| Input | Type | Description |
|---|---|---|
model | Object | The platform's Sequencer descriptor, which lists the channels. |
Returns the channel count as a number value.
const count = Sprites.Media.Sequencer.ChannelCount(Sprites.Platform.C64.Sequencer);
Sprites.Ui.Dom.Text('channels = ', count);
Sprites.Media.Sequencer.CurrentGrid Sprites.Reactive.Value
Sprites.Media.Sequencer.CurrentGrid(data, slot, model)
The cell volume of the selected slot's block, or the phantom volume when the slot is empty. The block is routed through a Sprites.Logic.Select to a defined stand in built with Sprites.Reactive.ReadOnly before the grid field is read, so no read ever sees an undefined source. A composite.
| Input | Type | Description |
|---|---|---|
data | Sprites.Reactive.Value | The tune data. |
slot | Number or Sprites.Reactive.Value | The tune slot. |
model | Object | The platform descriptor, used to size the phantom volume of an empty slot. |
Returns the block's { w, h, d, cells } volume, or the phantom volume.
const document = Sprites.Media.Sequencer.NewDocument(Sprites.Platform.C64.Sequencer);
const data = Sprites.Reactive.Value(document);
const grid = Sprites.Media.Sequencer.CurrentGrid(data, 0, Sprites.Platform.C64.Sequencer);
const voices = Sprites.Grid.Three.Width(grid);
const beats = Sprites.Grid.Three.Height(grid);
Sprites.Ui.Dom.Text('voices = ', voices, ', beats = ', beats);
Sprites.Media.Sequencer.DefaultBarsPerBlock Number
One of the defaults a new tune opens on: the bars in a block. A namespace constant, a plain literal the document factory sizes the first block from.
Value 4.
Sprites.Ui.Dom.Text('bars per block = ', Sprites.Media.Sequencer.DefaultBarsPerBlock);
Sprites.Media.Sequencer.DefaultBeatsPerBar Number
One of the defaults a new tune opens on: the beats in a bar. A namespace constant, a plain literal the document factory sizes the first block from.
Value 4.
Sprites.Ui.Dom.Text('beats per bar = ', Sprites.Media.Sequencer.DefaultBeatsPerBar);
Sprites.Media.Sequencer.DefaultBpm Number
One of the defaults a new tune opens on: the beats per minute. A namespace constant, a plain literal the document factory seeds the tempo with.
Value 125.
Sprites.Ui.Dom.Text('default bpm = ', Sprites.Media.Sequencer.DefaultBpm);
Sprites.Media.Sequencer.EmptyCell Object
The empty cell, no note and no effects, the value a blank grid position reads. A namespace constant, a plain literal, never mutated, since edits build new cells. Its optionalNote is absent, so it reads as undefined.
Value { effects: [] }.
const effects = Sprites.Object.Field(Sprites.Media.Sequencer.EmptyCell, 'effects');
const effectCount = Sprites.Array.Length(effects);
Sprites.Ui.Dom.Text('effects = ', effectCount);
Sprites.Media.Sequencer.NoteNames Array
The note names, C to B, that the label composite indexes by a semitone's pitch class. A plain constant list of the twelve names in an octave.
Value ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'].
const names = Sprites.Media.Sequencer.NoteNamesValue;
const count = Sprites.Array.Length(names);
const first = Sprites.Array.At(names, 0);
Sprites.Ui.Dom.Text('names = ', count, ', first = ', first);
Sprites.Media.Sequencer.NoteNamesValue Sprites.Reactive.Value
The note names wrapped as a reactive value, so the label composite can read them with Sprites.Array.At. It holds the same twelve names as NoteNames.
Value a reactive value of Sprites.Media.Sequencer.NoteNames.
const letter = Sprites.Array.At(Sprites.Media.Sequencer.NoteNamesValue, 9);
Sprites.Ui.Dom.Text('pitch class 9 = ', letter);
Sprites.Media.Sequencer.NoteText Sprites.Reactive.Value
Sprites.Media.Sequencer.NoteText(note)
The label of a reactive semitone, such as 60 to 'C4'. A composite over Sprites.Maths, Sprites.Array and Sprites.Text, so a live note gives a live label for the cell face, the keyboard and the dropdown.
| Input | Type | Description |
|---|---|---|
note | Number or Sprites.Reactive.Value | The semitone to label. |
Returns the note label as a string value.
const note = Sprites.Reactive.Value(60);
const label = Sprites.Media.Sequencer.NoteText(note);
Sprites.Ui.Input.Number(note);
Sprites.Ui.Dom.Text(' label = ', label);
Sprites.Media.Sequencer.PhantomGrid Sprites.Reactive.Value
Sprites.Media.Sequencer.PhantomGrid(data, model)
A blank cell volume sized to the current tempo, for an empty slot, so the grid still shows a full, fillable table instead of a blank. A composite over the tempo fields and Sprites.Grid.Three.Blank, so it follows a tempo change. The first edit through SetSelectedCell replaces it with a real block.
| Input | Type | Description |
|---|---|---|
data | Sprites.Reactive.Value | The tune data, read for its tempo. |
model | Object | The platform descriptor, read for its channel count. |
Returns a blank { w, h, d, cells } volume sized to the tempo.
const document = Sprites.Media.Sequencer.NewDocument(Sprites.Platform.C64.Sequencer);
const data = Sprites.Reactive.Value(document);
const phantom = Sprites.Media.Sequencer.PhantomGrid(data, Sprites.Platform.C64.Sequencer);
const bars = Sprites.Grid.Three.Depth(phantom);
Sprites.Ui.Dom.Text('bars = ', bars);
Sprites.Media.Sequencer.ReshapeTempo Object
Sprites.Media.Sequencer.ReshapeTempo(data, field, value, model)
New tune data with a tempo field set and every block's volume resized to match, so changing the beats per bar resizes the beat dimension of every block and changing the bars per block resizes the bar dimension, each landing in its own axis. A composite over Sprites.Object, Sprites.Array.Map and Sprites.Grid.Three.Resize. The field is 'beatsPerBar' or 'barsPerBlock'.
| Input | Type | Description |
|---|---|---|
data | Sprites.Reactive.Value | The tune data. |
field | String | The tempo field to set, 'beatsPerBar' or 'barsPerBlock'. |
value | Number or Sprites.Reactive.Value | The new field value, the new dimension size. |
model | Object | The platform descriptor, read for its channel count. |
Returns new tune data with the tempo set and every block resized.
const document = Sprites.Media.Sequencer.NewDocument(Sprites.Platform.C64.Sequencer);
const data = Sprites.Reactive.Value(document);
const widen = Sprites.Reactive.Bindingundefined, () => {
const next = Sprites.Media.Sequencer.ReshapeTempo(data, 'beatsPerBar', 8, Sprites.Platform.C64.Sequencer);
Sprites.Reactive.Set(data, next);
});
Sprites.Ui.Button.Act(widen, '8 beats a bar');
const grid = Sprites.Media.Sequencer.CurrentGrid(data, 0, Sprites.Platform.C64.Sequencer);
const beats = Sprites.Grid.Three.Height(grid);
Sprites.Ui.Dom.Text(' beats = ', beats);