/** * 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 #include "imgui.h" #include "IconsFontAwesome4.h" void FurnaceGUI::drawRefPlayer() { DivFilePlayer* fp=e->getFilePlayer(); if (nextWindow==GUI_WINDOW_REF_PLAYER) { refPlayerOpen=true; ImGui::SetNextWindowFocus(); nextWindow=GUI_WINDOW_NOTHING; } fp->setActive(refPlayerOpen); if (!refPlayerOpen) return; if (ImGui::Begin("Music Player",&refPlayerOpen,globalWinFlags,_("Music Player"))) { bool playPosNegative=false; ssize_t playPos=fp->getPos(); if (playPos<0) { playPos=-playPos; playPosNegative=true; } size_t minPos=0; size_t maxPos=fp->getFileInfo().frames; int fileRate=fp->getFileInfo().samplerate; if (fileRate<1) fileRate=1; int posHours=(playPos/fileRate)/3600; int posMinutes=((playPos/fileRate)/60)%60; int posSeconds=(playPos/fileRate)%60; int posMillis=(1000*(playPos%fileRate))/fileRate; if (fp->isLoaded()) { if (playPosNegative) { ImGui::Text("-%d:%02d:%02d.%03d",posHours,posMinutes,posSeconds,posMillis); } else { ImGui::Text("%d:%02d:%02d.%03d",posHours,posMinutes,posSeconds,posMillis); } } else { ImGui::TextUnformatted(_("no file loaded")); } ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); if (ImGui::SliderScalar("##Position",ImGuiDataType_U64,&playPos,&minPos,&maxPos,"")) { fp->setPos(playPos); } if (ImGui::Button(ICON_FA_FOLDER_OPEN "##Open")) { openFileDialog(GUI_FILE_MUSIC_OPEN); } if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) { e->synchronizedSoft([this,fp]() { if (!fp->closeFile()) { showError(_("you haven't loaded a file!")); } }); } ImGui::SetItemTooltip(_("open file\n(right click to unload current file)")); ImGui::SameLine(); if (ImGui::Button(ICON_FA_STEP_BACKWARD)) { // handled outside } if (fp->isPlaying()) { if (ImGui::IsItemClicked(ImGuiMouseButton_Left)) { int cueSeconds=0; int cueMicros=0; fp->stop(); e->getFilePlayerCue(cueSeconds,cueMicros); fp->setPosSeconds(cueSeconds,cueMicros); } if (ImGui::IsItemClicked(ImGuiMouseButton_Middle)) { fp->stop(); fp->setPos(0); } if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) { int cueSeconds=0; int cueMicros=0; e->getFilePlayerCue(cueSeconds,cueMicros); fp->setPosSeconds(cueSeconds,cueMicros); } ImGui::SetItemTooltip( _("left click: go to cue position\n" "middle click: go to beginning\n" "right click: go to cue position (but don't stop)") ); } else { if (ImGui::IsItemClicked(ImGuiMouseButton_Left)) { // try setting cue pos ssize_t curSeconds=0; unsigned int curMicros=0; fp->getPosSeconds(curSeconds,curMicros); DivSongTimestamps::Timestamp rowTS=e->curSubSong->ts.getTimes(curOrder,0); if (rowTS.seconds==-1) { showError(_("the first row of this order isn't going to play.")); } else { // calculate difference and set cue pos curSeconds-=rowTS.seconds; int curMicrosI=curMicros-rowTS.micros; while (curMicrosI<0) { curMicrosI+=1000000; curSeconds--; } e->setFilePlayerCue(curSeconds,curMicrosI); } } if (ImGui::IsItemClicked(ImGuiMouseButton_Middle)) { fp->setPos(0); } if (ImGui::BeginPopupContextItem("Edit Cue Position",ImGuiPopupFlags_MouseButtonRight)) { ImGui::TextUnformatted(_("Set cue position at first order:")); int cueSeconds=0; int cueMicros=0; bool altered=false; e->getFilePlayerCue(cueSeconds,cueMicros); // TODO: improve this... ImGui::SetNextItemWidth(240.0f*dpiScale); if (ImGui::InputInt(_("Seconds##CuePosS"),&cueSeconds)) { if (cueSeconds<-3600) cueSeconds=-3600; if (cueSeconds>3600) cueSeconds=3600; altered=true; } ImGui::SetNextItemWidth(240.0f*dpiScale); if (ImGui::InputInt(_("Microseconds##CuePosM"),&cueMicros,1000,10000)) { if (cueMicros<0) cueMicros=0; if (cueMicros>999999) cueMicros=999999; altered=true; } if (altered) { e->setFilePlayerCue(cueSeconds,cueMicros); } if (ImGui::Button(_("OK"))) { ImGui::CloseCurrentPopup(); } ImGui::EndPopup(); } ImGui::SetItemTooltip( _("left click: set cue position here\n" " - current playback time becomes position at first row of current order\n" "middle click: go to beginning\n" "right click: fine edit cue position") ); } ImGui::SameLine(); if (fp->isPlaying()) { pushToggleColors(true); if (ImGui::Button(ICON_FA_PAUSE "##Pause")) { fp->stop(); } if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) { // try setting cue pos ssize_t curSeconds=0; unsigned int curMicros=0; fp->getPosSeconds(curSeconds,curMicros); DivSongTimestamps::Timestamp rowTS=e->curSubSong->ts.getTimes(curOrder,0); if (rowTS.seconds==-1) { showError(_("the first row of this order isn't going to play.")); } else { // calculate difference and set cue pos curSeconds-=rowTS.seconds; int curMicrosI=curMicros-rowTS.micros; while (curMicrosI<0) { curMicrosI+=1000000; curSeconds--; } e->setFilePlayerCue(curSeconds,curMicrosI); fp->stop(); } } ImGui::SetItemTooltip(_("pause\n(right click to set cue position and pause)")); popToggleColors(); } else { if (ImGui::Button(ICON_FA_PLAY "##Play")) { fp->play(); } ImGui::SetItemTooltip(_("play")); } ImGui::SameLine(); if (ImGui::Button(ICON_FA_STEP_FORWARD "##PlayPos")) { // handled outside } if (ImGui::IsItemClicked(ImGuiMouseButton_Left) || ImGui::IsItemClicked(ImGuiMouseButton_Right)) { DivSongTimestamps::Timestamp rowTS; if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) { rowTS=e->curSubSong->ts.getTimes(cursor.order,cursor.y); } else { rowTS=e->curSubSong->ts.getTimes(curOrder,0); } int cueSeconds=0; int cueMicros=0; e->getFilePlayerCue(cueSeconds,cueMicros); if (rowTS.seconds==-1) { if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) { showError(_("the row that the pattern cursor is at isn't going to play. trying moving the cursor.")); } else { showError(_("the first row of this order isn't going to play. try another order.")); } } else { int finalSeconds=rowTS.seconds+cueSeconds; int finalMicros=rowTS.micros+cueMicros; while (finalMicros>=1000000) { finalMicros-=1000000; finalSeconds++; } fp->setPosSeconds(finalSeconds,finalMicros); fp->play(); } } if (ImGui::IsItemHovered() && (ImGui::IsMouseReleased(ImGuiMouseButton_Left) || ImGui::IsMouseReleased(ImGuiMouseButton_Right))) { fp->stop(); } ImGui::SetItemTooltip(_( "hold left click to play from current order\n" "hold right click to play from pattern cursor position\n" "release mouse button to stop" )); ImGui::SameLine(); pushToggleColors(filePlayerSync); if (ImGui::Button(_("Sync"))) { filePlayerSync=!filePlayerSync; } ImGui::SetItemTooltip(_("synchronize playback with tracker playback")); popToggleColors(); e->setFilePlayerSync(filePlayerSync); ImGui::SameLine(); ImGui::Text(_("Mix:")); float vol=fp->getVolume(); ImGui::SameLine(); ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x); if (ImGui::SliderFloat("##Volume",&vol,-1.0f,1.0f,_("<-- Tracker / Reference -->"))) { if (vol<-1.0f) vol=-1.0f; if (vol>1.0f) vol=1.0f; fp->setVolume(vol); } if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) { fp->setVolume(0.0f); } ImGui::SetItemTooltip(_("right click to reset")); //ImGui::Text("Memory usage: %" PRIu64 "K",fp->getMemUsage()>>10); } if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_REF_PLAYER; ImGui::End(); if (!refPlayerOpen) { fp->stop(); e->setFilePlayerSync(false); } }