file player cue point and loop tracking
no loop trail yet
This commit is contained in:
parent
3c106f7861
commit
319da2d391
7 changed files with 213 additions and 30 deletions
|
|
@ -1662,6 +1662,16 @@ void DivEngine::setFilePlayerSync(bool doSync) {
|
|||
filePlayerSync=doSync;
|
||||
}
|
||||
|
||||
void DivEngine::getFilePlayerCue(int& seconds, int& micros) {
|
||||
seconds=filePlayerCueSeconds;
|
||||
micros=filePlayerCueMicros;
|
||||
}
|
||||
|
||||
void DivEngine::setFilePlayerCue(int seconds, int micros) {
|
||||
filePlayerCueSeconds=seconds;
|
||||
filePlayerCueMicros=micros;
|
||||
}
|
||||
|
||||
void DivEngine::syncFilePlayer() {
|
||||
if (curFilePlayer==NULL) return;
|
||||
int finalSeconds=totalSeconds+filePlayerCueSeconds;
|
||||
|
|
|
|||
|
|
@ -604,6 +604,8 @@ class DivEngine {
|
|||
bool filePlayerSync;
|
||||
ssize_t filePlayerCueSeconds;
|
||||
unsigned int filePlayerCueMicros;
|
||||
int filePlayerLoopTrail;
|
||||
int curFilePlayerTrail;
|
||||
|
||||
size_t totalProcessed;
|
||||
|
||||
|
|
@ -630,8 +632,6 @@ class DivEngine {
|
|||
void runMidiTime(int totalCycles=1);
|
||||
bool shallSwitchCores();
|
||||
|
||||
void syncFilePlayer();
|
||||
|
||||
void testFunction();
|
||||
|
||||
bool loadDMF(unsigned char* file, size_t len);
|
||||
|
|
@ -762,6 +762,11 @@ class DivEngine {
|
|||
// get whether the player is synchronized with song playback.
|
||||
bool getFilePlayerSync();
|
||||
void setFilePlayerSync(bool doSync);
|
||||
// get/set file player cue position.
|
||||
void getFilePlayerCue(int& seconds, int& micros);
|
||||
void setFilePlayerCue(int seconds, int micros);
|
||||
// UNSAFE - sync file player to current playback position.
|
||||
void syncFilePlayer();
|
||||
|
||||
// save as .dmf.
|
||||
SafeWriter* saveDMF(unsigned char version);
|
||||
|
|
@ -1573,6 +1578,8 @@ class DivEngine {
|
|||
filePlayerSync(false),
|
||||
filePlayerCueSeconds(0),
|
||||
filePlayerCueMicros(0),
|
||||
filePlayerLoopTrail(0),
|
||||
curFilePlayerTrail(0),
|
||||
totalProcessed(0),
|
||||
renderPoolThreads(0),
|
||||
renderPool(NULL),
|
||||
|
|
|
|||
|
|
@ -208,6 +208,19 @@ void DivFilePlayer::mix(float** buf, int chans, unsigned int size) {
|
|||
}
|
||||
|
||||
for (unsigned int i=0; i<size; i++) {
|
||||
// acknowledge pending events
|
||||
if (pendingPosOffset==i) {
|
||||
pendingPosOffset=UINT_MAX;
|
||||
playPos=pendingPos;
|
||||
rateAccum=0;
|
||||
}
|
||||
if (pendingPlayOffset==i) {
|
||||
playing=true;
|
||||
}
|
||||
if (pendingStopOffset==i) {
|
||||
playing=false;
|
||||
}
|
||||
|
||||
ssize_t blockIndex=playPos>>DIV_FPCACHE_BLOCK_SHIFT;
|
||||
if (blockIndex!=lastWantBlock) {
|
||||
wantBlock=playPos;
|
||||
|
|
@ -285,22 +298,49 @@ ssize_t DivFilePlayer::getPos() {
|
|||
return playPos;
|
||||
}
|
||||
|
||||
void DivFilePlayer::getPosSeconds(ssize_t& seconds, unsigned int& micros) {
|
||||
if (sf==NULL) {
|
||||
seconds=0;
|
||||
micros=0;
|
||||
return;
|
||||
}
|
||||
double microsD=playPos%si.samplerate;
|
||||
seconds=playPos/si.samplerate;
|
||||
micros=(int)((1000000.0*microsD)/(double)si.samplerate);
|
||||
}
|
||||
|
||||
ssize_t DivFilePlayer::setPos(ssize_t newPos, unsigned int offset) {
|
||||
playPos=newPos;
|
||||
rateAccum=0;
|
||||
wantBlock=playPos;
|
||||
logD("DivFilePlayer: setPos(%" PRIi64 ")",newPos);
|
||||
return playPos;
|
||||
if (offset==UINT_MAX) {
|
||||
playPos=newPos;
|
||||
rateAccum=0;
|
||||
wantBlock=playPos;
|
||||
logD("DivFilePlayer: setPos(%" PRIi64 ")",newPos);
|
||||
return playPos;
|
||||
} else {
|
||||
pendingPosOffset=offset;
|
||||
pendingPos=newPos;
|
||||
wantBlock=playPos;
|
||||
logD("DivFilePlayer: offset %u setPos(%" PRIi64 ")",offset,newPos);
|
||||
return newPos;
|
||||
}
|
||||
}
|
||||
|
||||
ssize_t DivFilePlayer::setPosSeconds(ssize_t seconds, unsigned int micros, unsigned int offset) {
|
||||
if (sf==NULL) return 0;
|
||||
double microsD=(double)si.samplerate*((double)micros/1000000.0);
|
||||
playPos=seconds*si.samplerate+(int)microsD;
|
||||
rateAccum=0;
|
||||
wantBlock=playPos;
|
||||
logD("DivFilePlayer: setPosSeconds(%" PRIi64 ".%06d)",seconds,micros);
|
||||
return playPos;
|
||||
if (offset==UINT_MAX) {
|
||||
playPos=seconds*si.samplerate+(int)microsD;
|
||||
rateAccum=0;
|
||||
wantBlock=playPos;
|
||||
logD("DivFilePlayer: setPosSeconds(%" PRIi64 ".%06d)",seconds,micros);
|
||||
return playPos;
|
||||
} else {
|
||||
pendingPosOffset=offset;
|
||||
pendingPos=seconds*si.samplerate+(int)microsD;
|
||||
wantBlock=pendingPos;
|
||||
logD("DivFilePlayer: offset %u setPosSeconds(%" PRIi64 ".%06d)",offset,seconds,micros);
|
||||
return pendingPos;
|
||||
}
|
||||
}
|
||||
|
||||
size_t DivFilePlayer::getMemUsage() {
|
||||
|
|
@ -336,13 +376,23 @@ bool DivFilePlayer::isPlaying() {
|
|||
}
|
||||
|
||||
void DivFilePlayer::play(unsigned int offset) {
|
||||
logV("DivFilePlayer: playing");
|
||||
playing=true;
|
||||
if (offset!=UINT_MAX) {
|
||||
pendingPlayOffset=offset;
|
||||
logV("DivFilePlayer: playing (offset: %u)",offset);
|
||||
} else {
|
||||
playing=true;
|
||||
logV("DivFilePlayer: playing");
|
||||
}
|
||||
}
|
||||
|
||||
void DivFilePlayer::stop(unsigned int offset) {
|
||||
logV("DivFilePlayer: stopping");
|
||||
playing=false;
|
||||
if (offset!=UINT_MAX) {
|
||||
pendingStopOffset=offset;
|
||||
logV("DivFilePlayer: stopping (offset: %u)",offset);
|
||||
} else {
|
||||
playing=false;
|
||||
logV("DivFilePlayer: stopping");
|
||||
}
|
||||
}
|
||||
|
||||
bool DivFilePlayer::closeFile() {
|
||||
|
|
@ -492,6 +542,10 @@ DivFilePlayer::DivFilePlayer():
|
|||
quitThread(false),
|
||||
threadHasQuit(false),
|
||||
isActive(false),
|
||||
pendingPos(0),
|
||||
pendingPosOffset(UINT_MAX),
|
||||
pendingPlayOffset(UINT_MAX),
|
||||
pendingStopOffset(UINT_MAX),
|
||||
cacheThread(NULL) {
|
||||
memset(&si,0,sizeof(SF_INFO));
|
||||
sincTable=DivFilterTables::getSincTable8();
|
||||
|
|
|
|||
|
|
@ -57,6 +57,11 @@ class DivFilePlayer {
|
|||
bool threadHasQuit;
|
||||
bool isActive;
|
||||
|
||||
ssize_t pendingPos;
|
||||
unsigned int pendingPosOffset;
|
||||
unsigned int pendingPlayOffset;
|
||||
unsigned int pendingStopOffset;
|
||||
|
||||
std::thread* cacheThread;
|
||||
std::mutex cacheMutex;
|
||||
std::mutex cacheThreadLock;
|
||||
|
|
@ -73,15 +78,16 @@ class DivFilePlayer {
|
|||
|
||||
void mix(float** buf, int chans, unsigned int size);
|
||||
ssize_t getPos();
|
||||
ssize_t setPos(ssize_t newPos, unsigned int offset=0);
|
||||
ssize_t setPosSeconds(ssize_t seconds, unsigned int micros, unsigned int offset=0);
|
||||
void getPosSeconds(ssize_t& seconds, unsigned int& micros);
|
||||
ssize_t setPos(ssize_t newPos, unsigned int offset=UINT_MAX);
|
||||
ssize_t setPosSeconds(ssize_t seconds, unsigned int micros, unsigned int offset=UINT_MAX);
|
||||
|
||||
bool isBlockPresent(ssize_t pos);
|
||||
bool setBlockPriority(ssize_t pos, bool priority);
|
||||
bool isLoaded();
|
||||
bool isPlaying();
|
||||
void play(unsigned int offset=0);
|
||||
void stop(unsigned int offset=0);
|
||||
void play(unsigned int offset=UINT_MAX);
|
||||
void stop(unsigned int offset=UINT_MAX);
|
||||
bool closeFile();
|
||||
bool loadFile(const char* path);
|
||||
|
||||
|
|
|
|||
|
|
@ -3106,6 +3106,22 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi
|
|||
// used by audio export to determine how many samples to write (otherwise it'll add silence at the end)
|
||||
lastLoopPos=size-runLeftG;
|
||||
logD("last loop pos: %d for a size of %d and runLeftG of %d",lastLoopPos,size,runLeftG);
|
||||
// if file player is synchronized then set its position to that of the loop row
|
||||
if (curFilePlayer && filePlayerSync) {
|
||||
if (curFilePlayer->isPlaying()) {
|
||||
DivSongTimestamps::Timestamp rowTS=curSubSong->ts.loopStartTime;
|
||||
int finalSeconds=rowTS.seconds+filePlayerCueSeconds;
|
||||
int finalMicros=rowTS.micros+filePlayerCueMicros;
|
||||
|
||||
while (finalMicros>=1000000) {
|
||||
finalMicros-=1000000;
|
||||
finalSeconds++;
|
||||
}
|
||||
|
||||
curFilePlayer->setPosSeconds(finalSeconds,finalMicros,lastLoopPos);
|
||||
}
|
||||
}
|
||||
// increase total loop count
|
||||
totalLoops++;
|
||||
// stop playing once we hit a specific number of loops (set during audio export)
|
||||
if (remainingLoops>0) {
|
||||
|
|
|
|||
|
|
@ -5892,9 +5892,15 @@ bool FurnaceGUI::loop() {
|
|||
}
|
||||
break;
|
||||
case GUI_FILE_MUSIC_OPEN:
|
||||
if (!e->getFilePlayer()->loadFile(copyOfName.c_str())) {
|
||||
showError(fmt::sprintf(_("Error while loading file!")));
|
||||
}
|
||||
e->synchronizedSoft([this,copyOfName]() {
|
||||
bool wasPlaying=e->getFilePlayer()->isPlaying();
|
||||
if (!e->getFilePlayer()->loadFile(copyOfName.c_str())) {
|
||||
showError(fmt::sprintf(_("Error while loading file!")));
|
||||
} else if (wasPlaying && filePlayerSync && refPlayerOpen && e->isPlaying()) {
|
||||
e->syncFilePlayer();
|
||||
e->getFilePlayer()->play();
|
||||
}
|
||||
});
|
||||
break;
|
||||
case GUI_FILE_TEST_OPEN:
|
||||
showWarning(fmt::sprintf(_("You opened: %s"),copyOfName),GUI_WARN_GENERIC);
|
||||
|
|
@ -7392,6 +7398,16 @@ bool FurnaceGUI::loop() {
|
|||
if (!fp->isPlaying()) {
|
||||
DivSongTimestamps::Timestamp rowTS=e->curSubSong->ts.getTimes(cursor.order,cursor.y);
|
||||
if (rowTS.seconds!=-1) {
|
||||
int cueSeconds=0;
|
||||
int cueMicros=0;
|
||||
e->getFilePlayerCue(cueSeconds,cueMicros);
|
||||
rowTS.seconds+=cueSeconds;
|
||||
rowTS.micros+=cueMicros;
|
||||
while (rowTS.micros>=1000000) {
|
||||
rowTS.micros-=1000000;
|
||||
rowTS.seconds++;
|
||||
}
|
||||
|
||||
fp->setPosSeconds(rowTS.seconds,rowTS.micros);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,10 +47,14 @@ void FurnaceGUI::drawRefPlayer() {
|
|||
int posMinutes=((playPos/fileRate)/60)%60;
|
||||
int posSeconds=(playPos/fileRate)%60;
|
||||
int posMillis=(1000*(playPos%fileRate))/fileRate;
|
||||
if (playPosNegative) {
|
||||
ImGui::Text("-%d:%02d:%02d.%03d",posHours,posMinutes,posSeconds,posMillis);
|
||||
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::Text("%d:%02d:%02d.%03d",posHours,posMinutes,posSeconds,posMillis);
|
||||
ImGui::TextUnformatted(_("no file loaded"));
|
||||
}
|
||||
|
||||
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
|
||||
|
|
@ -58,13 +62,80 @@ void FurnaceGUI::drawRefPlayer() {
|
|||
fp->setPos(playPos);
|
||||
}
|
||||
|
||||
if (ImGui::Button("Open")) {
|
||||
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_FAST_BACKWARD)) {
|
||||
fp->stop();
|
||||
fp->setPos(0);
|
||||
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::Text("Edit me");
|
||||
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()) {
|
||||
|
|
@ -72,11 +143,13 @@ void FurnaceGUI::drawRefPlayer() {
|
|||
if (ImGui::Button(ICON_FA_PAUSE "##Pause")) {
|
||||
fp->stop();
|
||||
}
|
||||
ImGui::SetItemTooltip(_("pause"));
|
||||
popToggleColors();
|
||||
} else {
|
||||
if (ImGui::Button(ICON_FA_PLAY "##Play")) {
|
||||
fp->play();
|
||||
}
|
||||
ImGui::SetItemTooltip(_("play"));
|
||||
}
|
||||
ImGui::SameLine();
|
||||
|
||||
|
|
@ -84,6 +157,7 @@ void FurnaceGUI::drawRefPlayer() {
|
|||
if (ImGui::Button(_("Sync"))) {
|
||||
filePlayerSync=!filePlayerSync;
|
||||
}
|
||||
ImGui::SetItemTooltip(_("synchronize playback with tracker playback"));
|
||||
popToggleColors();
|
||||
e->setFilePlayerSync(filePlayerSync);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue