diff --git a/src/engine/dispatch.h b/src/engine/dispatch.h index b9d3a6080..e52746309 100644 --- a/src/engine/dispatch.h +++ b/src/engine/dispatch.h @@ -108,6 +108,7 @@ class DivDispatch { virtual bool isStereo(); virtual bool keyOffAffectsArp(int ch); + virtual void setPAL(bool pal); /** * initialize this DivDispatch. diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 300956978..34fd9c9ac 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -133,6 +133,38 @@ int DivEngine::getChannelCount(DivSystem sys) { return 0; } +const char* DivEngine::getSystemName(DivSystem sys) { + switch (sys) { + case DIV_SYSTEM_NULL: + return "Unknown"; + case DIV_SYSTEM_YMU759: + return "Yamaha YMU759"; + case DIV_SYSTEM_GENESIS: + return "Sega Genesis/Mega Drive"; + case DIV_SYSTEM_SMS: + return "Sega Master System"; + case DIV_SYSTEM_GB: + return "Game Boy"; + case DIV_SYSTEM_PCE: + return "PC Engine/TurboGrafx-16"; + case DIV_SYSTEM_NES: + return "NES"; + case DIV_SYSTEM_C64_6581: + return "Commodore 64 with 6581"; + case DIV_SYSTEM_C64_8580: + return "Commodore 64 with 8580"; + case DIV_SYSTEM_ARCADE: + return "Arcade"; + case DIV_SYSTEM_GENESIS_EXT: + return "Sega Genesis Extended Channel 3"; + case DIV_SYSTEM_YM2610: + return "Neo Geo"; + case DIV_SYSTEM_YM2610_EXT: + return "Neo Geo Extended Channel 3"; + } + return "Unknown"; +} + bool DivEngine::isFMSystem(DivSystem sys) { return (sys==DIV_SYSTEM_GENESIS || sys==DIV_SYSTEM_GENESIS_EXT || @@ -453,6 +485,7 @@ bool DivEngine::load(void* f, size_t slen) { if (ds.version>0x03) { ds.speed2=reader.readC(); ds.pal=reader.readC(); + ds.hz=(ds.pal)?60:50; ds.customTempo=reader.readC(); } else { ds.speed2=ds.speed1; @@ -1193,6 +1226,17 @@ void DivEngine::setOrder(unsigned char order) { isBusy.unlock(); } +void DivEngine::setSongRate(int hz, bool pal) { + isBusy.lock(); + song.pal=!pal; + song.hz=hz; + song.customTempo=(song.hz!=50 && song.hz!=60); + dispatch->setPAL((!song.pal) || (song.customTempo!=0 && song.hz<53)); + blip_set_rates(bb[0],dispatch->rate,got.rate); + blip_set_rates(bb[1],dispatch->rate,got.rate); + isBusy.unlock(); +} + void DivEngine::setAudio(DivAudioEngines which) { audioEngine=which; } diff --git a/src/engine/engine.h b/src/engine/engine.h index a08a675ee..72c44da7f 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -119,6 +119,9 @@ class DivEngine { // get sys channel count int getChannelCount(DivSystem sys); + // get sys name + const char* getSystemName(DivSystem sys); + // is FM system bool isFMSystem(DivSystem sys); @@ -152,6 +155,9 @@ class DivEngine { // go to order void setOrder(unsigned char order); + // set Hz + void setSongRate(int hz, bool pal); + // set remaining loops. -1 means loop forever. void setLoops(int loops); diff --git a/src/engine/pattern.cpp b/src/engine/pattern.cpp index f7396d45b..6f5832a8a 100644 --- a/src/engine/pattern.cpp +++ b/src/engine/pattern.cpp @@ -21,6 +21,15 @@ DivPattern* DivChannelData::getPattern(int index, bool create) { return data[index]; } +void DivChannelData::wipePatterns() { + for (int i=0; i<128; i++) { + if (data[i]!=NULL) { + delete data[i]; + data[i]=NULL; + } + } +} + DivChannelData::DivChannelData(): effectRows(1) { memset(data,0,128*sizeof(void*)); diff --git a/src/engine/pattern.h b/src/engine/pattern.h index 387c82b4a..14c8a26ef 100644 --- a/src/engine/pattern.h +++ b/src/engine/pattern.h @@ -14,5 +14,6 @@ struct DivChannelData { // 4-5+: effect/effect value DivPattern* data[128]; DivPattern* getPattern(int index, bool create); + void wipePatterns(); DivChannelData(); }; diff --git a/src/engine/platform/abstract.cpp b/src/engine/platform/abstract.cpp index 7c83f9397..992c46a3e 100644 --- a/src/engine/platform/abstract.cpp +++ b/src/engine/platform/abstract.cpp @@ -21,6 +21,9 @@ bool DivDispatch::keyOffAffectsArp(int ch) { return false; } +void DivDispatch::setPAL(bool pal) { +} + int DivDispatch::init(DivEngine* p, int channels, int sugRate, bool pal) { return 0; } diff --git a/src/engine/platform/c64.cpp b/src/engine/platform/c64.cpp index 48a0fce9d..78515e29b 100644 --- a/src/engine/platform/c64.cpp +++ b/src/engine/platform/c64.cpp @@ -302,13 +302,17 @@ void DivPlatformC64::setChipModel(bool is6581) { } } -int DivPlatformC64::init(DivEngine* p, int channels, int sugRate, bool pal) { - parent=p; +void DivPlatformC64::setPAL(bool pal) { if (pal) { rate=985248; } else { rate=1022727; } +} + +int DivPlatformC64::init(DivEngine* p, int channels, int sugRate, bool pal) { + parent=p; + setPAL(pal); reset(); diff --git a/src/engine/platform/c64.h b/src/engine/platform/c64.h index 05930a958..599be4596 100644 --- a/src/engine/platform/c64.h +++ b/src/engine/platform/c64.h @@ -57,6 +57,7 @@ class DivPlatformC64: public DivDispatch { int dispatch(DivCommand c); void reset(); void tick(); + void setPAL(bool pal); int init(DivEngine* parent, int channels, int sugRate, bool pal); void setChipModel(bool is6581); void quit(); diff --git a/src/engine/platform/genesis.cpp b/src/engine/platform/genesis.cpp index 75165b3aa..d1a9946bf 100644 --- a/src/engine/platform/genesis.cpp +++ b/src/engine/platform/genesis.cpp @@ -400,13 +400,17 @@ bool DivPlatformGenesis::keyOffAffectsArp(int ch) { return (ch>5); } -int DivPlatformGenesis::init(DivEngine* p, int channels, int sugRate, bool pal) { - parent=p; +void DivPlatformGenesis::setPAL(bool pal) { if (pal) { rate=211125; } else { rate=213068; } +} + +int DivPlatformGenesis::init(DivEngine* p, int channels, int sugRate, bool pal) { + parent=p; + setPAL(pal); // PSG psg.init(p,4,sugRate,pal); diff --git a/src/engine/platform/genesis.h b/src/engine/platform/genesis.h index ba57dbc65..09580be5f 100644 --- a/src/engine/platform/genesis.h +++ b/src/engine/platform/genesis.h @@ -55,6 +55,7 @@ class DivPlatformGenesis: public DivDispatch { void tick(); bool isStereo(); bool keyOffAffectsArp(int ch); + void setPAL(bool pal); int init(DivEngine* parent, int channels, int sugRate, bool pal); void quit(); ~DivPlatformGenesis(); diff --git a/src/engine/platform/nes.cpp b/src/engine/platform/nes.cpp index 6b99d3e09..491a43bd3 100644 --- a/src/engine/platform/nes.cpp +++ b/src/engine/platform/nes.cpp @@ -296,8 +296,7 @@ bool DivPlatformNES::keyOffAffectsArp(int ch) { return true; } -int DivPlatformNES::init(DivEngine* p, int channels, int sugRate, bool pal) { - parent=p; +void DivPlatformNES::setPAL(bool pal) { if (pal) { rate=1662607; freqBase=FREQ_BASE_PAL; @@ -305,6 +304,11 @@ int DivPlatformNES::init(DivEngine* p, int channels, int sugRate, bool pal) { rate=1789773; freqBase=FREQ_BASE; } +} + +int DivPlatformNES::init(DivEngine* p, int channels, int sugRate, bool pal) { + parent=p; + setPAL(pal); init_nla_table(500,500); reset(); diff --git a/src/engine/platform/nes.h b/src/engine/platform/nes.h index 29826ef1d..b1e916b70 100644 --- a/src/engine/platform/nes.h +++ b/src/engine/platform/nes.h @@ -47,6 +47,7 @@ class DivPlatformNES: public DivDispatch { void reset(); void tick(); bool keyOffAffectsArp(int ch); + void setPAL(bool pal); int init(DivEngine* parent, int channels, int sugRate, bool pal); void quit(); ~DivPlatformNES(); diff --git a/src/engine/platform/pce.cpp b/src/engine/platform/pce.cpp index 4a4c5f801..fe3126cb3 100644 --- a/src/engine/platform/pce.cpp +++ b/src/engine/platform/pce.cpp @@ -293,13 +293,17 @@ bool DivPlatformPCE::keyOffAffectsArp(int ch) { return true; } -int DivPlatformPCE::init(DivEngine* p, int channels, int sugRate, bool pal) { - parent=p; +void DivPlatformPCE::setPAL(bool pal) { if (pal) { // technically there is no PAL PC Engine but oh well... rate=1773448; } else { rate=1789773; } +} + +int DivPlatformPCE::init(DivEngine* p, int channels, int sugRate, bool pal) { + parent=p; + setPAL(pal); pce=new PCE_PSG(&tempL,&tempR,PCE_PSG::REVISION_HUC6280); reset(); return 6; diff --git a/src/engine/platform/pce.h b/src/engine/platform/pce.h index cb38b4e62..6673335ff 100644 --- a/src/engine/platform/pce.h +++ b/src/engine/platform/pce.h @@ -59,6 +59,7 @@ class DivPlatformPCE: public DivDispatch { void tick(); bool isStereo(); bool keyOffAffectsArp(int ch); + void setPAL(bool pal); int init(DivEngine* parent, int channels, int sugRate, bool pal); void quit(); ~DivPlatformPCE(); diff --git a/src/engine/platform/sms.cpp b/src/engine/platform/sms.cpp index 484b0a0d3..c444c5d4a 100644 --- a/src/engine/platform/sms.cpp +++ b/src/engine/platform/sms.cpp @@ -172,13 +172,17 @@ bool DivPlatformSMS::keyOffAffectsArp(int ch) { return true; } -int DivPlatformSMS::init(DivEngine* p, int channels, int sugRate, bool pal) { - parent=p; +void DivPlatformSMS::setPAL(bool pal) { if (pal) { rate=221681; } else { rate=223722; } +} + +int DivPlatformSMS::init(DivEngine* p, int channels, int sugRate, bool pal) { + parent=p; + setPAL(pal); sn=new sn76496_device(rate); reset(); return 4; diff --git a/src/engine/platform/sms.h b/src/engine/platform/sms.h index e3a55920b..2f0e8c534 100644 --- a/src/engine/platform/sms.h +++ b/src/engine/platform/sms.h @@ -37,6 +37,7 @@ class DivPlatformSMS: public DivDispatch { void reset(); void tick(); bool keyOffAffectsArp(int ch); + void setPAL(bool pal); int init(DivEngine* parent, int channels, int sugRate, bool pal); void quit(); ~DivPlatformSMS(); diff --git a/src/engine/song.cpp b/src/engine/song.cpp index 9e49d729c..0e71c9d7f 100644 --- a/src/engine/song.cpp +++ b/src/engine/song.cpp @@ -14,5 +14,9 @@ void DivSong::unload() { for (DivSample* i: sample) { delete i; } - sample.clear(); + sample.clear(); + + for (int i=0; i<17; i++) { + pat[i].wipePatterns(); + } } diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index f358954a4..55da48ac1 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -104,19 +104,24 @@ void FurnaceGUI::updateScroll(int amount) { nextScroll=lineHeight*amount; } -void FurnaceGUI::drawSongInfo() { - if (!songInfoOpen) return; - if (ImGui::Begin("Song Information",&songInfoOpen)) { - ImGui::InputText("Name",&e->song.name); - ImGui::InputText("Author",&e->song.author); - ImGui::InputScalar("Speed 1",ImGuiDataType_U8,&e->song.speed1,&_ONE,&_THREE); - ImGui::InputScalar("Speed 2",ImGuiDataType_U8,&e->song.speed2,&_ONE,&_THREE); - ImGui::InputScalar("Highlight 1",ImGuiDataType_U8,&e->song.hilightA,&_ONE,&_THREE); - ImGui::InputScalar("Highlight 2",ImGuiDataType_U8,&e->song.hilightB,&_ONE,&_THREE); - unsigned char ord=e->getOrder(); - if (ImGui::InputScalar("Order",ImGuiDataType_U8,&ord)) { - e->setOrder(ord); +void FurnaceGUI::updateWindowTitle() { + if (e->song.name.empty()) { + SDL_SetWindowTitle(sdlWin,fmt::sprintf("Furnace (%s)",e->getSystemName(e->song.system)).c_str()); + } else { + SDL_SetWindowTitle(sdlWin,fmt::sprintf("%s - Furnace (%s)",e->song.name,e->getSystemName(e->song.system)).c_str()); + } +} + +void FurnaceGUI::drawEditControls() { + if (!editControlsOpen) return; + if (ImGui::Begin("Play/Edit Controls",&editControlsOpen)) { + ImGui::Text("Octave"); + ImGui::SameLine(); + if (ImGui::InputInt("##Octave",&curOctave,1,1)) { + if (curOctave>6) curOctave=6; + if (curOctave<0) curOctave=0; } + if (ImGui::Button("Play")) { e->play(); } @@ -125,6 +130,64 @@ void FurnaceGUI::drawSongInfo() { e->stop(); } } + if (ImGui::IsWindowFocused()) curWindow=GUI_WINDOW_EDIT_CONTROLS; + ImGui::End(); +} + +void FurnaceGUI::drawSongInfo() { + if (!songInfoOpen) return; + if (ImGui::Begin("Song Information",&songInfoOpen)) { + ImGui::Text("Name"); + ImGui::SameLine(); + if (ImGui::InputText("##Name",&e->song.name)) updateWindowTitle(); + ImGui::Text("Author"); + ImGui::SameLine(); + ImGui::InputText("##Author",&e->song.author); + + ImGui::Text("TimeBase"); + ImGui::SameLine(); + ImGui::SetNextItemWidth(120.0f*dpiScale); + unsigned char realTB=e->song.timeBase+1; + if (ImGui::InputScalar("##TimeBase",ImGuiDataType_U8,&realTB,&_ONE,&_THREE)) { + if (realTB<1) realTB=1; + if (realTB>16) realTB=16; + e->song.timeBase=realTB-1; + } + + ImGui::Text("Speed"); + ImGui::SameLine(); + ImGui::SetNextItemWidth(120.0f*dpiScale); + ImGui::InputScalar("##Speed1",ImGuiDataType_U8,&e->song.speed1,&_ONE,&_THREE); + ImGui::SameLine(); + ImGui::SetNextItemWidth(120.0f*dpiScale); + ImGui::InputScalar("##Speed2",ImGuiDataType_U8,&e->song.speed2,&_ONE,&_THREE); + + ImGui::Text("Highlight"); + ImGui::SameLine(); + ImGui::SetNextItemWidth(120.0f*dpiScale); + ImGui::InputScalar("##Highlight1",ImGuiDataType_U8,&e->song.hilightA,&_ONE,&_THREE); + ImGui::SameLine(); + ImGui::SetNextItemWidth(120.0f*dpiScale); + ImGui::InputScalar("##Highlight2",ImGuiDataType_U8,&e->song.hilightB,&_ONE,&_THREE); + + ImGui::Text("Rate"); + ImGui::SameLine(); + ImGui::SetNextItemWidth(120.0f*dpiScale); + int setHz=e->song.hz; + if (ImGui::InputInt("##Rate",&setHz)) { + if (setHz<10) setHz=10; + if (setHz>999) setHz=999; + e->setSongRate(setHz,setHz<52); + } + if (e->song.hz==50) { + ImGui::SameLine(); + ImGui::Text("PAL"); + } + if (e->song.hz==60) { + ImGui::SameLine(); + ImGui::Text("NTSC"); + } + } if (ImGui::IsWindowFocused()) curWindow=GUI_WINDOW_SONG_INFO; ImGui::End(); } @@ -582,6 +645,7 @@ void FurnaceGUI::drawPattern() { startSelection(j,0,i); } if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem)) { + //ImGui::SetTooltip("N: %d O: %d",pat->data[i][0],pat->data[i][1]); updateSelection(j,0,i); } ImGui::PopStyleColor(); @@ -822,6 +886,11 @@ void FurnaceGUI::keyDown(SDL_Event& ev) { pat->data[selStart.y][0]=num%12; pat->data[selStart.y][1]=num/12; + if (pat->data[selStart.y][0]==0) { + pat->data[selStart.y][0]=12; + pat->data[selStart.y][1]--; + } + pat->data[selStart.y][2]=curIns; editAdvance(); } catch (std::out_of_range& e) { } @@ -994,6 +1063,7 @@ int FurnaceGUI::load(String path) { e->initDispatch(); e->reset(); } + updateWindowTitle(); return 0; } @@ -1080,6 +1150,7 @@ bool FurnaceGUI::loop() { ImGui::EndMenu(); } if (ImGui::BeginMenu("window")) { + if (ImGui::MenuItem("play/edit controls")) editControlsOpen=!editControlsOpen; if (ImGui::MenuItem("song information")) songInfoOpen=!songInfoOpen; if (ImGui::MenuItem("instruments")) insListOpen=!insListOpen; if (ImGui::MenuItem("instrument editor")) insEditOpen=!insEditOpen; @@ -1091,6 +1162,7 @@ bool FurnaceGUI::loop() { ImGui::DockSpaceOverViewport(); + drawEditControls(); drawSongInfo(); drawOrders(); drawInsList(); @@ -1191,6 +1263,8 @@ bool FurnaceGUI::init() { logE("could not load pattern font!\n"); return false; } + + updateWindowTitle(); return true; } @@ -1205,6 +1279,7 @@ FurnaceGUI::FurnaceGUI(): curOctave(3), oldRow(0), editStep(1), + editControlsOpen(true), ordersOpen(true), insListOpen(true), songInfoOpen(true), diff --git a/src/gui/gui.h b/src/gui/gui.h index 7869eadcc..5320ad71d 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -35,6 +35,7 @@ enum FurnaceGUIColors { enum FurnaceGUIWindows { GUI_WINDOW_NOTHING=0, + GUI_WINDOW_EDIT_CONTROLS, GUI_WINDOW_SONG_INFO, GUI_WINDOW_ORDERS, GUI_WINDOW_INS_LIST, @@ -69,7 +70,7 @@ class FurnaceGUI { ImVec4 volColors[128]; int curIns, curOctave, oldRow, editStep; - bool ordersOpen, insListOpen, songInfoOpen, patternOpen, insEditOpen; + bool editControlsOpen, ordersOpen, insListOpen, songInfoOpen, patternOpen, insEditOpen; SelectionPoint selStart, selEnd; bool selecting, curNibble; FurnaceGUIWindows curWindow; @@ -94,6 +95,9 @@ class FurnaceGUI { float nextScroll; + void updateWindowTitle(); + + void drawEditControls(); void drawSongInfo(); void drawOrders(); void drawInsList();