Merge branch 'master' into newFilePicker

This commit is contained in:
tildearrow 2025-09-26 23:50:40 -05:00
commit 5110538c3f
5 changed files with 96 additions and 127 deletions

23
doc/7-systems/multipcm.md Normal file
View file

@ -0,0 +1,23 @@
# Yamaha YMW258-F (MultiPCM)
a sound chip introduced in 1991 by Yamaha for their first General MIDI-compliant synthesizer, the Yamaha TG100. It also appeared on a number of higher-end consumer keyboards and specialized professional devices. The YMW258-F implements the GEW8 sound synthesis technology, which is PCM sample-based synthesis under Yamaha's "Advanced Wave Modulation" (AWM) umbrella.
Sega used the chip in several arcade games on the Sega System Multi 32, Sega Model 1, and Sega Model 2 arcade boards. The chip as found on these boards is called MultiPCM, and has the Sega internal part number 315-5560. Contrary to popular belief, it is not a custom Sega part and is not based on the earlier SegaPCM chip, but it was named the MultiPCM as a successor to that chip.
it has a whopping 28 channels of stereo 16-bit PCM and:
- hardware LFO
- 8bit and 12bit wave format support
- up to 4MB of wave memory
- full blown ADSR envelopes
## effects
- `20xx`: **set PCM LFO rate.**
- `21xx`: **set PCM LFO PM depth..**
- `22xx`: **set PCM LFO AM depth.**
## info
this chip uses the [MultiPCM](../4-instrument/multipcm.md) instrument editor.

View file

@ -1279,8 +1279,8 @@ SafeWriter* DivEngine::saveDMF(unsigned char version) {
for (int i=0; i<chans; i++) {
for (int j=0; j<curSubSong->ordersLen; j++) {
if (curOrders->ord[i][j]>0x7f) {
logE("order %d, %d is out of range (0-127)!",curOrders->ord[i][j]);
lastError=fmt::sprintf("order %d, %d is out of range (0-127)",curOrders->ord[i][j]);
logE("order %d, %d is out of range (0-127)!",i,j);
lastError=fmt::sprintf("order %d, %d is out of range (0-127)",i,j);
return NULL;
}
}

View file

@ -618,90 +618,14 @@ void FurnaceGUI::drawFindReplace() {
if ((i.noteMode==GUI_QUERY_RANGE || i.noteMode==GUI_QUERY_RANGE_NOT) && i.note>=120) {
i.note=0;
}
if (i.note==130) {
snprintf(tempID,1024,"%s##MREL",macroRelLabel);
} else if (i.note==129) {
snprintf(tempID,1024,"%s##NREL",noteRelLabel);
} else if (i.note==128) {
snprintf(tempID,1024,"%s##NOFF",noteOffLabel);
} else if (i.note>=-60 && i.note<120) {
snprintf(tempID,1024,"%c%c",noteNames[i.note+60][0],(noteNames[i.note+60][1]=='-')?' ':noteNames[i.note+60][1]);
} else {
snprintf(tempID,1024,"???");
i.note=0;
}
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x/2);
bool updateNote1=false;
int note1=i.note%12;
int oct1=i.note/12;
if (ImGui::BeginCombo("##NN1",tempID)) {
for (int j=0; j<12; j++) {
snprintf(tempID,1024,"%c%c",noteNames[j+72][0],(noteNames[j+72][1]=='-')?' ':noteNames[j+72][1]);
if (ImGui::Selectable(tempID,note1==j)) {
note1=j;
updateNote1=true;
}
}
if (i.noteMode!=GUI_QUERY_RANGE && i.noteMode!=GUI_QUERY_RANGE_NOT) {
if (ImGui::Selectable(noteOffLabel,note1==13)) {
i.note=128;
}
if (ImGui::Selectable(noteRelLabel,note1==14)) {
i.note=129;
}
if (ImGui::Selectable(macroRelLabel,note1==15)) {
i.note=130;
}
}
ImGui::EndCombo();
}
ImGui::SameLine();
if (i.note<128) {
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x/2);
if (ImGui::InputScalar("##NNO1",ImGuiDataType_S32,&oct1)) {
if (oct1<-5) oct1=-5;
if (oct1>9) oct1=9;
updateNote1=true;
}
}
if (updateNote1) {
i.note=oct1*12+note1;
}
NoteSelector(&i.note, i.noteMode!=GUI_QUERY_RANGE && i.noteMode!=GUI_QUERY_RANGE_NOT);
}
ImGui::TableNextColumn();
if (SECOND_VISIBLE(i.noteMode)) {
if (i.noteMax<-60 || i.noteMax>=120) {
i.noteMax=0;
}
if (i.noteMax>=-60 && i.noteMax<120) {
snprintf(tempID,1024,"%c%c",noteNames[i.noteMax+60][0],(noteNames[i.noteMax+60][1]=='-')?' ':noteNames[i.noteMax+60][1]);
} else {
snprintf(tempID,1024,"???");
}
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x/2);
bool updateNote2=false;
int note2=i.noteMax%12;
int oct2=i.noteMax/12;
if (ImGui::BeginCombo("##NN2",tempID)) {
for (int j=0; j<12; j++) {
snprintf(tempID,1024,"%c%c",noteNames[j+72][0],(noteNames[j+72][1]=='-')?' ':noteNames[j+72][1]);
if (ImGui::Selectable(tempID,note2==j)) {
note2=j;
updateNote2=true;
}
}
ImGui::EndCombo();
}
ImGui::SameLine();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x/2);
if (ImGui::InputScalar("##NNO2",ImGuiDataType_S32,&oct2)) {
if (oct2<-5) oct2=-5;
if (oct2>9) oct2=9;
updateNote2=true;
}
if (updateNote2) {
i.noteMax=oct2*12+note2;
}
NoteSelector(&i.noteMax, false);
}
ImGui::TableNextRow();
@ -912,53 +836,7 @@ void FurnaceGUI::drawFindReplace() {
ImGui::TableNextColumn();
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
if (queryReplaceNoteMode==GUI_QUERY_REPLACE_SET) {
if (queryReplaceNote==130) {
snprintf(tempID,1024,"%s##MREL",macroRelLabel);
} else if (queryReplaceNote==129) {
snprintf(tempID,1024,"%s##NREL",noteRelLabel);
} else if (queryReplaceNote==128) {
snprintf(tempID,1024,"%s##NOFF",noteOffLabel);
} else if (queryReplaceNote>=-60 && queryReplaceNote<120) {
snprintf(tempID,1024,"%c%c",noteNames[queryReplaceNote+60][0],(noteNames[queryReplaceNote+60][1]=='-')?' ':noteNames[queryReplaceNote+60][1]);
} else {
snprintf(tempID,1024,"???");
queryReplaceNote=0;
}
bool updateNote=false;
int note1=queryReplaceNote%12;
int oct1=queryReplaceNote/12;
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x/2);
if (ImGui::BeginCombo("##NRValueC",tempID)) {
for (int i=0; i<12; i++) {
snprintf(tempID,1024,"%c%c",noteNames[i+72][0],(noteNames[i+72][1]=='-')?' ':noteNames[i+72][1]);
if (ImGui::Selectable(tempID,note1==i)) {
note1=i;
updateNote=true;
}
}
if (ImGui::Selectable(noteOffLabel,note1==13)) {
queryReplaceNote=128;
}
if (ImGui::Selectable(noteRelLabel,note1==14)) {
queryReplaceNote=129;
}
if (ImGui::Selectable(macroRelLabel,note1==15)) {
queryReplaceNote=130;
}
ImGui::EndCombo();
}
ImGui::SameLine();
if (queryReplaceNote<128) {
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x/2);
if (ImGui::InputScalar("##NRValueCO",ImGuiDataType_S32,&oct1)) {
if (oct1<-5) oct1=-5;
if (oct1>9) oct1=9;
updateNote=true;
}
}
if (updateNote) {
queryReplaceNote=oct1*12+note1;
}
NoteSelector(&queryReplaceNote, true);
} else if (queryReplaceNoteMode==GUI_QUERY_REPLACE_ADD || queryReplaceNoteMode==GUI_QUERY_REPLACE_ADD_OVERFLOW) {
if (ImGui::InputInt("##NRValue",&queryReplaceNote,1,12)) {
if (queryReplaceNote<-180) queryReplaceNote=-180;

View file

@ -535,6 +535,72 @@ bool FurnaceGUI::InvCheckbox(const char* label, bool* value) {
return false;
}
bool FurnaceGUI::NoteSelector(int* value, bool showOffRel, int octaveMin, int octaveMax) {
bool ret=false, calcNote=false;
char tempID[64];
if (*value==130) {
snprintf(tempID,64,"%s##MREL",macroRelLabel);
} else if (*value==129) {
snprintf(tempID,64,"%s##NREL",noteRelLabel);
} else if (*value==128) {
snprintf(tempID,64,"%s##NOFF",noteOffLabel);
} else if (*value>=-60 && *value<120) {
snprintf(tempID,64,"%c%c",noteNames[*value%12+72][0],(noteNames[*value%12+72][1]=='-')?' ':noteNames[*value%12+72][1]);
} else {
snprintf(tempID,64,"???");
*value=0;
}
float width=ImGui::GetContentRegionAvail().x/2-ImGui::GetStyle().FramePadding.x;
ImGui::SetNextItemWidth(width);
int note=(*value+60)%12;
int oct=0;
if (*value<120) oct=(*value-note)/12;
ImGui::BeginGroup();
ImGui::PushID(value);
if (ImGui::BeginCombo("##NoteSelectorNote",tempID)) {
for (int j=0; j<12; j++) {
snprintf(tempID,64,"%c%c",noteNames[j+72][0],(noteNames[j+72][1]=='-')?' ':noteNames[j+72][1]);
if (ImGui::Selectable(tempID,note==j && *value<128)) {
note=j;
calcNote=true;
}
if (note==j && *value<120) ImGui::SetItemDefaultFocus();
}
if (showOffRel) {
if (ImGui::Selectable(noteOffLabel,*value==128)) {
*value=128;
ret=true;
}
if (ImGui::Selectable(noteRelLabel,*value==129)) {
*value=129;
ret=true;
}
if (ImGui::Selectable(macroRelLabel,*value==130)) {
*value=130;
ret=true;
}
if (*value>=128 && *value<=130) ImGui::SetItemDefaultFocus();
}
ImGui::EndCombo();
}
ImGui::SameLine();
if (*value<120) {
ImGui::SetNextItemWidth(width/2);
if (ImGui::InputScalar("##NoteSelectorOctave",ImGuiDataType_S32,&oct)) {
if (oct<octaveMin) oct=octaveMin;
if (oct>octaveMax) oct=octaveMax;
calcNote=true;
}
}
if (calcNote) {
*value=oct*12+note;
ret=true;
}
ImGui::PopID();
ImGui::EndGroup();
return ret;
}
bool FurnaceGUI::LocalizedComboGetter(void* data, int idx, const char** out_text) {
const char* const* items=(const char* const*)data;
if (out_text) *out_text=_(items[idx]);

View file

@ -2871,6 +2871,8 @@ class FurnaceGUI {
// inverted checkbox
bool InvCheckbox(const char* label, bool* value);
bool NoteSelector(int* value, bool showOffRel, int octaveMin=-5, int octaveMax=9);
// mixer stuff
ImVec2 calcPortSetSize(String label, int ins, int outs);
bool portSet(String label, unsigned int portSetID, int ins, int outs, int activeIns, int activeOuts, int& clickedPort, std::map<unsigned int,ImVec2>& portPos);