mojira.dev
MC-35714

Sounds loop/restart when exiting screens

When a jukebox playing music is broken, the music initially stops, but when I opened up my inventory then closed it again, the song plays again. This works with any disc in creative and survival, and in both single- and multiplayer.

1. Get Jukebox and disc of any kind
2. Play the disc
3. Break jukebox BEFORE song ends
4. Open your inventory
5. Close Inventory
6. Song plays without a jukebox

More generally, sounds can replay when a GUI is closed, when they shouldn't. This includes block breaking sounds, button sounds, and music. It also includes any GUI closing besides the pause menu in single player – your inventory, the chat GUI, or a chest all work.

Debug info

I recently figured out how to get minecraft to output sound debug logs by following this wiki.vg tutorial. Using the following configuration:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN" packages="net.minecraft,com.mojang">
    <Appenders>
        <Console name="SysOut" target="SYSTEM_OUT">
            <PatternLayout pattern="[%d{HH:mm:ss}] [%t/%level]: %msg%n" />
        </Console>
    </Appenders>
    <Loggers>
        <Root level="debug">
            <filters>
                <MarkerFilter marker="SOUNDS" onMatch="ACCEPT" onMismatch="DENY" />
            </filters>
            <AppenderRef ref="SysOut"/>
        </Root>
    </Loggers> 
</Configuration>

This is what the logs look like when the bug is reproduced:

[12:18:50] [Sound Library Loader/INFO]: Sound engine started
[12:19:01] [Client thread/DEBUG]: Playing sound minecraft:sounds/random/click.ogg for event minecraft:gui.button.press as channel 231386e6-ecda-431b-a2e7-12f936caedb2
[12:19:02] [Client thread/DEBUG]: Removed channel 231386e6-ecda-431b-a2e7-12f936caedb2 because it's not playing anymore
[12:19:06] [Client thread/DEBUG]: Skipped playing sound minecraft:sounds/music/menu/menu2.ogg, volume was zero.
[12:19:10] [Client thread/DEBUG]: Playing sound minecraft:sounds/random/click.ogg for event minecraft:gui.button.press as channel 229f3257-5902-4b42-bcae-eee3d53ec16c
[12:19:11] [Client thread/DEBUG]: Removed channel 229f3257-5902-4b42-bcae-eee3d53ec16c because it's not playing anymore
[12:19:22] [Client thread/DEBUG]: Skipped playing sound minecraft:sounds/music/game/calm2.ogg, volume was zero.
[12:19:37] [Client thread/DEBUG]: Playing sound minecraft:sounds/step/stone4.ogg for event minecraft:step.stone as channel 07f5ef05-28e9-4d6d-8f31-d0a7df0fe69b
[12:19:38] [Client thread/DEBUG]: Removed channel 07f5ef05-28e9-4d6d-8f31-d0a7df0fe69b because it's not playing anymore
[12:19:38] [Client thread/DEBUG]: Playing sound minecraft:sounds/step/grass3.ogg for event minecraft:step.grass as channel 1a781d6d-ab09-425b-bb92-7f6009b42362
[12:19:39] [Client thread/DEBUG]: Removed channel 1a781d6d-ab09-425b-bb92-7f6009b42362 because it's not playing anymore
[12:19:45] [Client thread/DEBUG]: Playing sound minecraft:sounds/dig/stone1.ogg for event minecraft:dig.stone as channel c16bccae-f170-44fc-8082-f1567ddcd783
[12:19:46] [Client thread/DEBUG]: Removed channel c16bccae-f170-44fc-8082-f1567ddcd783 because it's not playing anymore
[12:19:47] [Client thread/DEBUG]: Playing sound minecraft:sounds/dig/stone1.ogg for event minecraft:dig.stone as channel 73f94183-6a17-4355-b356-2374cd71ed68
[12:19:48] [Client thread/DEBUG]: Resuming channel 73f94183-6a17-4355-b356-2374cd71ed68
[12:19:48] [Client thread/DEBUG]: Resuming channel 73f94183-6a17-4355-b356-2374cd71ed68
[12:19:48] [Client thread/DEBUG]: Resuming channel 73f94183-6a17-4355-b356-2374cd71ed68
[12:19:48] [Client thread/DEBUG]: Resuming channel 73f94183-6a17-4355-b356-2374cd71ed68
[12:19:48] [Client thread/DEBUG]: Resuming channel 73f94183-6a17-4355-b356-2374cd71ed68
[12:19:48] [Client thread/DEBUG]: Resuming channel 73f94183-6a17-4355-b356-2374cd71ed68
[12:19:48] [Client thread/DEBUG]: Removed channel 73f94183-6a17-4355-b356-2374cd71ed68 because it's not playing anymore

For 73f94183-6a17-4355-b356-2374cd71ed68, I was mashing open/close inventory (which is how the bug is reproduced). As you can see, it's getting resumed multiple times, despite never getting paused. It only replays the sound once (since resuming the sound is simply playing it again), but probably the sound is getting played before it is removed.

Programmatic cause

Note: MCP names for Minecraft 1.8 (mcp910) are used here. Obfuscated names as of 1.9-pre1 appear at the bottom.

The reason that this bug occurs is that sounds are replayed whenever a GUI is closed. The most likely reason why sounds were replayed is that they are paused when certain GUIs open (such as the ingame pause menu) in single player. However, the sounds are played even if they were never paused (IE, they are played when any GUI is closed).

net.minecraft.client.Minecraft, lines 964 - 1005

/**
 * Sets the argument GuiScreen as the main (topmost visible) screen.
 */
public void displayGuiScreen(GuiScreen guiScreenIn)
{
    if (this.currentScreen != null)
    {
        this.currentScreen.onGuiClosed();
    }

    if (guiScreenIn == null && this.theWorld == null)
    {
        guiScreenIn = new GuiMainMenu();
    }
    else if (guiScreenIn == null && this.thePlayer.getHealth() <= 0.0F)
    {
        guiScreenIn = new GuiGameOver();
    }

    if (guiScreenIn instanceof GuiMainMenu)
    {
        this.gameSettings.showDebugInfo = false;
        this.ingameGUI.getChatGUI().clearChatMessages();
    }

    this.currentScreen = (GuiScreen)guiScreenIn;

    if (guiScreenIn != null)
    {
        this.setIngameNotInFocus();
        ScaledResolution var2 = new ScaledResolution(this, this.displayWidth, this.displayHeight);
        int var3 = var2.getScaledWidth();
        int var4 = var2.getScaledHeight();
        ((GuiScreen)guiScreenIn).setWorldAndResolution(this, var3, var4);
        this.skipRenderWorld = false;
    }
    else
    {
        this.mcSoundHandler.resumeSounds();
        this.setIngameFocus();
    }
}

When guiScreenIn is null, mcSoundHandler.resumeSounds is called. The SoundHandler methods simply call the same methods on the SoundManager.

net.minecraft.client.audio.SoundManager, lines 467 - 495

/**
 * Pauses all currently playing sounds
 */
public void pauseAllSounds()
{
    Iterator var1 = this.playingSounds.keySet().iterator();

    while (var1.hasNext())
    {
        String var2 = (String)var1.next();
        logger.debug(LOG_MARKER, "Pausing channel {}", new Object[] {var2});
        this.sndSystem.pause(var2);
    }
}

/**
 * Resumes playing all currently playing sounds (after pauseAllSounds)
 */
public void resumeAllSounds()
{
    Iterator var1 = this.playingSounds.keySet().iterator();

    while (var1.hasNext())
    {
        String var2 = (String)var1.next();
        logger.debug(LOG_MARKER, "Resuming channel {}", new Object[] {var2});
        this.sndSystem.play(var2);
    }
}

Note that resumeAllSounds works simply by calling play on all of the currently playing sounds. This works in most cases, as calling play while a sound is currently playing doesn't do anything. However, if the sound has finished playing but has not been cleaned up yet, calling play on it will cause it to play again, from the start. This is the core cause of this bug.

The simplest way to fix it is to create a list of sounds that were paused when pauseAllSounds is called, and then only play the sounds in that list when resumeAllSounds is called (and then clear the list).

Something like this:

Replacement for code in net.minecraft.client.audio.SoundManager

private List<String> pausedSounds = new ArrayList<String>();

/**
 * Pauses all currently playing sounds
 */
public void pauseAllSounds()
{
    Iterator var1 = this.playingSounds.keySet().iterator();

    while (var1.hasNext())
    {
        String var2 = (String)var1.next();
        logger.debug(LOG_MARKER, "Pausing channel {}", new Object[] {var2});
        this.sndSystem.pause(var2);
        
        pausedSounds.add(var2);
    }
}

/**
 * Resumes playing all currently playing sounds (after pauseAllSounds)
 */
public void resumeAllSounds()
{
    Iterator var1 = this.pausedSounds.iterator();

    while (var1.hasNext())
    {
        String var2 = (String)var1.next();
        logger.debug(LOG_MARKER, "Resuming channel {}", new Object[] {var2});
        this.sndSystem.play(var2);
    }
    
    this.pausedSounds.clear();
}

(It may be necessary to make pauseAllSounds and resumeAllSounds synchronized depending on if it's called from a separate thread, but I'm guessing not since it isn't currently needed; also, don't blame me for the ugly syntax there; that's MCP's fault)

I've tested this and found that it eliminates all of the looping sound issues, which is the main part of this bug.

Here are the code snippets from above with 1.9-pre1's obfuscation (jd is used to decompile):

The place where resumeSounds is called in displayGuiScreen:

bce, lines 864 to 893

public void a(bfa ) {
    if (this.m != null) {
      this.m.m();
    }
    
    if (( == null) && (this.f == null)) {
       = new bfh();
    } else if (( == null) && (this.h.bP() <= 0.0F)) {
       = new bel(null);
    }
    
    if ((( instanceof bfh)) || (( instanceof bgq))) {
      this.u.aq = false;
      this.r.d().a();
    }
    
    this.m = ;
    
    if ( != null) {
      p();
      bcw  = new bcw(this);
      int  = .a();
      int  = .b();
      .a(this, , );
      this.s = false;
    } else {
      this.aL.e(); // This is resumeAllSounds, line 890
      o();
    }
  }

Here is the code for resumeAllSounds and pauseAllSounds:

byu, lines 409 to 421

public void e() {
    for (String  : this.i.keySet()) {
      b.debug(a, "Pausing channel {}", new Object[] {  });
      this.f.pause();
    }
  }
  
  public void f() {
    for (String  : this.i.keySet()) {
      b.debug(a, "Resuming channel {}", new Object[] {  });
      this.f.play();
    }
  }

And here are resumeAllSounds and pauseAllSounds with the bug fixed:

byu

List<String> pausedSounds = new ArrayList<String>();
  
  public void e() {
    for (String  : this.i.keySet()) {
      b.debug(a, "Pausing channel {}", new Object[] {  });
      this.f.pause();
      pausedSounds.add();
    }
  }
  
  public void f() {
    for (String  : this.pausedSounds) {
      b.debug(a, "Resuming channel {}", new Object[] {  });
      this.f.play();
    }

    pausedSounds.clear();
  }

Obfuscation as of earlier versions can be found here.

There are other ways it could be fixed, but this is the simplest way.


Some additional oddities with jukeboxes that aren't necessarily part of this (from MC-35896):
a) In survival mode, ejecting a disc while breaking the jukebox block may make the jukebox music permanent (Goes off after exiting the world). (Both survival and creative mode). (separate bug?)
b) Sometimes, when destroying the jukebox block (After a) ALL music can go off (Not any music until exiting Minecraft). (Creative mode).
c) Sometimes, when playing jukebox exiting the world, jukebox music can stay on (Does not go off until exiting Minecraft). (Creative mode).

Linked issues

MC-36377 Jukeboxes infinitely loop record, even after being broken. Also, music overlap. Resolved MC-36379 Music Disks Play over each other Resolved MC-36382 Two musics playing at the same time Resolved MC-36400 Background Music Looping Resolved MC-36406 Music discs continue playing when removed/ play double Resolved

Comments 89

Can confirm. It didn't always work, but it did indeed occur.

Can not confirm in 13w42b

I get this with every music disc, and it does not happen because of opening and closing the inventory. I simply removed the disc from the jukebox and the song stopped for a few seconds, then continued.

This is one of the most annoying bugs in minecraft in my opinion, especially together with MC-35860.

Charles Dorpaxio

Can confirm in 13w43a. Jukebox don't stop music.

79 more comments
Fenhl (Max Dominik Weber)

Affects 16w07a.

Fenhl (Max Dominik Weber)

Affects 16w07b.

Very well described issue, I can indeed get a sound replaying but not the actual song.

The main issue seems to be that sounds can get stopped but when you pause them you should only keep track of the ACTUALLY playing sounds at that moment.

It is doing this now, this should fix this bug.

Dustin A Mundy

Cool thanks 🙂

Alexander Hoff

I tested it, and it's indeed fixed in the pre-release 2. Awesome!

Trevor

pokechu22

Erik Broes

Confirmed

audio, chest, inventory, jukebox, loop, music, music-disc, overlap, record, restart

Minecraft 13w42a, Minecraft 13w42b, Minecraft 13w43a, Minecraft 1.7, Minecraft 1.7.1, ..., Minecraft 16w05b, Minecraft 16w06a, Minecraft 16w07a, Minecraft 16w07b, Minecraft 1.9 Pre-Release 1

Minecraft 1.9 Pre-Release 2

Retrieved