6#include "absl/strings/str_format.h"
14 std::ostringstream out;
19 "==================================================================="
21 out <<
"; " << song.
name <<
"\n";
22 out <<
"; Exported from yaze music editor\n";
23 out <<
"; Format: Oracle of Secrets music_macros.asm compatible\n";
25 "==================================================================="
38 for (
size_t seg_idx = 0; seg_idx < song.
segments.size(); ++seg_idx) {
39 const auto& segment = song.
segments[seg_idx];
42 out <<
"; --- Segment " << seg_idx <<
" ---\n";
45 for (
int ch = 0; ch < 8; ++ch) {
46 const auto& track = segment.tracks[ch];
59 const std::string& path,
63 return result.status();
66 std::ofstream file(path);
67 if (!file.is_open()) {
68 return absl::FailedPreconditionError(
69 absl::StrFormat(
"Failed to open file for writing: %s", path));
72 file << result.value();
75 return absl::OkStatus();
80 std::ostringstream out;
90 uint16_t intro_offset = 0x0A;
91 uint16_t loop_offset = 0x1A;
93 out << absl::StrFormat(
"dw !ARAMAddr+$%02X ; Intro section\n",
96 out << absl::StrFormat(
"dw !ARAMAddr+$%02X ; Loop section\n",
99 out <<
"dw $00FF ; Default fade\n";
101 out <<
"dw !ARAMAddr+$02 ; Loop start\n";
102 out <<
"dw $0000 ; Reserved\n";
109 std::ostringstream out;
111 out <<
".Channels\n";
112 out << absl::StrFormat(
"!ARAMC = !ARAMAddr-%s\n", options.
label_prefix);
115 for (
int ch = 0; ch < 8; ++ch) {
116 bool has_data =
false;
117 for (
const auto& segment : song.
segments) {
118 if (!segment.tracks[ch].is_empty) {
125 out << absl::StrFormat(
"dw .Channel%d+!ARAMC\n", ch);
127 out <<
"dw $0000 ; Unused\n";
137 std::ostringstream out;
139 out << absl::StrFormat(
".Channel%d\n", channel_index);
141 uint8_t current_duration = 0;
143 for (
const auto& event : track.
events) {
145 if (!event_asm.empty()) {
146 out <<
" " << event_asm <<
"\n";
158 uint8_t& current_duration) {
159 switch (event.
type) {
174 uint8_t& current_duration) {
175 std::ostringstream out;
180 if (duration_const) {
182 out << absl::StrFormat(
"%%SetDurationN(%s, $%02X)\n ", duration_const,
185 out << absl::StrFormat(
"%%SetDuration(%s)\n ", duration_const);
189 out << absl::StrFormat(
"db $%02X\n ", note.
duration);
196 out <<
"db " << note_name;
203 std::ostringstream out;
217 out << absl::StrFormat(
"db $%02X", cmd.
opcode);
219 for (
int i = 0; i < param_count; ++i) {
220 out << absl::StrFormat(
", $%02X", cmd.
params[i]);
227 out <<
"%" << macro_name <<
"(";
229 for (
int i = 0; i < param_count; ++i) {
232 out << absl::StrFormat(
"$%02X", cmd.
params[i]);
241 if (dc.value == duration) {
259 return absl::StrFormat(
"$%02X", pitch);
263 int octave = (offset / 12) + 1;
264 int semitone = offset % 12;
266 static const char*
const note_names[] = {
"C",
"Cs",
"D",
"Ds",
"E",
"F",
267 "Fs",
"G",
"Gs",
"A",
"As",
"B"};
269 return absl::StrFormat(
"%s%d", note_names[semitone], octave);
274 if (im.id == instrument_id) {
284 return "SetInstrument";
294 return "SetMasterVolume";
296 return "MasterVolumeFade";
302 return "GlobalTranspose";
304 return "ChannelTranspose";
310 return "SetChannelVolume";
312 return "ChannelVolumeFade";
314 return "CallSubroutine";
316 return "VibratoFade";
318 return "PitchEnvelopeTo";
320 return "PitchEnvelopeFrom";
322 return "PitchEnvelopeOff";
332 return "EchoVolumeFade";
336 return "PercussionPatch";
std::string ConvertCommandToAsm(const MusicCommand &cmd, const AsmExportOptions &options)
static int GetCommandParamCount(uint8_t opcode)
std::string GenerateChannelData(const MusicTrack &track, int channel_index, const AsmExportOptions &options)
static const char * GetDurationConstant(uint8_t duration)
static const char * GetInstrumentMacro(uint8_t instrument_id)
static std::string GetNoteName(uint8_t pitch)
absl::Status ExportToFile(const MusicSong &song, const std::string &path, const AsmExportOptions &options)
Export a song to a file.
std::string GenerateChannelPointers(const MusicSong &song, const AsmExportOptions &options)
static const char * GetCommandMacro(uint8_t opcode)
absl::StatusOr< std::string > ExportSong(const MusicSong &song, const AsmExportOptions &options)
Export a song to ASM string.
std::string ConvertNoteToAsm(const Note ¬e, const AsmExportOptions &options, uint8_t ¤t_duration)
std::string GenerateHeader(const MusicSong &song, const AsmExportOptions &options)
std::string ConvertEventToAsm(const TrackEvent &event, const AsmExportOptions &options, uint8_t ¤t_duration)
constexpr uint8_t kNoteRest
constexpr DurationConstant kDurationConstants[]
constexpr uint8_t kTrackEnd
constexpr InstrumentMacro kInstrumentMacros[]
constexpr uint8_t kNoteMinPitch
constexpr int kCommandParamCount[32]
constexpr uint8_t kNoteTie
constexpr uint8_t kNoteMaxPitch
Options for ASM export in music_macros.asm format.
bool use_instrument_macros
uint16_t base_aram_address
Represents an N-SPC command (opcodes 0xE0-0xFF).
int GetParamCount() const
std::array< uint8_t, 3 > params
A complete song composed of segments.
std::vector< MusicSegment > segments
One of 8 channels in a music segment.
std::vector< TrackEvent > events
Represents a single musical note.
A single event in a music track (note, command, or control).