You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
In the following Discord server discussion, a user reported the volume levels heard from a MIDI file sounded louder in Signal than in another MIDI file application:
After investigating some, I think Signal's volume calculations should be updated. However, be aware that changing Signal's volume behavior may annoy existing users because it will make some parts of their existing compositions sound softer now.
Basic Problem
My understanding is MIDI channel volume, expression, and note on velocity should combine to form a relative sound wave amplitude like this:
V is the channel volume from 0 to 127
E is the expression from 0 to 127
Y is the note on velocity from 1 to 127
A is the relative sound wave amplitude from 0 (silence) to 1 (maximum)
( (V/127) × (E/127) × (Y/127) )² = A
According to some tests and peeking at Signal source code, I think Signal might be doing it like this:
(V/128) × (E/128) × (Y/128) = A
Without the square operation, the sound output in Signal will be louder than other MIDI devices. Signal is also using denominators of 128 instead of 127, which should probably also be corrected.
Formula Conversion Notes
I usually prefer to calculate a relative sound wave amplitude from 0 (silence) to 1 (maximum), and I think this is the kind of value that Signal is using during its sound wave volume calculations. However, the MIDI documents mentioned later use formulas that calculate a relative sound level in decibels from −∞ (silence) to 0 (maximum). The correspondence between a relative sound wave amplitude and a relative sound level is the following:
A is a relative sound wave amplitude from 0 (silence) to 1 (maximum).
L is a relative sound level in decibels from −∞ (silence) to 0 (maximum).
20 × log₁₀(A) = L
Also, when reading over the formulas in the MIDI documents, there are certain properties of logarithms that are useful to remember to be able to see how the different formulas are equivalent to one another:
Volume (CC#7) and Expression (CC#11) should be implemented as follows:
For situations in which only CC# 7 is used (CC#11 is assumed "127"):
L(dB) = 40 log (V/127) where V= CC#7 value
For situations in which both [Volume and Expression] are used:
L(dB) = 40 log (V/127²) where V = (volume × expression)
If you re-write that last formula in the format I was using:
V is the channel volume from 0 to 127.
E is the expression from 0 to 127.
L is the relative sound level in decibels from −∞ (silence) to 0 (maximum).
A is the relative sound wave amplitude from 0 (silence) to 1 (maximum).
L = 40 × log₁₀((V × E)/127²)
then use various properties and conversions:
L = 40 × log₁₀( (V × E)/(127 × 127) )
L = 40 × log₁₀( (V/127) × (E/127) )
L = 2 × 20 × log₁₀( (V/127) × (E/127) )
L = 20 × log₁₀( ((V/127) × (E/127))² )
A = ((V/127) × (E/127))²
In the document General MIDI 2, volume is described on printed pages 6 and 7, PDF pages 10 and 11:
Regarding the curve of volume change messages, the square of the value is proportional to the volume.
The formula used is: gain in dB = 40 * log₁₀(cc7/127)
Expression is described on printed page 8, PDF page 12:
The formula used is: Gain in dB = (40 * log₁₀(cc7/127)) + (40 * log₁₀(cc11/127))
If you re-write that last formula in the format I was using:
V is the channel volume from 0 to 127.
E is the expression from 0 to 127.
L is the relative sound level in decibels from −∞ (silence) to 0 (maximum).
A is the relative sound wave amplitude from 0 (silence) to 1 (maximum).
L = 40 × log₁₀(V/127) + 40 × log₁₀(E/127)
then use various properties and conversions:
L = 40 × ( log₁₀(V/127) + log₁₀(E/127) )
L = 40 × log₁₀( (V/127) × (E/127) )
L = 2 × 20 × log₁₀( (V/127) × (E/127) )
L = 20 × log₁₀( ((V/127) × (E/127))² )
A = ((V/127) × (E/127))²
In the General MIDI 2 document, note on velocity is described on printed page 5, PDF page 9. This document just says "The velocity effect on volume is not defined", but I believe most MIDI devices combine the note on velocity in the volume calculation in the same kind of way:
V is the channel volume from 0 to 127
E is the expression from 0 to 127
Y is the note on velocity from 1 to 127
A is the relative sound wave amplitude from 0 (silence) to 1 (maximum)
A = ( (V/127) × (E/127) × (Y/127) )²
Test
I made a MIDI file that steps through the eight possible combinations of channel volume, expression, and note on velocity with values of either 127 or 90.
V: Channel volume E: Expression Y: Note On velocity A1: Expected amplitude (percent of maximum) for the formula with the square operation A2: Expected amplitude (percent of maximum) for the formula without the square operation
V
E
Y
A1
A2
127
127
127
100
100
127
127
90
50
71
127
90
127
50
71
127
90
90
25
50
90
127
127
50
71
90
127
90
25
50
90
90
127
25
50
90
90
90
13
36
I played the test MIDI file with various output devices or software synthesizers and exported or recorded an audio file. I opened each audio file in Audacity, used the Amplify command to scale the maximum amplitude up to the 1.0 mark on the amplitude ruler, then I wrote down the rough amplitudes of the test notes by scrolling them next to the amplitude ruler.
Results so far: volume-test.zip (3.29 mebibytes, contains MIDI file, audio files, and OpenDocument spreadsheet)
In my tests, these software synthesizers matched the formula with the square operation:
Microsoft GS Wavetable Synth
Notation Player (using its Notation Software Synth)
Synthesia (using its Built-in MIDI Synthesizer, which uses BASS and BASSMIDI libraries, and "Built-in sounds by Voice Crystal")
From some previous testing, I also believe my Yamaha PSR-225 keyboard matches the formula with the square operation, but I don't have a way to record external devices at the moment to confirm.
These software synthesizers did not match the formula with the square operation:
VLC Media Player
Cakewalk (using its Cakewalk TTS-1 soft synth)
LMMS
But I think they might be using similar formulas with an exponent that is not exactly 2 (like maybe 1.75 or 1.5, or different exponents for volume, expression, and note on velocity), which is not like Signal's linear formula (or, in other words, an exponent of 1).
Additional information about LMMS: LMMS doesn't seem to use expression for anything. Also LMMS seems to have a bug where the initial volume only works the first time you play. When you play the second or later time, then the inital volume is forgotten and whatever the volume was last set to is what the volume starts out as.
I might gather more examples. I might design a different MIDI file to test with.
Code Changes
I think the following code changes will change Signal to use the formula with the square operation, but I don't know how to build changes to node_modules, so I haven't tested these changes.
function process( const volume = this.velocity * this.volume * this.sample.volume
I think this should be const volume = ((this.velocity * this.volume) ** 2) * this.sample.volume
The text was updated successfully, but these errors were encountered:
ryohey
added a commit
to ryohey/wavelet
that referenced
this issue
Nov 17, 2024
In the following Discord server discussion, a user reported the volume levels heard from a MIDI file sounded louder in Signal than in another MIDI file application:
the way volume is scaled in Signal is broken
After investigating some, I think Signal's volume calculations should be updated. However, be aware that changing Signal's volume behavior may annoy existing users because it will make some parts of their existing compositions sound softer now.
Basic Problem
My understanding is MIDI channel volume, expression, and note on velocity should combine to form a relative sound wave amplitude like this:
V is the channel volume from 0 to 127
E is the expression from 0 to 127
Y is the note on velocity from 1 to 127
A is the relative sound wave amplitude from 0 (silence) to 1 (maximum)
( (V/127) × (E/127) × (Y/127) )² = A
According to some tests and peeking at Signal source code, I think Signal might be doing it like this:
(V/128) × (E/128) × (Y/128) = A
Without the square operation, the sound output in Signal will be louder than other MIDI devices. Signal is also using denominators of 128 instead of 127, which should probably also be corrected.
Formula Conversion Notes
I usually prefer to calculate a relative sound wave amplitude from 0 (silence) to 1 (maximum), and I think this is the kind of value that Signal is using during its sound wave volume calculations. However, the MIDI documents mentioned later use formulas that calculate a relative sound level in decibels from −∞ (silence) to 0 (maximum). The correspondence between a relative sound wave amplitude and a relative sound level is the following:
A is a relative sound wave amplitude from 0 (silence) to 1 (maximum).
L is a relative sound level in decibels from −∞ (silence) to 0 (maximum).
20 × log₁₀(A) = L
Also, when reading over the formulas in the MIDI documents, there are certain properties of logarithms that are useful to remember to be able to see how the different formulas are equivalent to one another:
log(a × b) = log(a) + log(b)
log(a²) = 2 × log(a)
References
In the document General MIDI System Level 1 Developer Guidelines, volume and expression are described on printed page 9, PDF page 13:
If you re-write that last formula in the format I was using:
V is the channel volume from 0 to 127.
E is the expression from 0 to 127.
L is the relative sound level in decibels from −∞ (silence) to 0 (maximum).
A is the relative sound wave amplitude from 0 (silence) to 1 (maximum).
L = 40 × log₁₀((V × E)/127²)
then use various properties and conversions:
L = 40 × log₁₀( (V × E)/(127 × 127) )
L = 40 × log₁₀( (V/127) × (E/127) )
L = 2 × 20 × log₁₀( (V/127) × (E/127) )
L = 20 × log₁₀( ((V/127) × (E/127))² )
A = ((V/127) × (E/127))²
In the document General MIDI 2, volume is described on printed pages 6 and 7, PDF pages 10 and 11:
Expression is described on printed page 8, PDF page 12:
If you re-write that last formula in the format I was using:
V is the channel volume from 0 to 127.
E is the expression from 0 to 127.
L is the relative sound level in decibels from −∞ (silence) to 0 (maximum).
A is the relative sound wave amplitude from 0 (silence) to 1 (maximum).
L = 40 × log₁₀(V/127) + 40 × log₁₀(E/127)
then use various properties and conversions:
L = 40 × ( log₁₀(V/127) + log₁₀(E/127) )
L = 40 × log₁₀( (V/127) × (E/127) )
L = 2 × 20 × log₁₀( (V/127) × (E/127) )
L = 20 × log₁₀( ((V/127) × (E/127))² )
A = ((V/127) × (E/127))²
In the General MIDI 2 document, note on velocity is described on printed page 5, PDF page 9. This document just says "The velocity effect on volume is not defined", but I believe most MIDI devices combine the note on velocity in the volume calculation in the same kind of way:
V is the channel volume from 0 to 127
E is the expression from 0 to 127
Y is the note on velocity from 1 to 127
A is the relative sound wave amplitude from 0 (silence) to 1 (maximum)
A = ( (V/127) × (E/127) × (Y/127) )²
Test
I made a MIDI file that steps through the eight possible combinations of channel volume, expression, and note on velocity with values of either 127 or 90.
V: Channel volume
E: Expression
Y: Note On velocity
A1: Expected amplitude (percent of maximum) for the formula with the square operation
A2: Expected amplitude (percent of maximum) for the formula without the square operation
I played the test MIDI file with various output devices or software synthesizers and exported or recorded an audio file. I opened each audio file in Audacity, used the Amplify command to scale the maximum amplitude up to the 1.0 mark on the amplitude ruler, then I wrote down the rough amplitudes of the test notes by scrolling them next to the amplitude ruler.
Results so far: volume-test.zip (3.29 mebibytes, contains MIDI file, audio files, and OpenDocument spreadsheet)
In my tests, these software synthesizers matched the formula with the square operation:
From some previous testing, I also believe my Yamaha PSR-225 keyboard matches the formula with the square operation, but I don't have a way to record external devices at the moment to confirm.
These software synthesizers did not match the formula with the square operation:
But I think they might be using similar formulas with an exponent that is not exactly 2 (like maybe 1.75 or 1.5, or different exponents for volume, expression, and note on velocity), which is not like Signal's linear formula (or, in other words, an exponent of 1).
Additional information about LMMS: LMMS doesn't seem to use expression for anything. Also LMMS seems to have a bug where the initial volume only works the first time you play. When you play the second or later time, then the inital volume is forgotten and whatever the volume was last set to is what the volume starts out as.
I might gather more examples. I might design a different MIDI file to test with.
Code Changes
I think the following code changes will change Signal to use the formula with the square operation, but I don't know how to build changes to node_modules, so I haven't tested these changes.
file
node_modules\@ryohey\wavelet\src\processor\SynthProcessorCore.ts
function
noteOn(
volume = velocity / 0x80
should have a denominator of 127.function
setMainVolume(
state.volume = value / 0x80
should have a denominator of 127.function
expression(
state.expression = value / 0x80
should have a denominator of 127.function
process(
oscillator.volume = state.volume * state.expression
note: sets the "this.volume" value used in the following code.
file
node_modules\@ryohey\wavelet\src\processor\WavetableOscillator.ts
process(
const volume = this.velocity * this.volume * this.sample.volume
I think this should be
const volume = ((this.velocity * this.volume) ** 2) * this.sample.volume
The text was updated successfully, but these errors were encountered: