|
18 | 18 | package net.raphimc.noteblocklib.format.nbs; |
19 | 19 |
|
20 | 20 | import net.raphimc.noteblocklib.format.mcsp2.model.McSp2Song; |
| 21 | +import net.raphimc.noteblocklib.format.midi.MidiDefinitions; |
21 | 22 | import net.raphimc.noteblocklib.format.minecraft.MinecraftInstrument; |
22 | 23 | import net.raphimc.noteblocklib.format.nbs.model.NbsCustomInstrument; |
23 | 24 | import net.raphimc.noteblocklib.format.nbs.model.NbsLayer; |
|
32 | 33 | import net.raphimc.noteblocklib.model.song.Song; |
33 | 34 | import net.raphimc.noteblocklib.util.MathUtil; |
34 | 35 |
|
| 36 | +import java.util.IdentityHashMap; |
35 | 37 | import java.util.List; |
| 38 | +import java.util.Locale; |
| 39 | +import java.util.Map; |
36 | 40 | import java.util.stream.Collectors; |
37 | 41 |
|
| 42 | +import static net.raphimc.noteblocklib.format.nbs.NbsDefinitions.*; |
| 43 | + |
38 | 44 | public class NbsConverter { |
39 | 45 |
|
| 46 | + /** |
| 47 | + * Fills the general data of the given song from the NBS specific data. |
| 48 | + * |
| 49 | + * @param song The song |
| 50 | + */ |
| 51 | + public static void fillGeneralData(final NbsSong song) { |
| 52 | + song.getTempoEvents().set(0, song.getTempo() / 100F); |
| 53 | + final Map<NbsCustomInstrument, NbsCustomInstrument> customInstrumentMap = new IdentityHashMap<>(song.getCustomInstruments().size()); // Cache map to avoid creating new instances for each note |
| 54 | + for (NbsCustomInstrument customInstrument : song.getCustomInstruments()) { |
| 55 | + customInstrumentMap.put(customInstrument, customInstrument.copy().setPitch(F_SHARP_4_KEY)); |
| 56 | + } |
| 57 | + |
| 58 | + final Map<Integer, NbsLayer> layers = song.getLayers(); |
| 59 | + final boolean hasSoloLayers = layers.values().stream().anyMatch(layer -> layer.getStatus() == NbsLayer.Status.SOLO); |
| 60 | + for (Map.Entry<Integer, NbsLayer> entry : layers.entrySet()) { |
| 61 | + final NbsLayer layer = entry.getValue(); |
| 62 | + for (Map.Entry<Integer, NbsNote> noteEntry : layer.getNotes().entrySet()) { |
| 63 | + final NbsNote nbsNote = noteEntry.getValue(); |
| 64 | + |
| 65 | + final Note note = new Note(); |
| 66 | + note.setGroupId(entry.getKey()); |
| 67 | + final float effectiveKey = (float) (MathUtil.clamp(nbsNote.getKey(), LOWEST_KEY, HIGHEST_KEY) * PITCHES_PER_KEY + nbsNote.getPitch()) / PITCHES_PER_KEY; |
| 68 | + note.setMidiKey(MathUtil.clamp(LOWEST_MIDI_KEY + effectiveKey, MidiDefinitions.LOWEST_KEY, MidiDefinitions.HIGHEST_KEY)); |
| 69 | + |
| 70 | + if (nbsNote.getInstrument() < song.getVanillaInstrumentCount()) { |
| 71 | + note.setInstrument(MinecraftInstrument.fromNbsId(nbsNote.getInstrument())); |
| 72 | + } else { |
| 73 | + final NbsCustomInstrument nbsCustomInstrument = song.getCustomInstruments().get(nbsNote.getInstrument() - song.getVanillaInstrumentCount()); |
| 74 | + if (song.getVersion() >= 4) { |
| 75 | + if (TEMPO_CHANGER_CUSTOM_INSTRUMENT_NAME.equals(nbsCustomInstrument.getName())) { |
| 76 | + song.getTempoEvents().set(noteEntry.getKey(), Math.abs(nbsNote.getPitch() / 15F)); |
| 77 | + continue; |
| 78 | + } |
| 79 | + if (TOGGLE_RAINBOW_CUSTOM_INSTRUMENT_NAME.equals(nbsCustomInstrument.getName())) { |
| 80 | + song.getEvents().add(noteEntry.getKey(), NbsToggleRainbowEvent.INSTANCE); |
| 81 | + continue; |
| 82 | + } |
| 83 | + } |
| 84 | + if (song.getVersion() >= 5) { |
| 85 | + if (SOUND_STOPPER_CUSTOM_INSTRUMENT_NAME.equals(nbsCustomInstrument.getName())) { |
| 86 | + final short startLayer = (short) Math.max(nbsNote.getPitch(), 0); |
| 87 | + final short endLayer = (short) Math.max((short) (((nbsNote.getPanning() + 156) % 256) + ((nbsNote.getVelocity() + 156) % 256) * 256), startLayer); |
| 88 | + song.getEvents().add(noteEntry.getKey(), new NbsSoundStopperEvent(startLayer, endLayer)); |
| 89 | + continue; |
| 90 | + } |
| 91 | + if (SHOW_SAVE_POPUP_CUSTOM_INSTRUMENT_NAME.equals(nbsCustomInstrument.getName())) { |
| 92 | + song.getEvents().add(noteEntry.getKey(), NbsShowSavePopupEvent.INSTANCE); |
| 93 | + continue; |
| 94 | + } |
| 95 | + if (nbsCustomInstrument.getNameOr("").toLowerCase(Locale.ROOT).contains(CHANGE_COLOR_CUSTOM_INSTRUMENT_NAME.toLowerCase(Locale.ROOT))) { |
| 96 | + continue; |
| 97 | + } |
| 98 | + if (TOGGLE_BACKGROUND_ACCENT_CUSTOM_INSTRUMENT_NAME.equals(nbsCustomInstrument.getName())) { |
| 99 | + song.getEvents().add(noteEntry.getKey(), NbsToggleBackgroundAccentEvent.INSTANCE); |
| 100 | + continue; |
| 101 | + } |
| 102 | + } |
| 103 | + |
| 104 | + final int pitchModifier = nbsCustomInstrument.getPitch() - F_SHARP_4_KEY; |
| 105 | + if (pitchModifier != 0) { // Pre-apply pitch modifier to note to make it easier for player implementations |
| 106 | + note.setNbsKey(note.getNbsKey() + pitchModifier); |
| 107 | + note.setInstrument(customInstrumentMap.get(nbsCustomInstrument)); // Use custom instrument with no pitch modifier, because the pitch modifier is already applied to the note |
| 108 | + } else { |
| 109 | + note.setInstrument(nbsCustomInstrument); |
| 110 | + } |
| 111 | + } |
| 112 | + |
| 113 | + note.setVolume(MathUtil.clamp(Math.min(layer.getVolume() / 100F, 1F) * (nbsNote.getVelocity() / 100F), 0F, 1F)); |
| 114 | + if (layer.getPanning() == CENTER_PANNING) { // Special case |
| 115 | + note.setPanning(MathUtil.clamp((nbsNote.getPanning() - CENTER_PANNING) / 100F, -1F, 1F)); |
| 116 | + } else { |
| 117 | + note.setPanning(MathUtil.clamp(((layer.getPanning() - CENTER_PANNING) + (nbsNote.getPanning() - CENTER_PANNING)) / 200F, -1F, 1F)); |
| 118 | + } |
| 119 | + |
| 120 | + if (layer.getStatus() == NbsLayer.Status.LOCKED) { // Locked layers are muted |
| 121 | + note.setVolume(0F); |
| 122 | + } else if (hasSoloLayers && layer.getStatus() != NbsLayer.Status.SOLO) { // Non-solo layers are muted if there are solo layers |
| 123 | + note.setVolume(0F); |
| 124 | + } |
| 125 | + |
| 126 | + song.getNotes().add(noteEntry.getKey(), note); |
| 127 | + } |
| 128 | + } |
| 129 | + } |
| 130 | + |
40 | 131 | /** |
41 | 132 | * Creates a new NBS song from the general data of the given song (Also copies some format specific fields if applicable). |
42 | 133 | * |
|
0 commit comments