SFZ
SFZ
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?
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?
- dahnielson
- Moderator
- Posts: 632
- Joined: Wed Jan 23, 2008 11:25 pm
- Location: Linköping / Tranås, Sweden
- Contact:
Re: A Modest Proposal...
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
). 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
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.
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
![Mad :x](./images/smilies/icon_mad.gif)
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;
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
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
Re: A Modest Proposal...
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.
- dahnielson
- Moderator
- Posts: 632
- Joined: Wed Jan 23, 2008 11:25 pm
- Location: Linköping / Tranås, Sweden
- Contact:
Re: A Modest Proposal...
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).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.
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
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
- dahnielson
- Moderator
- Posts: 632
- Joined: Wed Jan 23, 2008 11:25 pm
- Location: Linköping / Tranås, Sweden
- Contact:
Re: A Modest Proposal...
As an example, this is what a SFZ file might look like:
And I think I've figured out how to parse the path to sample files. From the spec (my emphasis):
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.
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
The path is simply what comes after "sample=" and is terminated by either ".wav" or ".ogg"!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.
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
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
- dahnielson
- Moderator
- Posts: 632
- Joined: Wed Jan 23, 2008 11:25 pm
- Location: Linköping / Tranås, Sweden
- Contact:
Re: SFZ
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
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
- dahnielson
- Moderator
- Posts: 632
- Joined: Wed Jan 23, 2008 11:25 pm
- Location: Linköping / Tranås, Sweden
- Contact:
Re: SFZ
It is a bit embarrassing, I know, but I just realized SFZ is this simple to parse correctly according to the spec:
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.
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()
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
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
- dahnielson
- Moderator
- Posts: 632
- Joined: Wed Jan 23, 2008 11:25 pm
- Location: Linköping / Tranås, Sweden
- Contact:
Re: SFZ
OK, it's past midnight so it's technically "tomorrow". ![Wink ;)](./images/smilies/icon_e_wink.gif)
![Wink ;)](./images/smilies/icon_e_wink.gif)
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
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
- dahnielson
- Moderator
- Posts: 632
- Joined: Wed Jan 23, 2008 11:25 pm
- Location: Linköping / Tranås, Sweden
- Contact:
Re: SFZ
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).
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
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
Re: SFZ
Anders, thanks for your effort!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.
BTW, is SFZ 2.0 a free standard, too?