3#include "absl/status/status.h"
4#include "absl/status/statusor.h"
16#include "absl/strings/str_format.h"
17#include "nlohmann/json.hpp"
91 if (metadata.bank == bank) {
108 return static_cast<uint8_t
>((pc_offset >> 15) & 0xFF);
112 uint16_t addr =
static_cast<uint16_t
>(pc_offset & 0x7FFF);
113 return static_cast<uint8_t
>((addr >> 8) & 0x7F);
117 return static_cast<uint8_t
>(pc_offset & 0xFF);
122 uint32_t pc_offset) {
123 uint8_t preserved_mid = 0;
126 preserved_mid = mid_read.value() & 0x80;
144 uint32_t pc_offset) {
151 return absl::OkStatus();
159 "Noise",
"Rain",
"Timpani",
"Square wave",
"Saw wave",
160 "Clink",
"Wobbly lead",
"Compound saw",
"Tweet",
"Strings A",
161 "Strings B",
"Trombone",
"Cymbal",
"Ocarina",
"Chimes",
162 "Harp",
"Splash",
"Trumpet",
"Horn",
"Snare A",
163 "Snare B",
"Choir",
"Flute",
"Oof",
"Piano"};
173 return absl::FailedPreconditionError(
"ROM is not loaded");
183 std::vector<MusicSong> custom_songs;
184 custom_songs.reserve(8);
211 for (
auto& song : custom_songs) {
212 songs_.push_back(std::move(song));
226 return absl::OkStatus();
231 return absl::FailedPreconditionError(
"No music data loaded");
235 return absl::FailedPreconditionError(
"ROM is not loaded");
240 return absl::ResourceExhaustedError(
241 "Songs do not fit in ROM banks. Reduce song size or remove songs.");
272 return absl::OkStatus();
276 if (index < 0 || index >=
static_cast<int>(
songs_.size())) {
283 if (index < 0 || index >=
static_cast<int>(
songs_.size())) {
291 if (song_id <= 0 || song_id >
static_cast<int>(
songs_.size())) {
294 return &
songs_[song_id - 1];
300 song.
bank =
static_cast<uint8_t
>(bank);
305 for (
auto& track : segment.
tracks) {
306 track.is_empty =
true;
309 song.
segments.push_back(std::move(segment));
311 songs_.push_back(std::move(song));
312 return static_cast<int>(
songs_.size()) - 1;
321 new_song.
name +=
" (Copy)";
324 songs_.push_back(std::move(new_song));
325 return static_cast<int>(
songs_.size()) - 1;
329 return index >= 0 && index < kVanillaSongCount;
333 if (index < 0 || index >=
static_cast<int>(
songs_.size())) {
334 return absl::InvalidArgumentError(
"Invalid song index");
338 return absl::InvalidArgumentError(
"Cannot delete vanilla songs");
342 return absl::OkStatus();
346 std::vector<MusicSong*> result;
347 for (
auto& song :
songs_) {
348 if (
static_cast<Bank>(song.bank) == bank) {
349 result.push_back(&song);
356 if (index < 0 || index >=
static_cast<int>(
instruments_.size())) {
363 if (index < 0 || index >=
static_cast<int>(
instruments_.size())) {
385 if (index < 0 || index >=
static_cast<int>(
samples_.size())) {
392 if (index < 0 || index >=
static_cast<int>(
samples_.size())) {
399 const std::string&
name) {
407 for (
int i = 0; i < 1000; ++i) {
408 sample.
pcm_data[i] =
static_cast<int16_t
>(32040.0 * std::sin(i * 0.1));
411 samples_.push_back(std::move(sample));
414 return static_cast<int>(
samples_.size()) - 1;
422 for (
const auto& song :
songs_) {
423 if (
static_cast<Bank>(song.bank) == bank) {
495 for (
const auto& song :
songs_) {
506 for (
auto& song :
songs_) {
507 song.modified =
false;
523 return absl::OkStatus();
527 if (!opcode_result.ok()) {
528 return absl::OkStatus();
532 return absl::OkStatus();
540 if (!addr_low.ok() || !addr_mid.ok() || !addr_bank.ok()) {
541 return absl::OkStatus();
545 uint32_t jsl_target =
static_cast<uint32_t
>(addr_low.value()) |
546 (
static_cast<uint32_t
>(addr_mid.value()) << 8) |
547 (
static_cast<uint32_t
>(addr_bank.value()) << 16);
551 uint8_t target_bank = (jsl_target >> 16) & 0xFF;
552 if (target_bank > 0x3F && target_bank < 0x80) {
553 return absl::OkStatus();
566 return absl::OkStatus();
570 Rom& rom, std::vector<MusicSong>* custom_songs) {
572 return absl::OkStatus();
580 if (expanded_rom_offset + 4 >= rom.
size()) {
581 return absl::OkStatus();
585 if (!header_result.ok()) {
586 return absl::OkStatus();
589 const auto& header = header_result.value();
590 uint16_t block_size =
static_cast<uint16_t
>(header[0]) |
591 (
static_cast<uint16_t
>(header[1]) << 8);
592 uint16_t aram_dest =
static_cast<uint16_t
>(header[2]) |
593 (
static_cast<uint16_t
>(header[3]) << 8);
598 return absl::OkStatus();
602 const uint8_t expanded_spc_bank = 4;
606 const int max_songs =
611 if (!pointer_result.ok()) {
613 return absl::OkStatus();
616 std::vector<uint16_t> song_addresses = std::move(pointer_result.value());
619 int expanded_index = 0;
620 for (
const uint16_t spc_address : song_addresses) {
621 if (spc_address == 0)
627 if (parsed_song.ok()) {
628 song = std::move(parsed_song.value());
632 for (
auto& track : segment.
tracks) {
633 track.is_empty =
true;
636 song.
segments.push_back(std::move(segment));
639 song.
name = absl::StrFormat(
"Expanded Song %d", ++expanded_index);
644 custom_songs->push_back(std::move(song));
646 songs_.push_back(std::move(song));
655 return absl::OkStatus();
659 if (index < 0 || index >=
static_cast<int>(
songs_.size())) {
662 const auto& song =
songs_[index];
668 std::vector<MusicSong>* custom_songs) {
670 const size_t vanilla_slots =
static_cast<size_t>(range.
Count());
678 if (!pointer_result.ok()) {
680 pointer_result.status().code(),
681 absl::StrFormat(
"Failed to read song table for bank %d: %s",
682 static_cast<int>(bank),
683 pointer_result.status().message()));
686 std::vector<uint16_t> song_addresses = std::move(pointer_result.value());
687 if (song_addresses.empty()) {
688 return absl::InvalidArgumentError(absl::StrFormat(
689 "Song table for bank %d is empty",
static_cast<int>(bank)));
692 auto make_empty_song = []() ->
MusicSong {
695 for (
auto& track : segment.
tracks) {
696 track.is_empty =
true;
697 track.events.clear();
700 song.
segments.push_back(std::move(segment));
704 auto emit_song = [&](
MusicSong&& song,
bool is_custom) {
706 if (is_custom && custom_songs) {
707 custom_songs->push_back(std::move(song));
709 songs_.push_back(std::move(song));
713 auto parse_and_emit = [&](uint16_t spc_address,
714 const std::string& display_name,
715 bool is_custom) -> absl::Status {
717 if (spc_address == 0) {
718 song = make_empty_song();
722 if (!parsed_song.ok()) {
724 parsed_song.status().code(),
726 "Failed to parse song '%s' at $%04X (SPC bank %d): %s",
727 display_name, spc_address, spc_bank,
728 parsed_song.status().message()));
730 song = std::move(parsed_song.value());
733 song.
name = display_name;
734 song.
bank =
static_cast<uint8_t
>(bank);
735 emit_song(std::move(song), is_custom);
736 return absl::OkStatus();
746 for (
size_t i = 0; i < vanilla_slots; ++i) {
747 const uint16_t spc_address =
748 (i < song_addresses.size()) ? song_addresses[i] : 0;
749 const int song_id = range.
start_id +
static_cast<int>(i);
750 const std::string display_name =
751 (song_id > 0 && song_id <= kVanillaSongCount)
753 : absl::StrFormat(
"Vanilla Song %d", song_id);
755 parse_and_emit(spc_address, display_name,
false);
762 int custom_counter = 1;
763 for (
size_t table_index = vanilla_slots; table_index < song_addresses.size();
765 const uint16_t spc_address = song_addresses[table_index];
767 if (spc_address == 0)
770 const std::string display_name =
771 absl::StrFormat(
"Custom Song %d", custom_counter++);
773 auto status = parse_and_emit(spc_address, display_name,
true);
778 const int total_songs =
static_cast<int>(song_addresses.size());
791 return absl::OkStatus();
796 if (songs_in_bank.empty()) {
797 return absl::OkStatus();
800 const uint16_t pointer_entry_count =
801 static_cast<uint16_t
>(songs_in_bank.size());
802 const uint16_t pointer_table_size =
803 static_cast<uint16_t
>((pointer_entry_count + 1) * 2);
806 uint32_t current_spc_address =
807 static_cast<uint32_t
>(bank_base) + pointer_table_size;
808 const uint32_t bank_limit =
811 std::vector<uint8_t> payload;
812 payload.resize(pointer_table_size, 0);
814 auto write_pointer_entry = [&](
size_t index, uint16_t address) {
815 payload[index * 2] = address & 0xFF;
816 payload[index * 2 + 1] =
static_cast<uint8_t
>((address >> 8) & 0xFF);
819 size_t pointer_index = 0;
821 for (
auto* song : songs_in_bank) {
823 if (!serialized_or.ok()) {
824 return serialized_or.status();
826 auto serialized = std::move(serialized_or.value());
828 const uint32_t song_size = serialized.data.size();
829 if (current_spc_address + song_size > bank_limit) {
830 return absl::ResourceExhaustedError(absl::StrFormat(
831 "Bank %d overflow (%u bytes needed, limit %u)",
832 static_cast<int>(bank), current_spc_address + song_size - bank_base,
836 const uint16_t song_base =
static_cast<uint16_t
>(current_spc_address);
839 write_pointer_entry(pointer_index++, song_base);
840 payload.insert(payload.end(), serialized.data.begin(),
841 serialized.data.end());
843 song->rom_address = song_base;
844 song->modified =
false;
845 current_spc_address += song_size;
848 write_pointer_entry(pointer_index, 0);
851 return absl::ResourceExhaustedError(absl::StrFormat(
852 "Bank %d payload size %zu exceeds limit %d",
static_cast<int>(bank),
856 const uint16_t block_size =
static_cast<uint16_t
>(payload.size());
857 std::vector<uint8_t> block_data;
858 block_data.reserve(
static_cast<size_t>(block_size) + 8);
859 block_data.push_back(block_size & 0xFF);
860 block_data.push_back(
static_cast<uint8_t
>((block_size >> 8) & 0xFF));
861 block_data.push_back(bank_base & 0xFF);
862 block_data.push_back(
static_cast<uint8_t
>((bank_base >> 8) & 0xFF));
863 block_data.insert(block_data.end(), payload.begin(), payload.end());
864 block_data.push_back(0x00);
865 block_data.push_back(0x00);
866 block_data.push_back(0x00);
867 block_data.push_back(0x00);
870 if (rom_offset + block_data.size() > rom.
size()) {
871 return absl::OutOfRangeError(absl::StrFormat(
872 "Bank %d ROM write exceeds image size (offset=%u, "
873 "size=%zu, rom_size=%zu)",
874 static_cast<int>(bank), rom_offset, block_data.size(), rom.
size()));
878 rom.
WriteVector(
static_cast<int>(rom_offset), std::move(block_data));
882 status = UpdateDynamicBankPointer(rom, bank, rom_offset);
886 return absl::OkStatus();
892 const uint32_t rom_offset =
894 if (rom_offset == 0) {
895 return absl::InvalidArgumentError(
896 "Unable to resolve instrument table address in ROM");
899 const size_t table_size = kVanillaInstrumentCount * kInstrumentEntrySize;
900 if (rom_offset + table_size > rom.
size()) {
901 return absl::OutOfRangeError(
"Instrument table exceeds ROM bounds");
906 rom.
ReadByteVector(rom_offset,
static_cast<uint32_t
>(table_size)));
909 for (
int i = 0; i < kVanillaInstrumentCount; ++i) {
910 const size_t base =
static_cast<size_t>(i) * kInstrumentEntrySize;
914 inst.
gain = bytes[base + 3];
916 (
static_cast<uint16_t
>(bytes[base + 4]) << 8) | bytes[base + 5]);
917 inst.
name = kAltTpInstrumentNames[i];
921 return absl::OkStatus();
926 return absl::UnimplementedError(
"SaveInstruments not yet implemented");
936 const uint8_t* dir_data =
940 return absl::InternalError(
"Failed to locate sample directory in ROM");
945 const int max_samples = std::min(64, dir_length / 4);
947 for (
int i = 0; i < max_samples; ++i) {
948 uint16_t start_addr = dir_data[i * 4] | (dir_data[i * 4 + 1] << 8);
949 uint16_t loop_addr = dir_data[i * 4 + 2] | (dir_data[i * 4 + 3] << 8);
952 sample.
name = absl::StrFormat(
"Sample %02X", i);
955 (loop_addr >= start_addr) ? (loop_addr - start_addr) : 0;
960 if (rom_offset == 0 || rom_offset >= rom.
size()) {
962 samples_.push_back(std::move(sample));
967 const uint8_t* rom_ptr = rom.
data() + rom_offset;
968 size_t remaining = rom.
size() - rom_offset;
970 while (remaining >= 9) {
975 if (rom_ptr[0] & 0x01) {
976 sample.
loops = (rom_ptr[0] & 0x02) != 0;
989 samples_.push_back(std::move(sample));
992 return absl::OkStatus();
997 return absl::UnimplementedError(
"SaveSamples not yet implemented");
1004 for (
const auto& segment : song.
segments) {
1007 for (
const auto& track : segment.tracks) {
1008 if (track.is_empty) {
1012 size +=
static_cast<int>(track.events.size()) * 3;
1026 nlohmann::json root;
1027 nlohmann::json songs = nlohmann::json::array();
1028 for (
const auto& song :
songs_) {
1030 js[
"name"] = song.name;
1031 js[
"bank"] = song.bank;
1032 js[
"loop_point"] = song.loop_point;
1033 js[
"rom_address"] = song.rom_address;
1034 js[
"modified"] = song.modified;
1036 nlohmann::json segments = nlohmann::json::array();
1037 for (
const auto& segment : song.segments) {
1038 nlohmann::json jseg;
1039 jseg[
"rom_address"] = segment.rom_address;
1040 nlohmann::json tracks = nlohmann::json::array();
1041 for (
const auto& track : segment.tracks) {
1043 jt[
"rom_address"] = track.rom_address;
1044 jt[
"duration_ticks"] = track.duration_ticks;
1045 jt[
"is_empty"] = track.is_empty;
1046 nlohmann::json events = nlohmann::json::array();
1047 for (
const auto& evt : track.events) {
1048 nlohmann::json jevt;
1049 jevt[
"tick"] = evt.tick;
1050 jevt[
"rom_offset"] = evt.rom_offset;
1053 jevt[
"type"] =
"note";
1054 jevt[
"note"][
"pitch"] = evt.note.pitch;
1055 jevt[
"note"][
"duration"] = evt.note.duration;
1056 jevt[
"note"][
"velocity"] = evt.note.velocity;
1057 jevt[
"note"][
"has_duration_prefix"] =
1058 evt.note.has_duration_prefix;
1061 jevt[
"type"] =
"command";
1062 jevt[
"command"][
"opcode"] = evt.command.opcode;
1063 jevt[
"command"][
"params"] = evt.command.params;
1066 jevt[
"type"] =
"subroutine";
1067 jevt[
"command"][
"opcode"] = evt.command.opcode;
1068 jevt[
"command"][
"params"] = evt.command.params;
1072 jevt[
"type"] =
"end";
1075 events.push_back(std::move(jevt));
1077 jt[
"events"] = std::move(events);
1078 tracks.push_back(std::move(jt));
1080 jseg[
"tracks"] = std::move(tracks);
1081 segments.push_back(std::move(jseg));
1083 js[
"segments"] = std::move(segments);
1084 songs.push_back(std::move(js));
1087 nlohmann::json instruments = nlohmann::json::array();
1090 ji[
"name"] = inst.name;
1091 ji[
"sample_index"] = inst.sample_index;
1092 ji[
"attack"] = inst.attack;
1093 ji[
"decay"] = inst.decay;
1094 ji[
"sustain_level"] = inst.sustain_level;
1095 ji[
"sustain_rate"] = inst.sustain_rate;
1096 ji[
"gain"] = inst.gain;
1097 ji[
"pitch_mult"] = inst.pitch_mult;
1098 instruments.push_back(std::move(ji));
1101 nlohmann::json samples = nlohmann::json::array();
1102 for (
const auto& sample :
samples_) {
1103 nlohmann::json jsample;
1104 jsample[
"name"] = sample.name;
1105 jsample[
"loop_point"] = sample.loop_point;
1106 jsample[
"loops"] = sample.loops;
1107 jsample[
"pcm_data"] = sample.pcm_data;
1108 jsample[
"brr_data"] = sample.brr_data;
1109 samples.push_back(std::move(jsample));
1112 root[
"songs"] = std::move(songs);
1113 root[
"instruments"] = std::move(instruments);
1114 root[
"samples"] = std::move(samples);
1127 if (j.contains(
"songs") && j[
"songs"].is_array()) {
1128 for (
const auto& js : j[
"songs"]) {
1130 song.
name = js.value(
"name",
"");
1131 song.
bank = js.value(
"bank", 0);
1132 song.
loop_point = js.value(
"loop_point", -1);
1134 song.
modified = js.value(
"modified",
false);
1136 if (js.contains(
"segments") && js[
"segments"].is_array()) {
1137 for (
const auto& jseg : js[
"segments"]) {
1140 if (jseg.contains(
"tracks") && jseg[
"tracks"].is_array()) {
1142 for (
const auto& jt : jseg[
"tracks"]) {
1145 auto& track = seg.
tracks[track_idx++];
1146 track.rom_address = jt.value(
"rom_address", 0);
1147 track.duration_ticks = jt.value(
"duration_ticks", 0);
1148 track.is_empty = jt.value(
"is_empty",
false);
1149 if (jt.contains(
"events") && jt[
"events"].is_array()) {
1150 track.events.clear();
1151 for (
const auto& jevt : jt[
"events"]) {
1153 evt.
tick = jevt.value(
"tick", 0);
1154 evt.
rom_offset = jevt.value(
"rom_offset", 0);
1155 std::string type = jevt.value(
"type",
"end");
1156 if (type ==
"note" && jevt.contains(
"note")) {
1160 jevt[
"note"].value(
"duration", uint8_t{0});
1162 jevt[
"note"].value(
"velocity", uint8_t{0});
1164 jevt[
"note"].value(
"has_duration_prefix",
false);
1165 }
else if (type ==
"command" || type ==
"subroutine") {
1166 evt.
type = (type ==
"subroutine")
1170 jevt[
"command"].value(
"opcode", uint8_t{0});
1171 auto params = jevt[
"command"].value(
1172 "params", std::array<uint8_t, 3>{0, 0, 0});
1177 track.events.push_back(std::move(evt));
1182 song.
segments.push_back(std::move(seg));
1185 songs_.push_back(std::move(song));
1189 if (j.contains(
"instruments") && j[
"instruments"].is_array()) {
1190 for (
const auto& ji : j[
"instruments"]) {
1192 inst.
name = ji.value(
"name",
"");
1194 inst.
attack = ji.value(
"attack", 0);
1195 inst.
decay = ji.value(
"decay", 0);
1198 inst.
gain = ji.value(
"gain", 0);
1204 if (j.contains(
"samples") && j[
"samples"].is_array()) {
1205 for (
const auto& jsample : j[
"samples"]) {
1207 sample.
name = jsample.value(
"name",
"");
1208 sample.
loop_point = jsample.value(
"loop_point", 0);
1209 sample.
loops = jsample.value(
"loops",
false);
1210 sample.
pcm_data = jsample.value(
"pcm_data", std::vector<int16_t>{});
1211 sample.
brr_data = jsample.value(
"brr_data", std::vector<uint8_t>{});
1212 samples_.push_back(std::move(sample));
1221 return absl::OkStatus();
1222 }
catch (
const std::exception& e) {
1223 return absl::InvalidArgumentError(
1224 absl::StrFormat(
"Failed to parse music state: %s", e.what()));
1233 if (song_id <= 0 || song_id > kVanillaSongCount) {
1236 return kVanillaSongs[song_id].
name;
1240 if (song_id <= 0 || song_id > kVanillaSongCount) {
1243 return kVanillaSongs[song_id].
bank;
1247 if (
const auto* metadata = GetMetadataForBank(bank)) {
1248 return BankSongRange{metadata->vanilla_start_id, metadata->vanilla_end_id};
1254 if (
const auto* metadata = GetMetadataForBank(bank)) {
1255 return metadata->spc_bank;
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
absl::StatusOr< std::vector< uint8_t > > ReadByteVector(uint32_t offset, uint32_t length) const
absl::Status WriteByte(int addr, uint8_t value)
absl::Status WriteVector(int addr, std::vector< uint8_t > data)
absl::StatusOr< uint8_t > ReadByte(int offset)
static std::vector< int16_t > Decode(const std::vector< uint8_t > &brr_data, int *loop_start=nullptr)
Decode BRR data to PCM samples.
static uint8_t GetSpcBankId(Bank bank)
static uint32_t GetBankRomAddress(Bank bank)
Get the ROM address for a bank.
bool HasModifications() const
Check if any music data has been modified.
MusicInstrument * GetInstrument(int index)
Get an instrument by index.
int CreateNewInstrument(const std::string &name)
Create a new instrument.
static constexpr uint16_t GetSongTableAddress()
nlohmann::json ToJson() const
bool IsVanilla(int index) const
Check if a song is a vanilla (original) song.
absl::Status LoadFromJson(const nlohmann::json &j)
absl::Status LoadSamples(Rom &rom)
MusicSong * GetSongById(int song_id)
Get a song by vanilla ID (1-based).
MusicSong * GetSong(int index)
Get a song by index.
absl::Status DetectExpandedMusicPatch(Rom &rom)
absl::Status LoadInstruments(Rom &rom)
static int GetBankMaxSize(Bank bank)
Get the maximum size for a bank.
ExpandedBankInfo expanded_bank_info_
std::vector< MusicSample > samples_
absl::Status LoadSongTable(Rom &rom, Bank bank, std::vector< MusicSong > *custom_songs)
bool AllSongsFit() const
Check if all songs fit in their banks.
static BankSongRange GetBankSongRange(Bank bank)
absl::Status SaveInstruments(Rom &rom)
MusicSample * GetSample(int index)
Get a sample by index.
absl::Status DeleteSong(int index)
Delete a song by index.
std::vector< MusicSong > songs_
SpaceInfo CalculateSpaceUsage(Bank bank) const
Calculate space usage for a bank.
absl::StatusOr< int > ImportSampleFromWav(const std::string &filepath, const std::string &name)
Import a WAV file as a new sample.
absl::Status SaveSamples(Rom &rom)
absl::Status SaveToRom(Rom &rom)
Save all modified music data back to ROM.
absl::Status SaveSongTable(Rom &rom, Bank bank)
int auxiliary_song_count_
int CalculateSongSize(const MusicSong &song) const
bool IsExpandedSong(int index) const
Check if a song is from an expanded bank.
int overworld_song_count_
void ClearModifications()
Mark all data as unmodified (after save).
absl::Status LoadExpandedSongTable(Rom &rom, std::vector< MusicSong > *custom_songs)
bool instruments_modified_
std::vector< MusicInstrument > instruments_
int CreateNewSong(const std::string &name, Bank bank)
Create a new empty song.
absl::Status LoadFromRom(Rom &rom)
Load all music data from a ROM.
std::vector< MusicSong * > GetSongsInBank(Bank bank)
Get all songs in a specific bank.
int DuplicateSong(int index)
Duplicate a song.
static absl::StatusOr< MusicSong > ParseSong(Rom &rom, uint16_t address, uint8_t bank)
Parse a complete song from ROM.
static absl::StatusOr< std::vector< uint16_t > > ReadSongPointerTable(Rom &rom, uint16_t table_address, uint8_t bank, int max_entries=40)
Read the song pointer table for a given SPC bank.
static const uint8_t * GetSpcData(Rom &rom, uint16_t spc_address, uint8_t bank, int *out_length=nullptr)
Get a pointer to ROM data at an SPC address.
static uint32_t SpcAddressToRomOffset(Rom &rom, uint16_t spc_address, uint8_t bank)
Convert an SPC address to a ROM offset.
static absl::StatusOr< SerializeResult > SerializeSong(const MusicSong &song, uint16_t base_address)
static void ApplyBaseAddress(SerializeResult *result, uint16_t new_base_address)
#define ASSIGN_OR_RETURN(type_variable_name, expression)
constexpr BankPointerRegisters kCreditsPointerRegs
constexpr int kVanillaSongCount
uint8_t EncodeLoRomMid(uint32_t pc_offset)
constexpr VanillaSongInfo kVanillaSongs[]
constexpr const char * kAltTpInstrumentNames[kVanillaInstrumentCount]
absl::Status UpdateBankPointerRegisters(Rom &rom, const BankPointerRegisters ®s, uint32_t pc_offset)
constexpr BankMetadata kBankMetadata[]
uint8_t EncodeLoRomBank(uint32_t pc_offset)
absl::Status UpdateDynamicBankPointer(Rom &rom, MusicBank::Bank bank, uint32_t pc_offset)
constexpr int kVanillaInstrumentCount
constexpr int kInstrumentEntrySize
const BankMetadata * GetMetadataForBank(MusicBank::Bank bank)
uint8_t EncodeLoRomLow(uint32_t pc_offset)
constexpr BankPointerRegisters kOverworldPointerRegs
constexpr uint8_t kNoteRest
constexpr uint32_t kExpandedAuxBankRom
MusicBank::Bank GetVanillaSongBank(int song_id)
Get the bank for a vanilla song ID.
constexpr uint16_t kAuxSongTableAram
constexpr uint16_t kSampleTableAram
constexpr uint32_t kExpandedOverworldBankRom
constexpr uint8_t kJslOpcode
constexpr uint16_t kInstrumentTableAram
constexpr uint32_t kOverworldBankRom
constexpr uint16_t kSongTableAram
const char * GetVanillaSongName(int song_id)
Get the vanilla name for a song ID.
constexpr int kCreditsBankMaxSize
constexpr int kExpandedOverworldBankMaxSize
constexpr uint32_t kCreditsBankRom
constexpr int kDungeonBankMaxSize
constexpr uint32_t kDungeonBankRom
constexpr int kOverworldBankMaxSize
constexpr int kAuxBankMaxSize
constexpr uint32_t kExpandedMusicHookAddress
uint16_t aux_aram_address
std::string recommendation
std::array< uint8_t, 3 > params
An instrument definition with ADSR envelope.
void SetFromBytes(uint8_t ad, uint8_t sr)
A BRR-encoded audio sample.
std::vector< uint8_t > brr_data
std::vector< int16_t > pcm_data
A segment containing 8 parallel tracks.
std::array< MusicTrack, 8 > tracks
A complete song composed of segments.
std::vector< MusicSegment > segments
A single event in a music track (note, command, or control).
static TrackEvent MakeEnd(uint16_t tick)