add iPod and GRUB bootloader beeper tune export (#2441)
This commit is contained in:
parent
24b629c73c
commit
055266090a
|
@ -822,6 +822,8 @@ src/engine/export/amigaValidation.cpp
|
|||
src/engine/export/sapr.cpp
|
||||
src/engine/export/tiuna.cpp
|
||||
src/engine/export/zsm.cpp
|
||||
src/engine/export/ipod.cpp
|
||||
src/engine/export/grub.cpp
|
||||
|
||||
src/engine/effect/abstract.cpp
|
||||
src/engine/effect/dummy.cpp
|
||||
|
|
|
@ -675,6 +675,8 @@ class DivEngine {
|
|||
friend class DivExportSAPR;
|
||||
friend class DivExportTiuna;
|
||||
friend class DivExportZSM;
|
||||
friend class DivExportiPod;
|
||||
friend class DivExportGRUB;
|
||||
|
||||
public:
|
||||
DivSong song;
|
||||
|
|
|
@ -23,6 +23,8 @@
|
|||
#include "export/sapr.h"
|
||||
#include "export/tiuna.h"
|
||||
#include "export/zsm.h"
|
||||
#include "export/ipod.h"
|
||||
#include "export/grub.h"
|
||||
|
||||
DivROMExport* DivEngine::buildROM(DivROMExportOptions sys) {
|
||||
DivROMExport* exporter=NULL;
|
||||
|
@ -39,6 +41,12 @@ DivROMExport* DivEngine::buildROM(DivROMExportOptions sys) {
|
|||
case DIV_ROM_SAP_R:
|
||||
exporter=new DivExportSAPR;
|
||||
break;
|
||||
case DIV_ROM_IPOD:
|
||||
exporter=new DivExportiPod;
|
||||
break;
|
||||
case DIV_ROM_GRUB:
|
||||
exporter=new DivExportGRUB;
|
||||
break;
|
||||
default:
|
||||
exporter=new DivROMExport;
|
||||
break;
|
||||
|
|
|
@ -32,6 +32,8 @@ enum DivROMExportOptions {
|
|||
DIV_ROM_ZSM,
|
||||
DIV_ROM_TIUNA,
|
||||
DIV_ROM_SAP_R,
|
||||
DIV_ROM_IPOD,
|
||||
DIV_ROM_GRUB,
|
||||
|
||||
DIV_ROM_MAX
|
||||
};
|
||||
|
|
204
src/engine/export/grub.cpp
Normal file
204
src/engine/export/grub.cpp
Normal file
|
@ -0,0 +1,204 @@
|
|||
/**
|
||||
* 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 "grub.h"
|
||||
#include "../engine.h"
|
||||
#include "../ta-log.h"
|
||||
#include <fmt/printf.h>
|
||||
#include <array>
|
||||
#include <vector>
|
||||
|
||||
void DivExportGRUB::run() {
|
||||
bool grubExportBin=conf.getBool("exportBin",false);
|
||||
|
||||
int BEEPER=-1;
|
||||
int IGNORED=0;
|
||||
|
||||
// Locate system index.
|
||||
for (int i=0; i<e->song.systemLen; i++) {
|
||||
if (e->song.system[i] == DIV_SYSTEM_PCSPKR) {
|
||||
if (BEEPER>=0) {
|
||||
IGNORED++;
|
||||
logAppendf("Ignoring duplicate Beeper id %d",i);
|
||||
break;
|
||||
}
|
||||
BEEPER=i;
|
||||
logAppendf("PC Speaker detected as chip id %d",i);
|
||||
break;
|
||||
} else {
|
||||
IGNORED++;
|
||||
logAppendf("Ignoring chip id %d, system id %d",i,(int)e->song.system[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (BEEPER<0) {
|
||||
logAppendf("ERROR: Could not find PC Speaker/Beeper");
|
||||
failed=true;
|
||||
running=false;
|
||||
return;
|
||||
}
|
||||
if (IGNORED>0) {
|
||||
logAppendf("WARNING: iPod .tone export ignoring %d unsupported system%c",IGNORED,IGNORED>1?'s':' ');
|
||||
}
|
||||
|
||||
size_t tickCount=0;
|
||||
|
||||
e->stop();
|
||||
e->repeatPattern=false;
|
||||
e->setOrder(0);
|
||||
|
||||
logAppend("playing and logging register writes...");
|
||||
|
||||
int oldFreq = 0;
|
||||
int freq = 0;
|
||||
|
||||
e->synchronizedSoft([&]() {
|
||||
double origRate = e->got.rate;
|
||||
double rate = MIN(e->curSubSong->hz,1000.0);
|
||||
logAppendf("export rate is %d hz",(int)rate);
|
||||
int tempo = (int)(60000.0/(1000.0/rate));
|
||||
e->got.rate=rate;
|
||||
|
||||
// Determine loop point.
|
||||
int loopOrder=0;
|
||||
int loopRow=0;
|
||||
int loopEnd=0;
|
||||
e->walkSong(loopOrder,loopRow,loopEnd);
|
||||
logAppendf("loop point: %d %d",loopOrder,loopRow);
|
||||
e->warnings="";
|
||||
|
||||
auto w = new SafeWriter;
|
||||
w->init();
|
||||
|
||||
// Reset the playback state.
|
||||
e->curOrder=0;
|
||||
e->freelance=false;
|
||||
e->playing=false;
|
||||
e->extValuePresent=false;
|
||||
e->remainingLoops=-1;
|
||||
|
||||
e->disCont[BEEPER].dispatch->toggleRegisterDump(true);
|
||||
|
||||
// Prepare to write song data.
|
||||
e->playSub(false);
|
||||
bool done=false;
|
||||
|
||||
logAppend("writing data...");
|
||||
progress[0].amount=0.15f;
|
||||
|
||||
int wait_tempo = 0;
|
||||
if (grubExportBin)
|
||||
w->writeI(tempo); // write tempo
|
||||
else
|
||||
w->writeText(fmt::sprintf("%d",tempo)); // write tempo
|
||||
|
||||
while (!done) {
|
||||
if (e->nextTick(false,true) || !e->playing) {
|
||||
done=true;
|
||||
}
|
||||
|
||||
// get register dumps
|
||||
uint8_t* regPool = e->disCont[BEEPER].dispatch->getRegisterPool();
|
||||
int chipClock = e->disCont[BEEPER].dispatch->chipClock;
|
||||
freq = (int)(regPool[0]|(regPool[1]<<8));
|
||||
if (freq > 0) freq = chipClock/freq;
|
||||
|
||||
// write wait
|
||||
tickCount++;
|
||||
int totalWait=e->cycles;
|
||||
if (totalWait>0 && !done) {
|
||||
while (totalWait) {
|
||||
wait_tempo++;
|
||||
if (freq != oldFreq || wait_tempo == 65535) {
|
||||
if (grubExportBin) {
|
||||
w->writeS(oldFreq); // pitch
|
||||
w->writeS(wait_tempo); // duration
|
||||
} else {
|
||||
w->writeText(fmt::sprintf(" %d %d", oldFreq, wait_tempo));
|
||||
}
|
||||
oldFreq = freq;
|
||||
wait_tempo = 0;
|
||||
}
|
||||
totalWait--;
|
||||
tickCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!grubExportBin) w->writeText(fmt::sprintf("\n")); // end song
|
||||
// end of song
|
||||
|
||||
// done - close out.
|
||||
e->got.rate=origRate;
|
||||
e->disCont[BEEPER].dispatch->getRegisterWrites().clear();
|
||||
e->disCont[BEEPER].dispatch->toggleRegisterDump(false);
|
||||
|
||||
e->remainingLoops=-1;
|
||||
e->playing=false;
|
||||
e->freelance=false;
|
||||
e->extValuePresent=false;
|
||||
|
||||
output.push_back(DivROMExportOutput(grubExportBin?"export.bin":"export.txt",w));
|
||||
});
|
||||
|
||||
|
||||
progress[0].amount=1.0f;
|
||||
|
||||
logAppend("finished!");
|
||||
|
||||
running=false;
|
||||
}
|
||||
|
||||
bool DivExportGRUB::go(DivEngine* eng) {
|
||||
progress[0].name="Progress";
|
||||
progress[0].amount=0.0f;
|
||||
|
||||
e=eng;
|
||||
running=true;
|
||||
failed=false;
|
||||
mustAbort=false;
|
||||
exportThread=new std::thread(&DivExportGRUB::run,this);
|
||||
return true;
|
||||
}
|
||||
|
||||
void DivExportGRUB::wait() {
|
||||
if (exportThread!=NULL) {
|
||||
logV("waiting for export thread...");
|
||||
exportThread->join();
|
||||
delete exportThread;
|
||||
}
|
||||
}
|
||||
|
||||
void DivExportGRUB::abort() {
|
||||
mustAbort=true;
|
||||
wait();
|
||||
}
|
||||
|
||||
bool DivExportGRUB::isRunning() {
|
||||
return running;
|
||||
}
|
||||
|
||||
bool DivExportGRUB::hasFailed() {
|
||||
return failed;
|
||||
}
|
||||
|
||||
DivROMExportProgress DivExportGRUB::getProgress(int index) {
|
||||
if (index<0 || index>1) return progress[1];
|
||||
return progress[index];
|
||||
}
|
38
src/engine/export/grub.h
Normal file
38
src/engine/export/grub.h
Normal file
|
@ -0,0 +1,38 @@
|
|||
/**
|
||||
* 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 "../export.h"
|
||||
|
||||
#include <thread>
|
||||
|
||||
class DivExportGRUB: public DivROMExport {
|
||||
DivEngine* e;
|
||||
std::thread* exportThread;
|
||||
DivROMExportProgress progress[2];
|
||||
bool running, failed, mustAbort;
|
||||
void run();
|
||||
public:
|
||||
bool go(DivEngine* e);
|
||||
bool isRunning();
|
||||
bool hasFailed();
|
||||
void abort();
|
||||
void wait();
|
||||
DivROMExportProgress getProgress(int index=0);
|
||||
~DivExportGRUB() {}
|
||||
};
|
194
src/engine/export/ipod.cpp
Normal file
194
src/engine/export/ipod.cpp
Normal file
|
@ -0,0 +1,194 @@
|
|||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// thanks asiekierka! (I used your SAP-R export code as a base for this)
|
||||
|
||||
#include "ipod.h"
|
||||
#include "../engine.h"
|
||||
#include "../ta-log.h"
|
||||
#include <fmt/printf.h>
|
||||
#include <array>
|
||||
#include <vector>
|
||||
|
||||
void DivExportiPod::run() {
|
||||
int BEEPER=-1;
|
||||
int IGNORED=0;
|
||||
|
||||
// Locate system index.
|
||||
for (int i=0; i<e->song.systemLen; i++) {
|
||||
if (e->song.system[i] == DIV_SYSTEM_PCSPKR) {
|
||||
if (BEEPER>=0) {
|
||||
IGNORED++;
|
||||
logAppendf("Ignoring duplicate Beeper id %d",i);
|
||||
break;
|
||||
}
|
||||
BEEPER=i;
|
||||
logAppendf("PC Speaker detected as chip id %d",i);
|
||||
break;
|
||||
} else {
|
||||
IGNORED++;
|
||||
logAppendf("Ignoring chip id %d, system id %d",i,(int)e->song.system[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (BEEPER<0) {
|
||||
logAppendf("ERROR: Could not find PC Speaker/Beeper");
|
||||
failed=true;
|
||||
running=false;
|
||||
return;
|
||||
}
|
||||
if (IGNORED>0) {
|
||||
logAppendf("WARNING: iPod .tone export ignoring %d unsupported system%c",IGNORED,IGNORED>1?'s':' ');
|
||||
}
|
||||
|
||||
size_t tickCount=0;
|
||||
|
||||
double rate = 1000.0;
|
||||
|
||||
e->stop();
|
||||
e->repeatPattern=false;
|
||||
e->setOrder(0);
|
||||
|
||||
logAppend("playing and logging register writes...");
|
||||
|
||||
int oldFreq = 0;
|
||||
int freq = 0;
|
||||
|
||||
e->synchronizedSoft([&]() {
|
||||
double origRate = e->got.rate;
|
||||
e->got.rate=rate;
|
||||
|
||||
// Determine loop point.
|
||||
int loopOrder=0;
|
||||
int loopRow=0;
|
||||
int loopEnd=0;
|
||||
e->walkSong(loopOrder,loopRow,loopEnd);
|
||||
logAppendf("loop point: %d %d",loopOrder,loopRow);
|
||||
e->warnings="";
|
||||
|
||||
auto w = new SafeWriter;
|
||||
w->init();
|
||||
|
||||
w->writeText(fmt::sprintf("%s\n", e->song.name));
|
||||
|
||||
// Reset the playback state.
|
||||
e->curOrder=0;
|
||||
e->freelance=false;
|
||||
e->playing=false;
|
||||
e->extValuePresent=false;
|
||||
e->remainingLoops=-1;
|
||||
|
||||
e->disCont[BEEPER].dispatch->toggleRegisterDump(true);
|
||||
|
||||
// Prepare to write song data.
|
||||
e->playSub(false);
|
||||
bool done=false;
|
||||
|
||||
logAppend("writing data...");
|
||||
progress[0].amount=0.15f;
|
||||
|
||||
int wait_ms = 0;
|
||||
|
||||
while (!done) {
|
||||
if (e->nextTick(false,true) || !e->playing) {
|
||||
done=true;
|
||||
}
|
||||
|
||||
// get register dumps
|
||||
uint8_t* regPool = e->disCont[BEEPER].dispatch->getRegisterPool();
|
||||
int chipClock = e->disCont[BEEPER].dispatch->chipClock;
|
||||
freq = (int)(regPool[0]|(regPool[1]<<8));
|
||||
if (freq > 0) freq = chipClock/freq;
|
||||
|
||||
// write wait
|
||||
tickCount++;
|
||||
int totalWait=e->cycles;
|
||||
if (totalWait>0 && !done) {
|
||||
while (totalWait) {
|
||||
wait_ms++;
|
||||
if (freq != oldFreq) {
|
||||
w->writeText(fmt::sprintf("%d %d\n", oldFreq, wait_ms));
|
||||
oldFreq = freq;
|
||||
wait_ms = 0;
|
||||
}
|
||||
totalWait--;
|
||||
tickCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
// end of song
|
||||
|
||||
// done - close out.
|
||||
e->got.rate=origRate;
|
||||
e->disCont[BEEPER].dispatch->getRegisterWrites().clear();
|
||||
e->disCont[BEEPER].dispatch->toggleRegisterDump(false);
|
||||
|
||||
e->remainingLoops=-1;
|
||||
e->playing=false;
|
||||
e->freelance=false;
|
||||
e->extValuePresent=false;
|
||||
|
||||
output.push_back(DivROMExportOutput("export.tone",w));
|
||||
});
|
||||
|
||||
|
||||
progress[0].amount=1.0f;
|
||||
|
||||
logAppend("finished!");
|
||||
|
||||
running=false;
|
||||
}
|
||||
|
||||
bool DivExportiPod::go(DivEngine* eng) {
|
||||
progress[0].name="Progress";
|
||||
progress[0].amount=0.0f;
|
||||
|
||||
e=eng;
|
||||
running=true;
|
||||
failed=false;
|
||||
mustAbort=false;
|
||||
exportThread=new std::thread(&DivExportiPod::run,this);
|
||||
return true;
|
||||
}
|
||||
|
||||
void DivExportiPod::wait() {
|
||||
if (exportThread!=NULL) {
|
||||
logV("waiting for export thread...");
|
||||
exportThread->join();
|
||||
delete exportThread;
|
||||
}
|
||||
}
|
||||
|
||||
void DivExportiPod::abort() {
|
||||
mustAbort=true;
|
||||
wait();
|
||||
}
|
||||
|
||||
bool DivExportiPod::isRunning() {
|
||||
return running;
|
||||
}
|
||||
|
||||
bool DivExportiPod::hasFailed() {
|
||||
return failed;
|
||||
}
|
||||
|
||||
DivROMExportProgress DivExportiPod::getProgress(int index) {
|
||||
if (index<0 || index>1) return progress[1];
|
||||
return progress[index];
|
||||
}
|
38
src/engine/export/ipod.h
Normal file
38
src/engine/export/ipod.h
Normal file
|
@ -0,0 +1,38 @@
|
|||
/**
|
||||
* 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 "../export.h"
|
||||
|
||||
#include <thread>
|
||||
|
||||
class DivExportiPod: public DivROMExport {
|
||||
DivEngine* e;
|
||||
std::thread* exportThread;
|
||||
DivROMExportProgress progress[2];
|
||||
bool running, failed, mustAbort;
|
||||
void run();
|
||||
public:
|
||||
bool go(DivEngine* e);
|
||||
bool isRunning();
|
||||
bool hasFailed();
|
||||
void abort();
|
||||
void wait();
|
||||
DivROMExportProgress getProgress(int index=0);
|
||||
~DivExportiPod() {}
|
||||
};
|
|
@ -119,4 +119,27 @@ void DivEngine::registerROMExports() {
|
|||
},
|
||||
false, DIV_REQPOL_EXACT
|
||||
);
|
||||
|
||||
romExportDefs[DIV_ROM_IPOD]=new DivROMExportDef(
|
||||
"iPod .tone alarm", "AArt1256",
|
||||
"iPod Beeper (.tone) Alarm export\n"
|
||||
"for playback, you can drag the resulting file\n"
|
||||
"into iPod_Control/Tones to your iPod IN DISK MODE",
|
||||
"alarm tone files", ".tone",
|
||||
{
|
||||
DIV_SYSTEM_PCSPKR
|
||||
},
|
||||
false, DIV_REQPOL_ANY
|
||||
);
|
||||
|
||||
romExportDefs[DIV_ROM_GRUB]=new DivROMExportDef(
|
||||
"GRUB_INIT_TUNE", "AArt1256",
|
||||
"GRUB_INIT_TUNE export\n"
|
||||
"for use with the GRUB bootloader using the \"play\" command",
|
||||
"Text/Binary files", NULL,
|
||||
{
|
||||
DIV_SYSTEM_PCSPKR
|
||||
},
|
||||
false, DIV_REQPOL_ANY
|
||||
);
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@ const char* aboutLine[]={
|
|||
"",
|
||||
_N("-- program --"),
|
||||
"tildearrow",
|
||||
"AArt1256",
|
||||
_N("A M 4 N (intro tune)"),
|
||||
"Adam Lederer",
|
||||
"akumanatt",
|
||||
|
|
|
@ -371,6 +371,16 @@ void FurnaceGUI::drawExportROM(bool onWindow) {
|
|||
}
|
||||
break;
|
||||
}
|
||||
case DIV_ROM_GRUB: {
|
||||
bool grubExportBin=romConfig.getBool("exportBin",false);
|
||||
if (ImGui::Checkbox(_("export binary file"),&grubExportBin)) {
|
||||
altered=true;
|
||||
}
|
||||
if (altered) {
|
||||
romConfig.set("exportBin",grubExportBin);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_ROM_ABSTRACT:
|
||||
ImGui::TextWrapped("%s",_("select a target from the menu at the top of this dialog."));
|
||||
break;
|
||||
|
|
Loading…
Reference in a new issue