Replies: 12 comments 7 replies
-
|
Anybody knows how soundfont synthesis is supposed to properly handle the attenuation and what formulas are used? The soundfont documentation doesn't talk about it very much and the SF2-DLS spec also leaves a lot of holes (it doesn't even mention the SF2 specs and all it's connections, like it's modulators and generators). |
Beta Was this translation helpful? Give feedback.
-
|
I've created my own softsynth which passes all the SF2 compatibility tests and mostly matches fluidsynth in the produced audio. There are two things about attenuation you need to know:
I hope this helps. |
Beta Was this translation helpful? Give feedback.
-
|
Just compared it to Fluidsynth. Apparently fluidsynth divides the log output by ln10. Thus, I ended up with: Though some (short lasting) notes still sound too soft? Could it be because of the volume envelope malfunctioning (for short notes). Right now, release velocity is ignored and when a note is released (even when it's still attacking), it switches to release phase, calculates a linear slope from the current (attack/hold/decay/sustain) level and starts releasing the note. Is that correct behaviour? Or does it depend on if a note has a sustain or not? |
Beta Was this translation helpful? Give feedback.
-
|
What happens when a MIDI note off is received while the ADSR isn't at sustain yet? Will it enter release starting from the current attack/hold/decay level for the specified interval? Does release velocity affect it? Edit: The ADSR also uses roughly the same formula, except adjusted for 1440cB of attenuation: Normal concave/convex (for modulators) is simply this now: Maxvalue is usually 128. val is ranging from 0.0-127.0 The 960 is simply made 1440 in this formula, that's the entire difference (and maxvalue-1 division and inversion optimized out to make it more optimized). |
Beta Was this translation helpful? Give feedback.
-
|
Looked at the FluidSynth volume curve. It seems to use 10^(x/-200) too. So for now I've changed it back from -500 to -200. That at least fixes most of the volumes to not be too loud. Some instruments still oddly seem very loud though? Perhaps a generator/modulator handling issue somehow? The Soundfont documentation does document those, but various things are undocumented apparently, like:
|
Beta Was this translation helpful? Give feedback.
-
|
The Soundfont 2.04 spec for the PMOD chunk gives the following: Doesn't it mean 'preset zone' instead of 'instrument zone'? It's pointing to a PMOD entry, not a IMOD entry? In my implementation, it points to a PMOD entry (within the preset zone's list). |
Beta Was this translation helpful? Give feedback.
-
|
How do instrument/preset zones and modulators and generators (and default modulators) combine/override? Soundfont 2.04 section 9.4 is kind of confusing in how it describes priorities and how they combine (it even says 'supercedes or replaces' which sound like unclear? From what I gather local > global > default for identical modulators or generators? How so, "destination summing node of all zones in the given instrument" so all zones receive any local preset settings from all other zones that apply? So say one zone of note 60-65 and one zone of note 66-70 for example get their values of their non-index generators summed for both key ranges? Or if both are played? That doesn't make much sense to do? Or only if multiple parts of the same key/velocity range play for a single note being played (notes with multiple zones matching)? Edit: Improved the modulators and generators to properly apply local > global > default(modulators only). The default modulators are actually applied at the local step, but they check if any global or local modulators exist to determine if they're to be ignored or not.
Edit: OK. Loaded up the Soundfont inside Viena. The first 3 instruments play at 150(->169 modulated), 67(->86 modulated), 172(->191)cB respectively. But the notes in the test song all seem to use the same volume settings? So somehow it's not dampening the sound enough? |
Beta Was this translation helpful? Give feedback.
-
|
Thinking about it... Modulators modulating modulators... Perhaps their 0-1 range is mapped to amount units as usual, then the result being part of the destination modulator's source operand's range normalizarion (either 128 or 0x4000 or maybe even 32767 for none/linked), being added to the source that's modulating? That does match the pictures in the 2.04 documentation, where the -->(+) is placed before the normalization step on the target modulator. So for example, you could put a modulator modulating amount 127(/128) from controller 20h+ modulating a modulator that drives for example initialAttenuation from modulator 0+ to archieve a 14-bit volume controller. And for the case of the 'linked' modulator or 'no modulator', the range is simply 0x4000 or maybe even 32767 for maximum control. |
Beta Was this translation helpful? Give feedback.
-
|
Managed to get some 'missing' instruments to work. For example, preset 9 (glockenspiel) had multiple preset and instrument zones, with two zones needing playback to be working properly. The first playing zone had an issue that makes it end abruptly (giving a pop because of playing a single sine wave and then ending due to the loop setting not being specified (neither globally nor locally). Then the other preset zone points to two instrument zones, one which contains the proper instrument to play (with proper looping on the sample being enabled). I also added some bag range checks and some more Soundfont documented ignore cases (like ignoring all generators after instrument generator, ending operator checks (sometimes omitted from the soundfont itself) etc.). Despite the volume issues still (all over the place) it sounds way better without missing notes now. |
Beta Was this translation helpful? Give feedback.
-
|
https://www.polyphone.io/fr/forum/support-bug-reports/426-attenuation-modulator-destination-calculated-wrong |
Beta Was this translation helpful? Give feedback.
-
|
Oh, I seem to have misread your comment about the initialAttenuation generator. I applied it to all resulting values, including the modulators, which it shouldn't. Also shouldn't it be multiplied by 0.4 (40%) instead of 0.04? Although that depends on how you look at the used formulas (as the x0.1 multiplication is done in my generic attenuation precalcs (a lookup table) instead of at the generator/modulator processing). |
Beta Was this translation helpful? Give feedback.
-
|
Implemented it like that now. Modulators being unchanged, generators times 0.4. The ADSR envelope is effectively calculated with a peak of 1.0. The sustain level is simply inverted an normalized (y=1440-x or y=1000-x, then y/1440 or y/1000). The attack is performed using the normal MIDI concave formula (on the 0 to 1 curve). It's just optimized to use less redundant operations that the normal modulator concave/convex (inversion of inversion etc.). Release is started from the level the attack/hold/decay/sustain calculated at the sample it starts (the current ADSR is calculated before starting the release phase handling, so if for time point x the attack would be 0.5f (on the 1.0 scale), then the release starts ramping down from 0.5f to 0.0f linearly. Edit: Also, how does the Master Volume combine into all this? |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
I've been working on implementing my own Soundfont 2.04-based synthesizer for a while now.
I've managed to get basic MIDI parsing and basic playback of the samples with effects (ADSRs, volume, pitch and normal dynamic low-pass filters) and most basic effects done.
The chorus is simply made using the official method (playing a sample once raw and once with modulation).
The reverb is very simple: a small delay line (two taps) with loopback that has volume reduction and a low-pass filter based on the frequency of the sound at the loopback of samples onto itself, using a simple circular FIFO buffer with taps.
The code can be found at:
https://bitbucket.org/superfury/unipcemu/src/default/UniPCemu/hardware/midi/
for the synth implementation (and MIDI parsing).
Then the soundfont reading is at
https://bitbucket.org/superfury/unipcemu/src/default/UniPCemu/support/sf2.c
It's headers are at:
https://bitbucket.org/superfury/unipcemu/src/default/UniPCemu/headers/hardware/midi/
https://bitbucket.org/superfury/unipcemu/src/default/UniPCemu/headers/support/sf2.h
It uses my custom SDL (1.0/2.0/3.x)-based output system (that mixes all channels using their own sample rates).
All mixing happens at a very low level (groups of just a few samples at a time). Most of the synth itself runs at a very high latency (rendering it all live as commands are received). All blocks of rendered samples only render 42 samples at 44.1kHz speed for each buffer call.
One issue I currently have with it is that the volume for various instruments sounds messed up. Some instruments are way too loud, some are way too soft, some are roughly correct.
I'm using the basic soundfont 2.04 and look at the Fluidsynth source code to try and figure out what's going wrong with my formulas.
The ADSR is mostly linear in nature (runs using simple addition of a divided number based on the amount of samples to render a phase at), although I've customized it's convex curve based on the Fluidsynth formula combined with the SF2-DLS paper (**https://basicsynth.com/uploads/SF2-DLS.pdf) and official SF2 spec (2.04).
Can anyone see what's going wrong with my synth? Are the concave/convex formulas incorrect? Is the attenuation performed incorrectly (it uses a basic power of 10 with an exponent of x/-200 for that)? I can't seem to figure out why the sounds aren't attenuated correctly fully.
One odd thing I did notice in the official specs is that it uses sqrt (square root) on the square of the modulator's input. But no other synthesizer I can find (both the SF2-DLS documentation and Fluidsynth) seems to perform that step? I've used it in the ADSR attack phase, where it seems to do no harm, but when applying it to the modulator concave/convex curves it causes all sound to be (as good as) muted, which isn't supposed to happen?
The attenuation is simply applied using a 1440-entry lookup table, which has the 10^(x/-200) outputs loaded into it, that it then multipled onto the low-pass filtered sample that's retrieved from the soundfont to obtain the resulting sample to render in later steps (panning first, then applying reverb effects).
For people trying to use the app themselves, the simplest way test run it to play midi files is:
-- Set cpuspeed to 1 (to give the MIDI synth maximum speed to run at). Otherwise, the emulated system will slow down the MIDI synth's rendering to be in sync with it, which is too slow for live rendering.
-- Set soundfont to the filename of the soundfont (including .sf2 extension) in the soundfonts subdirectory.
-- Set MIDIsynthdefaultmode to the default mode of the MIDI synth (MMA, GM, GS or XG). This is applied when the synth is reset (or initialized) or when the GM/GS/XG mode is exited by a MIDI command.
Then, to play a MIDI file, start the app, backspace or click the Set button at the bottom right while the yellow text displays at the upper left corner (this enters the pre-boot menu, which is required to play music files using the built-in audio player). Navigate (using Q/W/left/right arrow keys) to the Advanced menu, click on Sound settings (or navigate there using cursor keys and press enter), next choose Music Player, navigate to the song to play using up/down keys (left/right keys fast scroll by the first letter of the filename) and press enter to play the song. During playback, the escape key stops playback. (alternatively numpad 6(escape) and numpad 2(enter) can be used, as they're the PSP-compatible key mappings for the same functionality of those keys).
Beta Was this translation helpful? Give feedback.
All reactions