SFZ

This forum section was originally created while we were discussing a new, additional engine and sampler format designed from scratch. In the meantime this resulted in our new SFZ2 engine, which is already implemented to a large extent. However this is still the right place for ideas, feature requests, drafts and plans for new engine / format concepts and ideas. We now have 3 sampler engines (Gig, SFZ2, SoundFont 2). Why not having more?
grishata
Developer
Posts: 138
Joined: Thu Jan 24, 2008 7:21 pm
Location: Bulgaria
Contact:

SFZ

Post by grishata » Thu Sep 04, 2008 12:01 pm

Is there a reason not to use the sfz format? Are there any shortcomings in the design? It is free, expandable and there are converters that support it like Extreme Sample Converter.
I'm not much familiar with the instrument formats and I can't say for sure that sfz format can be expanded to provide all we need, but it looks to me that it is better to use an existing free format and eventually expand it to our needs, instead of starting from scratch. Thus, with one engine we can load both sfz and our format, which in the perfect situation would be a sfz format just expanded with more opcodes.
Opinions, remarks?

User avatar
dahnielson
Moderator
Posts: 632
Joined: Wed Jan 23, 2008 11:25 pm
Location: Linköping / Tranås, Sweden
Contact:

Re: A Modest Proposal...

Post by dahnielson » Thu Sep 04, 2008 12:39 pm

Not at all. It would actually be preferable to replace my "LS Group" hack suggestion (of tricking the GIG engine into reading a text-based format) with a proper implementation of an SFZ engine.

While the SFZ 1.x spec can be found at Cakewalk DevXchange, Cakewalk's Dimension Pro, Drop Zone and Garritan/Plogue's ARIA player use the later SFZ 2.x spec version of the format described in the book Cakewalk Synthesizers (I will get the book once I get my frickin' money from CSN :x ). Note that ARIA include additional custom opcodes (see e.g. this thread at NS) and store samples in a proprietary format.

SFZ is anyway a very capable format that include both groups and regions and plenty of input controls. It will require a completely new engine written from scratch, my "LS Group" hack was just a short time intermediate solution. But writing
  • a libsfz (in the style of libgig) library to read SFZ files;
  • an SFZ engine to play back SFZ instruments;
will probably be a better long time plan.

The first problem to tackle with SFZ (at least the 1.x version) is ambiguity during parsing: whitespace is allowed in paths to samples and the path string doesn't have to be enclosed in quotation marks if it does (and opcodes can be listed on the same line so there will not necessary be newline to terminate the path string)! Other than that it's dead easy to parse.
Anders Dahnielson

Ardour2, Qtractor, Linuxsampler, M-AUDIO Delta 1010, Axiom 61, Korg D12, AKAI S2000, E-MU Proteus 2k, Roland R-5, Roland HP 1300e, Zoom RFX-1000, 4GB RAM x86_64 Intel Pentium Dual 1.80GHz Gentoo Linux

grishata
Developer
Posts: 138
Joined: Thu Jan 24, 2008 7:21 pm
Location: Bulgaria
Contact:

Re: A Modest Proposal...

Post by grishata » Thu Sep 04, 2008 3:20 pm

I wasn't able to find sfz v2 spec. Is it safe to assume that the differences are only in new opcodes? I only found this.

User avatar
dahnielson
Moderator
Posts: 632
Joined: Wed Jan 23, 2008 11:25 pm
Location: Linköping / Tranås, Sweden
Contact:

Re: A Modest Proposal...

Post by dahnielson » Thu Sep 04, 2008 3:49 pm

grishata wrote:I wasn't able to find sfz v2 spec. Is it safe to assume that the differences are only in new opcodes? I only found this.
No it's only available as a chapter and appendix in the aforementioned book. I guess it's safe to assume it's only new opcodes (but I've asked the same question over at NS).
Anders Dahnielson

Ardour2, Qtractor, Linuxsampler, M-AUDIO Delta 1010, Axiom 61, Korg D12, AKAI S2000, E-MU Proteus 2k, Roland R-5, Roland HP 1300e, Zoom RFX-1000, 4GB RAM x86_64 Intel Pentium Dual 1.80GHz Gentoo Linux

User avatar
dahnielson
Moderator
Posts: 632
Joined: Wed Jan 23, 2008 11:25 pm
Location: Linköping / Tranås, Sweden
Contact:

Re: A Modest Proposal...

Post by dahnielson » Thu Sep 04, 2008 4:48 pm

As an example, this is what a SFZ file might look like:

Code: Select all

<region> lokey=024 hikey=024 pitch_keycenter=024 ampeg_release=0.25 sample=BaldwinHard C 1.wav
<region> lokey=025 hikey=028 pitch_keycenter=028 ampeg_release=0.25 sample=BaldwinHard E 1.wav
<region> lokey=029 hikey=031 pitch_keycenter=031 ampeg_release=0.25 sample=BaldwinHard G 1.wav
<region> lokey=032 hikey=036 pitch_keycenter=036 ampeg_release=0.25 sample=BaldwinHard C 2.wav
<region> lokey=037 hikey=040 pitch_keycenter=040 ampeg_release=0.25 sample=BaldwinHard E 2.wav
<region> lokey=041 hikey=043 pitch_keycenter=043 ampeg_release=0.25 sample=BaldwinHard G 2.wav
<region> lokey=044 hikey=048 pitch_keycenter=048 ampeg_release=0.25 sample=BaldwinHard C 3.wav
<region> lokey=049 hikey=052 pitch_keycenter=052 ampeg_release=0.25 sample=BaldwinHard E 3.wav
<region> lokey=053 hikey=055 pitch_keycenter=055 ampeg_release=0.25 sample=BaldwinHard G 3.wav
<region> lokey=056 hikey=060 pitch_keycenter=060 ampeg_release=0.25 sample=BaldwinHard C 4.wav
<region> lokey=061 hikey=064 pitch_keycenter=064 ampeg_release=0.25 sample=BaldwinHard E 4.wav
<region> lokey=065 hikey=067 pitch_keycenter=067 ampeg_release=0.25 sample=BaldwinHard G 4.wav
And I think I've figured out how to parse the path to sample files. From the spec (my emphasis):
The value of this opcode is the filename of the sample file, including the extension. The filename must be stored in the same folder where the definition file is, or specified relatively to it.

If the sample file is not found, the player will ignore the whole region contents.

Long names and names with blank spaces and other special characters (excepting the = character) are allowed in the sample definition.
The path is simply what comes after "sample=" and is terminated by either ".wav" or ".ogg"!

Update: Actually, to be totally true to the spec, after a tokenization using whitespace as delimiter the path string will be everything after the '=' in the 'sample=' token, all following tokens not containing a '=' and terminated by the first occurrence of a token containing a '=' or a header token.

I also found an error in the spec: fillfo_depth is supposed to be an integer value and not a float value.
Anders Dahnielson

Ardour2, Qtractor, Linuxsampler, M-AUDIO Delta 1010, Axiom 61, Korg D12, AKAI S2000, E-MU Proteus 2k, Roland R-5, Roland HP 1300e, Zoom RFX-1000, 4GB RAM x86_64 Intel Pentium Dual 1.80GHz Gentoo Linux

User avatar
dahnielson
Moderator
Posts: 632
Joined: Wed Jan 23, 2008 11:25 pm
Location: Linköping / Tranås, Sweden
Contact:

Re: SFZ

Post by dahnielson » Fri Sep 05, 2008 10:39 am

Here's the answer to what's been changed and added between SFZ 1.0 and 2.0:
David Viens wrote:Lots were added, mostly the opcodes are evened out and standardized, for example everything that can be modulated by a cc uses a _oncc. And some non CC MIDI controllers have been given CC ids (example: pitch bend would use _oncc128).

Also EGs and LFOs are now numbered and can be sent to multiple targets (eg eg06_amplitude=100, eg06_pitch=-1200 instead of ampeg and pitcheg, etc).

There are also new sections like <control>, <curve>, <master>.
Again most of that is explained in the book.
Anders Dahnielson

Ardour2, Qtractor, Linuxsampler, M-AUDIO Delta 1010, Axiom 61, Korg D12, AKAI S2000, E-MU Proteus 2k, Roland R-5, Roland HP 1300e, Zoom RFX-1000, 4GB RAM x86_64 Intel Pentium Dual 1.80GHz Gentoo Linux

User avatar
dahnielson
Moderator
Posts: 632
Joined: Wed Jan 23, 2008 11:25 pm
Location: Linköping / Tranås, Sweden
Contact:

Re: SFZ

Post by dahnielson » Fri Sep 05, 2008 11:41 pm

It is a bit embarrassing, I know, but I just realized SFZ is this simple to parse correctly according to the spec:

Code: Select all

#!/usr/bin/env python

import sys

def parse(f):
    token_stack = []
    string_stack = []
    for line in f:
        # COMMENT
        slash_index = line.find('/')
        if slash_index != -1:
            line = line[:slash_index]
        # DEFINITION
        for token in line.split():
            if token.startswith('<') and token.endswith('>'):
                # HEAD
                if string_stack:
                    token_stack.append(" ".join(string_stack))
                    string_stack[:] = []
                string_stack.append(token)
            elif token.find('=') != -1:
                # HEAD
                if string_stack:
                    token_stack.append(" ".join(string_stack))
                    string_stack[:] = []
                string_stack.append(token)
            else:
                # TAIL
                string_stack.append(token)
        # EOL
        if string_stack:
            token_stack.append(" ".join(string_stack))
            string_stack[:] = []
    # Print tokens
    return token_stack

if __name__ == '__main__':
    sfzf = open(sys.argv[1], 'r')
    print parse(sfzf)
    sfzf.close()
The code above will parse a SFZ file and print a list of tokens. It's only Python but I will implement it as a stream reader in C++ tomorrow. No need for BNF.
Anders Dahnielson

Ardour2, Qtractor, Linuxsampler, M-AUDIO Delta 1010, Axiom 61, Korg D12, AKAI S2000, E-MU Proteus 2k, Roland R-5, Roland HP 1300e, Zoom RFX-1000, 4GB RAM x86_64 Intel Pentium Dual 1.80GHz Gentoo Linux

User avatar
dahnielson
Moderator
Posts: 632
Joined: Wed Jan 23, 2008 11:25 pm
Location: Linköping / Tranås, Sweden
Contact:

Re: SFZ

Post by dahnielson » Sat Sep 06, 2008 1:35 am

OK, it's past midnight so it's technically "tomorrow". ;)

Code: Select all

#include <fstream>
#include <iostream>
#include <sstream>
#include <vector>
#include <string>

int main(int argc, char **argv)
{
	std::ifstream file(argv[1]);

	std::vector<std::string> tokens;
	std::string token_string;

	std::string token;
	std::string line;

	while (std::getline(file, line))
	{
		// COMMENT
		std::string::size_type slash_index = line.find('/');
		if (slash_index != std::string::npos)
			line.resize(slash_index);

		// DEFINITION
		std::stringstream linestream(line);
		while (linestream >> token)
		{
			if (token[0] == '<' and token[token.size()-1] == '>')
			{
				// HEAD
				if (!token_string.empty())
				{
					tokens.push_back(token_string);
					token_string.erase();
				}
				token_string.append(token);
			}
			else if (token.find('=') != std::string::npos)
			{
				// HEAD
				if (!token_string.empty())
				{
					tokens.push_back(token_string);
					token_string.erase();
				}
				token_string.append(token);
			}
			else
			{
				// TAIL
				token_string.append(" ");
				token_string.append(token);
			}
		}

		// EOL
		if (!token_string.empty())
		{
			tokens.push_back(token_string);
			token_string.erase();
		}
	}

	for (std::vector<std::string>::iterator i = tokens.begin(); i != tokens.end(); ++i)
		std::cout << (*i) << std::endl;

	file.close();
	return 0;
}
Anders Dahnielson

Ardour2, Qtractor, Linuxsampler, M-AUDIO Delta 1010, Axiom 61, Korg D12, AKAI S2000, E-MU Proteus 2k, Roland R-5, Roland HP 1300e, Zoom RFX-1000, 4GB RAM x86_64 Intel Pentium Dual 1.80GHz Gentoo Linux

User avatar
dahnielson
Moderator
Posts: 632
Joined: Wed Jan 23, 2008 11:25 pm
Location: Linköping / Tranås, Sweden
Contact:

Re: SFZ

Post by dahnielson » Sat Sep 06, 2008 2:11 pm

I'm working on to flesh out a libsfz library to read and handle SFZ 1.0 files until I can read the 2.0 spec.

BTW, I just found out that SFZ 2.0 apparently no longer define the sample file format, it's now implementation dependent. Which is a good thing when converting EXS24 libraries that use AIFF (no need to convert them into WAV just to conform with the official spec).
Anders Dahnielson

Ardour2, Qtractor, Linuxsampler, M-AUDIO Delta 1010, Axiom 61, Korg D12, AKAI S2000, E-MU Proteus 2k, Roland R-5, Roland HP 1300e, Zoom RFX-1000, 4GB RAM x86_64 Intel Pentium Dual 1.80GHz Gentoo Linux

grishata
Developer
Posts: 138
Joined: Thu Jan 24, 2008 7:21 pm
Location: Bulgaria
Contact:

Re: SFZ

Post by grishata » Sun Sep 07, 2008 2:52 pm

dahnielson wrote:I'm working on to flesh out a libsfz library to read and handle SFZ 1.0 files until I can read the 2.0 spec.
Anders, thanks for your effort!
BTW, is SFZ 2.0 a free standard, too?

Post Reply