On Wed, Jul 23, 2008 at 16:41, Darren Landrum <darren.landrum@sbcglobal.net> wrote:
gordonjcp@gjcp.net wrote:
> It's not XML, it's a sort of flat-text-ish thing with various keywords
> for setting keys, keygroups, mutegroups and so on.
>
> Having briefly skimmed the spec over lunch, I'm not in much of a
> position to say how good it is, but it "looks right".
>
> Essentially an SFZ file is a text file describing what to do with a
> bunch of .wav or .ogg files.  It's almost worryingly clueful.

It wasn't actually created by Cakewalk, but by a small company that
Cakewalk had bought, and rather than closing up or quelching SFZ, they
decided to keep it open. That's the story best as I was able to divine
it, anyway. And now with the news that Tascam is discontinuing all
Gigastudio-related development (http://www.filmmusicmag.com/?p=1738 and
confirmed on the "Legacy" section of Tascam's web site:
http://www.tascam.com/legacy;37,7.html) it's possible that SFZ might
become a new standard for sample libraries to use. Garritan is
apparently releasing, or getting set to release, their libraries in SFZ now.

Hi Darren! :-)

Note that Garritan/Plogue are using a later version of the SFZ format with additional and sometimes custom opcodes in their ARIA player. (See e.g. http://www.northernsounds.com/forum/showthread.php?t=60929 )

Here's a simple tokenizer for SFZ I wrote once upon a time in Python. Not sure if I got it completely right.

[BEGIN PYTHON]

import sys

def find_delimiter(string, delimiter):
    """Find all occurences of delimiter."""
    start, i, ndx = 0, 0, []
    while True:
        i = string.find(delimiter, start)
        if i != -1:
            start = i + 1
            ndx.append((i, delimiter))
        else:
            break
    return ndx

def find_all_delimiters(string, delimiters):
    """Find all delimiters."""
    ndx = []
    for delimiter in delimiters:
        ndx.extend(find_delimiter(string, delimiter))
    return ndx

def tokenize_sfz(string):
    """Tokenize SFZ string."""
    delimiters = [
        # comments
        '\n',
        '//',
        # headers
        '<region>',
        '<group>',
        # sample definition
        'sample=',
        # input controls
        'lochan=', 'hichan=',
        'lokey=', 'hikey=', 'key=',
        'lovel=', 'hivel=',
        'lobend=', 'hibend=',
        'locahnaft=', 'hichanaft=',
        'lopolyaft=', 'hiplyaft=',
        'lorand=', 'hirand=',
        'lobpm=', 'hibmp=',
        'seq_length=', 'seq_position=',
        'sw_lokey=', 'sw_hikey=', 'sw_last=', 'sw_down=', 'sw_up=', 'sw_previous=', 'sw_vel=',
        'trigger=',
        'group=',
        'off_by=', 'off_mode=',
        ] + [
        'on_locc%d=' % cc for cc in range(128)
        ] + [
        'on_locc%d=' % cc for cc in range(128)
        ] + [
        # performance parameters
        'delay=', 'delay_random=',
        ] + [
        'delay_cc%d=' % cc for cc in range(128)
        ] + [
        'offset=', 'offset_random=',
        ] + [
        'offset_cc%d=' % cc for cc in range(128)
        ] + [
        'end=',
        'count=',
        'loop_mode=', 'loop_start=', 'loop_end=',
        'sync_beats=', 'sync_offset=',
        # pitch
        'transpose=',
        'tune=',
        'pitch_keycenter=', 'pitch_keytrack=', 'pitch_veltrack=', 'pitch_random=',
        'bend_up=', 'bend_down=', 'bend_step=',
        # pitch eg
        'pitcheg_delay=',
        'pitcheg_start=',
        'pitcheg_attack=',
        'pitcheg_hold=',
        'pitcheg_decay=',
        'pitcheg_sustain=',
        'pitcheg_release=',
        'pitcheg_depth=',
        'pitcheg_vel2delay=',
        'pitcheg_vel2attack=',
        'pitcheg_vel2hold=',
        'pitcheg_vel2decay=',
        'pitcheg_vel2sustain=',
        'pitcheg_vel2realease=',
        'pitcheg_vel2depth=',
        # pitch lfo
        'pitchlfo_delay=',
        'pitchlfo_fade=',
        'pitchlfo_freq=',
        'pitchlfo_depth=',
        ] + [
        'pitchlfo_depthcc%d=' % cc for cc in range(128)
        ] + [
        'pitchlfo_depthchanaft=',
        'pitchlfo_depthpolyaft=',
        ] + [
        'pitchlfo_freqcc%d=' % cc for cc in range(128)
        ] + [
        'pitchlfo_freqchanaft=',
        'pitchlfo_freqpolyaft=',
        # filter
        'fil_type=',
        'cutoff=',
        ] + [
        'cutoff_cc%d=' % cc for cc in range(128)
        ] + [
        'cutoff_chanaft=', 'cutoff_polyaft=',
        'resonance=',
        'fil_keytrack=', 'fil_keycenter=', 'fil_veltrack=', 'fil_random=',
        # filter eg
        'fileg_delay=',
        'fileg_start=',
        'fileg_attack=',
        'fileg_hold=',
        'fileg_decay=',
        'fileg_sustain=',
        'fileg_release=',
        'fileg_depth=',
        'fileg_vel2delay=',
        'fileg_vel2attack=',
        'fileg_vel2hold=',
        'fileg_vel2decay=',
        'fileg_vel2sustain=',
        'fileg_vel2realease=',
        'fileg_vel2depth=',
        # filter lfo
        'fillfo_delay=',
        'fillfo_fade=',
        'fillfo_freq=',
        'fillfo_depth=',
        ] + [
        'fillfo_depthcc%d=' % cc for cc in range(128)
        ] + [
        'fillfo_depthchanaft=',
        'fillfo_depthpolyaft=',
        ] + [
        'fillfo_freqcc%d=' % cc for cc in range(128)
        ] + [
        'fillfo_freqchanaft=',
        'fillfo_freqpolyaft=',
        # amplifier
        'volume=',
        'pan=',
        'width=',
        'position=',
        'amp_keytrack=', 'amp_keycenter=', 'amp_veltrack=',
        ] + [
        'amp_velcurve_%d=' % c for c in range(128)
        ] + [
        'amp_random=',
        'rt_decay=',
        'output=',
        ] + [
        'gain_cc%d=' % cc for cc in range(128)
        ] + [
        'xfin_lokey=', 'xfin_hikey=',
        'xfout_lokey=', 'xfout_hikey=',
        'xf_keycurve=',
        'xfin_lovel=', 'xfin_hilev=',
        'xfout_lovel=', 'xfout_hilev=',
        'xf_velcurve=',
        ]  + [
        'xfin_locc%d=' % cc for cc in range(128)
        ] + [
        'xfin_hicc%d=' % cc for cc in range(128)
        ] + [
        'xfout_locc%d=' % cc for cc in range(128)
        ] + [
        'xfout_hicc%d=' % cc for cc in range(128)
        ] + [
        'xf_cccurve=',
        # amplifier eg
        'ampeg_delay=',
        'ampeg_start=',
        'ampeg_attack=',
        'ampeg_hold=',
        'ampeg_decay=',
        'ampeg_sustain=',
        'ampeg_release=',
        'ampeg_vel2delay=',
        'ampeg_vel2attack=',
        'ampeg_vel2hold=',
        'ampeg_vel2decay=',
        'ampeg_vel2sustain=',
        'ampeg_vel2release=',
        ] + [
        'ampeg_delaycc%d=' % cc for cc in range(128)
        ] + [
        'ampeg_startcc%d=' % cc for cc in range(128)
        ] + [
        'ampeg_attackcc%d=' % cc for cc in range(128)
        ] + [
        'ampeg_holdcc%d=' % cc for cc in range(128)
        ] + [
        'ampeg_decaycc%d=' % cc for cc in range(128)
        ] + [
        'ampeg_sustaincc%d=' % cc for cc in range(128)
        ] + [
        'ampeg_releasecc%d=' % cc for cc in range(128)
        ] + [
        # amplifier lfo
        'amplfo_delay=',
        'amplfo_fade=',
        'amplfo_freq=',
        'amplfo_depth=',
        ] + [
        'amplfo_depthcc%d=' % cc for cc in range(128)
        ] + [
        'amplfo_depthchanaft=',
        'amplfo_depthpolyaft=',
        ] + [
        'amplfo_freqcc%d=' % cc for cc in range(128)
        ] + [
        'amplfo_freqchanaft=',
        'amplfo_freqpolyaft=',
        # equalizer
        'eq1_freq=', 'eq2_freq=', 'eq3_freq=',
        ] + [
        'eq1_freqcc%d=' % cc for cc in range(128)
        ] + [
        'eq2_freqcc%d=' % cc for cc in range(128)
        ] + [
        'eq3_freqcc%d=' % cc for cc in range(128)
        ] + [
        'eq1_vel2freq', 'eq2_vel2freq', 'eq3_vel2freq',
        'eq1_bw', 'eq2_bw', 'eq3_bw',
        ] + [
        'eq1_bwcc%d=' % cc for cc in range(128)
        ] + [
        'eq2_bwcc%d=' % cc for cc in range(128)
        ] + [
        'eq3_bwcc%d=' % cc for cc in range(128)
        ] + [
        'eq1_gain=', 'eq2_gain=', 'eq3_gain=',
        ] + [
        'eq1_gaincc%d=' % cc for cc in range(128)
        ] + [
        'eq2_gaincc%d=' % cc for cc in range(128)
        ] + [
        'eq3_gaincc%d=' % cc for cc in range(128)
        ] + [
        'eq1_vel2gain=', 'eq2_vel2gain=', 'eq3_vel2gain=',
        # effects
        'effect1=', 'effect2=',
        ]
    stack = find_all_delimiters(string, delimiters)
    stack.sort()
    stack.reverse()
    comment = False
    tokens = []
    while True:
        try:
            start = stack.pop()
            end = stack.pop()
            stack.append(end)
        except IndexError:
            break
        if start[1] == '//':
            comment = True
        elif start[1] == '\n':
            comment = False
            continue
        if not comment:
            tokens.append(sfz[start[0]:end[0]].rstrip().split('=', 1))
    return tokens


sfzf = open(sys.argv[1], 'r')
sfz = sfzf.read()
sfzf.close()
print tokenize_sfz(sfz)

[END PYTHON]

--
Anders Dahnielson
<anders@dahnielson.com>