Closed bwl21 closed 6 years ago
@moinejf wrote
Back on 3. I could not find more information about fading after looking at the source of fluidsynth. Anyway, I compared playing a steel guitar (25) with swami and in the editor (with both Vivaldi and Palemoon). For me, they sound the same: the sound becomes inaudible after about 6s. Which browser do you use?
I am using chrome, vivaldi and firefox. It does not work at all in Safari (but this is a safari problem ... see https://github.com/mdn/webaudio-examples/issues/5 )
I tried a bit in edit-1.xhtml
X:1
L:1/4
Q:1/4=120
K:C
CDEFGA
and setting the speed to very low, you can hear that it sounds very long. It sounds the same in Firefox.
from https://www.teachmeaudio.com/recording/sound-reproduction/sound-envelopes/
According to this diagram it seems that sustain is too high, decay is too short. Maybe it can be adjusted somewhere. I am not sure if these properties are set in the soundfont or in the player, or even in sf2_create within toaudio5.html
Two things:
safari From what I know, safari does not allow audio output if it is not initiated by the user (click). In the editor, I open the audio output when the user clicks on 'play'. Normally, safari should permit audio. Then, as I cannot test that, if this does not work, could you try to add a dummy playing just after audio open and see if the playing works thereafter? Here is the sequence (to put after "new (window.AudioContext")
// create an empty buffer
var buffer = ac.createBuffer(1, 1, 22050);
var source = ac.createBufferSource();
source.buffer = buffer;
// connect to output
source.connect(ac.destination);
// play the file
source.noteOn(0);
sustain/decay
This properties are defined in the SF2.
For instance, the steel guitar (25 - as many other percussion instruments) has:
So, the sustain volume is (100dB - 100dB) = 0. It cannot be smaller! It is reached 20s after the decay start time. As the decay is linear, half volume would occur at about 14s after decay start time.
In fact, this is not what should occur: with swami (fluidsynth), I hear no sound after 6s.
So, I changed the algorithm, dividing the decay time by 10. Then, using the html5 audio function setTargetAtTime(), the volume is 63% after this new decay time. Pratically, with the steel guitar:
Safari:
To double check, I discarded all my changes - an unbelievable, it still plays on safari.
TL:DR;
I made in toaudio5.js starting with line 390 (
// play the events
play: function(istart, i_iend, a_e) {
if (!a_e || !a_e.length) {
onend() // nothing to play
return
}
// initialize the audio subsystem if not done yet
// (needed for iPhone/iPad/...)
if (!gain) {
ac = conf.ac
if (!ac)
conf.ac = ac = new (window.AudioContext ||
window.webkitAudioContext);
// create an empty buffer
var buffer = ac.createBuffer(1, 1, 22050);
var source = ac.createBufferSource();
source.buffer = buffer;
// connect to output
source.connect(ac.destination);
// play the file
//source.start(0);
gain = ac.createGain();
gain.gain.value = gain_val
}
and it plays the sound on safari !!!
To double check, I discarded all my changes - an unbelievable, it still plays on safari.
I then tried with Zupfnoter, it also plays on safari Version 11.0.3 (11604.5.6.1.1). Now I am confused, as yesterday it did not work.
nevertheless, let us focus on the other stuff.
Even if there is not difference betwee swami (which I cannot run on my mac) and abc2svg, the sound is not satisfying.
I will prepare an audio/video recording such that you can hear how it sounds on my machine. then we see if you hear the same. I have users complaining about the playback with the old soundfont stuff.
As I am teaching a Zufpnoter workshop tomorrow, It will take til tomorrow night.
I don't follow all of what you are saying.
For an example, this one is enough:
X:1
M:C
L:1/4
K:C
%%MIDI program 25
c4-|c4-|c4-|c4-|
Safari (now) plays with as well as without the dummy play! I guess it is the other soundfont format which made it work again.
I am using:
chrome 65.0.3325.181 (Offizieller Build) (64-Bit) firefox 59.0.1 (64-Bit) safari Version 11.0.3 (11604.5.6.1.1)
All three sound the same. And sound to have the bad decay. But let me create the video tomorrow to see if we hear the same.
About safari, I think that the fix was the commit 8afe984 (don't use window.atob()).
I changed a bit the envelope, removing the call to the function setTargetAtTime(). Otherwise, I checked all what is possible with the web audio API, and I cannot do better. If you have some other ideas (not, not setValueCurveAtTime!). Here is the patch:
diff --git a/util/toaudio5.js b/util/toaudio5.js
index c129e03..835cd26 100644
--- a/util/toaudio5.js
+++ b/util/toaudio5.js
@@ -179,7 +179,7 @@ function Audio5(i_conf) {
hold: Math.pow(2, (gen.holdVolEnv ?
gen.holdVolEnv.amount : -12000) / 1200),
decay: Math.pow(2, (gen.decayVolEnv ?
- gen.decayVolEnv.amount : -12000) / 1200) / 10,
+ gen.decayVolEnv.amount : -12000) / 1200) / 3,
sustain: gen.sustainVolEnv ?
(gen.sustainVolEnv.amount / 1000) : 0,
// release: Math.pow(2, (gen.releaseVolEnv ?
@@ -191,6 +191,12 @@ function Audio5(i_conf) {
parm.hold += parm.attack;
parm.decay += parm.hold;
+ // sustain > 40dB is not audible
+ if (parm.sustain >= .4)
+ parm.sustain = 0.01 // must not be null
+ else
+ parm.sustain = 1 - parm.sustain / .4
+
sample_cp(parm.buffer, sample)
if (gen.sampleModes && (gen.sampleModes.amount & 1)) {
@@ -272,17 +278,21 @@ function Audio5(i_conf) {
// o.playbackRate.setValueAtTime(parm.rate, ac.currentTime);
o.playbackRate.value = rates[instr][key];
- var vol = .5;
g = ac.createGain();
- g.gain.setValueAtTime(0, t);
- g.gain.linearRampToValueAtTime(vol, t + parm.attack);
- g.gain.setTargetAtTime((1 - parm.sustain) * vol,
- t + parm.hold, parm.decay);
+ if (parm.hold < 0.002) {
+ g.gain.setValueAtTime(1, t)
+ } else {
+ if (parm.attack < 0.002) {
+ g.gain.setValueAtTime(1, t)
+ } else {
+ g.gain.setValueAtTime(0, t);
+ g.gain.linearRampToValueAtTime(1, t + parm.attack)
+ }
+ g.gain.setValueAtTime(1, t + parm.hold)
+ }
-//fixme: does not work
-// g.gain.setValueAtTime((1 - parm.sustain) * vol, t + d);
-// g.gain.linearRampToValueAtTime(0,
-// t + d);
+ g.gain.exponentialRampToValueAtTime(parm.sustain,
+ t + parm.decay);
o.connect(g);
g.connect(gain);
@@ -462,6 +472,6 @@ function Audio5(i_conf) {
else
gain_val = v;
set_cookie("volume", v.toFixed(2))
- }, // set_vol()
+ } // set_vol()
}
} // end Audio5
I tried to make the promised video today, but the quality of sound recording is not such that one can hear the difference well. So I made an audio recording in the attached file.
I thas
you can hear that
I had a talk with a friend about the issue. He assumes that the problem is in the samples. In order to save memory, sometimes only the attack is sampled, but the sustain repeats the same sample.
However it is, I see that e.g. 0.js is much smaller (256 k) than acoustic_grand_piano-ogg.js (1.5MB). Maybe this is the reason for the difference.
How do you produce the sample files? I see they hold an abc2svg comment in it. Could we try with samples of higher quality?
It seems that you don't know about SF2. First, about hearing.
About SF2.
About the sizes.
About the generation in javascript.
The web audio API offers only a few functions for the envelopes.
While SF2 says the envelopes use linear ramps, in practice, the web audio linear ramps do not render the same as what can be heard from swami (by fluidsynth). Also, with the web browsers I have, I could not hear a real difference between linear and exponential ramps.
So, I told before, I cannot do better.
About other soundfonts. You may easily use your own soundfonts. Here is how:
instrument_number.js
with a content:
abcsf2[instrument_number] = 'base64_encoded_SF2_of_the_instrument'
You may then put all these xx.js files in any directory, and set the SFU parameter to this one.
The instrument number is computed in ABC from %%MIDI commands:
%%MIDI control 0 bh % MSB bank number
%%MIDI control 0 bl % LSB bank number
%%MIDI program p
% instrument_number = (bh * 128 + bl) * 128 + p
It seems that you don't know about SF2.
Oh yes, this is fully true .. sorry; I tried to find a page with good explanation, but was not successful.
About SF2.
Thanks for the explanation. It helps for my understanding and confirms the assumption of my friend.
About other soundfonts.
swami seems not to be available on MacOs. But I found https://polyphone-soundfonts.com/en/. This site provides a soundfont editor (called polyphone, available on MacOs, Linux, Windows) and also some Soundfonts. So I will play around with this to get some knowledge. Maybe at the end I will manage to create my own soundfont optimized for table harp :-)
About the generation in Javascript ... So, I told before, I cannot do better.
Commit ca0ed08 provides a much better result at least for the piano (0). Thanks a lot. It provides an acceptable playback and I am not stuck to abc2svg 1.15. Instrument 25 still does not sonund well. Instrument 25 does not ramp down fast enough. But as far as I understood for now, you only have linear ramps. So for the time being, I will stop using 25 to emulate the table harp until I have a better understanding of all that stuff.
maybe we keep this ticket open for a while if you don't mind. Thanks a lot for your work and patience.
Yesterday I have read a lot about soundfonts and all that stuff.
I found these pages very useful:
I then did some experiments. My conclusion is, that I eventually I have to create my own optimized soundfonts.
TL:DR;
I tried to understand, what you are doing. The essential part is the following line which controls the decay.
g.gain.exponentialRampToValueAtTime(parm.sustain,
t + parm.decay);
Sith the following line, piano sounds much better for me - yes it is a matter of taste. So I do not expect that you set these parameters. I rather think, I need to create an optimized soundfont.
g.gain.exponentialRampToValueAtTime(parm.sustain,
t + parm.decay * 0.7 );
or even a combination which was even a little better
g.gain.exponentialRampToValueAtTime(parm.sustain,
t + parm.decay * 0.5);
g.gain.exponentialRampToValueAtTime(parm.sustain * 0.000001,
t + parm.decay);
For the steel guitar (25) the following setting sounds best for me:
g.gain.exponentialRampToValueAtTime(parm.sustain,
t + parm.decay * 0.2);
g.gain.exponentialRampToValueAtTime(parm.sustain * 0.000001,
t + parm.decay * 0.9);
Of course it is not possible to optimize this in the player, as it depends on the instrument. Maybe we could pass some extra parameters to the player, but I think this is the wrong direction. It needs to be handled in the soundfonts.
For your information, 'decay' is 20s and 'sustain' is 0 (100dB) for both the piano and the steel guitar. Then, your second ramp is of no use.
Hm, I still don't really know, what I am doing. I tried again and now I also do not find a diffence if the second ramp is there.
Nevertheless, i tried to manipulate the soundfonts today using polyphone https://polyphone-soundfonts.com/en/ . As a pity, the .sf2 - Files created by Polyphone did not work. It appears to me that Polyphone creates additional infos without samples. So I patched toaudio5.js to skip infos without sampleID
.
@@ -170,6 +170,8 @@ function Audio5(i_conf) {
rates[instr] = []
for (i = 0; i < infos.length; i++) {
gen = infos[i].generator;
if (! gen.sampleID)
continue;
sid = gen.sampleID.amount;
sampleRate = parser.sampleHeader[sid].sampleRate;
sample = parser.sample[sid];
Here I provide the soundfonts as I generated them for Zupfnoter: I have set decay to 8sec and sustain to 120db. The files only work with the mentioned patch. zupfnoter.zip
TL:DR;
In case other readers are interested: I created it in in following steps:
desc "compile zupfnoter soundfonts"
task :buildSoundfonts do
sources = Dir["../sf2_sources/*.sf2"]
i=0
sources.each do |sf|
outnumber = File.basename(sf).split("_").first
sfbase64 = Base64.encode64(File.read(sf)).split("\n").join("\\\n")
File.open("../public/soundfont/zupfnoter/#{outnumber}.js", "w") do |outfile|
outfile.puts (%Q{//ABC2SVG soundfont prepared for Zupfnoter
//created #{Time.now} from #{sf} by zupfnoter rake
abcsf2[#{outnumber}]='\\
#{sfbase64}'})
end
end
end
In 6., you don't need to set the sustain: in toaudio5, any value greater to 40dB sets the volume level to 0. It would have been interested to do this job with some other soundfont. For instance, the decay time is smaller in TimGM6mb for these two instruments...
I did it with http://polyphone-soundfonts.com/en/files/10-pianos/305-motif-es6-concert-piano-v2 without touching the parameters. It sound good but is 12 MB :-)
Next step will be to create my own harp samples.
I recorded some wav sample from my table harp and managed to create an SF2 file. It has 384 kb and sounds well.
I still need the change from https://github.com/moinejf/abc2svg/issues/84#issuecomment-376485119 to use it with abc2svg. Could you please add this patch.
May you send me this file for I have a look inside?
you can find testfiles in the https://github.com/moinejf/abc2svg/files/1851584/zupfnoter.zip
OK, seen, the first generator is empty. I will apply your patch. BTW, I wonder why you dit not use the harp (GM 46).
Simply since I did not know. And the sound of a vertical harp is different from a table harp. I am excitedly playing around with with recording and creating soundfonts. I recorded exactly one string of a table harp with good results. I now play around (just for fun and learning) with recoring my own voice and create a soundfont from that :-)
Thanks for applying the patch.
I think, we can close this. As I am now able to manipulate the soundfonts, I can optimize the playback there. Thanks for the helpful discussion and fixes.
I extract this topic from #83 to continue the discussion here.
Request