Skip to content

Extended attributes for instruments #2396

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
111 changes: 111 additions & 0 deletions src/engine/instrument.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1131,6 +1131,56 @@ void DivInstrument::writeFeatureS3(SafeWriter* w) {
FEATURE_END;
}

void DivInstrument::writeFeatureXA(SafeWriter* w) {
FEATURE_BEGIN("XA");
for (auto const i : this->std.xattrs) {
w->writeString(i.name, false);
w->writeC(i.type);
switch (i.type) {
case DIV_XATTR_STRING:
w->writeString(i.str_val, false);
break;
case DIV_XATTR_UINT: {
unsigned int value = i.uint_val;
unsigned char vlq_byte = 0;

while (value) {
vlq_byte = value & 0x7F;
value >>= 7;
vlq_byte |= (value != 0) << 7;
w->writeC(vlq_byte);
}
break;
}
case DIV_XATTR_INT: {
unsigned int value = i.uint_val;
unsigned char vlq_byte = 0;

// write initial VLQ byte (which contains sign bit)
if (i.int_val < 0) {
value = -value;
vlq_byte |= 0x40;
}

vlq_byte |= value & 0x3F;
value >>= 6;
vlq_byte |= (value != 0) << 7;
w->writeC(vlq_byte);

while (value) {
vlq_byte = value & 0x7F;
value >>= 7;
vlq_byte |= (value != 0) << 7;
w->writeC(vlq_byte);
}
break;
}
}
}
// a empty string, as feature termination
w->writeC(0);
FEATURE_END;
}
void DivInstrument::putInsData2(SafeWriter* w, bool fui, const DivSong* song, bool insName) {
size_t blockStartSeek=0;
size_t blockEndSeek=0;
Expand Down Expand Up @@ -1178,6 +1228,7 @@ void DivInstrument::putInsData2(SafeWriter* w, bool fui, const DivSong* song, bo
bool featurePN=false;
bool featureS2=false;
bool featureS3=false;
bool featureXA=false;

bool checkForWL=false;

Expand Down Expand Up @@ -1575,6 +1626,9 @@ void DivInstrument::putInsData2(SafeWriter* w, bool fui, const DivSong* song, bo
}
}

if (!std.xattrs.empty()) {
featureXA = true;
}
// write features
if (featureNA) {
writeFeatureNA(w);
Expand Down Expand Up @@ -1647,6 +1701,9 @@ void DivInstrument::putInsData2(SafeWriter* w, bool fui, const DivSong* song, bo
if (featureS3) {
writeFeatureS3(w);
}
if (featureXA) {
writeFeatureXA(w);
}

if (fui && (featureSL || featureWL)) {
w->write("EN",2);
Expand Down Expand Up @@ -2581,6 +2638,58 @@ void DivInstrument::readFeatureS3(SafeReader& reader, short version) {
READ_FEAT_END;
}

void DivInstrument::readFeatureXA(SafeReader& reader, short version) {
READ_FEAT_BEGIN;
DivInstrumentXattr xattr;

while (true) {
xattr.name=reader.readString();

if (!xattr.name.length()) {
break;
}
xattr.type=(DivXattrType)reader.readC();

switch (xattr.type) {
case DIV_XATTR_STRING:
xattr.str_val=reader.readString();
break;
case DIV_XATTR_UINT: {
unsigned char byte=0;
unsigned int shift_count=0;
do {
byte = reader.readC();
xattr.uint_val |= (byte & 0x7F) << shift_count;
shift_count += 7;
} while (byte & 0x80);
break;
}
case DIV_XATTR_INT: {
unsigned char byte=reader.readC();
unsigned char sign_bit=0;
unsigned int shift_count=6;

sign_bit = byte & 0x40;
xattr.uint_val |= byte & 0x3F;
while (byte & 0x80) {
byte=reader.readC();
xattr.uint_val |= (byte & 0x7F) << shift_count;
shift_count += 7;
}

// if the sign bit is on in the first byte of the VLQ encoded integer,
// negate the integer
if (sign_bit) {
xattr.int_val = -xattr.int_val;
}
break;
}
}
std.xattrs.push_back(xattr);
xattr = DivInstrumentXattr();
}
READ_FEAT_END;
}
DivDataErrors DivInstrument::readInsDataNew(SafeReader& reader, short version, bool fui, DivSong* song) {
unsigned char featCode[2];
bool volIsCutoff=false;
Expand Down Expand Up @@ -2657,6 +2766,8 @@ DivDataErrors DivInstrument::readInsDataNew(SafeReader& reader, short version, b
readFeatureS2(reader,version);
} else if (memcmp(featCode,"S3",2)==0) { // SID3
readFeatureS3(reader,version);
} else if (memcmp(featCode,"XA",2)==0) { // extended attributes
readFeatureXA(reader,version);
} else {
if (song==NULL && (memcmp(featCode,"SL",2)==0 || (memcmp(featCode,"WL",2)==0))) {
// nothing
Expand Down
30 changes: 29 additions & 1 deletion src/engine/instrument.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "../ta-utils.h"
#include "../pch.h"
#include "../fixedQueue.h"
#include <vector>

struct DivSong;
struct DivInstrument;
Expand Down Expand Up @@ -149,6 +150,12 @@ enum DivMacroTypeOp: unsigned char {
DIV_MACRO_OP_KSR,
};

enum DivXattrType: unsigned char {
DIV_XATTR_STRING,
DIV_XATTR_UINT,
DIV_XATTR_INT
};

// FM operator structure:
// - OPN:
// - AM, AR, DR, MULT, RR, SL, TL, RS, DT, D2R, SSG-EG
Expand Down Expand Up @@ -289,6 +296,23 @@ struct DivInstrumentMacro {
}
};

struct DivInstrumentXattr {
String name;
DivXattrType type;

String str_val;
union {
unsigned int uint_val;
int int_val;
};

DivInstrumentXattr():
name("example.empty"),
type(DIV_XATTR_STRING),
str_val(),
int_val(0) {};
};

struct DivInstrumentSTD {
DivInstrumentMacro volMacro;
DivInstrumentMacro arpMacro;
Expand All @@ -310,6 +334,7 @@ struct DivInstrumentSTD {
DivInstrumentMacro ex6Macro;
DivInstrumentMacro ex7Macro;
DivInstrumentMacro ex8Macro;
std::vector<DivInstrumentXattr> xattrs;

struct OpMacro {
// ar, dr, mult, rr, sl, tl, dt2, rs, dt, d2r, ssgEnv;
Expand Down Expand Up @@ -363,7 +388,8 @@ struct DivInstrumentSTD {
ex5Macro(DIV_MACRO_EX5),
ex6Macro(DIV_MACRO_EX6),
ex7Macro(DIV_MACRO_EX7),
ex8Macro(DIV_MACRO_EX8) {
ex8Macro(DIV_MACRO_EX8),
xattrs() {
for (int i=0; i<4; i++) {
opMacros[i].amMacro.macroType=DIV_MACRO_OP_AM+(i<<5);
opMacros[i].arMacro.macroType=DIV_MACRO_OP_AR+(i<<5);
Expand Down Expand Up @@ -1095,6 +1121,7 @@ struct DivInstrument : DivInstrumentPOD {
void writeFeaturePN(SafeWriter* w);
void writeFeatureS2(SafeWriter* w);
void writeFeatureS3(SafeWriter* w);
void writeFeatureXA(SafeWriter* w);

void readFeatureNA(SafeReader& reader, short version);
void readFeatureFM(SafeReader& reader, short version);
Expand All @@ -1119,6 +1146,7 @@ struct DivInstrument : DivInstrumentPOD {
void readFeaturePN(SafeReader& reader, short version);
void readFeatureS2(SafeReader& reader, short version);
void readFeatureS3(SafeReader& reader, short version);
void readFeatureXA(SafeReader& reader, short version);

DivDataErrors readInsDataOld(SafeReader& reader, short version);
DivDataErrors readInsDataNew(SafeReader& reader, short version, bool fui, DivSong* song);
Expand Down
79 changes: 79 additions & 0 deletions src/gui/insEdit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,12 @@ const char* sid3SpecialWaveforms[]={
_N("Clipped Saw")
};

const char* xattrTypeNames[3] = {
_N("String"),
_N("Unsigned integer"),
_N("Integer")
};

const bool opIsOutput[8][4]={
{false,false,false,true},
{false,false,false,true},
Expand Down Expand Up @@ -8637,6 +8643,79 @@ void FurnaceGUI::drawInsEdit() {
drawMacros(macroList,macroEditStateMacros);
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Attributes")) {
if (ImGui::BeginTable("AttrTable", 4)) {
ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch);
ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthStretch);
ImGui::TableSetupColumn("c3",ImGuiTableColumnFlags_WidthStretch);
ImGui::TableSetupColumn("c4",ImGuiTableColumnFlags_WidthStretch);
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
ImGui::TableNextColumn();
ImGui::Text(_("Name"));
ImGui::TableNextColumn();
ImGui::Text(_("Type"));
ImGui::TableNextColumn();
ImGui::Text(_("Value"));
ImGui::TableNextColumn();
ImGui::Text(_("Actions"));
for (unsigned int i=0;i<ins->std.xattrs.size();i++) {
ImGui::PushID(i);
ImGui::TableNextRow();
ImGui::TableNextColumn();
if (ImGui::InputText("##AttrName", &ins->std.xattrs[i].name, ImGuiInputTextFlags_UndoRedo)) {
MARK_MODIFIED;
}
ImGui::TableNextColumn();
if (ImGui::BeginCombo("##AttrType",xattrTypeNames[ins->std.xattrs[i].type])) {
if (ImGui::Selectable(_("String"), ins->std.xattrs[i].type==DIV_XATTR_STRING)) {
MARK_MODIFIED;
ins->std.xattrs[i].type = DIV_XATTR_STRING;
}
if (ImGui::Selectable(_("Unsigned integer"), ins->std.xattrs[i].type==DIV_XATTR_UINT)) {
MARK_MODIFIED;
ins->std.xattrs[i].type = DIV_XATTR_UINT;
}
if (ImGui::Selectable(_("Integer"), ins->std.xattrs[i].type==DIV_XATTR_INT)) {
MARK_MODIFIED;
ins->std.xattrs[i].type = DIV_XATTR_INT;
}
ImGui::EndCombo();
}
ImGui::TableNextColumn();
switch (ins->std.xattrs[i].type) {
case DIV_XATTR_STRING:
if (ImGui::InputText("##AttrValue", &ins->std.xattrs[i].str_val, ImGuiInputTextFlags_UndoRedo)) {
MARK_MODIFIED;
}
break;
case DIV_XATTR_UINT: {
// this is stupid
unsigned int int_step = 1;
if (ImGui::InputScalar("##AttrValue", ImGuiDataType_U32, &ins->std.xattrs[i].uint_val, &int_step)) {
MARK_MODIFIED;
}
}
break;
case DIV_XATTR_INT:
if (ImGui::InputInt("##AttrValue", &ins->std.xattrs[i].int_val)) {
MARK_MODIFIED;
}
break;
}
ImGui::TableNextColumn();
if (ImGui::Button(ICON_FA_TIMES "##AttrRemove")) {
}
ImGui::PopID();
}
ImGui::TableNextRow();
ImGui::TableNextColumn();
if (ImGui::Button(ICON_FA_PLUS)) {
ins->std.xattrs.push_back(DivInstrumentXattr());
}
ImGui::EndTable();
}
ImGui::EndTabItem();
}
if (ins->type==DIV_INS_AY) {
if (!ins->amiga.useSample)
{
Expand Down
Loading