
                            FlexSID User Manual
                            -------------------


Introduction:

 FlexSID is primarily a tool for you to create small footprint SID music. Yet it
provides flexibility and most features you usually need for good sound. To reach
this goal it's a bit different conceptually from the traditional music trackers:
Diatonic scale-degree based notation system for better music-data compression
where it is possible, and the single command-oriented instrument-table which is
responsible for the small player-code size. The result pretty much depends on
the musician as well.
 In this document I'll start with the fast facts you might need to look up again
later, and towards the end I'll give some more explanation,tables, history, etc.


Features:

 The editor is written in C (and some ca65 assembly), compiled with cc65 and gcc
with a quasi-emulated layer on top of SDL1.2, and therefore runs identically on
Linux (compiled for 64bit), Windows and of course C64. (On
C64 the defMON sync-adapter is supported now and is used automatically.)
(There is an installer in .deb package format for Linux which takes care of
installing FlexSID into /opt/FlexSID folder and creating appmenu-entries and
file-associations.)

There are 5 different player-types, let's summarize their needs and features:
                                                  Bare Light Medium Normal Extra
 -player-code memory-occupation (bytes) .......... 325   357   388    420   430
 -player zeropage-usage (bytes) ..................  64    64    65     67    67
 -estimated rastertime (depends on effect usage).. ~16   ~19   ~22    ~24   ~32
 -90 usable patterns (C code is memory-hungry)....  X     X     X      X     X
 -1x..8x framespeed (multispeed) playback.........  X     X     X      X     X
 -SID-initialization support ..................... ... .. X     X      X     X
 -Main Volume initialization support ............. ... .. X     X      X     X
 -Tempo-program support (funktempo,tempo-change).. ... .. X     X      X     X
 -Default duration (better pattern 1st note pack). ... .. X     X      X     X
 -Orderlist-effect (transpose, etc.) support ..... ... .. X     X      X     X
 -Simpler instrument-call ('RunFX',$8C not needed) ... .. X     X      X     X
 -Instrument/FX engine B (for e.g. pulsewidth-mod) ... ..... .. X      X     X
 -Instrument/FX engine C (may be filter-program).. ... ..... ..... ... X     X
 -Instrument/FX table row-repeat commands......... ... ..... ..... ... X     X
 -Octave 0 support (having deepest sounds)........ ... ..... .. X      X     X
 -Octave-transpose support (beside key-change).... ... ..... .. X      X     X
 -16bit sweep/slide support (pitch,PW,cutoff)..... ... ..... .. X      X     X
 -Hard-restart easily with $C6 command ........... ... ..... ..... ... X     X
 -No frame-skipping at notestarts (big rastertime) ... ..... ..... ......... X
 -Free transpose (more than 24 semitones shift)... ... ..... ..... ......... X

The saved music-data is compatible between the different player/editor-types and
any .flx workfile can be opened from any of them but due to the nature of the
player optimizations of light/bare/medium players the sound output can be
different and will most likely need manual tweaking: Orderlist-effects vs jumps,
tempo-table tempo-values/jumps, pattern effect-calls can get doubled/halved,
and key-changes/octaves can shift too. There's no automatic conversion between
the player-types, so its advisable to decide in the beginning which one to use.
 It's also important to mention that the players use illegal CPU-instructions
extensively, so emulation frameworks that don't support them will likely fail
to play the tunes. Fortunately most players seem to support illegal opcodes.
 Please note that 'bare' player doesn't initialize main volume so you won't hear
any sound unless you set it in InsFX table (command $B8 on channel1, see later).


Menu and disk operations:

 Menu-screen (called with F8, exit: Esc) is kindof self-explanatory, you can
select the possible operations and settings on the left side. Saving the
settings writes a file called FlexSID.cfg in your home-folder that will remember
selected zoom, SID-model, note-display mode, relocation and default pattern-
length. (Unused patterns not yet found in the orderlist, are set to the default
pattern-length upon clear/load/save operations.) A FlexSID.ini file is saved as
well upon exiting the program which will remember the working folder for the
next launch of FlexSID.
 On C64 the devicenumber is what FlexSID was loaded from and only up to 16-char
long lowercase filenames are supported, and there's no config-file.
 On menu-screen's right side there's a summary of player-features, available are
marked with '+' signs. The info area below it tells the selected playertype's
memory footprint and info about its inclusion in programs (init/play addresses).
 The load/save screen differs a little bit on C64 but usage is similar. In the
save-screen pressing cursor up/down enters into filename-selector mode, and any
key for note-typing or cursor left/right enters back into filename-typing mode.
 The filename will be split into author and title parts in the exported
SID or runnable-prg music, gets cut at the first occurrence of a '-' character.
(C64 variant can export only simple PRG and the C64 will be reset afterwards!)


The editor screen:

 The left side of the screen is occupied by the orderlists for SID channels
containing pattern numbers to be played sequentially. The middle section is the
pattern-editor. The right-hand side contains the combined Instrument/FX table
along with the tempo-program table at the bottom.
 At the top-left corner right to the 'FlexSID' logo you can see two single
letters, the 1st indicating the current player-type's first letter, and the
second shows the player-type of the loaded tune. It's best if they match.
 At the top of the pattern-columns you can see the selected/played pattern's ID
number and its size in memory. At the top of Ins/FX table a number shows the
table-position (instrument) selected for auditioning, next to it a little
context-sensitive help showing the shortname of currently edited table-command.
 At the bottom of the screen from left to right are the orderlist play-positions
and below the patterns their currently played music keys/modes instruments. At
the bottom-right corner with 'RT' label the estimated current and maximum
rastertime is displayed. Be aware that this is just an estimation, the player
in the editor is modified on the fly in every frame to permit pattern-playback,
jamming, mute/solo, orderlist-NOPs, etc., and so it takes much more rastertime
than the pure player that finally gets exported into PRG/SID format.
'ST' field stands for subtune but currently there is no support for subtunes,
and 'OC' stands for an octave selected for note-typing. The selected framespeed
('1x' by default) is shown right below it. Above all these is playtime-counter.
 In the PC-version of the editor there are oscilloscopes in the top-border above
each channel's pattern, and a scope on the right displays the main wave-output.
The small scopes in the top-left corner represent the ADSR rate-counters of SID
which are responsibe for ADSR delay-bugs if they orevflow. This condition can
be distinguished by a full-amplitude sawtooth-wave seen in these little screens,
and might be useful during construction of HardRestarts and stable sound-starts.
 The bottom border has some display-bars/scales, from left to right they are:
filter-cutoff (brown), pulsewidth (blue bars) for each channel and the estimated
rastertime (white).


Usable Keys (in editor):

I implemented the same keyboard-layout that is present in my other trackers
which were inspired by Goattracker's convenient layout. For a fast help you
can always press Control(C=)+H in the editor screen for a short cheatsheet.
But here comes a more verbose listing:

 Navigation: move cursor anywhere with cursor-keys or click with mouse(editor),
             fast movement is possible with TAB, Home/End, Page-up/down, scroll,
             or F5/F6 to toggle orderlist/patterns and F7 to toggle InsFX/tempo.
             Pressing ENTER on a Pattern-number in Orderlist, or Ins/FX-number
             in pattern or jump-address in InsFX-table will bring it into focus.
             (Shift+ENTER in Orderlist selects only one pattern.)
             As the text in bottom-left corner suggests, call menu with F8 and
             return from menu or file-screens with Esc (Run/Stop on C64)

 Playback: F1: play from the very beginning
           F2: play from marker (set in Orderlist by Shift+SPACE)
           F3: play selected patterns (which are displayed on screen)
           F4: pause/continue playback
           Pressing Control(C=) together with them activates follow-playback
            mode (editing gets disabled until next pause).
           Shift + SPACE: in pattern-tabs it plays patterns from cursor-position
           Shift/Ctrl(C=) + SPACE: in Orderlist it sets the play-start marker
            for playback with F2 (Ctrl+Space can set them individually)
           ` (<- back key on C64) : Fast-forward (4x speed playback)
           Shift + 1 / 2 / 3 : Mute/Unmute corresponding SID-channels

 Editing: In all tables except pattern note-columns simple hexadecimal numbers
          are to be typed with 0..9 and A..F keys. Insert/Delete keys work as
          expected, and orderlist/IFX references are readjusted automatically.
          Shift+N can be used to type short names for the first $20 jump entries
          of InsFX table (Instrument-names, so to speak). Deleting all the
          characters from a name removes it. Press Control+E (C= + E) in the 2nd
          (parameter) column if you want the jump addres set automatically to
          the end of InsFx table (this eases creating a new Instrument/FX).
          Pattern-length can be set by Control(C=) + ENTER on a row.
          Pattern Note editing specific keys:
          SPACE: toggles note-jamming or note-editing mode (cursor-blink change)
          Control(C=) + 1/2/3 or +/-: select octave for note-typing/jamming
          Z..M , . S..L Q..P 2..0 are piano keys to enter/try notes (jamming)
          (In Orderlist/InstrFX-table the cursor-column determines the channel
           on which the played note will be heard. This is useful if you have a
           filtered channel and want to test the instrument specifically on it.
           To test legato, don't leave any time-gap between piano-keypresses.)
          1 and A: deletes a note in place (puts a 'NOP' into the pattern)
          ENTER: inserts a rest '--' into the pattern that ends a note (gateoff)
          Control + X/C/V : cut / copy / paste pattern (in which cursor resides)
          Control + Q / W : transpose note-octaves up/down in current pattern
          Control + N: Cycle notedisplay modes (scale-degree / CDEFGAHC / solmi)
          Other editing-related keys:
          Shift + F / G: increase/decrease framespeed (2x is the max on C64)
          Control + S : save changes to the opened file (untitled.flx if new)


                               Table-values:
                               -------------

 Editing music is essentially typing values into tables. In the case of FlexSID
these values are not human-readable in memory but optimized for the small player
code. Good for you, the editor takes care of the realtime back&forth conversion
of data that you type in. As mentioned above, this can cause differences in the
same .flx workfile being opened in the different types of FlexSID editors.

 TempoTable: The simplest table of all. You can set a single tempo on its 1st
             line and jump back to it. But the 1st note gets delayed due to the
             optimized player-code: If you want funk-/swing-tempo, put a startup
             'delay' (e.g. 1 for short start) in the 1st row, then the 2 funk-
             tempos followed by a jump back to funktempo1 (odd rows). Even more
             complex tempo-programs can be constructed, and tempo-table is the
             key for tempo-changes that can be performed from Instr/FX table.
             $01..$7F: speed/tempo setting (frames to wait between pattern-rows)
             $80..$8F: jump to tempo-table position 0..F (in supporting players)

 Orderlist: Orderlist simply conducts for each SID-channel which patterns are
            played after each other. Possible hexadecimal values are:
            '..' (00): Does nothing, can be used to align numbers of patterns
                       with different lengths, isn't exported into output file
            $01..$5B : Pattern-numbers 1..91 to be played after each other
            $80..$BF : Jump/loop to (value minus $80) orderlist position
            $C0..$FF : Call to an Instrument/FX-table position (transpose/etc.)

 Pattern: The first column of a pattern contains the notes as (by default)
          scale-degrees of a diatonic scale, it is marked by a '#' or 'b' next
          to a note if it's modified to sharp/flat. The notes in the pattern
          can span 4 octaves, little strokes after them tell which octave they
          belong. To reach the full 8 octave range you will need to use octave
          transpose in music-key selection or in the arpeggio-table, see later.
          The default music mode (for note-entry and display) is minor because
          it seems to be used more frequently. It is advisable to select a
          proper music key and minor/major mode (see later in Instr./FX table),
          so as to have most notes without sharp/flat modifiers if possible:
          The tune-exporter packs notes/rests together into 1 byte with duration
          changes or effects where it can do so. This may produce smaller SIDs.
          (Duration of 1..4 rows and Instr/FX-call $20..$23 can be packed best
           if there is no change in octave between adjacent scale-notes/rests.)
          You can change the note-display mode to the CDEFGAHC-variant or sol-mi
          system but at the same time you get #/b-modifiers and it may not be
          as clear which notes are diatonic scale-notes and which are semitones.

          The 2nd and 3rd column of a pattern are effect-calls into Instr/FX
          table. These columns are identical, there is no dedicated instrumet or
          effect column, though they're executed in left-to-right order.
          (Effects and even drum-instruments can be used without a note/rest in
          1st column, but instruments without note won't trigger a new gate-on.)
          There are 2 types of them in players with 'RunIFX' support:
          $00..$1F: an Instrument/FX Engine-A table-position is set, in other
                    words instrument gets called/played (by channel Engine-A)
          $20..$3F: calls an Instrument/FX entry $00..$1F as a subroutine on a
                    frame, and then returns immediately, good for simple effects
          (On players without RunIFX (bare), all values behave like the latter.)


 Instrument/FX-table:

  The most complex table with the most commands because nearly all sound-control
  happens here. There is no strict distinction between instruments and effects:
  Everything is made from commands (left-column hex-numbers) and corresponding
  parameters. This is the only table for everything happening frame-by-frame, no
  special dedicated Waveform/Pulsewidth/Cutoff tables are present. This is the
  result of the optimized player-code. But anyway, more 'Engines' can run in a
  parallel fashion at different positions of this single table. To be exact, two
  engines per channel (EngineA and EngineB) and one common extra EngineC.
   This is in line with SIDs construction, so it is an obvious solution to use
  each channel's EngineA for Waveform+Pitch commands, their B-Engines for
  pulsewidth modulation programs, and the common EngineC for filter/vol-control.
   But this is only one of the possibilities with Ins/FX table, any command
  can be played by any Engine. For example light/bare players don't even have
  B and C Engines but with proper sequencing/timing of commands it's still
  possible to for example make arpeggio and pulsewidth-sweep at the same time.
   Furthermore the Instrument/FX calls from orderlists and patterns (above $20)
  mentioned earlier can be executed without disturbing the running Engines.
   Instr/FX table is not compressed, it's exported into the SID/PRG tune from
  its first entry to its last entry, including empties inbetween. For smaller
  result the unused rows, if there are any, should be deleted by hand.

  -There are some InsFX-table quirks to remember: Rests in patterns use the 00
   position of IFX-table by default so for normal rests it should be left empty.
  -Commands addressing common engines/registers are addressed differently on
   channels, 7 must be substracted for Channel2, 14 for 3rd. More on that later.
  -Combined (summed) chromatic note-shifting caused by MusicKey+Note+Arpeggio
   can't exceed 24 semitones (2 octaves). If in doubt, it's always safe to use
   default Hm/H key: every note is available there too, but there will be more
   unpackable semitones than with proper key. Combined Key+Note octave-shifts
   are also constrained to octave 0..3, but arp-octaveshift can be arbitrary.
   *Extra player has FreeTranspose so it's not suffering from these limitations.
 
   There are two main types of table commands, those that run immediately
  after each other without waiting, and those that wait 1 frame before leading
  to the next command. Now let's see the command-values:

  .. (00) : Jump to an Ins/FX table-position given in the parameter. If the
            parameter is .. (00) as well, it's an END (stop / return from FX)
            Jumps are useful in many ways, for example you can create a jump-
            table at the beginning of InsFX-table for the instruments spread
            in the rest of the table, and if you insert/delete rows these jumps
            will be adjusted automatically. Therefore in this scheme you don't
            have to change the patternFX calls when instrument positions change.
            On the other hand patterns cannot call InsFX-table effects above
            position $1F (or $3F without RunFX) anyway, and instruments outside
            of this region can only be reached by jumps from lower positions.

  $01..$88: Waveform+Pitch setting. Command is waveform-control value as
            represented by SID waveform-register, Parameter is pitch:
            $0x..7x: relative note: 1st digit is octave-shift 0..7
                     2nd digit 'x' is chromatic note-shift (arpeggio) 0..B
               (Pitches in FlexSID are essentially represented in base-12
                duodecimal/dozenal system. Which means if you for example
                want to shift 1 semitone down from tonic/base note, you'll
                need to decrease 1st 'octave' digit by 1 and set pitch to 'B'.
                Shift-values bigger than $B (11) will work but might cause range
                wraparounds if music-key shifts/transposes are also present. )
            $8x..Fx: absolute pitch: 1st digit is octave0..7, 2nd digit is pitch
            WaveForm+pitch command waits 1 frame. The bigger the octave the more
            rastertime is eaten by the played note due to more internal shifts.
            (There's only 1 octave of frequency-table in FlexSID player so the
             upper octave frequencies are generated by multiplying these.)

    $89: Tempo-table program-call: sets the tempotable-position aka tempo-change

    $8A: Sets Music Key and Mode: if 1st digit is bigger than 8 mode is major
         1st digit 0..3/8..B is Octave0..3 (0/1 in players without Hard-Restart)
         2nd digit is 'key' as chromatic note-shift aka 'transpose' upwards

    $8B: Set the InsFX EngineA table-position pointed by the parameter as an FX
         that gets called by new notes from now on a channel, aka 'instrument'.
         It might be a good idea to put this right before an instrument in table
         pointing to the instrument, so the instrument will run immediately.
         A possibility to save some bytes is to select only the frequently used
         instrument on a SID-channel with this method and call rarely used
         instruments directly without this command, so no need to switch back.

    $8C: Set InsFX EngineA table-position. Similar to $8B but this is not
         remembered for next notes. For player-types with RunFX support this is
         not needed because pattern-call $00..$1F sets EngineA run-position by
         default. This is for interim instruments used with only a few notes.

    $8D: Repeats the following EngineA InsFX table-command by the times given in
         the parameter (value 0 means '1 times', so subtract 1 from the bigger
         values). A repeat only makes sense for commands that wait a frame
         like waveform+pitch and slide/sweep. Due to player-code optimizations
         it won't wait forever after counting down, so if an END .. .. follows a
         repeated command, it might be needed to replace that with a jump to an
         other END instead, otherwise the sweep continues after 127 frames.

    $8E: Set Table-EngineB position, for example to call a concurrently executed
         pulsewidth-sweep program.

    $8F: Repeat the next EngineB command. Unfortunately due to strong playercode
         optimizations the $8D command can't be used for EngineB, only $8F. Its
         main purpose may be pulsewidth-sweep, see later.

    $9E( / channel2:$97 / channel3:$90): Set common EngineC execution-position.
                                (A parallel filter-program is a good candidate.)

    $9F( / channel2:$98 / channel3:$91): Repeat next EngineC command (filtsweep)

 $A0..$B8: Set corresponding SID-register $00..$18 (not waiting a frame).
           A fast reference for the sensible commands:
           $A0:pitch low-byte,        $A1:pitch high-byte,
           $A2-pulsewidth low-byte,   $A3:pulsewidth high-byte (values 00..0F),
           $A4-waveform+control reg.,
           $A5:Attack/Decay,          $A6:Sustain/Release

           Be aware about the quirk mentioned above:
           $A0..$A6 is the same on all channels but $B5..$B8 are common
           (filter/volume) and 7/14 must be subtracted from command-values on
           2nd and 3rd channels respectively (only in EngineA and EngineB):
           $B5 (/$AE/$A7): filter cutoff-frequency low 3 bits (values:0..7)
           $B6 (/$AF/$A8): filter cutoff-frequency high-byte
           $B7 (/$B0/$A9): filter resonance(1st digit) + channel (2nd digit)
           $B8 (/$B1/$AA): filter-band (1st digit) + main volume (2nd digit)

 $C0..$D8: Sets SID-registers just like $A0..$B8, but sets a ghost-register at
           the same time which is needed for pitch/pulsewidth/cutoff shift/sweep
           and also waits 1 frame. The register layout is the same as above,
           but $C6 is a special case: For players supporting Hard-Restart it
           not only sets Sustain/Release register, but if the parameter/value is
           smaller than $80 it turns off gate-bit in Waveform-control register,
           and this way for example a $C6-$00 command performs a Hard-Restart if
           it's used in two adjacent rows/frames. A softer restartmethod is also
           possible by using $C6-$0F for only 1 frame which ensures a delay-bug
           in SID in most cases and provides a nice short 8..10ms next waveform.
           The gate-bit gets enabled again by the next Waveform+pitch command.
            If you run hard-restart command on the same Engine (A) that runs the
           waveform+pitch, be aware that the arpeggio/drum/etc that still
           executes on that Engine gets interrupted by a new note-start and
           might get stuck at an intermediate pitch. Making drums shorter may
           be the easiest workaround, or using extra-player and only 1-frame HR.

 $E0..$F8: Adds/Subtracts to/from SID-registers (along with ghost-registers),
           and this is the way to achieve pitch up/down-slides or pulsewidth
           or filter-frequency sweeps. With player-types supporting 16bit sweep
           the addition/subtraction is 16bits-wide and lowbyte addresses should
           be used like $E0-xx for pitch-slide up/down, $E2 for pulsewidth-sweep
           and $F5 (on channel1) for filter-sweep. In the 8bitsweep-only players
           $E0, $E1, $E2, $E3, $F5, $F6 can be used independently as 1byte mods.

           The parameter-values look like this (on 16bit-sweep players):
           $00..$4B: slide/sweep upwards  $FF..$B4: sweep downwards
           The scale is kindof exponential so there is bigger difference between
           slides farther from $00/$FF. There are corresponding add-subtract
           value-pairs for ping-pong pulsewidth-program or vibrato, a table will
           follow below. 8bit-sweep players simply add 00..$7F or FF..80 (sub).

           These commands are only executed for 1 frame so if a continuous sweep
           or slide is wanted a jump command should point back to them
           continuously or a repeat-command should precede them.

 That is all about the Instrument-FX values. As mentioned earlier, a little help
is given at the top of the table to associate the values with meanings more
easily. One more thing: the common EngineC is executed on the 1st channel
internally so it is not necessary to be worried about the quirky -7 / -14
command-indexing in EngineC-program when it's called from 2nd or 3rd channels.
For more information look at the included example tunes and tutorials, hopefully
these will give some more ideas. In any case you can PM me at CSDb for help.


                                  APPENDIX
                                  --------


                              Conversion tables
                              -----------------

This table contains the list of 16-bit positive/negative (add/subtract)
slide/sweep values generated from the 8bit values typed into InsFX table.
(This sigmoid/exp-like nonlinear scale-generator is only present in players
that support 16bit sweeps. The rest use direct 2's complement 8bit values.)
 The up/down slide values are arranged into pairs, with identical (but opposite)
effects marked by a '*'. These are the pairs that can be used for ping-pong
sweeps/slides or vibratos without any drifting.
 'TABLE' column has the values written in InsFX table, 'pos/neg' has the 16 bit
values generated by the player on-the-fly, and 'input' shows the actual input
that the player gets inside. The latter is converted from the InsFX table value
by the editor automatically to ensure continuous increase between $00..$4B and
$FF..B4 as seen by the user. The 'shifts' field shows the number of
shift-operations done by the player to generate the 16bit result, which in turn
is responsible for gradually more-and-more rastertime in smaller/slower slides.

    TABLE  pos. neg.  input shifts   TABLE  pos. neg.  input shifts
    00 FF  0000 FFFF: 00 FF 0 7     *28-D7  0360-FCA0: 6C-94 4-4
    -- FE   --  FFFC:    FE   6     *29-D6  03A0-FC60: 74-8C 4-4
    02 FD  0007 FFF7: 07 F7 7 7     *2A-D5  03E0-FC20: 7C-84 4-4
   *03-FC  000C-FFF4: 06-FD 6-5     *2B-D4  0400-FC00: 08-F8 0-0
    04 FB  000F FFEF: 0F EF 7 7      2C D3  0440 FBB0: 11 BB 1 3
   *05-FA  0014-FFEC: 05-F6 5-6      2D D2  04B0 FB40: 4B DA 3 2
    06 F9  0017 FFE7: 17 E7 7 7      2E D1  0540 FAB0: 2A AB 2 3
    07 --  001C  -- : 0E    6        2F D0  05B0 FA40: 5B E9 3 1
   *08-F8  0020-FFE0: 04-FC 4-4      30 CF  0640 F9B0: 19 9B 1 3
    09 F6  0027 FFD7: 27 D7 7 7      31 CE  06B0 F940: 6B CA 3 2
   *0A-F5  002C-FFD4: 16-F5 6-5      32 CD  0740 F8B0: 3A 8B 2 3
   *0B-F4  0034-FFCC: 0D-E6 5-6      33 CC  07B0 F840: 7B E1 3 1
   *0C-F3  004C-FFB4: 26-ED 6-5     *34-CB  0800-F800: 10-F0 0-0
   *0D-F2  0054-FFAC: 15-D6 5-6      35 CA  0940 F740: 4A BA 2 2
   *0E-F1  0060-FFA0: 0C-F4 4-4      36 C9  0A40 F640: 29 D9 1 1
   *0F-F0  006C-FF94: 36-E5 6-5      37 C8  0B40 F540: 5A AA 2 2
   *10-EF  0074-FF8C: 1D-C6 5-6     *38-C7  0C00-F400: 18-E8 0-0
   *11-EE  008C-FF74: 46-DD 6-5      39 C6  0D40 F340: 6A 9A 2 2
   *12-ED  0094-FF6C: 25-B6 5-6      3A C5  0E40 F240: 39 C9 1 1
   *13-EC  00A0-FF60: 14-EC 4-4      3B C4  0F40 F140: 7A 8A 2 2
   *14-EB  00AC-FF54: 56-D5 6-5     *3C-C3  1000-F000: 20-E0 0-0
   *15-EA  00B4-FF4C: 2D-A6 5-6      3D C2  1240 EE40: 49 B9 1 1
   *16-E9  00CC-FF34: 66-CD 6-5     *3E-C1  1400-EC00: 28-D8 0-0
   *17-E8  00D4-FF2C: 35-96 5-6      3F C0  1640 EA40: 59 A9 1 1
   *18-E7  00E0-FF20: 1C-E4 4-4     *40-BF  1800-E800: 30-D0 0-0
   *19-E6  00EC-FF14: 76-C5 6-5      41 BE  1A40 E640: 69 99 1 1
   *1A-E5  00F4-FF0C: 3D-86 5-6     *42-BD  1C00-E400: 38-C8 0-0
    1B --  00FC  -- : 7E    6        43 BC  1E40 E240: 79 89 1 1
    1C E3  0114 FEF4: 45 BD 5 5     *44-BB  2000-E000: 40-C0 0-0
   *1D-E2  0120-FEE0: 24-DC 4-4     *45-BA  2400-DC00: 48-B8 0-0
    1E E1  0140 FEB4: 0A AD 2 5     *46-B9  2800-D800: 50-B0 0-0
   *1F-E0  0160-FEA0: 2C-D4 4-4     *47-B8  2C00-D400: 58-A8 0-0
    20 DF  0174 FE94: 5D A5 5 5     *48-B7  3000-D000: 60-A0 0-0
   *21-DE  01A0-FE60: 34-CC 4-4     *49-B6  3400-CC00: 68-98 0-0
   *22-DD  01E0-FE20: 3C-C4 4-4     *4A-B5  3800-C800: 70-90 0-0
   *23-DC  0220-FDE0: 44-BC 4-4     *4B-B4  3C00-C400: 78-88 0-0
   *24-DB  0260-FDA0: 4C-B4 4-4     (4C B3) 0030 FFDC: 03 EE 3 6 ;these are here
   *25-DA  02A0-FD60: 54-AC 4-4     (4D B2) 0037 FFCF: 37 CF 7 7 ;just to expand
   *26-D9  02E0-FD20: 5C-A4 4-4     (4E B1) 003C FFC7: 1E C7 6 7 ;the valuerange
   *27-D8  0320-FCE0: 64-9C 4-4     (4F B0) 0040 FFBF: 01 BF 1 7 ;to $00..$4F


It might come useful to know what 16bit pitch-values/frequencies are generated
by the 'waveform + absolute-pitch' commands in Instrument/FX table. For example
it came handy for me to cover the original demotunes, mimic their drums, etc.
(The absolute frequencies are different for players without Octave0 support.)
The formula to convert an '$xy' InsFX-table pitch-value to a SID pitch-value:

OutputFreqValue = ( $00xy << (($xy&7)+1) )    (if Octave0 is supported) :

IFXt int. SIDfr sh. IFXt int. SIDfr sh. IFXt int. SIDfr sh. IFXt int. SIDfr sh.
$80->$80->$0100 1   $A0->$82->$0410 3   $C0->$84->$1080 5   $E0->$86->$4300 7
$81->$88->$0110 1   $A1->$8A->$0450 3   $C1->$8C->$1180 5   $E1->$8E->$4700 7
$82->$90->$0120 1   $A2->$92->$0490 3   $C2->$94->$1280 5   $E2->$96->$4B00 7
$83->$98->$0130 1   $A3->$9A->$04D0 3   $C3->$9C->$1380 5   $E3->$9E->$4F00 7
$84->$A0->$0140 1   $A4->$A2->$0510 3   $C4->$A4->$1480 5   $E4->$A6->$5300 7
$85->$A8->$0150 1   $A5->$AA->$0550 3   $C5->$AC->$1580 5   $E5->$AE->$5700 7
$86->$B0->$0160 1   $A6->$B2->$0590 3   $C6->$B4->$1680 5   $E6->$B6->$5B00 7
$87->$B8->$0170 1   $A7->$BA->$05D0 3   $C7->$BC->$1780 5   $E7->$BE->$5F00 7
$88->$C0->$0180 1   $A8->$C2->$0610 3   $C8->$C4->$1880 5   $E8->$C6->$6300 7
$89->$C8->$0190 1   $A9->$CA->$0650 3   $C9->$CC->$1980 5   $E9->$CE->$6700 7
$8A->$D0->$01A0 1   $AA->$D2->$0690 3   $CA->$D4->$1A80 5   $EA->$D6->$6B00 7
$8B->$D8->$01B0 1   $AB->$DA->$06D0 3   $CB->$DC->$1B80 5   $EB->$DE->$6F00 7
$8C->$E0->$01C0 1   $AC->$E2->$0710 3   $CC->$E4->$1C80 5   $EC->$E6->$7300 7
$8D->$E8->$01D0 1   $AD->$EA->$0750 3   $CD->$EC->$1D80 5   $ED->$EE->$7700 7
$8E->$F0->$01E0 1   $AE->$F2->$0790 3   $CE->$F4->$1E80 5   $EE->$F6->$7B00 7
$8F->$F8->$01F0 1   $AF->$FA->$07D0 3   $CF->$FC->$1F80 5   $EF->$FE->$7F00 7
$90->$81->$0204 2   $B0->$83->$0830 4   $D0->$85->$2140 6   $F0->$87->$8700 8
$91->$89->$0224 2   $B1->$8B->$08B0 4   $D1->$8D->$2340 6   $F1->$8F->$8F00 8
$92->$91->$0244 2   $B2->$93->$0930 4   $D2->$95->$2540 6   $F2->$97->$9700 8
$93->$99->$0264 2   $B3->$9B->$09B0 4   $D3->$9D->$2740 6   $F3->$9F->$9F00 8
$94->$A1->$0284 2   $B4->$A3->$0A30 4   $D4->$A5->$2940 6   $F4->$A7->$A700 8
$95->$A9->$02A4 2   $B5->$AB->$0AB0 4   $D5->$AD->$2B40 6   $F5->$AF->$AF00 8
$96->$B1->$02C4 2   $B6->$B3->$0B30 4   $D6->$B5->$2D40 6   $F6->$B7->$B700 8
$97->$B9->$02E4 2   $B7->$BB->$0BB0 4   $D7->$BD->$2F40 6   $F7->$BF->$BF00 8
$98->$C1->$0304 2   $B8->$C3->$0C30 4   $D8->$C5->$3140 6   $F8->$C7->$C700 8
$99->$C9->$0324 2   $B9->$CB->$0CB0 4   $D9->$CD->$3340 6   $F9->$CF->$CF00 8
$9A->$D1->$0344 2   $BA->$D3->$0D30 4   $DA->$D5->$3540 6   $FA->$D7->$D700 8
$9B->$D9->$0364 2   $BB->$DB->$0DB0 4   $DB->$DD->$3740 6   $FB->$DF->$DF00 8
$9C->$E1->$0384 2   $BC->$E3->$0E30 4   $DC->$E5->$3940 6   $FC->$E7->$E700 8
$9D->$E9->$03A4 2   $BD->$EB->$0EB0 4   $DD->$ED->$3B40 6   $FD->$EF->$EF00 8
$9E->$F1->$03C4 2   $BE->$F3->$0F30 4   $DE->$F5->$3D40 6   $FE->$F7->$F700 8
$9F->$F9->$03E4 2   $BF->$FB->$0FB0 4   $DF->$FD->$3F40 6   $FF->$FF->$FF00 8

OutputFreqValue = ( $01xy << (($xy&7)+1) )    (no Octave0-support) :

IFXt int. SIDfr sh. IFXt int. SIDfr sh. IFXt int. SIDfr sh. IFXt int. SIDfr sh.
$80->$80->$0300 1   $A0->$82->$0C10 3   $C0->$84->$3080 5   $E0->$86->$C300 7
$81->$88->$0310 1   $A1->$8A->$0C50 3   $C1->$8C->$3180 5   $E1->$8E->$C700 7
$82->$90->$0320 1   $A2->$92->$0C90 3   $C2->$94->$3280 5   $E2->$96->$CB00 7
$83->$98->$0330 1   $A3->$9A->$0CD0 3   $C3->$9C->$3380 5   $E3->$9E->$CF00 7
$84->$A0->$0340 1   $A4->$A2->$0D10 3   $C4->$A4->$3480 5   $E4->$A6->$D300 7
$85->$A8->$0350 1   $A5->$AA->$0D50 3   $C5->$AC->$3580 5   $E5->$AE->$D700 7
$86->$B0->$0360 1   $A6->$B2->$0D90 3   $C6->$B4->$3680 5   $E6->$B6->$DB00 7
$87->$B8->$0370 1   $A7->$BA->$0DD0 3   $C7->$BC->$3780 5   $E7->$BE->$DF00 7
$88->$C0->$0380 1   $A8->$C2->$0E10 3   $C8->$C4->$3880 5   $E8->$C6->$E300 7
$89->$C8->$0390 1   $A9->$CA->$0E50 3   $C9->$CC->$3980 5   $E9->$CE->$E700 7
$8A->$D0->$03A0 1   $AA->$D2->$0E90 3   $CA->$D4->$3A80 5   $EA->$D6->$EB00 7
$8B->$D8->$03B0 1   $AB->$DA->$0ED0 3   $CB->$DC->$3B80 5   $EB->$DE->$EF00 7
$8C->$E0->$03C0 1   $AC->$E2->$0F10 3   $CC->$E4->$3C80 5   $EC->$E6->$F300 7
$8D->$E8->$03D0 1   $AD->$EA->$0F50 3   $CD->$EC->$3D80 5   $ED->$EE->$F700 7
$8E->$F0->$03E0 1   $AE->$F2->$0F90 3   $CE->$F4->$3E80 5   $EE->$F6->$FB00 7
$8F->$F8->$03F0 1   $AF->$FA->$0FD0 3   $CF->$FC->$3F80 5   $EF->$FE->$FF00 7
$90->$81->$0604 2   $B0->$83->$1830 4   $D0->$85->$6140 6  ($F0->$87->$8700 8)
$91->$89->$0624 2   $B1->$8B->$18B0 4   $D1->$8D->$6340 6  ($F1->$8F->$8F00 8)
$92->$91->$0644 2   $B2->$93->$1930 4   $D2->$95->$6540 6  ($F2->$97->$9700 8)
$93->$99->$0664 2   $B3->$9B->$19B0 4   $D3->$9D->$6740 6  ($F3->$9F->$9F00 8)
$94->$A1->$0684 2   $B4->$A3->$1A30 4   $D4->$A5->$6940 6  ($F4->$A7->$A700 8)
$95->$A9->$06A4 2   $B5->$AB->$1AB0 4   $D5->$AD->$6B40 6  ($F5->$AF->$AF00 8)
$96->$B1->$06C4 2   $B6->$B3->$1B30 4   $D6->$B5->$6D40 6  ($F6->$B7->$B700 8)
$97->$B9->$06E4 2   $B7->$BB->$1BB0 4   $D7->$BD->$6F40 6  ($F7->$BF->$BF00 8)
$98->$C1->$0704 2   $B8->$C3->$1C30 4   $D8->$C5->$7140 6  ($F8->$C7->$C700 8)
$99->$C9->$0724 2   $B9->$CB->$1CB0 4   $D9->$CD->$7340 6  ($F9->$CF->$CF00 8)
$9A->$D1->$0744 2   $BA->$D3->$1D30 4   $DA->$D5->$7540 6  ($FA->$D7->$D700 8)
$9B->$D9->$0764 2   $BB->$DB->$1DB0 4   $DB->$DD->$7740 6  ($FB->$DF->$DF00 8)
$9C->$E1->$0784 2   $BC->$E3->$1E30 4   $DC->$E5->$7940 6  ($FC->$E7->$E700 8)
$9D->$E9->$07A4 2   $BD->$EB->$1EB0 4   $DD->$ED->$7B40 6  ($FD->$EF->$EF00 8)
$9E->$F1->$07C4 2   $BE->$F3->$1F30 4   $DE->$F5->$7D40 6  ($FE->$F7->$F700 8)
$9F->$F9->$07E4 2   $BF->$FB->$1FB0 4   $DF->$FD->$7F40 6  ($FF->$FF->$FF00 8)

legend: 'IFXt' is the pitch-value shown in InsFX table, 'int.' is its internal
        version in memory, and 'SIDfr' is the value written into SID registers.
        'sh' is the amount of shifts performed to get the desired octave and
        at the same time the amount of additional rastertime.


           Some in-depth info about Hard-Restart and ADSR-delaybug
           -------------------------------------------------------
 
 It's not essential to have hard-restart in your arsenal, great SID-musicians in
the past were aware about the SID-delaybug and selected ADSR values carefully
to avoid it, or cause it if that was what they needed...
 Typical players perform Hard-Restart automatically. This is not the case with
FlexSID, but at least the specialized C6 command makes it possible in less space
than it could be done with ordinary InsFX-table commands. It's usage has been
told above, but how Hard-Restart works in general is a mystery to many people.
 So here I take the chance to share what I know about it, with the experience I
had by coding players and SID-emulation engines several times (FlexSID contains
my cSID engine, you can find the source-code in file 'SIDemulation.c'.)

 To understand this, one needs to know some internal workings of the SID's ADSR:
The ADSR delay-bug which makes SID soundstarts unreliable sometimes is caused by
a lacking/simplified implementation of the so-called 'rate-counters'. These are
affected by a lookup-table and the values written into SID ADSR registers, and
they determine the Attack/Decay/Release speeds/rates of the ADSR-envelope curve.
How? Rate-counter counts at 1MHz and when it reaches the looked-up value it is
reset to 0. This  is done periodically, and the envelope-counter (essentially
the ADSR curve) 8bit register can increase/decrease at each period to eventually
reach the target value (which is 255 for attack, 0 for release, and the Sustain
value for Decay). Decay and Release sometimes skip these steps to ensure non-
linear fadeout which is more natural to the ears, but it's not important here.

 There are 2 main problems: First there is only 1 rate-counter (per channel) in
the SID, so it's shared between these 3 ADSR phases. This wouldn't be much of a
problem, but the rate-counter compare-value is only tested for equality. That
can cause the bug, because the compare-value depends on the phase/state of the
ADSR and the rate-counter is not reset when the ADSR advances from one phase to
another, only when it is equal to the compare-value. The reason behind this must
be the fact that rate-counters are not actual binary counters but simpler LFSRs,
in other words, pseudo-random generators. They go through all possible values
just like counters, but not in a linear fashion. And that makes only equality
comparison possible in a simple circuitry (pobably by XOR-ing). Linear counting
and magnitude comparison is out of the question, probably due to chip-area
constraints at the time of SID's development. Let's see through an example in
slow-motion why/how this can be a problem and cause delay-bug:

 Let's say we have an Attack set to 4 in SID by the C64, and we turn on the
Gate-bit in Waveform-control register. The ADSR then goes to Attack phase and
the rate-counter, at whatever value it is currently, is now compared to a new
value periodically, which corresponds to its 150th step, whatever it is for the
LFSR. Let's pretend from now on the rate-counter is linear. So it counts, and
when it reaches 149 (assigned to Attack value 4 in the table), it resets back
to 0 which allows one step up on the Attack curve. Normally, at 1MHz clock, the
rate-counter period is 150 microseconds, and if everything goes fine, the Attack
gradually steps up to the envelope top-value 255 in 150us*255 = 38ms to give
place for the next 'Decay' phase. But what if rate-counter was not between the
0..149 values before the very first Attack step? If it was set bigger by a
bigger Release previously, it doesn't get reset until it reaches the maximum
value 32767 (being a 15bit counter) where it wraps around back to 0. But
32768*1us=32.8ms has elapsed meanwhile, without any increase in the envelope
value. Our Attack phase was delayed by this amount of time, we're facing an
audible delay bug in this case.
 This problem won't happen in the Attack-to-Decay transition if Attack-rate is
bigger than Decay-rate, because the transition between these 2 phases is
strictly determined by the rate-counter, and is synchronized by it.
However, there is a second place too in the ADSR curve where this delay-bug can
happen, the transition from Sustain-phase to Release-phase, caused by a gate-bit
turn-on at any time, no matter where the rate-counter is in counting. It's not
as audible usually as the Attack-bug described first. This happens when the
Decay rate was set bigger than the Release, so the rate-counter could possibly
have passed through the new Release-compare-value to wrap around again.

 Now we know the problem, and we have a solution for it called 'Hard-Restart'.
To ensure the rate-counter being below the rate-compare-value of the new note's
Attack, we reset the previous note's rate-counter to 0. As there's no direct way
to do it, first we need to know whether we're in Decay or Release phase, and set
its rate to 0. Usually it's done by turning off the gate-bit, so the phase is
known to become 'Release', then setting only Release to 0. But if it's not sure
that the previous note was turned off, setting Decay register-value (rateperiod)
to 0 at the same time can be beneficial. Because we never know where the rate-
counter is in the counting at any moment, we can only be sure that it reaches
zero after resetting ADSR to 0, if we wait at least the above mentioned 32.8ms.
 So if the wrap-around delay-bug happened, we give enough time for the
rate-counter to 'settle down'. As most music routines work at 50Hz PAL rate,
this takes 2 screen-refresh/vsync frames. After this we have a fresh start.

 But prefroming HardRestart is only half of the story, it's important too how
we start a new note. Turning on Gate-bit of course starts the note. But we have
to set new ADSR values for the new note. Before turning on the Gate-bit, we're
in Release phase if gate was turned off and the rate-counter rapidly counts
between 0 and 9 (the internal compare-value for a Release value 0). If we now
change SR register (to the instrument SR-data) before turning on gate-bit,
we lose control over the rate-counter again, because it leaves the 0..9 region
if the new Release is bigger than 0. So it's clearly seen it does matter in
what order and timing we set the new AD/SR and gate-bit to start the new note.
 If we were really in Release-phase during the hard-restart, Attack/Decay
register can be set without a problem before turning gate-bit on. If we set it
afterwards, and Attack/Decay-register was not reset during the Hard-Restart,
we can cause a delay-bug if the previous note's Attack was bigger than the new,
because the old Attack is being performed with larger rate-period, only then
comes the new Attack with smaller period, possibly missing a big compare-value.
 Sustain/Release register can safely be set right after turning gate-bit on,
because its value is not used in Attack-phase as rate-counter data-source. In
short, the safest ADSR vs Gate setting order would be: AD -> Gate -> SR.

 As with other quirks of C64, we can turn this delay-bug too to our advantage,
and cause it intentionally. They sometimes call this method the 'sexy-start'.
 The waveform-sequencer table's 1st waveform, which takes a 20ms PAL-frame, will
be inaudible during the 32.8ms delay-period, but the 2nd waveform's end can
be heard in the last 2*20ms-32.8ms = 7.2ms part of the 2nd frame. it's shortened
significantly compared to 20ms, and sound-start is nicer, more 'percussive',
nearly all 1x-framespeed SID music today exploits this effect.
 This is not necessarily preceded by a hard-restart, we can cause fairly stable
delay bug by setting the new note's Attack much smaller than the previous
note's Release. Statistically the delay-bug will happen in nearly 100% of the
cases. Though sometimes glitches in the new notes can happen if the rate-counter
was in the region of the new small Attack's period. If we want even more stable
sexy-soundstart, a hard-restart before it can ensure a more predictable output,
albeit the 4 frames of hardrestart+delaybug activity aggregates the sounds more.
 Life is still not easy, because to cause a delay-bug for sure after a hard-
restart, Release should be set bigger than Attack, enough CPU-cycles must be
waited before turning on gate, so the rate-counter counted up to a region above
the new Attack's counting region. In SID-Wizard I mention it in the source code,
and Lft's BlackBird player has this kind of cyclecounting in the source as well,
but these in-player timings only ensure delay-bug with Release-values above 2,
if Attack-value is smaller.

 A 1-frame shorter/smaller Hard-restart variant exists as well, which is based
on this sexy-restart idea. This method turns off gate-bit and sets Release to
the maximum F value and waits 1 whole frame to give the highest probability for
the rate-counter to count into the region around 20000 during the 20ms
time-period of this frame. This is also seen in BlackBird and in Cadaver's new
mini-player at github (look for lda #$0F sta $d406,x).
 It doesn't work for all ADSR values, but most Attack/Decay/Release rate-periods
are much smaller than the one corresponding to $F, which is 31251. Value $E has
19532, which is cca half of it, and the other values are getting exponentially
smaller. Even with A/D/R value '$D' counting between 0..11720, setting $F for
1 whole frame, rate-counter counts up to maximum 11720+20000=31720 which is
already safe from wrapping around at 32767. Now that we know our rate-counter
is bigger than 20000 and smaller than 32767 at the end of the 20ms frame,
any Attack-value below $F will result in a wraparound aka delay-bug in the next
frame. The only problem with this approach is that for Attack/Release values
above $D the delay can be small and jittering. But for $0..$C the total delay is
around 29000, as rate-period of $C is 3907, much smaller than with $D..$F.
 Other advantage of this method is that even the typical $09 inaudible 1st-frame
waveform can be omitted, because 20ms was already spent with the 'Hard-Restart',
and the first audible waveform is soon audible in the 2nd half of he next frame.
 This 1frame HardRestart is best for sounds that have short-enough decay/release
or they end/decay before the next note, because the release-value set to $F for
1 frame, while sets the rate-period, it won't ensure a total envelope-decay till
the next gate-on, and while the next note is predictable and always sounds the
same, the Attack phase starts from a nonzero envelope value, is not percussive.

 There's a 'new kind of hard-restart' mentioned at CodeBase64 (by Shrydar, and
Lft is involved here too), they call it 'Bottle', and it's a totally different
cycle-exact code approach. It is able to reset the rate-counter in the timeframe
of about 10 rasterlines (less than 1ms) instead of a 20ms frame, by utilizing
the delaybug-free safe transition from a slow attack to a fast decay. There's an
other ADSR-bug in SID too: envelope-counter too can wrap around when it is at
value $FF and an Attack is triggered. This is used to bring the envelope back
to $00 fast in this 'Bottle' approach.
Only time will tell how soon this restart-method gets implemented in players...


    Info about assembling & compiling from source-code, and development
    -------------------------------------------------------------------

 It's my first try with CC65 toolchain, and I applied some ugly hacks to connect
the C64 assembly and C sources together. On top of that at the end of the
development I was starting to lose my patience with adding imports/exports and
global variables when introducing new modifications. Also file-selection screen
functions could have been better arranged/structured in the source-code.
 Because of these I didn't want to go open-source with this project which I
created for myself in the first place, but the restriction with SDL-1.2 LGPL
requires to share the source-code for statically linked apps. Therefore I
cleaned the code up from garbage and beautified it, hopefully it's readable.
 You can check the pre-requisites to assemble/compile in the Makefile. I use
Debian Linux for development so the Makefile is not tested within other OS-es
at all. For the PC-versions of the app you need the standard gcc and mingw-gcc
toolchains and libsdl1.2-dev packages. There are no other big dependencies. The
1.2 SDL-version seems to have issues with full-screen mode on some new Linux
systems/machines, so you have to try if it doesn't crash on your system if you
want full-screen (Alt+F11 toggles the mode). A sort of solution to this at the
moment is the adjustable zoom-factor in the menu, but later the project might be
ported to SDL2, which proved to be stable in this regard, based on some fast
fullscreen-toggling tests.
 The C64 version needs to be compiled in any case with the ca65 and cc65 tools
because it's included as a whole (converted to binary) in the PC-version.
 Additional tools might be required to create the complete package: exomizer
to compress the final C64 executables, c1541 to pack them into a nice D64,
dpkg-deb to make the .deb packages for Linux, and zip to create Windows pack.

 If you want to customize the player-types/features for your taste or a
specific tune, feel free to do so. Some people did the same with SID-Wizad,
they created their custom players by modifying the defines affecting the player.
 Developing this software further or modifying the editor can be more tricky
than you might think. The C code parts of editor are fairly straightforward
modular functions, but when assembly-code crosses C code and they share defines,
variables it gets harder to follow their connections. For example the assembly
code 'playadapter.asm' is for writing the player-code on the fly during editing.
This way the 'player.asm' file could stay intact and doesn't get cumbered by
editor-specific code like it's seen in SID-Wizard's player for example.
 The cost of this is that modifying the player-code should be done very
carefully, and should be done in sync with playadapter.asm where necessary.
In case you want to contribute to the further evolution of FlexSID, it's a
good idea to get in contact with me through CSDb PM about more details...


                                  History
                                  -------

 This project was started by a request from Viti several years ago. He wanted
small SID tunes for his 4k intro entries. At the time I checked possibilities
and found no way to create SID music in less than 1 kbyte, other than using an
assembler. To many it's still something that they wouldn't ever try because they
can't compose music without actually hearing the notes typed. I admire who
can make good music this way, but I guess it's not an easy task for them either.
 My other tool 1raster-tracker was not suitable for the task because while the
player is only about 256 bytes the musicdata itself is not compressed, whats
more, notes are stored as 16bit frequency values, so the music is cca 2.5kbytes.
Ninjatracker was not a ready option either for this small size but it gave a
lot of ideas just as the recent mini-player by Cadaver residing at github. The
instrument and effect treated as similar entities was a nice idea for a start.
 The other inspiration was Geir Tjelta's Macro-player. Though the overall
resulting music was not that small, the macro/commandtable based 'threaded code'
approach opened up my eyes for a similar solution, and I pushed this to the
extreme in FlexSID. This approach can give great flexibility to a player and
music because all aspects of music are commands that one can decide to use or
not. It is more like a virtual RISC CPU and its code, than the usual music-
editing where waveform/pulse/etc tables are granted. This flexibility is one
of the main driving forces behind the development of FlexSID.

 Then for several years I abandoned the project and it rested in a sketch-only
phase until September 2021, when we visited a Function Party with Viti. He
mentioned the need for small music again, and I took up FlexSID from where it
was left. The idea changed somewhat, because the limitations were strict, music
made with it should fit into 500..1000 bytes and rastertime should fit into
16..20 rasterlines to give space for full-border effects in 4k-footprint intros.
 The original idea drifted towards a tiny player instead of a highly universal
one, but I still managed to keep many parts of the original idea like funktempo
and assigning slide/sweep to anything. Early previews of player proved this.
Glissando (step note up/down relative to the previous note in InsFX-table)
and keyboard-tracking, and other nice extras were also planned but they belong
to another league, maybe they get realized later in a normal-sized player...

 OK, the player can be very small, but how to keep the music-data small as well?
At least the player, despite the tiny size, should contain a pattern-packing
scheme. I took some more inspiration from the tunes created for Tiny SID compos
in 2005..2006. I disassembled the player codes and checked Ice00-s explanations
in SIDin magazines. People had many clever ideas and approaches for small
player code and music data, but these were coded specifically for the respective
tunes. So most players were not universal, so to speak, that was the only
way to squeeze music into 256..512 bytes.
 Stored frequency-tables are out of the question, and the ICC-compo which I
needed to contribute music to (in Unlimited Textures) prohibited pre-calculated
frequency-tables like the ones 4MAT makes in his code. Fortunately after some
tests it seemed that 1 octave, even the lowest one, is precise enough for
listenable music, and the whole 1..8 octave range can be covered by shifts, as
it is usually done in these small players.

 What eats most memory in music in general? What parts of it can be reduced?
To answer this I put hard work into making statistics of existing music.
I constrained myself to SID music, and SWMconvert supplied with SID-Wizard
came very handy after some modifications to it. I counted how much of these
happenings are present in 37 SID-Wizard tunes made by many people:
-Patterndata takes the majority of bytes, e.g. 2.5 kbytes in a 2.7kbyte tune
 -Note-entries are the most frequent data, around half of music is notes
   -Diatonic scale-notes, especially tonic and 4th/5th (and rest) are the most
    frequent, semitones alltogether take very small percentage (about 4%)
 -Duration-changes are the second at cca 20% occurrence, the most frequent
  durations are 1..4 (counted in pattern-rows), the rest appear quite rarely
 -Octave-changes take about 12% which is good to know for note-compression,
  and there are not much octave-jumps of more than plus/minus 1 octave
 -Instrument changes or pattern-effects take cca 12% of data as well, the most
  frequent effect is legato (10% of notes, 5% of musicdata), then comes note
  volume (accent/dynamics) and portamento/vibrato, other effects are rare

From this list it's easily seen that notes and durations should be compressed
whenever it's possible. In FlexSID I harnessed the fact that semitones appear
rarely, compared to natural unmodified scale-notes. The 7 scale-notes and rest
can be stored in only 3 bits, and that makes it possible to squeeze more data
into 1 byte. First I tried huffman-coding, a similar approach that is achieved
by Lft in his BlackBird player, but with this tiny player which already needs
some rastertime to calculate frequencies, I abandoned that idea. Arithmetic
coding was another approach I tried but it took some processing power too.
 Instead, I kept 8bit byte-boundaries as data-storage units, and started a long
trial-and-error reseach, which note+duration or note+effect or note+note
combinations could fit in 1 byte and are worth the effort in player to decode.
 Though not as sophisticated as huffman-coding, it seems that small durations
(1..4 rows) are the best candidates to store in a byte with a note, as ~30% of
notes could encounter this scenario. Octave-change is implicit in players
which work by continuous frequency tables, but here in FlexSID it's a 2bit
command beside a 4bit note (scalenote or chromatic). There were still some
value ranges left, so the 3rd compression event is putting a note and an InsFX
together into 1 byte where it's feasible (were no duration/octave changes)
 There is another compression possibility with this, which looks good at first
glance, namely putting 2 scale-notes/rests (2*3bits) into 1 byte. However, the
spared 20..30 bytes per tune don't compensate for the extra code needed in
the player to handle these double-notes, so I didn't bother implementing this.

 In the end I'm more-or-less pleased with the results, because this universal,
and not tune-specific player together with musicdata takes about 1.5 times the
size of the highly tune-optimized music. For example the 512byte tune called
'Empty' by 4MAT is remade in 678 bytes in FlexSID, the result is similar for
'Plaster' by GRG.

 Coding the player in this small size was the hardest coding I've ever done.
When you work for a day or a week just on 1..20 lines of code to make it as
small as possible, can be a very tiring task. I hope it was worth the effort.

                                        Hermit (Mihaly Horvath),  July 2022
