1 codename: crush sequencer
4 It's a midi sequencer. It is a state machine mainly consisting of
5 midi events arranged in time (with some structure convenient for
6 editing) and also some auxilliary machinery to support certain
9 It has two main functions which it carries out concurrently: it
10 plays back the events synchronous with some callback framework,
11 and it allows a client app to make concurrent changes to the sequence.
14 Think of it as a tape deck with buttons that read and write to
15 the tape (the client app's interface) and two ports for audio out
16 (midi out) and mic input (for recording input). When the tape is
17 playing, or recording, the user has no direct control over the
18 behavior of the I/O port.
22 To support realtime operation, time consuming processes like memory
23 management are only initiated by actions of the client code, not
24 the I/O callback code.
27 features intentionally missing:
28 There are some higher level operations a midi sequencer might be
29 expected to have, but they are not provided at this level and
30 should be implemented as a layer on top. The idea here is to only
31 address the complexity of multithreaded audio programming, realtime
32 memory management, low level data consistency, timing, and undo/redo.
36 This documentation attempt will explain the structure of the events,
37 possible editing operations, the asynchronous i/o interface, and
38 the few extra features of the sequencer (auto cutoff).
42 the sequencer has zero or more tracks
43 tracks have chan and port, and contain a sequence of blocks
44 blocks have length and have one or more references to chunks
45 chunks have a color and contain a sequence of events
46 events are basic midi events without channel
49 Blocks may reference more than one chunk, but only one chunk
50 is active at a given time. These are sometimes called
51 layers but they dont have a separate name here except
52 the chunks in the block. Blocks may share chunks, changes
53 to one chunk show up in another block immediately.
55 nonstandard api description:
56 The api will be described abstractly, but more clearly than a C
57 header file. The C signatures are listed in the seq.h header file.
58 A method listed as X -> Y is a procedure that takes arguments X and
59 produces Y. The word effects in the Y part is used to signify that
60 using this operation changes the sequencers internal state. Possible
61 memory allocation is not considered an effect. If an operation takes no
62 arguments it is listed without the X ->. If a value is listed with
63 a ? suffix, it means it may be NULL. The possibility of having no
64 effect at all is not considered to be ?. The possibility of failure to
65 allocate memory is not considered to be ?. See the section on custom
66 memory management. The expression Y + effects is used when a procedure
67 results in effects and values. in C effects are not values. Read + as
68 'in addition to'. when there are more than one result values, the
69 C api will use passed-in pointers to write some of the results.
72 The 'gui thread', client code that provides a user interface to the
73 sequence, has several operations available to support playback, editing,
74 undo/redo, time config, looping (between two global static times),
75 outputting an immediate event (not scheduled), and reading back data
79 seq_add_track :: effects
80 seq_delete_track :: track -> effects
81 seq_insert_block :: (track, tick, chunk) -> effects
82 seq_copy_block :: (block, track, tick) -> effects
83 seq_resize_block :: (block,length) -> effects
84 seq_delete_block :: block -> effects
85 seq_push_chunk :: (block,chunk) -> effects
86 seq_rrotate_chunk :: block -> effects
87 seq_lrotate_chunk :: block -> effects
88 seq_insert_event :: (chunk,tick,type,u,v) -> effects
89 seq_delete_event :: (chunk,event) -> effects
90 seq_accept_recording :: effects
93 seq_play :: enable -> effects
95 seq_seek :: tick -> effects
97 seq_record :: enable -> effects
98 seq_set_record_track :: index -> effects
99 seq_loop :: enable -> effects
100 seq_loop_limits :: (tick1,tick2) -> effects
101 seq_time_config :: (srate, tpb, bpm) -> effects
102 seq_all_notes_off :: effects
105 seq_init :: (alloc?,free?,oom?) -> effects
106 seq_uninit :: effects
109 seq_commit :: effects
110 seq_clear_all :: effects
111 seq_instant :: (track,type,u,v) -> effects
112 seq_mk_chunk :: chunk
115 seq_layer_number :: block -> index
116 seq_walk_tracks :: effects
117 seq_next_track :: track? + effects
118 seq_walk_blocks :: track -> effects
119 seq_next_block :: block? + effects
120 seq_walk_events :: chunk -> effects
121 seq_next_event :: event? + effects
126 sequence data is public:
127 The structures which represent the tracks, blocks, chunks and events
128 are publically exposed to avoid a giant interface to simply read the
129 fields. Some fields are meant to be read/write to avoid a get/set
130 interface. These are listed in the header file. Some fields are
131 better not used by client code, these are not listed as read nor
132 read/write in the header file.
134 custom memory management:
135 The client code may request memory be dynamically allocated. seq_init
136 accepts up to three routines which handle this as the client code sees
137 fit. alloc should deliver a pointer to newly allocated memory of the
138 desired size. free should release the memory pointed to by the argument.
139 oom is a routine to handle the case where alloc fails. By passing NULL
140 in for any of these, the default allocator will be used (std malloc).
141 Please provide both alloc and free or neither of them because your
142 custom allocator is probably not compatible with the default free and
143 vice versa. The default oom handler writes a message to stderr and
144 exits with code 13, so its compatible with anything.
146 oom may either find more memory and report success, it may report
147 failure, or it may choose to not return (exit the program). If it
148 reports failure, then the internal sequencer will use emergency static
149 allocated storage in order to return from the current operation without
150 incident. After this you are guarantee not to have a reliable sequencer,
151 so the oom routine should consider cutting off the client code from the
152 sequencer a high priority. If using the tape deck analogy from above,
153 think of oom failure as either causing the tape deck itself to explode,
154 or to leave it intact, but might explode the next time it is operated.
159 asynchronous i/o interface:
160 The 'audio thread' which represents the cable carrying playback
161 events and delivering input events to the recorder takes the form
164 seq_advance :: sample_count -> (status, nused, port, type, chan, u, v) + effects
165 seq_record_event :: (type, chan, u, v) -> effects
167 The audio driver charged with handling the next N samples of sequence
168 should use seq_advance to advance the sequencer and return the next
169 event if any. You know when there are no events left by looking at the
170 status code. The number of samples actually advanced is returned. If this
171 number if less than N then the driver code needs to continue extracting
174 The audio driver with incoming midi events should dump them to the
175 sequencer with seq_record_event. The module will internally decide
176 to ignore them or to record them. Because editing of the sequence
177 might cause memory management record will be deferred until the client
178 thread issues a periodic 'accept_recording' signal. accept_recording
179 is only necessary if recording has been enabled by the client code.
180 When recording is disabled, any pending events will be expunged, future
188 This sequencer comes with a fancy auto note-off mechanism which
189 will send note offs for any note ons which have not be cut when
190 the client code stops playback. It will also auto cut notes which
191 are on during editting operations that could result in stuck notes.
192 this feature is tricky so sometimes more notes are cut than necessary.
196 Editting of sequence events can be undone. To support higher level
197 operations that are undone with fewer steps, undoing works with
198 commits. After completing an operation you wish to be able to totally
199 undo, commit the changes. Redo will redo all changes until a commit.
200 editing will clear any redoable actions on the stack.