Jump to content

[Solved] Creating seamless music loop from a tile entity: is it possible?


サムエル
 Share

Recommended Posts

So I'm working on a mod that generates a huge (32 x 32 x 32) maze and I want it to have custom background music for added atmosphere. I have achieved this by creating a block at the entrance that plays the pre-loop part when a player touches its bounds and then the tileEntity it creates handles the looped portion from there on. The problem is that there is a short but clear seam between each playSound and it ruins the atmosphere. I thought of making playSound() happen before the previous one ends but I don't know how that can be done considering the sound is not synchronized with the amount of times the update() method is fired in the tileEntity. Is there another way to play background music where I can get a consistent amount of ticks or something else that allows the music to always line up seamlessly? On top of that, I have no idea how to keep the default Minecraft music from starting in the middle of my custom music but I'll leave that for another thread unless someone wants to tell me here.

 

Here is my code: (I'm aware there's no code yet to stop the music but I might as well leave it for when I actually know if this is possible)

 

BlockChaosLabyrinthEntrance.java

 

package com.samuel.chaosblock;

 

import java.util.List;

 

import com.samuel.chaosblock.entities.EntityBlockChaosPrimed;

import com.samuel.chaosblock.tileentities.TileEntityChaosLabyrinthEntrance;

 

import net.minecraft.block.Block;

import net.minecraft.block.BlockContainer;

import net.minecraft.block.material.Material;

import net.minecraft.block.properties.PropertyBool;

import net.minecraft.block.state.IBlockState;

import net.minecraft.client.Minecraft;

import net.minecraft.client.audio.PositionedSoundRecord;

import net.minecraft.client.audio.SoundHandler;

import net.minecraft.creativetab.CreativeTabs;

import net.minecraft.entity.Entity;

import net.minecraft.entity.EntityLivingBase;

import net.minecraft.entity.item.EntityTNTPrimed;

import net.minecraft.item.ItemStack;

import net.minecraft.tileentity.TileEntity;

import net.minecraft.util.AxisAlignedBB;

import net.minecraft.util.BlockPos;

import net.minecraft.util.DamageSource;

import net.minecraft.util.ResourceLocation;

import net.minecraft.world.Explosion;

import net.minecraft.world.IBlockAccess;

import net.minecraft.world.World;

import net.minecraftforge.fml.common.registry.GameRegistry;

 

public class BlockChaosLabyrinthEntrance extends BlockContainer {

   

    public static final String name = "unbreakable_chaos_block";

    public static boolean tileEntityActivated = false;

    public static TileEntityChaosLabyrinthEntrance tileEntity;

   

    public BlockChaosLabyrinthEntrance(String unlocalizedName, Material material, float hardness, float resistance) {

        super(material);

        this.setUnlocalizedName(unlocalizedName);

        this.setCreativeTab(CreativeTabs.tabBlock);

        this.setHardness(hardness);

        this.setResistance(resistance);

        this.setLightLevel(14F);

        this.setBlockBounds(0.0F, 0.0F, 0.0F, 1.0F, 0.0625F, 1.0F);

    }

   

    public BlockChaosLabyrinthEntrance(String unlocalizedName, float hardness, float resistance) {

        this(unlocalizedName, Material.rock, hardness, resistance);

    }

 

    public BlockChaosLabyrinthEntrance(String unlocalizedName) {

        this(unlocalizedName, -1.0f, 18000000.0f);

    }

   

    public AxisAlignedBB getCollisionBoundingBoxFromPool(World world, int i, int j, int k)

    {

        float f = 0.0625F;

        return AxisAlignedBB.fromBounds((float)i + f, j, (float)k + f, (float)(i + 1) - f, (float)(j + 1) - f, (float)(k + 1) - f);

    }

   

    public void onEntityCollidedWithBlock(World worldIn, BlockPos pos, IBlockState state, Entity entityIn)

    {

    if (!this.tileEntityActivated && !tileEntity.sh.isSoundPlaying(ChaosBlock.bgm)

    && !tileEntity.sh.isSoundPlaying(ChaosBlock.bgm_preloop)) {

    tileEntity.sh.stopSounds();

    tileEntity.sh.playSound(ChaosBlock.bgm_preloop);

    this.tileEntityActivated = true;

    }

    }

   

    public String getName() {

        return name;

    }

 

@Override

public TileEntity createNewTileEntity(World worldIn, int meta) {

return tileEntity = new TileEntityChaosLabyrinthEntrance(this);

}

}

 

 

TileEntityChaosLabyrinthEntrance.java

 

package com.samuel.chaosblock.tileentities;

 

import com.samuel.chaosblock.BlockChaosLabyrinthEntrance;

import com.samuel.chaosblock.ChaosBlock;

 

import net.minecraft.client.Minecraft;

import net.minecraft.client.audio.PositionedSoundRecord;

import net.minecraft.client.audio.SoundHandler;

import net.minecraft.server.gui.IUpdatePlayerListBox;

import net.minecraft.tileentity.TileEntity;

import net.minecraft.util.ChatComponentText;

import net.minecraft.util.ResourceLocation;

import net.minecraft.world.World;

 

public class TileEntityChaosLabyrinthEntrance extends TileEntity implements IUpdatePlayerListBox {

 

public BlockChaosLabyrinthEntrance block = null;

public SoundHandler sh = Minecraft.getMinecraft().getSoundHandler();

 

public TileEntityChaosLabyrinthEntrance(BlockChaosLabyrinthEntrance block) {

this.block = block;

}

 

@Override

public void update() {

if (block.tileEntityActivated && !this.worldObj.isRemote) {

if (!sh.isSoundPlaying(ChaosBlock.bgm) && !sh.isSoundPlaying(ChaosBlock.bgm_preloop)) {

sh.stopSounds();

sh.playSound(ChaosBlock.bgm);

}

}

}

}

 

Link to comment
Share on other sites

So I replaced the tile entity with a movingsound and I can't get anything to play. I'm also not sure how to get the preloop to work with this without a seam but for now I just want to be able to get it to play the looped portion. Can anyone see what I did wrong here? I couldn't find anything online about registering movingsounds so if they actually do need to be registered then that's likely part of my problem if not the whole problem.

 

MovingSoundChaosLabyrinthBGM.java

 

package com.samuel.chaosblock.movingsounds;

 

import com.samuel.chaosblock.BlockChaosLabyrinthEntrance;

 

import net.minecraft.client.audio.ISound;

import net.minecraft.client.audio.MovingSound;

import net.minecraft.entity.Entity;

import net.minecraft.entity.player.EntityPlayer;

import net.minecraft.util.ResourceLocation;

 

public class MovingSoundChaosLabyrinthBGM extends MovingSound {

    private final EntityPlayer player;

    private final BlockChaosLabyrinthEntrance block;

 

    public MovingSoundChaosLabyrinthBGM(Entity entityIn, BlockChaosLabyrinthEntrance block)

    {

        super(new ResourceLocation("dark_halls"));

        this.player = (EntityPlayer) entityIn;

        this.block = block;

        this.attenuationType = ISound.AttenuationType.NONE;

        this.repeat = true;

        this.repeatDelay = 0;

    }

 

    /**

    * Updates the JList with a new model.

    */

    public void update()

    {

        if (block.bgmActivated)

            this.volume = 1.0F;

        else

            this.donePlaying = true;

    }

}

 

Link to comment
Share on other sites

I might be wrong but PositionedSound seems to only play sounds from a location where I want it to play for a player regardless of location. If you actually just meant MovingSound then it would make more sense since you suggested MovingSound in the first place. If you did mean PositionedSound, how do I get it to follow the player?

Link to comment
Share on other sites

What I have tried is this:

 

MovingSoundChaosLabyrinthBGM.java

 

package com.samuel.chaosblock.movingsounds;

 

import com.samuel.chaosblock.BlockChaosLabyrinthEntrance;

import com.samuel.chaosblock.ChaosBlock;

 

import net.minecraft.client.audio.ISound;

import net.minecraft.client.audio.MovingSound;

import net.minecraft.entity.Entity;

import net.minecraft.entity.player.EntityPlayer;

import net.minecraft.util.ResourceLocation;

 

public class MovingSoundChaosLabyrinthBGM extends MovingSound {

    private final BlockChaosLabyrinthEntrance block;

    public boolean bgmActivated = false;

    public EntityPlayer thePlayer;

    protected boolean repeat = true;

    protected int repeatDelay = 0;

    protected float pitch;

 

    public MovingSoundChaosLabyrinthBGM(Entity entityIn, BlockChaosLabyrinthEntrance block)

    {

        super(ChaosBlock.bgmRes);

        this.block = block;

        this.repeat = true;

        this.volume = 0.8f;

        this.pitch = 1.0F;

    }

 

    public void setDonePlaying()

    {

        this.repeat = false;

        this.donePlaying = true;

        this.repeatDelay = 0;

    }

 

    @Override

    public boolean isDonePlaying()

    {

        return this.donePlaying;

    }

 

    @Override

    public void update()

    {

      byte status = 0;

      if(thePlayer == null || thePlayer.worldObj == null)

        {

            setDonePlaying();

        }

        if (bgmActivated) {

            xPosF = (float)thePlayer.posX;

            yPosF = (float)thePlayer.posY;

            zPosF = (float)thePlayer.posZ;

        } else {

        setDonePlaying();

        }

    }

 

    @Override

    public boolean canRepeat()

    {

        return this.repeat;

    }

 

    @Override

    public float getVolume()

    {

        return this.volume;

    }

 

    @Override

    public float getPitch()

    {

        return this.pitch;

    }

 

    @Override

    public int getRepeatDelay(){ return this.repeatDelay; }

 

    @Override

    public AttenuationType getAttenuationType()

    {

        return AttenuationType.LINEAR;

    }

}

 

 

I've never really worked with packets and I'm not sure how I would apply your tutorial to a sound.

Link to comment
Share on other sites

Unless you just mean calling SoundHandler.playSound() from the tile entity, I still don't know how I would apply the method in your tutorial to this. I'm also confused about whether I even need the tile entity when I have the moving sound. If I just have to call playSound() from the tile entity then do I just call the moving sound like I would a non-tickable sound?

Link to comment
Share on other sites

I tried the playSound with the tile entity and it's still not working. When you're talking about using packets, I can't tell if you're referring to how everything works internally or if you're saying I need to do what it's in your tutorial. If the latter, it's not obvious to me what needs to be changed in that code for it to work with a sound instead of a message and I can't seem to find any other examples for it.

Link to comment
Share on other sites

Nevermind, I had two versions of the same boolean and I only had it enabled in one of them. It seems to work now and loops as well even though there's a minor click in between. So my next question is how can I play the preloop part of the music and then switch to the looped part without a seam?

Link to comment
Share on other sites

First, if there is a click that is a problem with your audio file. I can highly recommend Audacity for a simple but very powerful tool to modify audio files.

This should also help you do the other question you asked.

 

And also (again!): PLEASE post your TE code. This is quite tricky to get right because of server and client. Just because you got the sound to play does not mean your code is correct and future proof.

Link to comment
Share on other sites

Alright, here's my TE code:

 

TileEntityChaosLabyrinthEntrance.java

 

package com.samuel.chaosblock.tileentities;

 

import com.samuel.chaosblock.BlockChaosLabyrinthEntrance;

import com.samuel.chaosblock.ChaosBlock;

 

import net.minecraft.client.Minecraft;

import net.minecraft.client.audio.PositionedSoundRecord;

import net.minecraft.client.audio.SoundHandler;

import net.minecraft.server.gui.IUpdatePlayerListBox;

import net.minecraft.tileentity.TileEntity;

import net.minecraft.util.ChatComponentText;

import net.minecraft.util.ResourceLocation;

import net.minecraft.world.World;

 

public class TileEntityChaosLabyrinthEntrance extends TileEntity implements IUpdatePlayerListBox {

 

  public SoundHandler sh = Minecraft.getMinecraft().getSoundHandler();

 

  public TileEntityChaosLabyrinthEntrance(BlockChaosLabyrinthEntrance block) {

  }

 

  @Override

  public void update() {

      if (ChaosBlock.BGMActivated && !this.worldObj.isRemote) {

        if (!sh.isSoundPlaying(ChaosBlock.movingSoundBGM) && !sh.isSoundPlaying(ChaosBlock.bgm_preloop)) {

        sh.playSound(ChaosBlock.movingSoundBGM);

        }

      }

  }

}

 

 

I found out before you posted that the audio file was actually the problem with the looped click and I've since fixed it. I want to know here if I can play the preloop and transition to the looped portion without a seam but if not I can just fade in the looped portion. ChaosBlock is the main mod class in case you might think it's a block.

Link to comment
Share on other sites

First, that TileEntity class will not work properly, TEs always need a constructor without any arguments. The argument you are using here is not used (and useless) so you can just delete the constructor you have.

 

Also, as I suspected, you are directly accessing client-only classes (SoundHandler, Minecraft, ISound). This will crash a dedicated sever. Also you are playing the sound from the server thread (world.isRemote is false). This is not correct and will (at some point) crash with very weird error messages.

 

Also you must create a new ISound whenever you play the sound, do not cache it. And do not store it in a static variable, otherwise no more than one of your sounds can play.

Link to comment
Share on other sites

If that's the case, how do I play the sound without accessing the SoundHandler (Edit: I found out I can probably just use "this.worldObj" but if I can just play it on the player's client then I'd like to know how to do that)? Also, to tell if the song is playing (so as to not play another copy over itself if the block collision is triggered twice), do I just use the BGMActivated boolean alone?

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share



×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.