multiple instrument playback, part 1
not implemented for MIDI yet
This commit is contained in:
parent
08a27be76f
commit
5b145b7121
15 changed files with 276 additions and 34 deletions
|
|
@ -969,6 +969,7 @@ src/gui/about.cpp
|
|||
src/gui/channels.cpp
|
||||
src/gui/chanOsc.cpp
|
||||
src/gui/clock.cpp
|
||||
src/gui/commandPalette.cpp
|
||||
src/gui/compatFlags.cpp
|
||||
src/gui/csPlayer.cpp
|
||||
src/gui/cursor.cpp
|
||||
|
|
@ -988,8 +989,8 @@ src/gui/log.cpp
|
|||
src/gui/memory.cpp
|
||||
src/gui/mixer.cpp
|
||||
src/gui/midiMap.cpp
|
||||
src/gui/multiInsSetup.cpp
|
||||
src/gui/newSong.cpp
|
||||
src/gui/commandPalette.cpp
|
||||
src/gui/orders.cpp
|
||||
src/gui/osc.cpp
|
||||
src/gui/patManager.cpp
|
||||
|
|
|
|||
|
|
@ -3665,7 +3665,7 @@ int DivEngine::getViableChannel(int chan, int off, int ins) {
|
|||
return (chan+off)%chans;
|
||||
}
|
||||
|
||||
bool DivEngine::autoNoteOn(int ch, int ins, int note, int vol) {
|
||||
bool DivEngine::autoNoteOn(int ch, int ins, int note, int vol, int transpose) {
|
||||
bool isViable[DIV_MAX_CHANS];
|
||||
bool canPlayAnyway=false;
|
||||
bool notInViableChannel=false;
|
||||
|
|
@ -3708,7 +3708,7 @@ bool DivEngine::autoNoteOn(int ch, int ins, int note, int vol) {
|
|||
if ((!midiPoly) || (isViable[finalChan] && chan[finalChan].midiNote==-1 && (insInst->type==DIV_INS_OPL || getChannelType(finalChan)==finalChanType || notInViableChannel))) {
|
||||
chan[finalChan].midiNote=note;
|
||||
chan[finalChan].midiAge=midiAgeCounter++;
|
||||
pendingNotes.push_back(DivNoteEvent(finalChan,ins,note,vol,true));
|
||||
pendingNotes.push_back(DivNoteEvent(finalChan,ins,note+transpose,vol,true));
|
||||
return true;
|
||||
}
|
||||
if (++finalChan>=chans) {
|
||||
|
|
@ -3729,7 +3729,7 @@ bool DivEngine::autoNoteOn(int ch, int ins, int note, int vol) {
|
|||
|
||||
chan[candidate].midiNote=note;
|
||||
chan[candidate].midiAge=midiAgeCounter++;
|
||||
pendingNotes.push_back(DivNoteEvent(candidate,ins,note,vol,true));
|
||||
pendingNotes.push_back(DivNoteEvent(candidate,ins,note+transpose,vol,true));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1195,7 +1195,7 @@ class DivEngine {
|
|||
void noteOff(int chan);
|
||||
|
||||
// returns whether it could
|
||||
bool autoNoteOn(int chan, int ins, int note, int vol=-1);
|
||||
bool autoNoteOn(int chan, int ins, int note, int vol=-1, int transpose=0);
|
||||
void autoNoteOff(int chan, int note, int vol=-1);
|
||||
void autoNoteOffAll();
|
||||
|
||||
|
|
|
|||
|
|
@ -367,7 +367,7 @@ void FurnaceGUI::drawPalette() {
|
|||
openRecentFile(recentFile[i]);
|
||||
break;
|
||||
case CMDPAL_TYPE_INSTRUMENTS:
|
||||
curIns=i-1;
|
||||
setCurIns(i-1);
|
||||
break;
|
||||
case CMDPAL_TYPE_SAMPLES:
|
||||
curSample=i;
|
||||
|
|
|
|||
|
|
@ -150,10 +150,42 @@ void FurnaceGUI::insListItem(int i, int dir, int asset) {
|
|||
} else {
|
||||
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_TEXT]);
|
||||
}
|
||||
bool insReleased=ImGui::Selectable(name.c_str(),(i==-1)?(curIns<0 || curIns>=e->song.insLen):(curIns==i));
|
||||
bool insSelected=(curIns==i);
|
||||
int multiInsColor=-1;
|
||||
if (i==-1) {
|
||||
insSelected=(curIns<0 || curIns>=e->song.insLen);
|
||||
} else {
|
||||
for (int j=0; j<7; j++) {
|
||||
if (multiIns[j]==i) {
|
||||
insSelected=true;
|
||||
multiInsColor=j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// push multi ins colors if necessary
|
||||
if (multiInsColor>=0) {
|
||||
ImVec4 colorActive=uiColors[GUI_COLOR_MULTI_INS_1+multiInsColor];
|
||||
ImVec4 colorHover=ImVec4(colorActive.x,colorActive.y,colorActive.z,colorActive.w*0.5);
|
||||
ImVec4 color=ImVec4(colorActive.x,colorActive.y,colorActive.z,colorActive.w*0.25);
|
||||
ImGui::PushStyleColor(ImGuiCol_Header,color);
|
||||
ImGui::PushStyleColor(ImGuiCol_HeaderHovered,colorHover);
|
||||
ImGui::PushStyleColor(ImGuiCol_HeaderActive,colorActive);
|
||||
}
|
||||
|
||||
bool insReleased=ImGui::Selectable(name.c_str(),insSelected);
|
||||
|
||||
if (multiInsColor>=0) {
|
||||
ImGui::PopStyleColor(3);
|
||||
}
|
||||
bool insPressed=ImGui::IsItemActivated();
|
||||
if (insReleased || (!insListDir && insPressed && !settings.draggableDataView)) {
|
||||
curIns=i;
|
||||
if (mobileMultiInsToggle || ImGui::IsKeyDown(ImGuiKey_LeftShift) || ImGui::IsKeyDown(ImGuiKey_RightShift)) {
|
||||
setMultiIns(i);
|
||||
} else {
|
||||
setCurIns(i);
|
||||
}
|
||||
if (!insReleased || insListDir || settings.draggableDataView) {
|
||||
wavePreviewInit=true;
|
||||
updateFMPreview=true;
|
||||
|
|
@ -181,7 +213,7 @@ void FurnaceGUI::insListItem(int i, int dir, int asset) {
|
|||
}
|
||||
|
||||
if (ImGui::BeginPopupContextItem("InsRightMenu")) {
|
||||
curIns=i;
|
||||
setCurIns(i);
|
||||
updateFMPreview=true;
|
||||
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_TEXT]);
|
||||
if (ImGui::MenuItem(_("edit"))) {
|
||||
|
|
@ -732,6 +764,16 @@ void FurnaceGUI::drawInsList(bool asChild) {
|
|||
}
|
||||
}
|
||||
|
||||
if (mobileUI) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
pushToggleColors(mobileMultiInsToggle);
|
||||
if (ImGui::SmallButton("Select multiple")) {
|
||||
mobileMultiInsToggle=!mobileMultiInsToggle;
|
||||
}
|
||||
popToggleColors();
|
||||
}
|
||||
|
||||
if (settings.unifiedDataView) {
|
||||
ImGui::Unindent();
|
||||
|
||||
|
|
|
|||
|
|
@ -143,16 +143,18 @@ void FurnaceGUI::doAction(int what) {
|
|||
}
|
||||
break;
|
||||
case GUI_ACTION_INS_UP:
|
||||
if (--curIns<-1) {
|
||||
curIns=-1;
|
||||
setCurIns(curIns-1);
|
||||
if (curIns<-1) {
|
||||
setCurIns(-1);
|
||||
}
|
||||
wavePreviewInit=true;
|
||||
wantScrollListIns=true;
|
||||
updateFMPreview=true;
|
||||
break;
|
||||
case GUI_ACTION_INS_DOWN:
|
||||
if (++curIns>=(int)e->song.ins.size()) {
|
||||
curIns=((int)e->song.ins.size())-1;
|
||||
setCurIns(curIns+1);
|
||||
if (curIns>=(int)e->song.ins.size()) {
|
||||
setCurIns(((int)e->song.ins.size())-1);
|
||||
}
|
||||
wavePreviewInit=true;
|
||||
wantScrollListIns=true;
|
||||
|
|
@ -360,6 +362,9 @@ void FurnaceGUI::doAction(int what) {
|
|||
case GUI_ACTION_WINDOW_REF_PLAYER:
|
||||
nextWindow=GUI_WINDOW_REF_PLAYER;
|
||||
break;
|
||||
case GUI_ACTION_WINDOW_MULTI_INS_SETUP:
|
||||
nextWindow=GUI_WINDOW_MULTI_INS_SETUP;
|
||||
break;
|
||||
|
||||
case GUI_ACTION_COLLAPSE_WINDOW:
|
||||
collapseWindow=true;
|
||||
|
|
@ -471,6 +476,9 @@ void FurnaceGUI::doAction(int what) {
|
|||
case GUI_WINDOW_REF_PLAYER:
|
||||
refPlayerOpen=false;
|
||||
break;
|
||||
case GUI_WINDOW_MULTI_INS_SETUP:
|
||||
multiInsSetupOpen=false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
@ -739,7 +747,7 @@ void FurnaceGUI::doAction(int what) {
|
|||
break;
|
||||
}
|
||||
}
|
||||
curIns=e->addInstrument(cursor.xCoarse);
|
||||
setCurIns(e->addInstrument(cursor.xCoarse));
|
||||
if (curIns==-1) {
|
||||
showError(_("too many instruments!"));
|
||||
} else {
|
||||
|
|
@ -768,7 +776,7 @@ void FurnaceGUI::doAction(int what) {
|
|||
case GUI_ACTION_INS_LIST_DUPLICATE:
|
||||
if (curIns>=0 && curIns<(int)e->song.ins.size()) {
|
||||
int prevIns=curIns;
|
||||
curIns=e->addInstrument(cursor.xCoarse);
|
||||
setCurIns(e->addInstrument(cursor.xCoarse));
|
||||
if (curIns==-1) {
|
||||
showError(_("too many instruments!"));
|
||||
} else {
|
||||
|
|
@ -820,13 +828,15 @@ void FurnaceGUI::doAction(int what) {
|
|||
insEditOpen=true;
|
||||
break;
|
||||
case GUI_ACTION_INS_LIST_UP:
|
||||
if (--curIns<0) curIns=0;
|
||||
setCurIns(curIns-1);
|
||||
if (curIns<0) setCurIns(0);
|
||||
wantScrollListIns=true;
|
||||
wavePreviewInit=true;
|
||||
updateFMPreview=true;
|
||||
break;
|
||||
case GUI_ACTION_INS_LIST_DOWN:
|
||||
if (++curIns>=(int)e->song.ins.size()) curIns=((int)e->song.ins.size())-1;
|
||||
setCurIns(curIns+1);
|
||||
if (curIns>=(int)e->song.ins.size()) setCurIns(((int)e->song.ins.size())-1);
|
||||
wantScrollListIns=true;
|
||||
wavePreviewInit=true;
|
||||
updateFMPreview=true;
|
||||
|
|
@ -1715,7 +1725,7 @@ void FurnaceGUI::doAction(int what) {
|
|||
}
|
||||
|
||||
DivSample* sample=e->song.sample[curSample];
|
||||
curIns=e->addInstrument(cursor.xCoarse);
|
||||
setCurIns(e->addInstrument(cursor.xCoarse));
|
||||
if (curIns==-1) {
|
||||
showError(_("too many instruments!"));
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -1912,7 +1912,7 @@ void FurnaceGUI::doAbsorbInstrument() {
|
|||
// absorb most recent instrument
|
||||
if (!foundIns && pat->newData[i][DIV_PAT_INS] >= 0) {
|
||||
foundIns=true;
|
||||
curIns=pat->newData[i][DIV_PAT_INS];
|
||||
setCurIns(pat->newData[i][DIV_PAT_INS]);
|
||||
}
|
||||
|
||||
// absorb most recent octave (i.e. set curOctave such that the "main row" (QWERTY) of
|
||||
|
|
@ -1932,7 +1932,7 @@ void FurnaceGUI::doAbsorbInstrument() {
|
|||
}
|
||||
|
||||
// if no instrument has been set at this point, the only way to match it is to use "none"
|
||||
if (!foundIns) curIns=-1;
|
||||
if (!foundIns) setCurIns(-1);
|
||||
|
||||
logD("doAbsorbInstrument -- searched %d orders", curOrder-orderIdx);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -868,6 +868,38 @@ void FurnaceGUI::autoDetectSystem() {
|
|||
}
|
||||
}
|
||||
|
||||
void FurnaceGUI::setCurIns(int newIns) { curIns=newIns;
|
||||
memset(multiIns,-1,7*sizeof(int));
|
||||
}
|
||||
|
||||
bool FurnaceGUI::setMultiIns(int newIns) {
|
||||
// set primary instrument if not set
|
||||
if (curIns<0) {
|
||||
setCurIns(newIns);
|
||||
return true;
|
||||
}
|
||||
// don't allow using the primary instrument twice
|
||||
if (curIns==newIns) return false;
|
||||
for (int i=0; i<7; i++) {
|
||||
// don't allow using an instrument more than once
|
||||
if (multiIns[i]==newIns) return false;
|
||||
// if slot is empty, assign instrument to it
|
||||
if (multiIns[i]==-1) {
|
||||
multiIns[i]=newIns;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// no more slots available
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FurnaceGUI::isMultiInsActive() {
|
||||
for (int i=0; i<7; i++) {
|
||||
if (multiIns[i]>=0) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void FurnaceGUI::updateROMExportAvail() {
|
||||
memset(romExportAvail,0,sizeof(bool)*DIV_ROM_MAX);
|
||||
romExportExists=false;
|
||||
|
|
@ -1333,6 +1365,11 @@ void FurnaceGUI::previewNote(int refChan, int note, bool autoNote) {
|
|||
e->setMidiBaseChan(refChan);
|
||||
e->synchronized([this,note]() {
|
||||
if (!e->autoNoteOn(-1,curIns,note)) failedNoteOn=true;
|
||||
for (int mi=0; mi<7; mi++) {
|
||||
if (multiIns[mi]!=-1) {
|
||||
e->autoNoteOn(-1,multiIns[mi],note,-1,multiInsTranspose[mi]);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -1469,7 +1506,7 @@ void FurnaceGUI::valueInput(int num, bool direct, int target) {
|
|||
}
|
||||
}
|
||||
if (settings.absorbInsInput) {
|
||||
curIns=pat->newData[y][target];
|
||||
setCurIns(pat->newData[y][target]);
|
||||
wavePreviewInit=true;
|
||||
updateFMPreview=true;
|
||||
}
|
||||
|
|
@ -1977,7 +2014,7 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) {
|
|||
if (curIns!=-2) {
|
||||
prevIns=curIns;
|
||||
}
|
||||
curIns=-2;
|
||||
setCurIns(-2);
|
||||
}
|
||||
}
|
||||
for (DivInstrument* i: instruments) delete i;
|
||||
|
|
@ -3858,6 +3895,7 @@ bool FurnaceGUI::loop() {
|
|||
DECLARE_METRIC(effectList)
|
||||
DECLARE_METRIC(userPresets)
|
||||
DECLARE_METRIC(refPlayer)
|
||||
DECLARE_METRIC(multiInsSetup)
|
||||
DECLARE_METRIC(popup)
|
||||
|
||||
#ifdef IS_MOBILE
|
||||
|
|
@ -4045,7 +4083,7 @@ bool FurnaceGUI::loop() {
|
|||
instrumentCount=e->addInstrumentPtr(i);
|
||||
}
|
||||
if (instrumentCount>=0 && settings.selectAssetOnLoad) {
|
||||
curIns=instrumentCount-1;
|
||||
setCurIns(instrumentCount-1);
|
||||
}
|
||||
nextWindow=GUI_WINDOW_INS_LIST;
|
||||
MARK_MODIFIED;
|
||||
|
|
@ -4239,8 +4277,8 @@ bool FurnaceGUI::loop() {
|
|||
break;
|
||||
case TA_MIDI_PROGRAM:
|
||||
if (midiMap.programChange && !(midiMap.directChannel && midiMap.directProgram)) {
|
||||
curIns=msg.data[0];
|
||||
if (curIns>=(int)e->song.ins.size()) curIns=e->song.ins.size()-1;
|
||||
setCurIns(msg.data[0]);
|
||||
if (curIns>=(int)e->song.ins.size()) setCurIns(e->song.ins.size()-1);
|
||||
wavePreviewInit=true;
|
||||
updateFMPreview=true;
|
||||
}
|
||||
|
|
@ -4480,6 +4518,7 @@ bool FurnaceGUI::loop() {
|
|||
IMPORT_CLOSE(csPlayerOpen);
|
||||
IMPORT_CLOSE(userPresetsOpen);
|
||||
IMPORT_CLOSE(refPlayerOpen);
|
||||
IMPORT_CLOSE(multiInsSetupOpen);
|
||||
} else if (pendingLayoutImportStep==1) {
|
||||
// let the UI settle
|
||||
} else if (pendingLayoutImportStep==2) {
|
||||
|
|
@ -4852,6 +4891,7 @@ bool FurnaceGUI::loop() {
|
|||
if (ImGui::MenuItem(_("play/edit controls"),BIND_FOR(GUI_ACTION_WINDOW_EDIT_CONTROLS),editControlsOpen)) editControlsOpen=!editControlsOpen;
|
||||
if (ImGui::MenuItem(_("piano/input pad"),BIND_FOR(GUI_ACTION_WINDOW_PIANO),pianoOpen)) pianoOpen=!pianoOpen;
|
||||
if (ImGui::MenuItem(_("reference music player"),BIND_FOR(GUI_ACTION_WINDOW_REF_PLAYER),refPlayerOpen)) refPlayerOpen=!refPlayerOpen;
|
||||
if (ImGui::MenuItem(_("multi-ins setup"),BIND_FOR(GUI_ACTION_WINDOW_MULTI_INS_SETUP),multiInsSetupOpen)) multiInsSetupOpen=!multiInsSetupOpen;
|
||||
if (spoilerOpen) if (ImGui::MenuItem(_("spoiler"),NULL,spoilerOpen)) spoilerOpen=!spoilerOpen;
|
||||
|
||||
ImGui::EndMenu();
|
||||
|
|
@ -5052,6 +5092,7 @@ bool FurnaceGUI::loop() {
|
|||
MEASURE(effectList,drawEffectList());
|
||||
MEASURE(userPresets,drawUserPresets());
|
||||
MEASURE(refPlayer,drawRefPlayer());
|
||||
MEASURE(multiInsSetup,drawMultiInsSetup());
|
||||
MEASURE(patManager,drawPatManager());
|
||||
|
||||
} else {
|
||||
|
|
@ -5098,6 +5139,7 @@ bool FurnaceGUI::loop() {
|
|||
MEASURE(effectList,drawEffectList());
|
||||
MEASURE(userPresets,drawUserPresets());
|
||||
MEASURE(refPlayer,drawRefPlayer());
|
||||
MEASURE(multiInsSetup,drawMultiInsSetup());
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -5167,7 +5209,7 @@ bool FurnaceGUI::loop() {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
curIns=prevIns;
|
||||
setCurIns(prevIns);
|
||||
wavePreviewInit=true;
|
||||
updateFMPreview=true;
|
||||
}
|
||||
|
|
@ -5673,7 +5715,7 @@ bool FurnaceGUI::loop() {
|
|||
MARK_MODIFIED;
|
||||
}
|
||||
if (instrumentCount>=0 && settings.selectAssetOnLoad) {
|
||||
curIns=instrumentCount-1;
|
||||
setCurIns(instrumentCount-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -6550,7 +6592,7 @@ bool FurnaceGUI::loop() {
|
|||
e->lockEngine([this]() {
|
||||
e->song.clearInstruments();
|
||||
});
|
||||
curIns=-1;
|
||||
setCurIns(-1);
|
||||
MARK_MODIFIED;
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
|
@ -6755,7 +6797,7 @@ bool FurnaceGUI::loop() {
|
|||
strncpy(temp,insTypes[i][0],1023);
|
||||
if (ImGui::MenuItem(temp)) {
|
||||
// create ins
|
||||
curIns=e->addInstrument(-1,i);
|
||||
setCurIns(e->addInstrument(-1,i));
|
||||
if (curIns==-1) {
|
||||
showError(_("too many instruments!"));
|
||||
} else {
|
||||
|
|
@ -8249,6 +8291,7 @@ void FurnaceGUI::syncState() {
|
|||
spoilerOpen=e->getConfBool("spoilerOpen",false);
|
||||
userPresetsOpen=e->getConfBool("userPresetsOpen",false);
|
||||
refPlayerOpen=e->getConfBool("refPlayerOpen",false);
|
||||
multiInsSetupOpen=e->getConfBool("multiInsSetupOpen",false);
|
||||
|
||||
insListDir=e->getConfBool("insListDir",false);
|
||||
waveListDir=e->getConfBool("waveListDir",false);
|
||||
|
|
@ -8414,6 +8457,7 @@ void FurnaceGUI::commitState(DivConfig& conf) {
|
|||
conf.set("spoilerOpen",spoilerOpen);
|
||||
conf.set("userPresetsOpen",userPresetsOpen);
|
||||
conf.set("refPlayerOpen",refPlayerOpen);
|
||||
conf.set("multiInsSetupOpen",multiInsSetupOpen);
|
||||
|
||||
// commit dir state
|
||||
conf.set("insListDir",insListDir);
|
||||
|
|
@ -8769,6 +8813,7 @@ FurnaceGUI::FurnaceGUI():
|
|||
curPaletteChoice(0),
|
||||
curPaletteType(0),
|
||||
soloTimeout(0.0f),
|
||||
mobileMultiInsToggle(false),
|
||||
purgeYear(2021),
|
||||
purgeMonth(4),
|
||||
purgeDay(4),
|
||||
|
|
@ -8818,6 +8863,7 @@ FurnaceGUI::FurnaceGUI():
|
|||
cvOpen(false),
|
||||
userPresetsOpen(false),
|
||||
refPlayerOpen(false),
|
||||
multiInsSetupOpen(false),
|
||||
cvNotSerious(false),
|
||||
shortIntro(false),
|
||||
insListDir(false),
|
||||
|
|
@ -9285,6 +9331,9 @@ FurnaceGUI::FurnaceGUI():
|
|||
|
||||
memset(romExportAvail,0,sizeof(bool)*DIV_ROM_MAX);
|
||||
|
||||
memset(multiIns,-1,7*sizeof(int));
|
||||
memset(multiInsTranspose,0,7*sizeof(int));
|
||||
|
||||
strncpy(noteOffLabel,"OFF",32);
|
||||
strncpy(noteRelLabel,"===",32);
|
||||
strncpy(macroRelLabel,"REL",32);
|
||||
|
|
|
|||
|
|
@ -309,6 +309,14 @@ enum FurnaceGUIColors {
|
|||
GUI_COLOR_MACRO_ENVELOPE,
|
||||
GUI_COLOR_MACRO_GLOBAL,
|
||||
|
||||
GUI_COLOR_MULTI_INS_1,
|
||||
GUI_COLOR_MULTI_INS_2,
|
||||
GUI_COLOR_MULTI_INS_3,
|
||||
GUI_COLOR_MULTI_INS_4,
|
||||
GUI_COLOR_MULTI_INS_5,
|
||||
GUI_COLOR_MULTI_INS_6,
|
||||
GUI_COLOR_MULTI_INS_7,
|
||||
|
||||
GUI_COLOR_INSTR_STD,
|
||||
GUI_COLOR_INSTR_FM,
|
||||
GUI_COLOR_INSTR_GB,
|
||||
|
|
@ -568,6 +576,7 @@ enum FurnaceGUIWindows {
|
|||
GUI_WINDOW_CS_PLAYER,
|
||||
GUI_WINDOW_USER_PRESETS,
|
||||
GUI_WINDOW_REF_PLAYER,
|
||||
GUI_WINDOW_MULTI_INS_SETUP,
|
||||
GUI_WINDOW_SPOILER
|
||||
};
|
||||
|
||||
|
|
@ -774,6 +783,7 @@ enum FurnaceGUIActions {
|
|||
GUI_ACTION_WINDOW_CS_PLAYER,
|
||||
GUI_ACTION_WINDOW_USER_PRESETS,
|
||||
GUI_ACTION_WINDOW_REF_PLAYER,
|
||||
GUI_ACTION_WINDOW_MULTI_INS_SETUP,
|
||||
|
||||
GUI_ACTION_COLLAPSE_WINDOW,
|
||||
GUI_ACTION_CLOSE_WINDOW,
|
||||
|
|
@ -2376,13 +2386,19 @@ class FurnaceGUI {
|
|||
int pendingLayoutImportStep;
|
||||
FixedQueue<bool*,64> pendingLayoutImportReopen;
|
||||
|
||||
int curIns, curWave, curSample, curOctave, curOrder, playOrder, prevIns, oldRow, editStep, editStepCoarse, soloChan, orderEditMode, orderCursor;
|
||||
// do not set curIns directly! use setCurIns() instead.
|
||||
int curIns, curWave, curSample;
|
||||
int curOctave, curOrder, playOrder, prevIns, oldRow, editStep, editStepCoarse, soloChan, orderEditMode, orderCursor;
|
||||
int isClipping, newSongCategory, latchTarget, undoOrder;
|
||||
int wheelX, wheelY, dragSourceX, dragSourceXFine, dragSourceY, dragSourceOrder, dragDestinationX, dragDestinationXFine, dragDestinationY, dragDestinationOrder, oldBeat, oldBar;
|
||||
int curGroove, exitDisabledTimer;
|
||||
int curPaletteChoice, curPaletteType;
|
||||
float soloTimeout;
|
||||
|
||||
int multiIns[7];
|
||||
int multiInsTranspose[7];
|
||||
bool mobileMultiInsToggle;
|
||||
|
||||
int purgeYear, purgeMonth, purgeDay;
|
||||
|
||||
bool patExtraButtons, patChannelNames, patChannelPairs;
|
||||
|
|
@ -2395,6 +2411,7 @@ class FurnaceGUI {
|
|||
bool pianoOpen, notesOpen, channelsOpen, regViewOpen, logOpen, effectListOpen, chanOscOpen;
|
||||
bool subSongsOpen, findOpen, spoilerOpen, patManagerOpen, sysManagerOpen, clockOpen, speedOpen;
|
||||
bool groovesOpen, xyOscOpen, memoryOpen, csPlayerOpen, cvOpen, userPresetsOpen, refPlayerOpen;
|
||||
bool multiInsSetupOpen;
|
||||
|
||||
bool cvNotSerious;
|
||||
|
||||
|
|
@ -3000,6 +3017,7 @@ class FurnaceGUI {
|
|||
void drawXYOsc();
|
||||
void drawUserPresets();
|
||||
void drawRefPlayer();
|
||||
void drawMultiInsSetup();
|
||||
|
||||
float drawSystemChannelInfo(const DivSysDef* whichDef, int keyHitOffset=-1, float width=-1.0f);
|
||||
void drawSystemChannelInfoText(const DivSysDef* whichDef);
|
||||
|
|
@ -3155,6 +3173,10 @@ class FurnaceGUI {
|
|||
const char* getSystemName(DivSystem which);
|
||||
const char* getSystemPartNumber(DivSystem sys, DivConfig& flags);
|
||||
|
||||
void setCurIns(int newIns);
|
||||
bool setMultiIns(int newIns);
|
||||
bool isMultiInsActive();
|
||||
|
||||
public:
|
||||
void editStr(String* which);
|
||||
void showWarning(String what, FurnaceGUIWarnings type);
|
||||
|
|
|
|||
|
|
@ -657,6 +657,7 @@ const FurnaceGUIActionDef guiActions[GUI_ACTION_MAX]={
|
|||
D("WINDOW_CS_PLAYER", _N("Command Stream Player"), 0),
|
||||
D("WINDOW_USER_PRESETS", _N("User Presets"), 0),
|
||||
D("WINDOW_REF_PLAYER", _N("Reference Music Player"), 0),
|
||||
D("MULTI_INS_SETUP", _N("Multi-Instrument Setup"), 0),
|
||||
|
||||
D("COLLAPSE_WINDOW", _N("Collapse/expand current window"), 0),
|
||||
D("CLOSE_WINDOW", _N("Close current window"), FURKMOD_SHIFT|SDLK_ESCAPE),
|
||||
|
|
@ -1005,6 +1006,14 @@ const FurnaceGUIColorDef guiColors[GUI_COLOR_MAX]={
|
|||
D(GUI_COLOR_MACRO_ENVELOPE,"",ImVec4(0.0f,1.0f,0.5f,1.0f)),
|
||||
D(GUI_COLOR_MACRO_GLOBAL,"",ImVec4(1.0f,0.1f,0.1f,1.0f)),
|
||||
|
||||
D(GUI_COLOR_MULTI_INS_1,"",ImVec4(0.2f,1.0f,1.0f,1.0f)),
|
||||
D(GUI_COLOR_MULTI_INS_2,"",ImVec4(0.2f,1.0f,0.2f,1.0f)),
|
||||
D(GUI_COLOR_MULTI_INS_3,"",ImVec4(0.7f,1.0f,0.2f,1.0f)),
|
||||
D(GUI_COLOR_MULTI_INS_4,"",ImVec4(1.0f,0.7f,0.2f,1.0f)),
|
||||
D(GUI_COLOR_MULTI_INS_5,"",ImVec4(1.0f,0.2f,0.2f,1.0f)),
|
||||
D(GUI_COLOR_MULTI_INS_6,"",ImVec4(1.0f,0.2f,1.0f,1.0f)),
|
||||
D(GUI_COLOR_MULTI_INS_7,"",ImVec4(0.4f,0.2f,1.0f,1.0f)),
|
||||
|
||||
D(GUI_COLOR_INSTR_STD,"",ImVec4(0.6f,1.0f,0.5f,1.0f)),
|
||||
D(GUI_COLOR_INSTR_FM,"",ImVec4(0.6f,0.9f,1.0f,1.0f)),
|
||||
D(GUI_COLOR_INSTR_GB,"",ImVec4(1.0f,1.0f,0.5f,1.0f)),
|
||||
|
|
|
|||
|
|
@ -6649,10 +6649,10 @@ void FurnaceGUI::drawInsEdit() {
|
|||
for (size_t i=0; i<e->song.ins.size(); i++) {
|
||||
name=fmt::sprintf("%.2X: %s##_INSS%d",i,e->song.ins[i]->name,i);
|
||||
if (ImGui::Selectable(name.c_str(),curIns==(int)i)) {
|
||||
curIns=i;
|
||||
setCurIns(i);
|
||||
wavePreviewInit=true;
|
||||
updateFMPreview=true;
|
||||
ins = e->song.ins[curIns];
|
||||
ins=e->song.ins[curIns];
|
||||
}
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
|
|
@ -6700,7 +6700,7 @@ void FurnaceGUI::drawInsEdit() {
|
|||
for (size_t i=0; i<e->song.ins.size(); i++) {
|
||||
name=fmt::sprintf("%.2X: %s##_INSS%d",i,e->song.ins[i]->name,i);
|
||||
if (ImGui::Selectable(name.c_str(),curIns==(int)i)) {
|
||||
curIns=i;
|
||||
setCurIns(i);
|
||||
ins=e->song.ins[curIns];
|
||||
wavePreviewInit=true;
|
||||
updateFMPreview=true;
|
||||
|
|
|
|||
94
src/gui/multiInsSetup.cpp
Normal file
94
src/gui/multiInsSetup.cpp
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2025 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "gui.h"
|
||||
#include "imgui.h"
|
||||
#include "IconsFontAwesome4.h"
|
||||
|
||||
void FurnaceGUI::drawMultiInsSetup() {
|
||||
if (nextWindow==GUI_WINDOW_MULTI_INS_SETUP) {
|
||||
multiInsSetupOpen=true;
|
||||
ImGui::SetNextWindowFocus();
|
||||
nextWindow=GUI_WINDOW_NOTHING;
|
||||
}
|
||||
if (!multiInsSetupOpen && !isMultiInsActive()) return;
|
||||
if (ImGui::Begin("Multi-Ins Setup",isMultiInsActive()?NULL:&multiInsSetupOpen,globalWinFlags,_("Multi-Ins Setup"))) {
|
||||
if (ImGui::BeginTable("MultiInsSlots",8,ImGuiTableFlags_SizingStretchSame)) {
|
||||
ImGui::TableNextRow();
|
||||
for (int i=0; i<8; i++) {
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%d",i+1);
|
||||
}
|
||||
ImGui::TableNextRow();
|
||||
for (int i=0; i<8; i++) {
|
||||
String id;
|
||||
int tr=(i==0)?0:multiInsTranspose[i-1];
|
||||
bool thisInsOn=(i==0)?true:(multiIns[i-1]!=-1);
|
||||
if (tr==0) {
|
||||
id=fmt::sprintf("±%d###TrAmount",tr);
|
||||
} else if (tr>0) {
|
||||
id=fmt::sprintf("+%d###TrAmount",tr);
|
||||
} else {
|
||||
id=fmt::sprintf("%d###TrAmount",tr);
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::PushID(i);
|
||||
|
||||
ImGui::PushItemFlag(ImGuiItemFlags_ButtonRepeat,true);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding,ImVec2(0.0f,0.0f));
|
||||
if (ImGui::Button(ICON_FA_CHEVRON_UP "##Up",ImVec2(ImGui::GetContentRegionAvail().x,0))) {
|
||||
if (i>0) multiInsTranspose[i-1]++;
|
||||
}
|
||||
ImGui::PopStyleVar();
|
||||
ImGui::PopItemFlag();
|
||||
|
||||
if (i>0) {
|
||||
ImVec4 colorActive=uiColors[GUI_COLOR_MULTI_INS_1+i-1];
|
||||
ImVec4 colorHover=ImVec4(colorActive.x,colorActive.y,colorActive.z,colorActive.w*0.5);
|
||||
ImVec4 color=ImVec4(colorActive.x,colorActive.y,colorActive.z,colorActive.w*0.25);
|
||||
ImGui::PushStyleColor(ImGuiCol_Header,color);
|
||||
ImGui::PushStyleColor(ImGuiCol_HeaderHovered,colorHover);
|
||||
ImGui::PushStyleColor(ImGuiCol_HeaderActive,colorActive);
|
||||
}
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_SelectableTextAlign,ImVec2(0.5f,0.5f));
|
||||
ImGui::Selectable(id.c_str(),thisInsOn,0,ImVec2(
|
||||
ImGui::GetContentRegionAvail().x,
|
||||
ImGui::GetContentRegionAvail().y-ImGui::GetTextLineHeightWithSpacing()
|
||||
));
|
||||
ImGui::PopStyleVar();
|
||||
if (i>0) {
|
||||
ImGui::PopStyleColor(3);
|
||||
}
|
||||
|
||||
ImGui::PushItemFlag(ImGuiItemFlags_ButtonRepeat,true);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding,ImVec2(0.0f,0.0f));
|
||||
if (ImGui::Button(ICON_FA_CHEVRON_DOWN "##Down",ImVec2(ImGui::GetContentRegionAvail().x,0))) {
|
||||
if (i>0) multiInsTranspose[i-1]--;
|
||||
}
|
||||
ImGui::PopStyleVar();
|
||||
ImGui::PopItemFlag();
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_MULTI_INS_SETUP;
|
||||
ImGui::End();
|
||||
}
|
||||
|
|
@ -429,6 +429,11 @@ void FurnaceGUI::drawPiano() {
|
|||
} else {
|
||||
e->synchronized([this,note]() {
|
||||
if (!e->autoNoteOn(-1,curIns,note)) failedNoteOn=true;
|
||||
for (int mi=0; mi<7; mi++) {
|
||||
if (multiIns[mi]!=-1) {
|
||||
e->autoNoteOn(-1,multiIns[mi],note,-1,multiInsTranspose[mi]);
|
||||
}
|
||||
}
|
||||
});
|
||||
if (edit && curWindow!=GUI_WINDOW_INS_LIST && curWindow!=GUI_WINDOW_INS_EDIT) noteInput(note,0);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4166,6 +4166,16 @@ void FurnaceGUI::drawSettings() {
|
|||
UI_COLOR_CONFIG(GUI_COLOR_MACRO_HIGHLIGHT,_("Step Highlight"));
|
||||
ImGui::TreePop();
|
||||
}
|
||||
if (ImGui::TreeNode(_("Multi-instrument Play"))) {
|
||||
UI_COLOR_CONFIG(GUI_COLOR_MULTI_INS_1,_("Second instrument"));
|
||||
UI_COLOR_CONFIG(GUI_COLOR_MULTI_INS_2,_("Third instrument"));
|
||||
UI_COLOR_CONFIG(GUI_COLOR_MULTI_INS_3,_("Fourth instrument"));
|
||||
UI_COLOR_CONFIG(GUI_COLOR_MULTI_INS_4,_("Fifth instrument"));
|
||||
UI_COLOR_CONFIG(GUI_COLOR_MULTI_INS_5,_("Sixth instrument"));
|
||||
UI_COLOR_CONFIG(GUI_COLOR_MULTI_INS_6,_("Seventh instrument"));
|
||||
UI_COLOR_CONFIG(GUI_COLOR_MULTI_INS_7,_("Eighth instrument"));
|
||||
ImGui::TreePop();
|
||||
}
|
||||
if (ImGui::TreeNode(_("Instrument Types"))) {
|
||||
UI_COLOR_CONFIG(GUI_COLOR_INSTR_FM,_("FM (OPN)"));
|
||||
UI_COLOR_CONFIG(GUI_COLOR_INSTR_STD,_("SN76489/Sega PSG"));
|
||||
|
|
|
|||
|
|
@ -233,7 +233,7 @@ bool FurnaceGUI::parseSysEx(unsigned char* data, size_t len) {
|
|||
for (DivInstrument* i: instruments) {
|
||||
logI("got instrument from MIDI: %s",i->name);
|
||||
e->addInstrumentPtr(i);
|
||||
curIns=e->song.insLen-1;
|
||||
setCurIns(e->song.insLen-1);
|
||||
}
|
||||
} catch (EndOfFileException e) {
|
||||
logW("end of data already?");
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue