minecraft-src/net/minecraft/client/sounds/MusicManager.java
2025-09-18 12:27:44 +00:00

227 lines
6.5 KiB
Java

package net.minecraft.client.sounds;
import com.mojang.serialization.Codec;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.Minecraft;
import net.minecraft.client.resources.sounds.SimpleSoundInstance;
import net.minecraft.client.resources.sounds.Sound;
import net.minecraft.client.resources.sounds.SoundInstance;
import net.minecraft.sounds.Music;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.util.Mth;
import net.minecraft.util.OptionEnum;
import net.minecraft.util.RandomSource;
import net.minecraft.util.StringRepresentable;
import org.jetbrains.annotations.Nullable;
/**
* The MusicManager class manages the playing of music in Minecraft.
*/
@Environment(EnvType.CLIENT)
public class MusicManager {
/**
* The delay before starting to play the next song.
*/
private static final int STARTING_DELAY = 100;
private final RandomSource random = RandomSource.create();
private final Minecraft minecraft;
@Nullable
private SoundInstance currentMusic;
private MusicManager.MusicFrequency gameMusicFrequency;
private float currentGain = 1.0F;
/**
* The delay until the next song starts.
*/
private int nextSongDelay = 100;
private boolean toastShown = false;
public MusicManager(Minecraft minecraft) {
this.minecraft = minecraft;
this.gameMusicFrequency = minecraft.options.musicFrequency().get();
}
/**
* Called every tick to manage the playing of music.
*/
public void tick() {
MusicInfo musicInfo = this.minecraft.getSituationalMusic();
float f = musicInfo.volume();
if (this.currentMusic != null && this.currentGain != f) {
boolean bl = this.fadePlaying(f);
if (!bl) {
return;
}
}
Music music = musicInfo.music();
if (music == null) {
this.nextSongDelay = Math.max(this.nextSongDelay, 100);
} else {
if (this.currentMusic != null) {
if (musicInfo.canReplace(this.currentMusic)) {
this.minecraft.getSoundManager().stop(this.currentMusic);
this.nextSongDelay = Mth.nextInt(this.random, 0, music.minDelay() / 2);
}
if (!this.minecraft.getSoundManager().isActive(this.currentMusic)) {
this.currentMusic = null;
this.nextSongDelay = Math.min(this.nextSongDelay, this.gameMusicFrequency.getNextSongDelay(music, this.random));
}
}
this.nextSongDelay = Math.min(this.nextSongDelay, this.gameMusicFrequency.getNextSongDelay(music, this.random));
if (this.currentMusic == null && this.nextSongDelay-- <= 0) {
this.startPlaying(musicInfo);
}
}
}
public void startPlaying(MusicInfo music) {
SoundEvent soundEvent = music.music().event().value();
this.currentMusic = SimpleSoundInstance.forMusic(soundEvent, music.volume());
switch (this.minecraft.getSoundManager().play(this.currentMusic)) {
case STARTED:
this.minecraft.getToastManager().showNowPlayingToast();
this.toastShown = true;
break;
case STARTED_SILENTLY:
this.toastShown = false;
}
this.nextSongDelay = Integer.MAX_VALUE;
this.currentGain = music.volume();
}
public void showNowPlayingToastIfNeeded() {
if (!this.toastShown) {
this.minecraft.getToastManager().showNowPlayingToast();
this.toastShown = true;
}
}
/**
* Stops playing the specified {@linkplain Music} selector.
*
* @param music the {@linkplain Music} selector to stop playing
*/
public void stopPlaying(Music music) {
if (this.isPlayingMusic(music)) {
this.stopPlaying();
}
}
/**
* Stops playing the current {@linkplain Music} selector.
*/
public void stopPlaying() {
if (this.currentMusic != null) {
this.minecraft.getSoundManager().stop(this.currentMusic);
this.currentMusic = null;
this.minecraft.getToastManager().hideNowPlayingToast();
}
this.nextSongDelay += 100;
}
private boolean fadePlaying(float volume) {
if (this.currentMusic == null) {
return false;
} else if (this.currentGain == volume) {
return true;
} else {
if (this.currentGain < volume) {
this.currentGain = this.currentGain + Mth.clamp(this.currentGain, 5.0E-4F, 0.005F);
if (this.currentGain > volume) {
this.currentGain = volume;
}
} else {
this.currentGain = 0.03F * volume + 0.97F * this.currentGain;
if (Math.abs(this.currentGain - volume) < 1.0E-4F || this.currentGain < volume) {
this.currentGain = volume;
}
}
this.currentGain = Mth.clamp(this.currentGain, 0.0F, 1.0F);
if (this.currentGain <= 1.0E-4F) {
this.stopPlaying();
return false;
} else {
this.minecraft.getSoundManager().setVolume(this.currentMusic, this.currentGain);
return true;
}
}
}
/**
* {@return {@code true} if the {@linkplain Music} selector is currently playing, {@code false} otherwise}
*
* @param selector the {@linkplain Music} selector to check for
*/
public boolean isPlayingMusic(Music selector) {
return this.currentMusic == null ? false : selector.event().value().location().equals(this.currentMusic.getLocation());
}
@Nullable
public String getCurrentMusicTranslationKey() {
if (this.currentMusic != null) {
Sound sound = this.currentMusic.getSound();
if (sound != null) {
return sound.getLocation().toShortLanguageKey();
}
}
return null;
}
public void setMinutesBetweenSongs(MusicManager.MusicFrequency musicFrequency) {
this.gameMusicFrequency = musicFrequency;
this.nextSongDelay = this.gameMusicFrequency.getNextSongDelay(this.minecraft.getSituationalMusic().music(), this.random);
}
@Environment(EnvType.CLIENT)
public static enum MusicFrequency implements OptionEnum, StringRepresentable {
DEFAULT(20),
FREQUENT(10),
CONSTANT(0);
public static final Codec<MusicManager.MusicFrequency> CODEC = StringRepresentable.fromEnum(MusicManager.MusicFrequency::values);
private static final String KEY_PREPEND = "options.music_frequency.";
private final int id;
private final int maxFrequency;
private final String key;
private MusicFrequency(final int maxFrequency) {
this.id = maxFrequency;
this.maxFrequency = maxFrequency * 1200;
this.key = "options.music_frequency." + this.name().toLowerCase();
}
int getNextSongDelay(@Nullable Music music, RandomSource random) {
if (music == null) {
return this.maxFrequency;
} else if (this == CONSTANT) {
return 100;
} else {
int i = Math.min(music.minDelay(), this.maxFrequency);
int j = Math.min(music.maxDelay(), this.maxFrequency);
return Mth.nextInt(random, i, j);
}
}
@Override
public int getId() {
return this.id;
}
@Override
public String getKey() {
return this.key;
}
@Override
public String getSerializedName() {
return this.name();
}
}
}