-
Notifications
You must be signed in to change notification settings - Fork 5
/
TODO
676 lines (550 loc) · 28 KB
/
TODO
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
TODO: -------------------------------------------------------------------------
* 'fork' unit - unity gain panmix without the controls, basically.
* Have this added automatically when subvoice output doesn't match the
parent voice?
* Instructions to wait for a one-shot wave to end, and/or test if it is still
playing.
* Proper constant power panning in panmix!
* GUI media player style app? Player plugins?
* Positional audio demo!
* Extra package with Win32 binaries + demos!
* Segfault when doing a2_FreeBank() and reloading banks a few times.
* Only happens in certain cases, apparently in conjunction with some
compile errors!
* The (now commented out) old section of test.a2s seems to trigger it.
* Should not be using FIFOs with offline states! We can push messages directly
into those, eliminating the overhead, and the risk of buffer overflows.
* New wiring system:
* A2_structitem has one 8 bit "buffer address code" for each output and
each input.
* Top N bits is a bus index:
0: Nestlevel scratch buffers
1: Voice main outputs
2: Voice effect sends
* Remaining bits form the buffer ("bus channel") index.
* We also need something corresponding to A2_IO_WIREOUT - or do
we just ignore the above and do what we're doing now instead?
* Use {...} blocks in struct defs to explicitly define subchains!
* Any inputs on the first unit, and any outputs of the last
unit of the subchain will be wired like inputs of a normal
unit in the parent chain.
* Inter-unit wiring within the chain is done via separate
buffers, not interfering with the parent chain, or other
chains.
* Explicit 'mix' directive along with 'wire' in struct definitions!
This way we can specify exactly what we mean in a clean way, and the
implementation details are left to the engine and units.
* Unit output mode control registers?
* Replace
* Add
* Multiply
* Ring modulate
* Logic somewhere (API, core...?) that uses a suitable builtin program when
someone attempts to play a wave like an instrument!
* Maybe local function calls or subvoice spawnning should have a different call
syntax? Now they look the same, which is really rather confusing...
* Compiler should fail or at least warn about SET on non control registers!
* Compiler warnings for unused local functions and non-exported programs!
* Voices need to lock banks in place while using them!
* Consistent naming conventions - not a2_FreeThis() and a2_ThatFree()!
* API for detecting available drivers?
* TDELAY* instructions need to use a separate timer/accumulator that deals in
ticks, eliminating rounding error buildup as long as "clean" delay values are
used for 'td'.
* WAKE should only have a SLEEPing voice continue execution! The current WAKE
behavior should be a different instruction; "FORCE-if-sleeping" or similar.
Or we should just plain remove that, and have FORCE work like the current
WAKE...?
NOTE: FORCE can only ever interrupt SLEEP, END or DELAY instructions
anyway!
* Panning with Haas effect...?
* Parametric EQ!
* Use BLIT (temporary switch to alternate oscillator implementation) to handle
the transients when using 'phase'?
* Port a bunch of oscillators from Audiality 1!? (Some would preferably be
reimplemented using IFFT synthesis and stuff.)
* A parameter interface might be useful for this. We may not want to
implement everything as true control regs, for performance and
complexity issue, and the overhead of a larger register frame. This
can be of use for other voice structure units and FX units as well!
* Forward declarations of functions, for mutual recursion...?
* Make the VM PC a straight pointer...?
* We trust the VM code to be safe and correct anyway! No change there.
* Need to change branch instructions to PC relative, as they're
currently absolute.
* a2_KillSub() is actually a bitch to implement properly...! How do we find and
release any handles that might be associated with the subvoices?
* Voices with no message handlers should just die, or at least stop processing
entirely in the END instruction!
* Should probably have different instructions, so we can have programs
explicitly do this even when they do have handlers.
* Maybe we should also have a non-processing sleep state from which
voices can be woken up by messages?
* Simple VM optimization:
* Add an extra set of register write instructions (arith etc), so we
have one for control registers and one for temp/var registers.
* Move the a2_RTMark() inline first in each instruction implementation.
* Put the labels for the temp/var variants after the a2_RTMark()s.
* Have a2c_Code() check which instruction variant to use!
* Unloading objects is an ugly hack. Three basic options:
1) Insta-kill all voices whenever unloading anything that the realtime
context *might* be using. (What we do now.)
2) Scan the whole graph and kill any voices using the objects that are
being destroyed.
3) Refcount everything on the realtime side as well, and just detach
released handles on the API side.
* Releasing objects that don't belong to you is not very nice - but maybe the
engine should handle it to some extent? There should at least not be crashes.
* Could we pre-allocate a range of handles that the VM can use for juggling
live instantiated objects...?
* We'd add a realtime handle "sub-manager" with its own LIFO stack of
free handles. That way, we're actually dealing with arbitrary
individual handles (as opposed to a fixed range), so we can add new
handles to the pool as needed. For example, we can send released API
handles there instead of back to the API when we're running low. (The
API can just add blocks whenever it needs to.)
* Implement TK_STRINGLIT and then wrap that with TK_VALUE etc up as a constant
expression rule, s we don't have to handle TK_STRINGLIT and TK_STRING all
over the place.
* TK_STRINGLIT is handy for 'import' etc as it doesn't create objects!
* Pull in miniz tinfl.c or similar, to directly support compressed scripts?
* Compile time conditionals and stuff using upper case keywords?
* There should be a way to pass "constants" to modules when loading and
compiling them.
* Constant expression evaluation would be nice to go with this!
* Move builtin programs and waves into their own banks, so they don't pollute
the namespace of everything!!!
* Add DEBUG instructions for printing info on objects!
* Nick the MIDI file loader/player from the old Audiality? Nice and handy, and
we could make some tiny but awesome GM patch sets to go with it!
* Should be wrapped up as some kind of unit or other scriptable object!
Thus, we can use MIDI files like any other data from within normal
a2s modules, hiding them completely from the API, while providing
the same interface and level of control as for "normal" songs.
* Do we even need an initial root voice, or can we just make it a special case
in a2_Start()?
* We should probably switch to passing filled-out A2_message structs over the
FIFO (only pointers in the fixed-size FIFO!), and send those messages back
over a new response FIFO when done with them.
* Need to keep track of where messages were allocated, as we might be
using different memory managers!
* Simple on-demand waves: Register symbols with userdata + callback that the
compiler runs whenever it hits one of these. This could actually be used for
anything - not just waves.
* Unit instantiation arguments!!! The compiler can just keep parsing constant
expressions until end-of-statement - but where to send those figures?
* Related: Should some stage of the wiring be done by, or specified by,
the unit constructors? For example, it would be handy for the panmix
unit to specify an additional number of outputs for FX sends - but
with just a static list of target codes/indices, this can't be done
properly with dynamic configurations, as when using '>'.
* Scratch oscillator! Basically a wtosc that has no frequency register, but
instead a ramping phase register, making it easy to hit specific waveform
positions, and also allowing the oscillator to play backwards.
* Reverse feature for wtosc...? Apart from some wave padding trickery (do we
need the same amount of padding on both ends?), the main issue is that linear
pitch cannot express negative frequencies.
* Ramping control:
* Auto mode (default; what we do currently on most control regs):
* Ramps are automatically calculated when delay instructions
are executed, so as to reach the register target values when
the VM wakes up again.
* Instant mode:
* Changes are applied instantly - no ramping!
* Asynchronous mode:
* Ramping speeds are calculated as if constantly aiming a
configurable amount of time ahead, rather than being driven
by the delay instructions.
* wtosc:
* Linear ramping of the phase delta per sample
* Granular oscillator:
* Plays one-shot waveforms, triggering one repetition for
each period as controlled by p + sp.
* Uses an extra register to control actual waveform
playback pitch.
* Use another register for timing randomization?
* Manual trig mode?
* Why are we getting objects in slot 0, and builtin objects in strange orders?
* Doesn't seem to cause any trouble...?
* Timing should probably be part of one of the drivers!
* Not relevant for offline rendering and the like! It's only used by
the API when controlling a realtime context.
* 'decimator' unit!
* Bandlimited downsampling + non-interpolated upsampling.
* Sample quantization.
* "Poor electronics" filters? (Separate unit?)
* "Tiny speaker" simulator? (Separate unit?)
* Multi-sine "organ" waves?
* FFT based offline waveform generator!
* We could just strap it on as a tool that uses normal waves as
frequency domain data, so we can use the same tools for
generating spectra as for generating waveforms.
* A proper IFFT synthesis engine?
* Could implement that as one or more voice units, probably...
* Latency and transient response are the major issues with IFFT
synthesis - but augmenting it with subsample accurate time domain
synthesis might be interesting.
* APIs for transferring or copying objects across master states...?
* Allow subvoices to process audio from their parent voice...? The 'inline'
unit would need inputs, and subvoices for this would need a special exception
from the "first unit must have no inputs" rule.
* ChipVoice style auto crossfading twin oscillator!
* Registers:
p Pitch
a Amplitude
phase Phase (forces BOTH oscillators!)
detune Detune for next wave
gain Gain for next wave
w Wave. Latches 'detune', 'gain' and 'time', and starts
the crossfade!
* Implement/prototype as A2S?
* WonkyVerb - multitap feedback delay network with modulated taps.
* WeirdoVerb:
* Set up a pool of fragment buffers.
* Record input audio into buffers, overwriting, picking from
the oldest ones in the pool with a slight fuzz factor.
* Apply a triangular or Hann window or similar to the buffers!
* At random intervals, play buffers at slightly random pitch,
volume and pan positions.
* Maybe add some randomized resonant filtering and other FX?
* Normalize recording levels with a fast compressor/limiter,
wired to a slow envelope tracker, to more or less eliminate
transients?
* Realtime groove transforms for tick timing!!!
* Structures!
* Global registry of named structure types! Make them module objects?
* Instruction for getting and typechecking instances.
* Compiler does static compile time typechecking and just adds the
fields to a namespace, much like how named units are handled.
* Environments! (Structs, basically.)
* Whenever a program of a module is started as top level, an
environment instance is created.
* When a program is started on a subvoice, it gets access to the
environment instance from the nearest program of the same module up
the processing tree.
* Declared per CSL module; valid in all programs in the module.
* Alternatively, we declare environments as typed structures,
and declare in each program which environment type (if any)
it should use.
* Could implement environments as module objects, and
just add a VM instruction GETENV that creates or gets
the nearest environment of the specified type.
* Fast initialization mechanism for hardcoded VM and control registers?
* Control register write callbacks should probably be set by the Initialize
callback, just like the Process callback... Different configurations may call
for different register implementations.
* The FORCE instruction is expected to read back the current state of
interrupted control ramps in progress, but we need proper register read
callbacks in the unit API for this!
* Simply add an array similar to the groups, for master fx busses? These would
theoretically run ordinary programs, although they're fed with audio from
send units all over the graph, in the same way a normal voice gets audio from
inline processed subvoices.
* Make sure these are processed last!
* Can have subvoices, but fx sends won't work reliably on those...
* FX send units:
* How do we address the fx busses? For positional sound effects
and instruments with per-voice send levels, it would be handy
if the output bus could be selected at spawn, rather than
hardwired into the voice program.
* FX busses on the API/compiler level:
* Can be module local or exported, like programs.
* Bound to objects, like waves, programs etc.
* Is the root voice an FX bus...? We need to be able to
explicitly send audio there anyway. And it needs master
effects, like compressor/limiter.
* Instructions + syntax for indexing and interpolating values out of waveforms!
* Period and/or sample oriented versions?
* Probably obey looping flags, plain and simple...
* Mipmap level selection?
* Tables!
* Constant and/or dynamic...?
* Linked lists of chunks for dynamic sizing?
* Initialization constructs:
* Compile time table construction?
* Run-time construction? Could use the register field to
encode element count, to avoid instruction decoding
overhead.
* Indexing and interpolation instructions + code syntax.
* Separate driver interface destruction from Close() method?
* Three operand instructions! A constant table instead of immediate values
would make room for that nicely, and avoid the nasty f20 numbers.
Alternatively, we add another word to instructions with an immediate
value argument.
* CS_PCPULOADAVG seems to report roughly 50% of the load that is reported
by JACK...
* What's with the regular 60+% spikes in the CPU load in cstest?
* Per-program analysis tool for optimizing songs and sound effects!
---------------------------------------------------------------------------
Structure sizes as of 20120905, 0.2.0:
CS_object: 4
CS_wave: 144
CS_bank: 32
CS_program: 368
CS_stackentry: 136
CS_voice: 544
CS_oscillator: 48
CS_panmixer: 32
CS_message: 56
CS_block: 256
---------------------------------------------------------------------------
* File system driver interface, so we can use PhysicsFS and the like?
* What's with the a2c_SkipLF()s in various constructs? Aren't we supposed to do
free formatting, since the braces are required anyway?
* Minus before literal is always unary unless followed by whitespace. Might be
a bit annoying if one doesn't have a habit of always surrounding operators by
spaces anyway.
* Sleeping voices should not burn CPU! Also, inaudible voices should optimize
away audio rendering, filters etc. Handy for continuous sound effects and the
like - though that might be tricky to implement. Basically, we need to keep
the VM running as usual to have it in the right state if/when it's time to
wake it up again. Which means, doing mostly nothing, unless it's a VM
intensive sound, so it could be a massive win.
* VM command to have a voice force detach and die! Many sounds that need
realtime control will still go to sleep to never respond again once they
finish, so no point in having them hang around because someone happens to be
holding a handle.
* "Multiwaves? Waves that are essentially used like normal waves, but actually
map to multiple other waves as functions of pitch, amplitude, manual control,
or just random selection.
First thought was to use this when pre-rendering sounds with noise and
other random components, to avoid making the sounds stiff and repetitive.
* Free running phase! (Seems like we have that already...) We don't
want waves to be reset to phase 0 whenever they're switched in. We
want the whole "wave bank" to play as one, just cross-fading between
"tracks".
* Have the asynchronous API calls return "tracking tickets" that can be used
for reference if errors occur, to check delivery status etc?
* Programs must be able to send messages back to the API!!!
* Binary files! Make the compiler optional. Packed VM code? Or is it better to
just leave that to lzma or bzip2? Hint: Just dropping the null bytes actually
makes the compressed file one byte larger in a naive test...! Theoretically,
the only thing that can shrink compressed results is removing actual
information. If we use Huffman encoding based on VM instruction use frequency,
we remove the need for the compressor to store the results of a similar
analysis in each file. That might save a few bytes.
* Editor/synth application with:
* ALSA and/or other MIDI interface
* Code editor
* MIDI record feature that just inserts code based on some
switches and settings! Example:
td %qdelta
%vvid:piano %pitch %vel
* Interactive VM debugger
* Voice status visualization
* Live multi-voice visualization in the code editor!
* Overlay voice status boxes with oscilloscopes, most
interesting regs etc, and use lines or arrows to indicate
where they are in the code.
* Use loop arrows and/or afterglow to visualize code that runs
too fast for realtime display.
* Use a bunch of simple filters to eliminate silent voices,
voices running, or not running, specific programs etc.
---------------------------------------------------------------------------
BUGS:
* Timing should be managed centrally somehow, to avoid voices
drifting apart due to rounding errors. Then again, this is not
a real issue unless we have independent threads running in
parallell for "ages" without occasional re-synchronization.
* Rip the MIDI code out! It belongs in the test programs, or as
some sort of optional add-on module...
* MIDI should use one sub-group per channel, to allow
proper implementation of channel-wide controllers.
* CPU load and related properties and pretty much broken, as
SDL_GetTicks() has way too low resolution for measuring such
short intervals as the audio callback.
* There is a detach chain reaction that causes some minor issues;
if a program is detached, it can still receive broadcast
messages for as long as it hangs around. Now, if that program
just starts a few other programs and then goes to sleep, the
'sleep' will abort and the voice will 'return' - detaching all
subvoices! This means that the program can no longer control
it's subvoices, despite being able to receive messages.
Now, you're not *really* supposed to talk to detached voices,
so we pretty much have this coming if we do. However, this is
illogical and inconcistent. We should either not be able to
control detached voices AT ALL, or it should work as expected
(ie, it works as long as the voices stick around) at all times.
* Undefined forward declarations give nasty error messages. Will
need to report based on the offending fixups somehow. Store
source code position in them?
* Compile errors just abort compilation, but any code generated,
symbols exported etc until the point of the error are left
around! Can result in "interesting" programs... :-)
* There might be an occasional symbol leaked if a compile error
occurs in the wrong place. a2c_Grab() should probably wire the
symbols up for some sort of garbage collection.
---------------------------------------------------------------------------
Ideas:
* "Effectively silent" conditional jump? (Checking the amplitude
for some "very low" value is rather ugly...) The "silent" level
could be configurable (quality option), but programs using this
should be designed to handle any valid setting!
* "Approach" instructions?
* Linear!
* Inverse exponential: @x y c ==> +x (y - x * c)
* 'for' variants; which one(s) do we implement?
* for <var> { }
Iterate from the initial value of <var> down to 0,
using <var> as counter.
* for <var> <end> { }
Iterate from 0 through <end>.
* for <var> <start> <end> { }
Iterate from <start> through <end>. Direction is
automatically detected.
* for <var> <start> <end> <step> { }
Iterate from <start> through <end> in <step>
increments. Direction is automatically detected,
and the sign of <step> is ignored.
* Conditional to check if a waveform is looped or one-shot!
(Mostly for a builtin "PlayWave" program - though one might as
well handle that by having cs_Start*() select the appropriate
program.)
* Finish the forward branches - or just drop the labels...? A
'switch' would most likely need this. Pretty much anything else
can be solved with the existing loops and conditionals - though
labels + jumps might actually be less messy sometimes. We have
no memory management to worry about here, so the situation
differs slightly from that of general purpose languages...
* Multichannel routing:
* We have a stack of mixing buffers, covering the maximum
supported channel count, and some temporary buffers.
* Within a voice, units just deal with whatever number of
voices they are designed for - so, units need to be
combined correctly, inserting splitters and mixers as
needed.
* Similarly, subvoices need final output stages that mix
into the parent's buffers appropriately. How do we
select them...?
* Audio rate windows/envelopes for granular synthesis? Or just make
it AM with two normal oscillators? Slightly heavier, but much
more flexible and generally useful. On the plus side, it's very
easy to add; just spawn a child voice that modulates our output
instead of adding into it.
* An .a2s file might want to provide hints as to how many voices,
stack entries, various kinds of units and stuff like that it may
need, so we can pre-allocate sufficient resources to handle it
in a realtime safe manner.
* Basic, textbook PWM pulse implementation:
* Have a Dirac impulse oneshot wave
* Control PW with oscillator pitch
* Control pitch with impulse frequency
Problems:
* PW needs to be restricted at high pitches to avoid
aliasing as oscillator "retrig" cuts into the band-
limited transients of the waveform.
* DC offset... We could work around that by making the
impulse -1, 1, -1, and extend the latter -1 part enough
that the waveform never ends before we retrig.
* A default tick setting per bank would be handy... The default
tick would be used whenever the parent voice/group has a zero
tick duration. If the bank doesn't have a default, the engine's
global default is used, and that can be changed by the host
application.
* Alternative instrument control approach to consider:
* A program implements a full "MIDI style" voice - mono or
poly! Arguments are init parameters only.
* Selecting an instrument is starting a single voice with
one of those programs. This can initialize output
routing and whatnot.
* Notes are started and controlled entirely using messages
to the "master program". The program implements mono,
poly, arpeggiatos etc.
For a monophonic instrument, the net result is essentially what
we have already. For a polyphonic instrument, the main program
would be more like the current song "patterns" (only spawns and
controls other voices), but would do it's job mainly based on
received messages.
Do we need extra language features for this? How do we manage
voices in polyphonic instruments? Is it sufficient to just take
voice IDs as message arguments, and use them directly as VM
subvoice IDs? Some 32 IDs should be enough, but mapping from
MIDI becomes less trivial than it is with 128+ IDs, like we have
in the API.
* Conditional jump to sense if the voice has been detached! We may
want to do stuff, rather than just SLEEPing.
Alternatively, we can define an optional message that is sent
to a voice as it's detached. That is, in fact, a much more
powerful solution, as it can abort long delays and loops if
desired - and it can also just set a variable, effectively
implementing the conditional jump variant.
* 'mac' for defining macros that are pasted as text into the
source. (Need stack for the source reading interface.)
* Forward declarations of programs! (There are workarounds...)
* RNG control instructions? We might want to separate the noise
generator from the RAND* instructions, as the noise generator's
consumption may vary depending on sample rate and ChipSound
version.
* Realtime control API!
* We need a callback that runs from the audio callback.
* We could open up the message subsystem, allowing this
callback to receive messages from the application.
Or, we use user defined, native code voices for this.
These would be started and controlled much like any other
voices, and can start other voices and send and receive
messages to/from other voices.
* Tables? Functions! Would be very handy for feeding simple loops
with sequenced data, instead of unrolling the loops or trying to
calculate the data. Obviously, we would also have various math
functions, waveforms, perlin noise and whatnot. Using waves as
tables would also be possible.
One would use various instructions to index
the tables in different ways; interpolation types, clamping/
wrapping etc.
IND[I][W] r f i
Set r to f(i), where IND* grabs the nearest point,
INDI* uses linear interpolation, and IND*W uses
wrapping (rather than clamping) at the ends of a
finite range function.
READ[W] r f i
Set r to f(i), and the increment i by 1.
Asm syntax:
r f(i[+])[i][w]
(add|sub|mul) r f(i[+])[i][w]
Table definition:
name [ values... ] // List of the usual real values
name "string" // One [0..255] value per char
tab notes [0 1 1 0 1 0 0 1]
for x 0 7
{
p (notes[x] * p2 + p1); + x 1; tdelay 8
}
'c' that parses into a single [0..255] value would be
handy as well.
We could construct a SWITCH instruction based on this, but we
should probably not make those tables publically available, as
that could send the VM anywhere...!
switch <expr> <label> [<label>...]
* Reverse playback of waveforms...? (Needs some minor extra logic,
but no major issues... I think.)
---------------------------------------------------------------------------
OPTIMIZATIONS:
* VM push/pop should only save registers that are actually
clobbered by the callie! (Performance optimization...)
* MUL2, MUL3, ADD2, ADD3 etc, for quick construction of
the final output of envelopes, LFOs, volume, velocity
etc to control regs?
* The 'inscount' safety thing only needs to count in flow
control instructions. And, it shouldn't really be needed
for production VM code anyway, so maybe use it only in
DEBUG builds and authoring software?
* case OP_AMOVE4:
carg[reg + 3] = v->r[(arg >> 15) & 0x1f];
case OP_AMOVE3:
carg[reg + 2] = v->r[(arg >> 10) & 0x1f];
case OP_AMOVE2:
carg[reg + 1] = v->r[(arg >> 5) & 0x1f];
case OP_AMOVE:
carg[reg] = v->r[arg & 0x1f];
break;
* Build a peephole optimizer right into a2c_Code()?
* Keep a "last break" position in the state.
* When declaring a label, program or other jump target,
reset the last break pos to 0.
* Reset the last break pos after issuing a branch, return
or other "exit".
* When issuing an instruction, run peephole optimization
from the last break to the new instruction. If we deal
only with two instructions at a time, we only need to
check that "last break" is non-zero before optimizing.
--------------------------------------------------------------------------