SNES: add an option to not encode BRR with unstable filters

This is useful for seeking to anywhere within the sample using sample offset commands
This commit is contained in:
Natt Akuma 2024-06-22 14:37:00 +07:00
parent 6a7e7ba571
commit bdc66ae33e
9 changed files with 40 additions and 16 deletions

View file

@ -586,6 +586,7 @@ size | description
| - 0: BRR emphasis
1 | flags 2 (>=159) or reserved
| - 0: dither
| - 1: no BRR filters (>=213)
4 | loop start
| - -1 means no loop
4 | loop end

View file

@ -138,7 +138,7 @@ void brrEncodeBlock(const short* buf, unsigned char* out, unsigned char range, u
}
}
long brrEncode(short* buf, unsigned char* out, long len, long loopStart, unsigned char emphasis) {
long brrEncode(short* buf, unsigned char* out, long len, long loopStart, unsigned char emphasis, unsigned char noFilter) {
if (len==0) return 0;
// encoding process:
@ -157,6 +157,7 @@ long brrEncode(short* buf, unsigned char* out, long len, long loopStart, unsigne
long total=0;
unsigned char filter=0;
unsigned char range=0;
unsigned char numFilters=noFilter?2:4;
short x0=0;
short x1=0;
@ -211,7 +212,7 @@ long brrEncode(short* buf, unsigned char* out, long len, long loopStart, unsigne
}
// encode
for (int j=0; j<4; j++) {
for (int j=0; j<numFilters; j++) {
for (int k=0; k<13; k++) {
brrEncodeBlock(in,possibleOut[j][k],k,j,&last1[j][k],&last2[j][k],&avgError[j][k]);
}
@ -228,7 +229,7 @@ long brrEncode(short* buf, unsigned char* out, long len, long loopStart, unsigne
}
}
} else {
for (int j=0; j<4; j++) {
for (int j=0; j<numFilters; j++) {
for (int k=0; k<13; k++) {
if (avgError[j][k]<candError) {
candError=avgError[j][k];
@ -245,7 +246,7 @@ long brrEncode(short* buf, unsigned char* out, long len, long loopStart, unsigne
out[j+1]=possibleOut[filter][range][j];
}
for (int j=0; j<4; j++) {
for (int j=0; j<numFilters; j++) {
for (int k=0; k<13; k++) {
last1[j][k]=last1[filter][range];
last2[j][k]=last2[filter][range];

View file

@ -34,9 +34,10 @@ extern "C" {
* @param len input length (should be a multiple of 16. if it isn't, the output will be padded).
* @param loopStart beginning of loop area (may be -1 for no loop). this is used to ensure the respective block has no filter in order to loop properly.
* @param emphasis apply filter to compensate for Gaussian interpolation high frequency loss.
* @param noFilter do not use filters in any block. this is used to allow seeking to any sample position.
* @return number of written samples.
*/
long brrEncode(short* buf, unsigned char* out, long len, long loopStart, unsigned char emphasis);
long brrEncode(short* buf, unsigned char* out, long len, long loopStart, unsigned char emphasis, unsigned char noFilter);
/**
* read len bytes from buf, decode BRR and output to out.

View file

@ -54,8 +54,8 @@ class DivWorkPool;
//#define DIV_UNSTABLE
#define DIV_VERSION "0.6.4"
#define DIV_ENGINE_VERSION 212
#define DIV_VERSION "dev213"
#define DIV_ENGINE_VERSION 213
// for imports
#define DIV_VERSION_MOD 0xff01
#define DIV_VERSION_FC 0xff02

View file

@ -261,6 +261,7 @@ SafeWriter* DivEngine::saveText(bool separatePatterns) {
w->writeText(fmt::sprintf(" - mode: %s\n",sampleLoopModes[sample->loopMode&3]));
}
w->writeText(fmt::sprintf("- BRR emphasis: %s\n",trueFalse[sample->brrEmphasis?1:0]));
w->writeText(fmt::sprintf("- no BRR filters: %s\n",trueFalse[sample->brrNoFilter?1:0]));
w->writeText(fmt::sprintf("- dither: %s\n",trueFalse[sample->dither?1:0]));
// TODO' render matrix

View file

@ -56,7 +56,7 @@ void DivSample::putSampleData(SafeWriter* w) {
w->writeC(depth);
w->writeC(loopMode);
w->writeC(brrEmphasis);
w->writeC(dither);
w->writeC((dither?1:0)|(brrNoFilter?2:0));
w->writeI(loop?loopStart:-1);
w->writeI(loop?loopEnd:-1);
@ -134,7 +134,9 @@ DivDataErrors DivSample::readSampleData(SafeReader& reader, short version) {
reader.readC();
}
if (version>=159) {
dither=reader.readC()&1;
signed char c=reader.readC();
dither=c&1;
if (version>=213) brrNoFilter=c&2;
} else {
reader.readC();
}
@ -1423,7 +1425,7 @@ void DivSample::render(unsigned int formatMask) {
if (NOT_IN_FORMAT(DIV_SAMPLE_DEPTH_BRR)) { // BRR
int sampleCount=loop?loopEnd:samples;
if (!initInternal(DIV_SAMPLE_DEPTH_BRR,sampleCount)) return;
brrEncode(data16,dataBRR,sampleCount,loop?loopStart:-1,brrEmphasis);
brrEncode(data16,dataBRR,sampleCount,loop?loopStart:-1,brrEmphasis,brrNoFilter);
}
if (NOT_IN_FORMAT(DIV_SAMPLE_DEPTH_VOX)) { // VOX
if (!initInternal(DIV_SAMPLE_DEPTH_VOX,samples)) return;
@ -1564,9 +1566,9 @@ DivSampleHistory* DivSample::prepareUndo(bool data, bool doNotPush) {
duplicate=new unsigned char[getCurBufLen()];
memcpy(duplicate,getCurBuf(),getCurBufLen());
}
h=new DivSampleHistory(duplicate,getCurBufLen(),samples,depth,rate,centerRate,loopStart,loopEnd,loop,brrEmphasis,dither,loopMode);
h=new DivSampleHistory(duplicate,getCurBufLen(),samples,depth,rate,centerRate,loopStart,loopEnd,loop,brrEmphasis,brrNoFilter,dither,loopMode);
} else {
h=new DivSampleHistory(depth,rate,centerRate,loopStart,loopEnd,loop,brrEmphasis,dither,loopMode);
h=new DivSampleHistory(depth,rate,centerRate,loopStart,loopEnd,loop,brrEmphasis,brrNoFilter,dither,loopMode);
}
if (!doNotPush) {
while (!redoHist.empty()) {
@ -1600,6 +1602,7 @@ DivSampleHistory* DivSample::prepareUndo(bool data, bool doNotPush) {
loopEnd=h->loopEnd; \
loop=h->loop; \
brrEmphasis=h->brrEmphasis; \
brrNoFilter=h->brrNoFilter; \
dither=h->dither; \
loopMode=h->loopMode;

View file

@ -65,10 +65,10 @@ struct DivSampleHistory {
unsigned int length, samples;
DivSampleDepth depth;
int rate, centerRate, loopStart, loopEnd;
bool loop, brrEmphasis, dither;
bool loop, brrEmphasis, brrNoFilter, dither;
DivSampleLoopMode loopMode;
bool hasSample;
DivSampleHistory(void* d, unsigned int l, unsigned int s, DivSampleDepth de, int r, int cr, int ls, int le, bool lp, bool be, bool di, DivSampleLoopMode lm):
DivSampleHistory(void* d, unsigned int l, unsigned int s, DivSampleDepth de, int r, int cr, int ls, int le, bool lp, bool be, bool bf, bool di, DivSampleLoopMode lm):
data((unsigned char*)d),
length(l),
samples(s),
@ -79,10 +79,11 @@ struct DivSampleHistory {
loopEnd(le),
loop(lp),
brrEmphasis(be),
brrNoFilter(bf),
dither(di),
loopMode(lm),
hasSample(true) {}
DivSampleHistory(DivSampleDepth de, int r, int cr, int ls, int le, bool lp, bool be, bool di, DivSampleLoopMode lm):
DivSampleHistory(DivSampleDepth de, int r, int cr, int ls, int le, bool lp, bool be, bool bf, bool di, DivSampleLoopMode lm):
data(NULL),
length(0),
samples(0),
@ -93,6 +94,7 @@ struct DivSampleHistory {
loopEnd(le),
loop(lp),
brrEmphasis(be),
brrNoFilter(bf),
dither(di),
loopMode(lm),
hasSample(false) {}
@ -118,7 +120,7 @@ struct DivSample {
// - 13: IMA ADPCM
// - 16: 16-bit PCM
DivSampleDepth depth;
bool loop, brrEmphasis, dither;
bool loop, brrEmphasis, brrNoFilter, dither;
// valid values are:
// - 0: Forward loop
// - 1: Backward loop
@ -337,6 +339,7 @@ struct DivSample {
depth(DIV_SAMPLE_DEPTH_16BIT),
loop(false),
brrEmphasis(true),
brrNoFilter(false),
dither(false),
loopMode(DIV_SAMPLE_LOOP_FORWARD),
data8(NULL),

View file

@ -931,6 +931,7 @@ void FurnaceGUI::doAction(int what) {
sample->loop=prevSample->loop;
sample->loopMode=prevSample->loopMode;
sample->brrEmphasis=prevSample->brrEmphasis;
sample->brrNoFilter=prevSample->brrNoFilter;
sample->dither=prevSample->dither;
sample->depth=prevSample->depth;
if (sample->init(prevSample->samples)) {

View file

@ -541,6 +541,19 @@ void FurnaceGUI::drawSampleEdit() {
}
}
}
if (sample->depth!=DIV_SAMPLE_DEPTH_BRR && isThereSNES) {
bool bf=sample->brrNoFilter;
if (ImGui::Checkbox(_("no BRR filters"),&bf)) {
sample->prepareUndo(true);
sample->brrNoFilter=bf;
e->renderSamplesP(curSample);
updateSampleTex=true;
MARK_MODIFIED;
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip(_("enable this option to not use BRR blocks with filters\nand allow sample offset commands to be used safely."));
}
}
if (sample->depth!=DIV_SAMPLE_DEPTH_8BIT && e->getSampleFormatMask()&(1L<<DIV_SAMPLE_DEPTH_8BIT)) {
bool di=sample->dither;
if (ImGui::Checkbox(_("8-bit dither"),&di)) {