From f4c822a0b582923ace21a66cd63d2f765a40f0eb Mon Sep 17 00:00:00 2001 From: wzp Date: Tue, 8 Aug 2023 16:21:08 +0800 Subject: [PATCH] feat: adding other function --- .../java/cn/wzpmc/DreamRunnerModClient.java | 29 +-- .../wzpmc/configuration/GuiConfiguration.java | 13 ++ .../java/cn/wzpmc/entities/MusicObject.java | 174 ------------------ .../java/cn/wzpmc/entities/SongObject.java | 121 ++++++++++++ .../cn/wzpmc/gui/NowSongDetailWeight.java | 33 ++-- .../java/cn/wzpmc/gui/OnWorldTickStart.java | 24 ++- .../java/cn/wzpmc/gui/SongDetailWeight.java | 25 +++ .../cn/wzpmc/mixin/SoundManagerMixin.java | 12 ++ .../java/cn/wzpmc/mixin/SoundSystemMixin.java | 17 ++ .../cn/wzpmc/sounds/MusicSoundInstance.java | 27 ++- .../java/cn/wzpmc/sounds/MyAudioStream.java | 29 ++- src/client/java/cn/wzpmc/utils/TimeUtils.java | 10 + src/client/resources/der.client.mixins.json | 2 + 13 files changed, 288 insertions(+), 228 deletions(-) create mode 100644 src/client/java/cn/wzpmc/configuration/GuiConfiguration.java delete mode 100644 src/client/java/cn/wzpmc/entities/MusicObject.java create mode 100644 src/client/java/cn/wzpmc/entities/SongObject.java create mode 100644 src/client/java/cn/wzpmc/gui/SongDetailWeight.java create mode 100644 src/client/java/cn/wzpmc/mixin/SoundManagerMixin.java create mode 100644 src/client/java/cn/wzpmc/mixin/SoundSystemMixin.java create mode 100644 src/client/java/cn/wzpmc/utils/TimeUtils.java diff --git a/src/client/java/cn/wzpmc/DreamRunnerModClient.java b/src/client/java/cn/wzpmc/DreamRunnerModClient.java index bea0703..3b30fa1 100644 --- a/src/client/java/cn/wzpmc/DreamRunnerModClient.java +++ b/src/client/java/cn/wzpmc/DreamRunnerModClient.java @@ -1,6 +1,6 @@ package cn.wzpmc; -import cn.wzpmc.entities.MusicObject; +import cn.wzpmc.entities.SongObject; import cn.wzpmc.gui.OnWorldTickStart; import com.google.gson.JsonArray; import com.google.gson.JsonElement; @@ -36,7 +36,7 @@ public class DreamRunnerModClient implements ClientModInitializer { public static final Identifier packageChannelId = Objects.requireNonNull(Identifier.of("der", "music_channel")); public static OnWorldTickStart worldTickStart = new OnWorldTickStart(); - public static List musics = new ArrayList<>(); + public static List musics = new ArrayList<>(); public boolean isLogin = false; public String cookies = ""; @@ -48,7 +48,6 @@ public class DreamRunnerModClient implements ClientModInitializer { //https://music.163.com/song?id=1369828361&userid=347345537 Long songId = Long.valueOf(e.getArgument("id", Integer.class)); ByteBuf buffer = Unpooled.buffer(); - buffer.writeLong(songId); HttpClient.Builder builder = HttpClient.newBuilder(); HttpClient build = builder.build(); HttpRequest request = HttpRequest.newBuilder(URI.create("http://192.168.1.2:1200/song/url/v1?id=" + songId + "&level=exhigh")).setHeader("Cookie", cookies).build(); @@ -79,6 +78,7 @@ public class DreamRunnerModClient implements ClientModInitializer { } JsonObject alObject = asJsonObject.get("al").getAsJsonObject(); String picURL = alObject.get("picUrl").getAsString(); + System.out.println(picURL); HttpRequest.Builder builder3 = HttpRequest.newBuilder(URI.create("http://192.168.1.2:1200/lyric?id=" + songId)).setHeader("Cookie", this.cookies); String lrc = JsonParser.parseString(build.send(builder3.build(), HttpResponse.BodyHandlers.ofString()).body()).getAsJsonObject().get("lrc").getAsJsonObject().get("lyric").getAsString(); byte[] bytes = lrc.getBytes(StandardCharsets.UTF_8); @@ -191,7 +191,6 @@ public class DreamRunnerModClient implements ClientModInitializer { } // Packet Struct: - // SongId // SongName Length // SongName // Artist List Length @@ -203,21 +202,22 @@ public class DreamRunnerModClient implements ClientModInitializer { // Song Length // Song Byte Array public void packetHandler(MinecraftClient client, ByteBuf buf) { - long songId = buf.readLong(); int duringTime = buf.readInt(); int songNameLength = buf.readInt(); byte[] data = new byte[songNameLength]; buf.readBytes(data); - List artistNames = new ArrayList<>(); String songName = new String(data, StandardCharsets.UTF_8); + StringBuilder artistsBuilder = new StringBuilder(); int artistListLength = buf.readInt(); for (int i = 0; i < artistListLength; i++) { int artistNameLength = buf.readInt(); data = new byte[artistNameLength]; buf.readBytes(data); String artistName = new String(data, StandardCharsets.UTF_8); - artistNames.add(artistName); + artistsBuilder.append(artistName).append(','); } + artistsBuilder.deleteCharAt(artistsBuilder.lastIndexOf(",")); + String artistNames = artistsBuilder.toString(); int lrcLength = buf.readInt(); data = new byte[lrcLength]; buf.readBytes(data); @@ -228,11 +228,16 @@ public class DreamRunnerModClient implements ClientModInitializer { int songByteArrayLength = buf.readInt(); byte[] songByteArray = new byte[songByteArrayLength]; buf.readBytes(songByteArray); - MusicObject musicObject = new MusicObject(songName, songId, artistNames, duringTime, picByteArray, lrc, songByteArray); - if (musics.isEmpty()) { - musicObject.play(); - + SongObject songObject; + try { + songObject = new SongObject(client, songName, artistNames, duringTime, picByteArray, songByteArray, lrc); + } catch (IOException e) { + DreamRunnerMod.LOGGER.error("读取图片时出现错误", e); + throw new RuntimeException(e); } - musics.add(musicObject); + if (musics.isEmpty()) { + songObject.playSong(); + } + musics.add(songObject); } } \ No newline at end of file diff --git a/src/client/java/cn/wzpmc/configuration/GuiConfiguration.java b/src/client/java/cn/wzpmc/configuration/GuiConfiguration.java new file mode 100644 index 0000000..90d5d17 --- /dev/null +++ b/src/client/java/cn/wzpmc/configuration/GuiConfiguration.java @@ -0,0 +1,13 @@ +package cn.wzpmc.configuration; + +public class GuiConfiguration { + public static final int coverWidth = 64; + public static final int coverHeight = 64; + public static final int baseX = 8; + public static final int baseY = 8; + public static final int backgroundColor = 0x32_FFFFFF; + public static final int listBackgroundColor = 0x32_FFFFFF; + public static final int textColor = 0xFF_FFFFFF; + public static final int progressBackgroundColor = 0x48_6754E6; + public static final int progressColor = 0x5F_6754E6; +} diff --git a/src/client/java/cn/wzpmc/entities/MusicObject.java b/src/client/java/cn/wzpmc/entities/MusicObject.java deleted file mode 100644 index 6852951..0000000 --- a/src/client/java/cn/wzpmc/entities/MusicObject.java +++ /dev/null @@ -1,174 +0,0 @@ -package cn.wzpmc.entities; - -import cn.wzpmc.DreamRunnerMod; -import cn.wzpmc.DreamRunnerModClient; -import cn.wzpmc.sounds.MusicSoundInstance; -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.network.ClientPlayerEntity; -import net.minecraft.client.sound.SoundInstance; -import net.minecraft.client.sound.SoundManager; -import net.minecraft.client.texture.NativeImage; -import net.minecraft.client.texture.NativeImageBackedTexture; -import net.minecraft.util.Identifier; - -import javax.imageio.ImageIO; -import java.awt.*; -import java.awt.image.BufferedImage; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.util.List; -import java.util.*; - -@Environment(EnvType.CLIENT) -public class MusicObject { - private final String name; - private final long songId; - private final int duringTime; - private final List artists; - private final Map lrc = new HashMap<>(); - private final List lrcTime = new ArrayList<>(); - private final SoundInstance soundInstance; - private byte[] picByteArray; - private NativeImage image; - private NativeImageBackedTexture backedTexture; - private Identifier imageId; - private long startT; - - public MusicObject(String name, long songId, List artists, int duringTime, byte[] picByteArray, String lrc, byte[] songByteArray) { - this.name = name; - this.songId = songId; - this.artists = artists; - this.duringTime = duringTime; - this.picByteArray = picByteArray; - this.startT = new Date().getTime(); - String[] lineSplit = lrc.split("\n"); - for (String fileLine : lineSplit) { - String[] timeLrcSplit = fileLine.split("]"); - String time = timeLrcSplit[0].replace("[", ""); - String[] msSplit = time.split("\\."); - String[] minSecSplit = msSplit[0].split(":"); - long min = Long.parseLong(minSecSplit[0]); - long sec = Long.parseLong(minSecSplit[1]); - long ms = Long.parseLong(msSplit[1]); - StringBuilder line = new StringBuilder(); - for (int i = 1; i < timeLrcSplit.length; i++) { - line.append(timeLrcSplit[i]); - } - long lrcTime = (min * 60 + sec) * 1000 + ms; - this.lrc.put(lrcTime, line.toString()); - this.lrcTime.add(lrcTime); - } - this.lrcTime.sort(Comparator.comparingLong(Long::longValue).reversed()); - // soundInstance - ClientPlayerEntity player = MinecraftClient.getInstance().player; - soundInstance = new MusicSoundInstance(player, new ByteArrayInputStream(songByteArray), this); - } - - public static String getTimeString(long duringTime) { - long secDuringTime = duringTime / 1000; - long sec = secDuringTime % 60; - long min = secDuringTime / 60; - return String.format("%d:%02d", min, sec); - } - - public int getDuringTime() { - return duringTime; - } - - public List getArtists() { - return artists; - } - - public String getDuring() { - return getTimeString(getDuringLong()); - } - - public long getDuringLong() { - long l = new Date().getTime() - startT; - if (l >= this.duringTime) { - DreamRunnerModClient.musics.remove(this); - if (!DreamRunnerModClient.musics.isEmpty()) { - MusicObject next = DreamRunnerModClient.musics.get(0); - next.play(); - } - return this.duringTime; - } - return l; - } - - public void start() { - this.startT = new Date().getTime(); - } - - public void play() { - SoundManager soundManager = MinecraftClient.getInstance().getSoundManager(); - soundManager.play(soundInstance); - } - - public NativeImage getImage() { - if (image == null) { - try { - BufferedImage originalImage = ImageIO.read(new ByteArrayInputStream(this.picByteArray)); - BufferedImage resizedImage = new BufferedImage(64, 64, BufferedImage.TYPE_INT_RGB); - Graphics2D g2d = resizedImage.createGraphics(); - g2d.drawImage(originalImage, 0, 0, 64, 64, null); - g2d.dispose(); - ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); - ImageIO.write(resizedImage, "png", byteArrayOutputStream); - byte[] byteArray = byteArrayOutputStream.toByteArray(); - this.picByteArray = byteArray; - this.image = NativeImage.read(new ByteArrayInputStream(byteArray)); - } catch (IOException e) { - DreamRunnerMod.LOGGER.error("读取图片失败!", e); - return null; - } - } - return image; - } - - public NativeImageBackedTexture getBackedTexture() { - if (backedTexture == null) { - backedTexture = new NativeImageBackedTexture(this.getImage()); - } - return backedTexture; - } - - public Identifier getImageId() { - if (this.imageId == null) { - this.imageId = MinecraftClient.getInstance().getTextureManager().registerDynamicTexture("der", this.getBackedTexture()); - } - return this.imageId; - } - - public String getLrc() { - long dt = this.getDuringLong(); - for (Long time : this.lrcTime) { - if (time <= dt) { - return this.lrc.get(time); - } - } - return ""; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - MusicObject that = (MusicObject) o; - return songId == that.songId && duringTime == that.duringTime && Arrays.equals(picByteArray, that.picByteArray) && Objects.equals(artists, that.artists); - } - - @Override - public int hashCode() { - int result = Objects.hash(songId, duringTime, artists); - result = 31 * result + Arrays.hashCode(picByteArray); - return result; - } - - public String getName() { - return name; - } -} diff --git a/src/client/java/cn/wzpmc/entities/SongObject.java b/src/client/java/cn/wzpmc/entities/SongObject.java new file mode 100644 index 0000000..4e9bbf7 --- /dev/null +++ b/src/client/java/cn/wzpmc/entities/SongObject.java @@ -0,0 +1,121 @@ +package cn.wzpmc.entities; + +import cn.wzpmc.configuration.GuiConfiguration; +import cn.wzpmc.mixin.SoundManagerMixin; +import cn.wzpmc.mixin.SoundSystemMixin; +import cn.wzpmc.sounds.MusicSoundInstance; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.sound.SoundManager; +import net.minecraft.client.texture.NativeImage; +import net.minecraft.client.texture.NativeImageBackedTexture; +import net.minecraft.util.Identifier; + +import javax.imageio.ImageIO; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.List; +import java.util.*; + +@Environment(EnvType.CLIENT) +public class SongObject { + private final String songName; + private final MusicSoundInstance soundInstance; + private final String artists; + private final Identifier imageId; + private final LinkedHashMap lyrics = new LinkedHashMap<>(); + private final long totalDuringTime; + private final MinecraftClient minecraftClient; + private long nowSongDuringTime; + private int startTick = -1; + + public SongObject(MinecraftClient minecraftClient, String songName, String artists, long totalDuringTime, byte[] picByteArray, byte[] songByteArray, String lyrics) throws IOException { + this.minecraftClient = minecraftClient; + this.songName = songName; + this.soundInstance = new MusicSoundInstance(minecraftClient.player, new ByteArrayInputStream(songByteArray), this); + this.artists = artists; + this.nowSongDuringTime = 0; + this.totalDuringTime = totalDuringTime; + // 图片处理部分 + BufferedImage originalImage = ImageIO.read(new ByteArrayInputStream(picByteArray)); + BufferedImage resizedImage = new BufferedImage(GuiConfiguration.coverWidth, GuiConfiguration.coverHeight, BufferedImage.TYPE_INT_RGB); + Graphics2D g2d = resizedImage.createGraphics(); + g2d.drawImage(originalImage, 0, 0, GuiConfiguration.coverWidth, GuiConfiguration.coverHeight, null); + g2d.dispose(); + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + ImageIO.write(resizedImage, "png", byteArrayOutputStream); + byte[] byteArray = byteArrayOutputStream.toByteArray(); + NativeImage image = NativeImage.read(new ByteArrayInputStream(byteArray)); + NativeImageBackedTexture backedTexture = new NativeImageBackedTexture(image); + this.imageId = minecraftClient.getTextureManager().registerDynamicTexture("der", backedTexture); + // 歌词处理部分 + String[] lineSplit = lyrics.split("\n"); + for (String fileLine : lineSplit) { + String[] timeLrcSplit = fileLine.split("]"); + String time = timeLrcSplit[0].replace("[", ""); + String[] msSplit = time.split("\\."); + String[] minSecSplit = msSplit[0].split(":"); + long min = Long.parseLong(minSecSplit[0]); + long sec = Long.parseLong(minSecSplit[1]); + long ms = Long.parseLong(msSplit[1]); + StringBuilder line = new StringBuilder(); + for (int i = 1; i < timeLrcSplit.length; i++) { + line.append(timeLrcSplit[i]); + } + long lrcTime = (min * 60 + sec) * 1000 + ms; + this.lyrics.put(lrcTime, line.toString()); + } + } + + public void playSong() { + SoundManager soundManager = this.minecraftClient.getSoundManager(); + soundManager.playNextTick(this.soundInstance); + } + + public Identifier getImageId() { + return this.imageId; + } + + public String getName() { + return this.songName; + } + + public String getArtists() { + return this.artists; + } + + public Long getNowDuringTime() { + SoundManagerMixin soundManager = (SoundManagerMixin) this.minecraftClient.getSoundManager(); + SoundSystemMixin soundSystem = (SoundSystemMixin) soundManager.getSoundSystem(); + if (startTick == -1 && soundSystem.getSoundEndTick().containsKey(this.soundInstance)) { + startTick = soundSystem.getTicks(); + } + int ticks = soundSystem.getTicks() - startTick; + this.nowSongDuringTime = ticks * 50L; + return this.nowSongDuringTime; + } + + public Long getTotalDuringTime() { + return this.totalDuringTime; + } + + public String getLrc() { + List> entries = new ArrayList<>(this.lyrics.entrySet()); + entries.sort(Map.Entry.comparingByKey(Comparator.reverseOrder())); + for (Map.Entry timeLyrics : entries) { + Long time = timeLyrics.getKey(); + if (time <= this.nowSongDuringTime) { + return timeLyrics.getValue(); + } + } + return ""; + } + + public boolean isDone() { + return this.nowSongDuringTime >= this.totalDuringTime; + } +} diff --git a/src/client/java/cn/wzpmc/gui/NowSongDetailWeight.java b/src/client/java/cn/wzpmc/gui/NowSongDetailWeight.java index e28e818..ac766a1 100644 --- a/src/client/java/cn/wzpmc/gui/NowSongDetailWeight.java +++ b/src/client/java/cn/wzpmc/gui/NowSongDetailWeight.java @@ -1,7 +1,9 @@ package cn.wzpmc.gui; import cn.wzpmc.DreamRunnerModClient; -import cn.wzpmc.entities.MusicObject; +import cn.wzpmc.configuration.GuiConfiguration; +import cn.wzpmc.entities.SongObject; +import cn.wzpmc.utils.TimeUtils; import io.github.cottonmc.cotton.gui.client.ScreenDrawing; import io.github.cottonmc.cotton.gui.widget.WWidget; import net.fabricmc.api.EnvType; @@ -26,31 +28,32 @@ public class NowSongDetailWeight extends WWidget { speed = 0d; } + private static final int textBaseX = GuiConfiguration.coverWidth + 2; @Override public void paint(DrawContext context, int x, int y, int mouseX, int mouseY) { if (!DreamRunnerModClient.musics.isEmpty()) { - MusicObject musicObject = DreamRunnerModClient.musics.get(0); + SongObject musicObject = DreamRunnerModClient.musics.get(0); if ((int) nowWidth != targetWidth) { nowWidth += speed; } speed = (targetWidth - nowWidth) / 60; - // 总宽度计算:文本最大宽度 + 70 - ScreenDrawing.coloredRect(context, x - 2, y - 2, (int) nowWidth, 68, 0x32_FFFFFF); - context.drawTexture(musicObject.getImageId(), x, y, 0, 0, 0, 64, 64, 64, 64); + ScreenDrawing.coloredRect(context, x - 2, y - 2, (int) nowWidth, GuiConfiguration.coverHeight + 4, GuiConfiguration.backgroundColor); + context.drawTexture(musicObject.getImageId(), x, y, 0, 0, 0, GuiConfiguration.coverWidth, GuiConfiguration.coverHeight, GuiConfiguration.coverWidth, GuiConfiguration.coverHeight); String musicName = musicObject.getName(); - String artistsName = String.join(", ", musicObject.getArtists()); + String artists = musicObject.getArtists(); int musicNameWidth = client.textRenderer.getWidth(musicName); - int artistsNameWidth = client.textRenderer.getWidth(artistsName); - context.drawText(client.textRenderer, musicName, 66 + x, y, 0xFFFFFF, false); - context.drawText(client.textRenderer, artistsName, 66 + x, client.textRenderer.fontHeight + y, 0xFFFFFF, false); - // 进度条长度100 - ScreenDrawing.coloredRect(context, 66 + x, client.textRenderer.fontHeight * 2 + y, (int) (((double) musicObject.getDuringLong() / musicObject.getDuringTime()) * 100), client.textRenderer.fontHeight + 4, 0x5F_6754E6); - ScreenDrawing.coloredRect(context, 66 + x, client.textRenderer.fontHeight * 2 + y, 100, client.textRenderer.fontHeight + 4, 0x48_6754E6); - context.drawText(client.textRenderer, musicObject.getDuring() + " / " + MusicObject.getTimeString(musicObject.getDuringTime()), 68 + x, client.textRenderer.fontHeight * 2 + 2 + y, 0xFFFFFF, false); + int artistsNameWidth = client.textRenderer.getWidth(artists); + Long nowDuringTime = musicObject.getNowDuringTime(); + Long totalDuringTime = musicObject.getTotalDuringTime(); + context.drawText(client.textRenderer, musicName, textBaseX + x, y, GuiConfiguration.textColor, false); + context.drawText(client.textRenderer, artists, textBaseX + x, client.textRenderer.fontHeight + y, GuiConfiguration.textColor, false); + ScreenDrawing.coloredRect(context, GuiConfiguration.coverWidth + 2 + x, client.textRenderer.fontHeight * 2 + y, (int) (((double) nowDuringTime / totalDuringTime) * 100), client.textRenderer.fontHeight + 4, GuiConfiguration.progressColor); + ScreenDrawing.coloredRect(context, GuiConfiguration.coverWidth + 2 + x, client.textRenderer.fontHeight * 2 + y, 100, client.textRenderer.fontHeight + 4, GuiConfiguration.progressBackgroundColor); + context.drawText(client.textRenderer, TimeUtils.getTimeString(nowDuringTime) + " / " + TimeUtils.getTimeString(totalDuringTime), GuiConfiguration.coverWidth + 4 + x, client.textRenderer.fontHeight * 2 + 2 + y, GuiConfiguration.textColor, false); String lrc = musicObject.getLrc(); - context.drawText(client.textRenderer, lrc, 66 + x, client.textRenderer.fontHeight * 3 + 4 + y, 0xFFFFFF, false); + context.drawText(client.textRenderer, lrc, textBaseX + x, client.textRenderer.fontHeight * 3 + 4 + y, GuiConfiguration.textColor, false); int lrcWidth = client.textRenderer.getWidth(lrc); - targetWidth = Math.max(Math.max(Math.max(musicNameWidth, artistsNameWidth), lrcWidth), 100) + 70; + targetWidth = Math.max(Math.max(Math.max(musicNameWidth, artistsNameWidth), lrcWidth), 100) + GuiConfiguration.coverWidth + 6; } else { reset(); } diff --git a/src/client/java/cn/wzpmc/gui/OnWorldTickStart.java b/src/client/java/cn/wzpmc/gui/OnWorldTickStart.java index f3d447f..c4063c7 100644 --- a/src/client/java/cn/wzpmc/gui/OnWorldTickStart.java +++ b/src/client/java/cn/wzpmc/gui/OnWorldTickStart.java @@ -1,13 +1,19 @@ package cn.wzpmc.gui; +import cn.wzpmc.DreamRunnerModClient; +import cn.wzpmc.configuration.GuiConfiguration; +import cn.wzpmc.entities.SongObject; import io.github.cottonmc.cotton.gui.client.CottonHud; import io.github.cottonmc.cotton.gui.widget.WWidget; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; import net.minecraft.client.MinecraftClient; import java.util.ArrayList; import java.util.List; +@Environment(EnvType.CLIENT) public class OnWorldTickStart implements ClientTickEvents.StartTick { private final List songList = new ArrayList<>(); private WWidget nowSongDetail; @@ -17,8 +23,21 @@ public class OnWorldTickStart implements ClientTickEvents.StartTick { if (nowSongDetail != null) { CottonHud.remove(nowSongDetail); } + for (WWidget wWidget : songList) { + CottonHud.remove(wWidget); + } + songList.clear(); + int i = 0; + for (SongObject music : DreamRunnerModClient.musics) { + if (i != 0) { + SongDetailWeight songDetailWeight = new SongDetailWeight(music); + CottonHud.add(songDetailWeight, GuiConfiguration.baseX, (i * 2) + ((i - 1) * (MinecraftClient.getInstance().textRenderer.fontHeight + 4)) + GuiConfiguration.coverHeight + GuiConfiguration.baseY + 4); + songList.add(songDetailWeight); + } + i++; + } nowSongDetail = new NowSongDetailWeight(); - CottonHud.add(nowSongDetail, 10, 10); + CottonHud.add(nowSongDetail, GuiConfiguration.baseX + 2, GuiConfiguration.baseY + 2); } public void remove() { @@ -26,6 +45,9 @@ public class OnWorldTickStart implements ClientTickEvents.StartTick { CottonHud.remove(nowSongDetail); } nowSongDetail = null; + for (WWidget wWidget : songList) { + CottonHud.remove(wWidget); + } songList.clear(); } } diff --git a/src/client/java/cn/wzpmc/gui/SongDetailWeight.java b/src/client/java/cn/wzpmc/gui/SongDetailWeight.java new file mode 100644 index 0000000..05699b6 --- /dev/null +++ b/src/client/java/cn/wzpmc/gui/SongDetailWeight.java @@ -0,0 +1,25 @@ +package cn.wzpmc.gui; + +import cn.wzpmc.configuration.GuiConfiguration; +import cn.wzpmc.entities.SongObject; +import io.github.cottonmc.cotton.gui.client.ScreenDrawing; +import io.github.cottonmc.cotton.gui.widget.WWidget; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.DrawContext; + +public class SongDetailWeight extends WWidget { + private final SongObject songObject; + private final MinecraftClient minecraftClient; + + public SongDetailWeight(SongObject songObject) { + this.songObject = songObject; + this.minecraftClient = MinecraftClient.getInstance(); + } + + @Override + public void paint(DrawContext context, int x, int y, int mouseX, int mouseY) { + int fontHeight = this.minecraftClient.textRenderer.fontHeight; + ScreenDrawing.coloredRect(context, x, y, GuiConfiguration.coverWidth + 106, fontHeight + 4, GuiConfiguration.listBackgroundColor); + context.drawText(this.minecraftClient.textRenderer, this.songObject.getName() + '-' + this.songObject.getArtists(), x + 2, y + 2, GuiConfiguration.textColor, false); + } +} diff --git a/src/client/java/cn/wzpmc/mixin/SoundManagerMixin.java b/src/client/java/cn/wzpmc/mixin/SoundManagerMixin.java new file mode 100644 index 0000000..8326c8f --- /dev/null +++ b/src/client/java/cn/wzpmc/mixin/SoundManagerMixin.java @@ -0,0 +1,12 @@ +package cn.wzpmc.mixin; + +import net.minecraft.client.sound.SoundManager; +import net.minecraft.client.sound.SoundSystem; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(SoundManager.class) +public interface SoundManagerMixin { + @Accessor("soundSystem") + SoundSystem getSoundSystem(); +} diff --git a/src/client/java/cn/wzpmc/mixin/SoundSystemMixin.java b/src/client/java/cn/wzpmc/mixin/SoundSystemMixin.java new file mode 100644 index 0000000..b59576b --- /dev/null +++ b/src/client/java/cn/wzpmc/mixin/SoundSystemMixin.java @@ -0,0 +1,17 @@ +package cn.wzpmc.mixin; + +import net.minecraft.client.sound.SoundInstance; +import net.minecraft.client.sound.SoundSystem; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.Map; + +@Mixin(SoundSystem.class) +public interface SoundSystemMixin { + @Accessor("soundEndTicks") + Map getSoundEndTick(); + + @Accessor("ticks") + int getTicks(); +} diff --git a/src/client/java/cn/wzpmc/sounds/MusicSoundInstance.java b/src/client/java/cn/wzpmc/sounds/MusicSoundInstance.java index fa3622e..9844aa5 100644 --- a/src/client/java/cn/wzpmc/sounds/MusicSoundInstance.java +++ b/src/client/java/cn/wzpmc/sounds/MusicSoundInstance.java @@ -1,15 +1,12 @@ package cn.wzpmc.sounds; import cn.wzpmc.DreamRunnerMod; -import cn.wzpmc.entities.MusicObject; +import cn.wzpmc.entities.SongObject; import javazoom.jl.decoder.BitstreamException; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.client.network.ClientPlayerEntity; -import net.minecraft.client.sound.AbstractSoundInstance; -import net.minecraft.client.sound.AudioStream; -import net.minecraft.client.sound.SoundInstance; -import net.minecraft.client.sound.SoundLoader; +import net.minecraft.client.sound.*; import net.minecraft.sound.SoundCategory; import net.minecraft.util.Identifier; @@ -18,16 +15,16 @@ import java.io.InputStream; import java.util.concurrent.CompletableFuture; @Environment(EnvType.CLIENT) -public class MusicSoundInstance extends AbstractSoundInstance { +public class MusicSoundInstance extends AbstractSoundInstance implements TickableSoundInstance { private final ClientPlayerEntity entity; private final InputStream inputStream; - private final MusicObject object; + private final SongObject songObject; - public MusicSoundInstance(ClientPlayerEntity entity, InputStream inputStream, MusicObject object) { + public MusicSoundInstance(ClientPlayerEntity entity, InputStream inputStream, SongObject songObject) { super(new Identifier("der", "music"), SoundCategory.RECORDS, SoundInstance.createRandom()); this.entity = entity; this.inputStream = inputStream; - this.object = object; + this.songObject = songObject; } @Override @@ -51,10 +48,20 @@ public class MusicSoundInstance extends AbstractSoundInstance { @Override public CompletableFuture getAudioStream(SoundLoader loader, Identifier id, boolean repeatInstantly) { try { - return CompletableFuture.completedFuture(new MyAudioStream(inputStream, this.object)); + MyAudioStream myAudioStream = new MyAudioStream(inputStream, songObject); + return CompletableFuture.completedFuture(myAudioStream); } catch (IOException | BitstreamException e) { DreamRunnerMod.LOGGER.error("读取音频文件出错!", e); } return null; } + + @Override + public boolean isDone() { + return this.songObject.isDone(); + } + + @Override + public void tick() { + } } diff --git a/src/client/java/cn/wzpmc/sounds/MyAudioStream.java b/src/client/java/cn/wzpmc/sounds/MyAudioStream.java index 8309a4b..fdcb20f 100644 --- a/src/client/java/cn/wzpmc/sounds/MyAudioStream.java +++ b/src/client/java/cn/wzpmc/sounds/MyAudioStream.java @@ -1,7 +1,8 @@ package cn.wzpmc.sounds; import cn.wzpmc.DreamRunnerMod; -import cn.wzpmc.entities.MusicObject; +import cn.wzpmc.DreamRunnerModClient; +import cn.wzpmc.entities.SongObject; import javazoom.jl.decoder.*; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; @@ -18,14 +19,12 @@ public class MyAudioStream implements AudioStream { private final Bitstream bitstream; private final Decoder decoder; private final AudioFormat format; - private final MusicObject object; - private boolean isStart; + private final SongObject songObject; - public MyAudioStream(InputStream inputStream, MusicObject object) throws BitstreamException, IOException { + public MyAudioStream(InputStream inputStream, SongObject songObject) throws BitstreamException, IOException { this.bitstream = new Bitstream(inputStream); this.decoder = new Decoder(); - this.object = object; - this.isStart = false; + this.songObject = songObject; Header header = bitstream.readFrame(); if (header == null) { throw new IOException("读取MP3文件头失败!"); @@ -33,7 +32,6 @@ public class MyAudioStream implements AudioStream { this.format = new AudioFormat(header.frequency(), 16, header.mode() == Header.SINGLE_CHANNEL ? 1 : 2, true, false); bitstream.unreadFrame(); } - @Override public void close() { try { @@ -41,8 +39,13 @@ public class MyAudioStream implements AudioStream { } catch (Exception e) { DreamRunnerMod.LOGGER.error("关闭资源失败!", e); } + DreamRunnerModClient.musics.remove(this.songObject); + if (DreamRunnerModClient.musics.isEmpty()) { + return; + } + SongObject nextSong = DreamRunnerModClient.musics.get(0); + nextSong.playSong(); } - @Override public AudioFormat getFormat() { return this.format; @@ -50,10 +53,6 @@ public class MyAudioStream implements AudioStream { @Override public ByteBuffer getBuffer(int size) { - if (!this.isStart) { - this.object.start(); - isStart = true; - } ByteBuffer buffer = ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder()); try { while (buffer.hasRemaining()) { @@ -61,17 +60,15 @@ public class MyAudioStream implements AudioStream { if (frameHeader == null) { break; } - SampleBuffer output = (SampleBuffer) decoder.decodeFrame(frameHeader, bitstream); - + int bufferLength = output.getBufferLength(); short[] pcm = output.getBuffer(); - for (int i = 0; i < output.getBufferLength(); i++) { + for (int i = 0; i < bufferLength; i++) { if (!buffer.hasRemaining()) { break; } buffer.putShort(pcm[i]); } - bitstream.closeFrame(); } } catch (Exception e) { diff --git a/src/client/java/cn/wzpmc/utils/TimeUtils.java b/src/client/java/cn/wzpmc/utils/TimeUtils.java new file mode 100644 index 0000000..c256a59 --- /dev/null +++ b/src/client/java/cn/wzpmc/utils/TimeUtils.java @@ -0,0 +1,10 @@ +package cn.wzpmc.utils; + +public class TimeUtils { + public static String getTimeString(Long time) { + long secDuringTime = time / 1000; + long sec = secDuringTime % 60; + long min = secDuringTime / 60; + return String.format("%d:%02d", min, sec); + } +} diff --git a/src/client/resources/der.client.mixins.json b/src/client/resources/der.client.mixins.json index 6198c86..768caf1 100644 --- a/src/client/resources/der.client.mixins.json +++ b/src/client/resources/der.client.mixins.json @@ -3,6 +3,8 @@ "package": "cn.wzpmc.mixin", "compatibilityLevel": "JAVA_17", "client": [ + "SoundManagerMixin", + "SoundSystemMixin" ], "injectors": { "defaultRequire": 1