SFZ format documentation

Sampler and Sampling discussion (techniques, tips and tricks, etc.)
Post Reply New Topic
RELATED
PRODUCTS
Bjoerns Sample Mapper

Post

pough wrote: Fri Apr 18, 2025 6:12 pm Does anyone know of any good documentation regarding effects, or just an already-existing SFZ that includes it so I can see how it's done? And do any players actually support effect, apart from the ARIA players that have their own thing with the mda effects? I'm trying learn how to add a delay effect. It would be nice to find out how it's done as a part of the SFZ v2 spec. Unless no players even support it.
Hmm ... I think you're in the wrong place. The sfz format describes how to play samples—especially multisamples—as ergonomically as possible using the keyboard. This includes velocity layers, sample overlaps, dynamic controls, etc.

This sample organization alone is complex enough—and that's why there are various extensions to sfz, called sfz2 and sfz2+. The only effects are the equalizer settings: There are three equalizers that can be controlled as follows:

eq1_freq=50
eq2_freq=500
eq3_freq=5000


eq1_gain=6
eq2_gain=0
eq3_gain=-6


Of course, this can also be defined so that the 3 equalizers are automated via MIDI controller.

Proper effects—such as compressors, shapers, or other effects—would simply be added to the plugin chain after the sfz player. It's simple and transparent.
:tu:
Last edited by enroe on Sat Apr 19, 2025 1:27 pm, edited 1 time in total.
free mp3s + info: andy-enroe.de songs + weird stuff: enroe.de

Post

Yup, the ARIA effects are entirely an ARIA extension for their own use and not part of SFZ at all. Try to keep sample management, sample triggering and playback, and sound effects separate. The latter shouldn't be affected by whether the sound is coming from samples, synthesis or audio capture / recording.

Post

enroe wrote: Sat Apr 19, 2025 7:15 am Hmm ... I think you're in the wrong place. ... The only effects are the equalizer settings: There are three equalizers that can be controlled as follows:
According to the documentation, there are effects. They're just not described well enough for me to be able to (forgive me) effectively implement them. Or they're not implemented by any of the players I've tried. It's hard for me to know which is the case.

https://sfzformat.com/headers/effect/
https://sfzformat.com/opcodes/type/

Code: Select all

<effect>
type=delay
That doesn't do anything in either sforzando or sfizz, no matter where I put it. I can successfully get a delay to work in sforzando if I use com.mda.Delay as the type but not only do I want to learn how to do it the standard way, I really don't like the mda delay.

It's not urgent, but I thought I would ask. Thanks for responding!
Surely there must be consensus by now...

Post

pljones wrote: Sat Apr 19, 2025 8:46 am Try to keep sample management, sample triggering and playback, and sound effects separate.
That's what I did in the end, if only because the spec suggests something might be able to be accomplished but it seems like none of the player makers have either gotten around to implementing it or want to implement it. I should note that Plogue have implemented their own thing and also that I haven't tried every player out there.

I actually tried making a library with a delay effect in Kontakt and then using ConvertWithMoss to see if it knew how to add the effect to SFZ, but it just ignored the effect.

I might be wasting my time on this particular aspect of SFZ, but I don't think it's a waste of my time learning all the other things about it. Thanks for the response!
Surely there must be consensus by now...

Post

You have to realise there is no "specification" for SFZ. There's documentation on specific implementations and these often provide similar features for the same opcode. But no one has published a specification and said "this is what being an SFZ sample player means".

You can probably rely on

Code: Select all

<region> sample=sine.wav
the player to go looking for a file called "sine.wav", in the same directory as the file you put that in (so long as it's called something ending in ".sfz") when receives any MIDI note on and send the audio somewhere. Beyond that, it's pretty much implementation specific - with vast amounts of overlap, admittedly, particularly with the original implementation in SFZ.DLL.

Post

Is it possible to add a certain value (e.g.+20) to all incoming velocity values in SFZ?

I've already decreased amp_veltrack in my definition. But now I need a static velocity bump that the instrument feels right playing with my fingers.

Post

Etienne1973 wrote: Fri Oct 17, 2025 9:27 am Is it possible to add a certain value (e.g.+20) to all incoming velocity values in SFZ?
You can use amp_velcurve_N.
If you write amp_velcurve_1=0.16535433, and then play a real velocity of 1, it will use velocity 21 for selecting groups/regions in the multisample etc.
The 0.16535433 is simply 21/127. It's 21 because we're setting the value for velocity 1, and you'd like to add +20.
The linked spec recommends not setting amp_veltrack at the same time. It's recommended to only use either amp_velcurve or amp_veltrack, not both. You can if you want, it's just confusing.

Post

Etienne1973 wrote: Fri Oct 17, 2025 9:27 am Is it possible to add a certain value (e.g.+20) to all incoming velocity values in SFZ?

I've already decreased amp_veltrack in my definition. But now I need a static velocity
bump that the instrument feels right playing with my fingers.
Every DAW has one or more MIDI plugins that do exactly this: They add any value, for
example 20, to each velocity value. You need to place such a MIDI plugin in the input
chain.

For an sfz instrument, you can only change the dynamic curve, or even repaint it to
your liking with the command "amp_velcurve_N".
free mp3s + info: andy-enroe.de songs + weird stuff: enroe.de

Post

Yup, you need a MIDI preprocessor for this, which the currently available "big" sfz players do not have.

Post

@declassified, @enroe and @DSmolken

Thank you all for participating and helping solve my velocity response problem. :)

Using an external velocity pre-process aka extra MIDI FX plugin works like a charm. :tu:

But I don't want to believe there's no way implementing this behavior within SFZ.

I'm still trying to get opcode amp_velcurve_N working with Sforzando.

I need a convex dynamic curve triggering my 32 velocity layers selective accordingly.

Bildschirmfoto 2025-10-20 um 21.09.20.png

Code: Select all

amp_velcurve_1=.0307 amp_velcurve_2=.0604 amp_velcurve_3=.0892 amp_velcurve_4=.1172 amp_velcurve_5=.1443 amp_velcurve_6=.1706 amp_velcurve_7=.1961 amp_velcurve_8=.2209 amp_velcurve_9=.2449 amp_velcurve_10=.2682 amp_velcurve_11=.2908 amp_velcurve_12=.3127 amp_velcurve_13=.3340 amp_velcurve_14=.3546 amp_velcurve_15=.3746 amp_velcurve_16=.3940 amp_velcurve_17=.4128 amp_velcurve_18=.4311 amp_velcurve_19=.4488 amp_velcurve_20=.4659 amp_velcurve_21=.4826 amp_velcurve_22=.4988 amp_velcurve_23=.5144 amp_velcurve_24=.5296 amp_velcurve_25=.5444 amp_velcurve_26=.5587 amp_velcurve_27=.5726 amp_velcurve_28=.5860 amp_velcurve_29=.5991 amp_velcurve_30=.6118 amp_velcurve_31=.6240 amp_velcurve_32=.6360 amp_velcurve_33=.6475 amp_velcurve_34=.6587 amp_velcurve_35=.6696 amp_velcurve_36=.6802 amp_velcurve_37=.6904 amp_velcurve_38=.7003 amp_velcurve_39=.7100 amp_velcurve_40=.7193 amp_velcurve_41=.7284 amp_velcurve_42=.7371 amp_velcurve_43=.7457 amp_velcurve_44=.7539 amp_velcurve_45=.7620 amp_velcurve_46=.7697 amp_velcurve_47=.7773 amp_velcurve_48=.7846 amp_velcurve_49=.7917 amp_velcurve_50=.7986 amp_velcurve_51=.8053 amp_velcurve_52=.8118 amp_velcurve_53=.8180 amp_velcurve_54=.8241 amp_velcurve_55=.8301 amp_velcurve_56=.8358 amp_velcurve_57=.8414 amp_velcurve_58=.8468 amp_velcurve_59=.8520 amp_velcurve_60=.8571 amp_velcurve_61=.8620 amp_velcurve_62=.8668 amp_velcurve_63=.8714 amp_velcurve_64=.8759 amp_velcurve_65=.8803 amp_velcurve_66=.8845 amp_velcurve_67=.8886 amp_velcurve_68=.8926 amp_velcurve_69=.8964 amp_velcurve_70=.9002 amp_velcurve_71=.9038 amp_velcurve_72=.9073 amp_velcurve_73=.9108 amp_velcurve_74=.9141 amp_velcurve_75=.9173 amp_velcurve_76=.9204 amp_velcurve_77=.9234 amp_velcurve_78=.9264 amp_velcurve_79=.9292 amp_velcurve_80=.9320 amp_velcurve_81=.9347 amp_velcurve_82=.9373 amp_velcurve_83=.9398 amp_velcurve_84=.9422 amp_velcurve_85=.9446 amp_velcurve_86=.9469 amp_velcurve_87=.9491 amp_velcurve_88=.9513 amp_velcurve_89=.9534 amp_velcurve_90=.9554 amp_velcurve_91=.9574 amp_velcurve_92=.9593 amp_velcurve_93=.9612 amp_velcurve_94=.9630 amp_velcurve_95=.9647 amp_velcurve_96=.9664 amp_velcurve_97=.9681 amp_velcurve_98=.9697 amp_velcurve_99=.9712 amp_velcurve_100=.9727 amp_velcurve_101=.9742 amp_velcurve_102=.9756 amp_velcurve_103=.9770 amp_velcurve_104=.9783 amp_velcurve_105=.9796 amp_velcurve_106=.9808 amp_velcurve_107=.9820 amp_velcurve_108=.9832 amp_velcurve_109=.9844 amp_velcurve_110=.9855 amp_velcurve_111=.9866 amp_velcurve_112=.9876 amp_velcurve_113=.9886 amp_velcurve_114=.9896 amp_velcurve_115=.9905 amp_velcurve_116=.9915 amp_velcurve_117=.9924 amp_velcurve_118=.9932 amp_velcurve_119=.9941 amp_velcurve_120=.9949 amp_velcurve_121=.9957 amp_velcurve_122=.9964 amp_velcurve_123=.9972 amp_velcurve_124=.9979 amp_velcurve_125=.9986 amp_velcurve_126=.9993 amp_velcurve_127=1
Source: Online Curve Generator V2 Website

Doesn't work with my Sforzando SFZ Player. At least not as opcodes all placed under <global> header and under each <region> headers.

What Am I doing wrong? :help:
You do not have the required permissions to view the files attached to this post.

Post

What you want can be done, but it's a bit more complicated. What sort of instrument is this? Assuming it's maybe a drum or a piano, as those would likely have that many velocity layers and big concerns about velocity response...

There's an easy way... if you have lots of velocity layers, just map the layers to have the correct lovel/hivel range and set amp_veltrack=0. This means velocity will not be used to alter the volume of the sample, the sample will play at 100% normal volume no matter what the velocity is, but when a sample only covers a few velocities from, say, 96 to 99, well, scaling it to be a little quieter at velocity 96 than velocity 99 might not be worth it.

But if you want to apply the full 127-point curve to every velocity point, if you have 32 velocity layers, you don't need to apply all 127 velcurve_N opcodes to all layers, only for the Ns which fall within that layer. Everything that follows here assumes the samples aren't all normalized. For the bottom layer, which I guess would be velocities 0-3, you'd only want amp_velcurve_1 through amp_velcurve_3. Also amp_velcurve_3 should be 1, so that sample then plays at (its) full volume - assuming that's what you want and you aren't trying to tweak it to be quieter or louder... which can be done, it'd just involve more math than what's below. For the second layer which would be velocities 4-7, you'd want amp_velcurve_4 through amp_velcurve_7, and again keeping the same set of assumptions, amp_velcurve_7 should be 1.

So, forgetting scaling within each velocity layer to simplify things for now, to get each layer to play at its full volume at that layer's top velocity (which, once you get past the bottom few layers, is effectively almost the same thing as setting amp_veltrack=0), you would do:

Code: Select all

<global>
//Don't really need anything velocity-related here

<region>
hivel=3
amp_velcurve_3=1
sample=velocity_layer_1.wav //Assuming there are no round robins

<region>
lovel=4
hivel=7
amp_velcurve_7=1
sample=velocity_layer_2.wav

<region>
lovel=8
hivel=11
amp_velcurve_11=1
sample=velocity_layer_3.wav

<region>
lovel=12
hivel=15
amp_velcurve_15=1
sample=velocity_layer_4.wav

<region>
lovel=16
hivel=19
amp_velcurve_19=1
sample=velocity_layer_5.wav

<region>
lovel=20
hivel=23
amp_velcurve_23=1
sample=velocity_layer_6.wav

<region>
lovel=24
hivel=27
amp_velcurve_27=1
sample=velocity_layer_7.wav

<region>
lovel=28
hivel=31
amp_velcurve_31=1
sample=velocity_layer_8.wav

<region>
lovel=32
hivel=35
amp_velcurve_35=1
sample=velocity_layer_9.wav

<region>
lovel=36
hivel=39
amp_velcurve_39=1
sample=velocity_layer_10.wav

<region>
lovel=40
hivel=43
amp_velcurve_43=1
sample=velocity_layer_11.wav

<region>
lovel=44
hivel=47
amp_velcurve_47=1
sample=velocity_layer_12.wav

<region>
lovel=48
hivel=51
amp_velcurve_51=1
sample=velocity_layer_13.wav

<region>
lovel=52
hivel=55
amp_velcurve_55=1
sample=velocity_layer_14.wav

<region>
lovel=56
hivel=59
amp_velcurve_59=1
sample=velocity_layer_15.wav

<region>
lovel=60
hivel=63
amp_velcurve_63=1
sample=velocity_layer_16.wav

<region>
lovel=64
hivel=67
amp_velcurve_67=1
sample=velocity_layer_17.wav

<region>
lovel=68
hivel=71
amp_velcurve_71=1
sample=velocity_layer_18.wav

<region>
lovel=72
hivel=75
amp_velcurve_75=1
sample=velocity_layer_19.wav

<region>
lovel=76
hivel=79
amp_velcurve_79=1
sample=velocity_layer_20.wav

<region>
lovel=80
hivel=83
amp_velcurve_83=1
sample=velocity_layer_21.wav

<region>
lovel=84
hivel=87
amp_velcurve_87=1
sample=velocity_layer_22.wav

<region>
lovel=88
hivel=91
amp_velcurve_91=1
sample=velocity_layer_23.wav

<region>
lovel=92
hivel=95
amp_velcurve_95=1
sample=velocity_layer_24.wav

<region>
lovel=96
hivel=99
amp_velcurve_99=1
sample=velocity_layer_25.wav

<region>
lovel=100
hivel=103
amp_velcurve_103=1
sample=velocity_layer_26.wav

<region>
lovel=104
hivel=107
amp_velcurve_107=1
sample=velocity_layer_27.wav

<region>
lovel=108
hivel=111
amp_velcurve_111=1
sample=velocity_layer_28.wav

<region>
lovel=112
hivel=115
amp_velcurve_115=1
sample=velocity_layer_29.wav

<region>
lovel=116
hivel=119
amp_velcurve_119=1
sample=velocity_layer_30.wav

<region>
lovel=120
hivel=123
amp_velcurve_123=1
sample=velocity_layer_31.wav

<region>
lovel=124
sample=velocity_layer_32.wav
So as to save space, here's how you'd scale the bottom three layers so that the amp_velcurve for the top velocity number of the layer ends up at 1, and the ones below it remain in proportion, to really follow your curve:

Code: Select all

<region>
hivel=3
amp_velcurve_1=0.344
amp_velcurve_2=0.677
amp_velcurve_3=1
sample=velocity_layer_1.wav //Assuming there are no round robins

<region>
lovel=4
hivel=7
amp_velcurve_4=0.597
amp_velcurve_5=0.735
amp_velcurve_6=0.869
amp_velcurve_7=1
sample=velocity_layer_2.wav

<region>
lovel=8
hivel=11
amp_velcurve_8=0.759
amp_velcurve_9=0.842
amp_velcurve_10=0.922
amp_velcurve_11=1
sample=velocity_layer_3.wav
The way I got there for the third layer is... you had:

amp_velcurve_8=.2209 amp_velcurve_9=.2449 amp_velcurve_10=.2682 amp_velcurve_11=.2908

We want amp_velcurve_11=1, and diving 1 by 0.2908 gives us 3.43878954607978. So, 0.2908 multiplied by 3.43878954607978 is 1. For amp_velcurve=10, 0.2682 multiplied by 3.43878954607978 gives us 0.922283356258597. And so on.

The rest is left, as they say, as an exercise to the reader - but I think this sort of thing should work. So, yes, this curve can be set up in sfz, but you will probably need to make a spreadsheet to calculate all this stuff. The good news is you can also use a spreadsheet with a few concatenation commands to spit out all the sfz you need.

Post

@DSmolken

Thank you so much for your detailed answer, showing me all the options I have. I get it. :phones:

I've sampled a Wurlitzer Piano plugin for exercise (personal use!) in SFZ land with the goal in mind to make the SFZ instrument as close sounding and feeling to play as the original.

ATM I have organized these 32 velocity layers in 32 groups. Each group contains 88 horizontal keys samples. So total 2816 regions. Equals total samples for this whole instrument.

Do amp_Velcurve_N opcodes exclusively and only work under each <region> header? Distribute them to groups and write them under each <group> header doesn't work, right?

Post

I'm pretty sure you should be able to do the velocity and velcurve opcodes under a <group> header for each velocity layer, and then only do key and sample opcodes under <region>:

Code: Select all

<group>
hivel=3
amp_velcurve_1=0.344
amp_velcurve_2=0.677
amp_velcurve_3=1
<region>
key=36
sample=C1_vl1.wav
<region>
key=37
sample=Db1_vl1.wav
<region>
key=38
sample=D1_vl1.wav
//...and so on

<group>
lovel=4
hivel=7
amp_velcurve_4=0.597
amp_velcurve_5=0.735
amp_velcurve_6=0.869
amp_velcurve_7=1
<region>
key=36
sample=C1_vl2.wav
<region>
key=37
sample=Db1_vl2.wav
<region>
key=38
sample=D1_vl2.wav
//...and so on

<group>
lovel=8
hivel=11
amp_velcurve_8=0.759
amp_velcurve_9=0.842
amp_velcurve_10=0.922
amp_velcurve_11=1
<region>
key=36
sample=C1_vl3.wav
<region>
key=37
sample=Db1_vl3.wav
<region>
key=38
sample=D1_vl3.wav
//...and so on

Post

Here are some thoughts...
I'd make some efforts to slim down and simplify the sample base a bit. First of all, you shouldn't need 88 keys, since a Wurlitzer (at least a 200A) only has 64 keys.
Then, listen carefully to see if each of the 32 layers really have different information from one another. Most sampled piano-type instruments really only have 3-8 layers (though they might have round robins), so no need to get crazy with layers if the original source doesn't. If it's synthesized Wurlie (I think Pianoteq has one) there might be more nuances to capture, but personally I would never bother with more than 8 layers.
The reason I suggest to simplify, is that with fewer layers you can just stick them manually in aonegroup per layer, and then just simply tweak the hivel and lovel by hand for those groups until it feels right. No need to fuss around with velcurves.

Post

How can the dynamics be generated?
The sfz format offers a total of 4 different options:

--------------------------------------------------------------------------------------

1. Different velocity layers

Different volume levels are assigned different samples. This is a
complex solution, but in many cases it also sounds very
realistically dynamic.

But it depends heavily on the instrument: If the sound spectrum
changes significantly with velocity, then one should opt for this
solution.

2. "amp_veltrack = -100 ... 0 ... +100"

This opcode allows you to directly control the volume via velocity.
This is the simplest way to add dynamics to a sampled instrument.

The formula here is: amplitude (dB) = - 20 * log(b * 127/in_velocity)^2

"b" is the value of "amp_veltrack" divided by one hundred. This
results in a concave shape when b = 1 (100%). This shape is
sufficient for 99% of use cases – that is, for almost all sample
instruments.

3. "amp_velcurve_N = 0 ... 1"

This allows you to directly set the value for any velocity value N.
This means you can plot the entire curve in detail. This can be
useful for extreme or completely custom settings.

4. Treble filtering by:

fil_type=lpf_2p
cutoff=200
resonance=6
fil_veltrack=6000

Here, everything above 200 Hz is cut off. At the same time, the
frequency at 200 Hz is boosted by 6 dB. The high frequencies
are boosted towards higher velocities: At 127, the sensitivity is
shifted by 5 octaves, and thus we have a sound rich in treble
again.

--------------------------------------------------------------------------------------

Perhaps there are even more ways to "play" with the dynamics.
I've listed the four that first come to mind. :D
free mp3s + info: andy-enroe.de songs + weird stuff: enroe.de

Post Reply

Return to “Samplers, Sampling & Sample Libraries”