When using the /playsound command to replicate musical notes, it is important that the notes are tuned correctly. However this does not appear to be the case, as sometimes notes are slightly sharp or flat when they shouldn't be.
To Reproduce:
1. Create a test world
2. Place a repeating command block, set to "always active", with the following command:
/playsound block.note.harp master @p ~ ~ ~ 1 1 1
A note should begin playing.
3. Change the command to
/playsound block.note.harp master @p ~ ~ ~ 1 1.05946309436 1
A new note will begin playing. It should be exactly 1 semitone higher, however it is off by more than 20 cents. If you actually wanted to play the note 20 cents lower, you should type
/playsound block.note.harp master @p ~ ~ ~ 1 1.04729412282 1
You may need an equal-tempered tuner to properly conduct the measurements.
Linked issues
Comments 14
I'm not sure. Seems random. Here is the world I was testing the tunings with, though it uses custom sounds tuned exactly C, and still is off. Perhaps it's a system issue?
I've done a bit more testing, and I've found significant differences between 1.8 and the snapshots, leading me to believe that this is a software issue. It doesn't only apply to pitches in /playsound, though; regular note blocks are also affected. Observing for the piano instrument, I noticed that in 1.8.8 the B was more in tune while in 15w44a the Bâ™ and C♯ were the most in tune. The F♯ was pretty good in both versions and the G,Aâ™,A, and Eâ™ were poorly tuned in both. Here is the world I was using for testing, and Here is the tuning app I was using.
Also: Here is the log file during one session of the testworld:
Completely ignored arguments: [--nativeLauncherVersion, 285]
[00:58:47] [Client thread/INFO]: Setting user: PhilosophioStone
[00:58:47] [Client thread/INFO]: (Session ID is token:fb0f1f8ce0f34c5698f508e5f0083cdc:d8ae73c8e8ee4815820717fd888bfca7)
[00:58:48] [Client thread/INFO]: LWJGL Version: 2.9.2
[00:58:49] [Client thread/INFO]: Reloading ResourceManager: Default
[00:58:49] [Client thread/WARN]: Missing sound for event: minecraft:enchant.thorns.hit
[00:58:49] [Client thread/WARN]: Missing sound for event: minecraft:entity.guardian.thorns
[00:58:49] [Client thread/WARN]: Missing sound for event: minecraft:entity.horse.eat
[00:58:49] [Client thread/WARN]: Missing sound for event: minecraft:entity.rabbit.attack
[00:58:49] [Sound Library Loader/INFO]: Starting up SoundSystem...
[00:58:49] [Thread-5/INFO]: Initializing LWJGL OpenAL
[00:58:49] [Thread-5/INFO]: (The LWJGL binding of OpenAL. For more information, see http://www.lwjgl.org)
2015-10-30 00:58:49.699 java[90739:2238078] 00:58:49.699 WARNING: 140: This application, or a library it uses, is using the deprecated Carbon Component Manager for hosting Audio Units. Support for this will be removed in a future release. Also, this makes the host incompatible with version 3 audio units. Please transition to the API's in AudioComponent.h.
[00:58:49] [Thread-5/INFO]: OpenAL initialized.
[00:58:50] [Sound Library Loader/INFO]: Sound engine started
[00:58:51] [Client thread/INFO]: Created: 1024x512 textures-atlas
[00:58:52] [Client thread/ERROR]: ########## GL ERROR ##########
[00:58:52] [Client thread/ERROR]: @ Post startup
[00:58:52] [Client thread/ERROR]: 1281: Invalid value
[00:58:52] [Client thread/ERROR]: ########## GL ERROR ##########
[00:58:52] [Client thread/ERROR]: @ Pre render
[00:58:52] [Client thread/ERROR]: 1281: Invalid value
[00:59:17] [Server thread/INFO]: Starting integrated minecraft server version 15w44a
[00:59:17] [Server thread/INFO]: Generating keypair
[00:59:17] [Server thread/INFO]: Preparing start region for level 0
[00:59:18] [Server thread/INFO]: Changing view distance to 16, from 10
[00:59:18] [Server thread/INFO]: PhilosophioStone[local:E:e1ab8ad3] logged in with entity id 0 at (5.3707756287232575, 56.0, 9.315376549089452)
[00:59:18] [Server thread/INFO]: PhilosophioStone joined the game
[00:59:19] [Server thread/INFO]: Saving and pausing game...
[00:59:19] [Server thread/INFO]: Saving chunks for level 'Noteblock Bug Testworld'/Overworld
[00:59:19] [Server thread/INFO]: Saving chunks for level 'Noteblock Bug Testworld'/Nether
[00:59:19] [Server thread/INFO]: Saving chunks for level 'Noteblock Bug Testworld'/The End
[00:59:37] [Server thread/INFO]: Saving and pausing game...
[00:59:37] [Server thread/INFO]: Saving chunks for level 'Noteblock Bug Testworld'/Overworld
[00:59:37] [Server thread/INFO]: Saving chunks for level 'Noteblock Bug Testworld'/Nether
[00:59:37] [Server thread/INFO]: Saving chunks for level 'Noteblock Bug Testworld'/The End
[00:59:38] [Server thread/INFO]: Stopping server
[00:59:38] [Server thread/INFO]: Saving players
[00:59:38] [Server thread/INFO]: Saving worlds
[00:59:38] [Server thread/INFO]: Saving chunks for level 'Noteblock Bug Testworld'/Overworld
[00:59:38] [Server thread/INFO]: Saving chunks for level 'Noteblock Bug Testworld'/Nether
[00:59:38] [Server thread/INFO]: Saving chunks for level 'Noteblock Bug Testworld'/The End
[00:59:40] [Client thread/INFO]: Deleting level Noteblock Bug Testworld
[00:59:40] [Client thread/INFO]: Attempt 1...
[00:59:42] [Client thread/INFO]: Stopping!
[00:59:42] [Client thread/INFO]: SoundSystem shutting down...
[00:59:42] [Client thread/WARN]: Author: Paul Lamb, www.paulscode.com
Now the event name isn't note.harp
anymore, it's block.note.harp
(check MC-91102); might be good to change the description. 😉
1 semitone is a musical interval. It is what you get when you play the middle C on a piano and then play a C#. If a note has a frequency f, then the note 1 semitone higher will have a frequency of f*2^(1/12). The problem I'm getting is that when I use that double value, which is very close to 2^(1/12), as my pitch
parameter, I get a note that is slightly less than 1 semitone higher; rather I get a note that is about 80 cents (a cent is 1/100 of a semitone) higher than the original frequency, or if the initial frequency is f, the resultant frequency I am getting is closer to f*2^(80/1200).
There are plenty of tuner apps for android, by the way.
So actually found what is going on.
When we send the pitch over the wire we multiply it by a constant and then when it gets off the wire, we divide again. This all so it fits in a single byte but in process this destroys accuracy on a linear scale.
The broken version:float PITCH_ACCURACY = Byte.MAX_VALUE / 2f;
The 'working version':float PITCH_ACCURACY = Byte.MAX_VALUE / 2;
For 1.10 I'll remove this completely and just send the raw float we have over the wire, that is far more accurate especially since pitch is not a linear scale.
So I tested it in 1.9.3 and it's definitely better than it was before. Some of the notes were still off when testing with noteblocks. When testing with a command block I found that
/playsound block.note.harp master @p ~ ~ ~ 1 1.05946309436 1
was still giving me a note that was about 20 cents flat. I suspect that keeping full float precision will fix this, so I'd mark it as fixed for 1.10. When it comes I'll be sure to do more testing.
I'm not sure what to make of this in all honesty 😞
Is the difference a linear thing? Can this be solved by simply multiplying by a fixed ratio?