dev129 - SNES: add option to toggle BRR emphasis

This commit is contained in:
tildearrow 2022-12-07 02:57:19 -05:00
parent b149d1b92e
commit 056e67b228
7 changed files with 113 additions and 24 deletions

View file

@ -32,6 +32,8 @@ these fields are 0 in format versions prior to 100 (0.6pre1).
the format versions are: the format versions are:
- 129: Furnace dev129
- 128: Furnace dev128
- 127: Furnace dev127 - 127: Furnace dev127
- 126: Furnace dev126 - 126: Furnace dev126
- 125: Furnace dev125 - 125: Furnace dev125
@ -1120,9 +1122,11 @@ size | description
| - 16: 16-bit PCM | - 16: 16-bit PCM
1 | loop direction (>=123) or reserved 1 | loop direction (>=123) or reserved
| - 0: forward | - 0: forward
| - 0: backward | - 1: backward
| - 0: ping-pong | - 2: ping-pong
2 | reserved 1 | flags (>=129) or reserved
| - 0: BRR emphasis
1 | reserved
4 | loop start 4 | loop start
| - -1 means no loop | - -1 means no loop
4 | loop end 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) { long brrEncode(short* buf, unsigned char* out, long len, long loopStart, unsigned char emphasis) {
if (len==0) return 0; if (len==0) return 0;
// encoding process: // encoding process:
@ -158,7 +158,12 @@ long brrEncode(short* buf, unsigned char* out, long len, long loopStart) {
unsigned char filter=0; unsigned char filter=0;
unsigned char range=0; unsigned char range=0;
short in[16]; short x0=0;
short x1=0;
short x2=0;
int emphOut=0;
short in[17];
short last1[4][13]; short last1[4][13];
short last2[4][13]; short last2[4][13];
@ -172,9 +177,9 @@ long brrEncode(short* buf, unsigned char* out, long len, long loopStart) {
memset(possibleOut,0,4*13*8); memset(possibleOut,0,4*13*8);
for (long i=0; i<len; i+=16) { for (long i=0; i<len; i+=16) {
if (i+16>len) { if (i+17>len) {
long p=i; long p=i;
for (int j=0; j<16; j++) { for (int j=0; j<17; j++) {
if (p>=len) { if (p>=len) {
if (loopStart<0 || loopStart>=len) { if (loopStart<0 || loopStart>=len) {
in[j]=0; in[j]=0;
@ -187,7 +192,22 @@ long brrEncode(short* buf, unsigned char* out, long len, long loopStart) {
} }
} }
} else { } else {
memcpy(in,&buf[i],16*sizeof(short)); memcpy(in,&buf[i],17*sizeof(short));
}
// emphasis
if (emphasis) {
for (int j=0; j<17; j++) {
x0=x1;
x1=x2;
x2=in[j];
if (j==0) continue;
emphOut=((x1<<11)-x0*370-in[j]*374)/1305;
if (emphOut<-32768) emphOut=-32768;
if (emphOut>32767) emphOut=32767;
in[j-1]=emphOut;
}
} }
// encode // encode
@ -237,13 +257,27 @@ long brrEncode(short* buf, unsigned char* out, long len, long loopStart) {
// encode loop block // encode loop block
if (loopStart>=0) { if (loopStart>=0) {
long p=loopStart; long p=loopStart;
for (int i=0; i<16; i++) { for (int i=0; i<17; i++) {
if (p>=len) { if (p>=len) {
p=loopStart; p=loopStart;
} }
in[i]=buf[p++]; in[i]=buf[p++];
} }
if (emphasis) {
for (int j=0; j<17; j++) {
x0=x1;
x1=x2;
x2=in[j];
if (j==0) continue;
emphOut=((x1<<11)-x0*370-in[j]*374)/1305;
if (emphOut<-32768) emphOut=-32768;
if (emphOut>32767) emphOut=32767;
in[j-1]=emphOut;
}
}
// encode (filter 0/1 only) // encode (filter 0/1 only)
for (int j=0; j<2; j++) { for (int j=0; j<2; j++) {
for (int k=0; k<13; k++) { for (int k=0; k<13; k++) {
@ -315,9 +349,11 @@ long brrEncode(short* buf, unsigned char* out, long len, long loopStart) {
*out=next<<1; \ *out=next<<1; \
out++; out++;
long brrDecode(unsigned char* buf, short* out, long len) { long brrDecode(unsigned char* buf, short* out, long len, unsigned char emphasis) {
if (len==0) return 0; if (len==0) return 0;
short* outOrig=out;
long total=0; long total=0;
int last1=0; int last1=0;
@ -344,5 +380,20 @@ long brrDecode(unsigned char* buf, short* out, long len) {
buf+=9; buf+=9;
} }
if (emphasis) {
short x0=0;
short x1=0;
short x2=0;
for (long i=0; i<=total; i++) {
x0=x1;
x1=x2;
x2=(i>=total)?0:outOrig[i];
if (i==0) continue;
outOrig[i-1]=(x0*370+x1*1305+x2*374)>>11;
}
}
return total; return total;
} }

View file

@ -33,18 +33,20 @@ extern "C" {
* @param out output buffer. shall be at least 9*((15+len)/16) shorts in size (9 more if loopStart is not -1!) * @param out output buffer. shall be at least 9*((15+len)/16) shorts in size (9 more if loopStart is not -1!)
* @param len input length (should be a multiple of 16. if it isn't, the output will be padded). * @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 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.
* @return number of written samples. * @return number of written samples.
*/ */
long brrEncode(short* buf, unsigned char* out, long len, long loopStart); long brrEncode(short* buf, unsigned char* out, long len, long loopStart, unsigned char emphasis);
/** /**
* read len bytes from buf, decode BRR and output to out. * read len bytes from buf, decode BRR and output to out.
* @param buf input data. * @param buf input data.
* @param out output buffer. shall be at least 16*(len/9) shorts in size. * @param out output buffer. shall be at least 16*(len/9) shorts in size.
* @param len input length (shall be a multiple of 9). * @param len input length (shall be a multiple of 9).
* @param emphasis apply filter to simulate Gaussian interpolation high frequency loss.
* @return number of written bytes. * @return number of written bytes.
*/ */
long brrDecode(unsigned char* buf, short* out, long len); long brrDecode(unsigned char* buf, short* out, long len, unsigned char emphasis);
#ifdef __cplusplus #ifdef __cplusplus
} }

View file

@ -47,8 +47,8 @@
#define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock(); #define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock();
#define BUSY_END isBusy.unlock(); softLocked=false; #define BUSY_END isBusy.unlock(); softLocked=false;
#define DIV_VERSION "dev128" #define DIV_VERSION "dev129"
#define DIV_ENGINE_VERSION 128 #define DIV_ENGINE_VERSION 129
// for imports // for imports
#define DIV_VERSION_MOD 0xff01 #define DIV_VERSION_MOD 0xff01
#define DIV_VERSION_FC 0xff02 #define DIV_VERSION_FC 0xff02

View file

@ -52,8 +52,8 @@ void DivSample::putSampleData(SafeWriter* w) {
w->writeI(centerRate); w->writeI(centerRate);
w->writeC(depth); w->writeC(depth);
w->writeC(loopMode); w->writeC(loopMode);
w->writeC(brrEmphasis);
w->writeC(0); // reserved w->writeC(0); // reserved
w->writeC(0);
w->writeI(loop?loopStart:-1); w->writeI(loop?loopStart:-1);
w->writeI(loop?loopEnd:-1); w->writeI(loop?loopEnd:-1);
@ -125,9 +125,13 @@ DivDataErrors DivSample::readSampleData(SafeReader& reader, short version) {
reader.readC(); reader.readC();
} }
if (version>=129) {
brrEmphasis=reader.readC();
} else {
reader.readC();
}
// reserved // reserved
reader.readC(); reader.readC();
reader.readC();
loopStart=reader.readI(); loopStart=reader.readI();
loopEnd=reader.readI(); loopEnd=reader.readI();
@ -1041,7 +1045,7 @@ void DivSample::render(unsigned int formatMask) {
} }
break; break;
case DIV_SAMPLE_DEPTH_BRR: // BRR case DIV_SAMPLE_DEPTH_BRR: // BRR
brrDecode(dataBRR,data16,lengthBRR); brrDecode(dataBRR,data16,lengthBRR,brrEmphasis);
break; break;
case DIV_SAMPLE_DEPTH_VOX: // VOX case DIV_SAMPLE_DEPTH_VOX: // VOX
oki_decode(dataVOX,data16,samples); oki_decode(dataVOX,data16,samples);
@ -1100,7 +1104,7 @@ void DivSample::render(unsigned int formatMask) {
} }
if (NOT_IN_FORMAT(DIV_SAMPLE_DEPTH_BRR)) { // BRR if (NOT_IN_FORMAT(DIV_SAMPLE_DEPTH_BRR)) { // BRR
if (!initInternal(DIV_SAMPLE_DEPTH_BRR,samples)) return; if (!initInternal(DIV_SAMPLE_DEPTH_BRR,samples)) return;
brrEncode(data16,dataBRR,samples,loop?loopStart:-1); brrEncode(data16,dataBRR,samples,loop?loopStart:-1,brrEmphasis);
} }
if (NOT_IN_FORMAT(DIV_SAMPLE_DEPTH_VOX)) { // VOX if (NOT_IN_FORMAT(DIV_SAMPLE_DEPTH_VOX)) { // VOX
if (!initInternal(DIV_SAMPLE_DEPTH_VOX,samples)) return; if (!initInternal(DIV_SAMPLE_DEPTH_VOX,samples)) return;
@ -1174,9 +1178,9 @@ DivSampleHistory* DivSample::prepareUndo(bool data, bool doNotPush) {
duplicate=new unsigned char[getCurBufLen()]; duplicate=new unsigned char[getCurBufLen()];
memcpy(duplicate,getCurBuf(),getCurBufLen()); memcpy(duplicate,getCurBuf(),getCurBufLen());
} }
h=new DivSampleHistory(duplicate,getCurBufLen(),samples,depth,rate,centerRate,loopStart,loopEnd,loop,loopMode); h=new DivSampleHistory(duplicate,getCurBufLen(),samples,depth,rate,centerRate,loopStart,loopEnd,loop,brrEmphasis,loopMode);
} else { } else {
h=new DivSampleHistory(depth,rate,centerRate,loopStart,loopEnd,loop,loopMode); h=new DivSampleHistory(depth,rate,centerRate,loopStart,loopEnd,loop,brrEmphasis,loopMode);
} }
if (!doNotPush) { if (!doNotPush) {
while (!redoHist.empty()) { while (!redoHist.empty()) {

View file

@ -60,10 +60,10 @@ struct DivSampleHistory {
unsigned int length, samples; unsigned int length, samples;
DivSampleDepth depth; DivSampleDepth depth;
int rate, centerRate, loopStart, loopEnd; int rate, centerRate, loopStart, loopEnd;
bool loop; bool loop, brrEmphasis;
DivSampleLoopMode loopMode; DivSampleLoopMode loopMode;
bool hasSample; bool hasSample;
DivSampleHistory(void* d, unsigned int l, unsigned int s, DivSampleDepth de, int r, int cr, int ls, int le, bool lp, 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, DivSampleLoopMode lm):
data((unsigned char*)d), data((unsigned char*)d),
length(l), length(l),
samples(s), samples(s),
@ -73,9 +73,10 @@ struct DivSampleHistory {
loopStart(ls), loopStart(ls),
loopEnd(le), loopEnd(le),
loop(lp), loop(lp),
brrEmphasis(be),
loopMode(lm), loopMode(lm),
hasSample(true) {} hasSample(true) {}
DivSampleHistory(DivSampleDepth de, int r, int cr, int ls, int le, bool lp, DivSampleLoopMode lm): DivSampleHistory(DivSampleDepth de, int r, int cr, int ls, int le, bool lp, bool be, DivSampleLoopMode lm):
data(NULL), data(NULL),
length(0), length(0),
samples(0), samples(0),
@ -85,6 +86,7 @@ struct DivSampleHistory {
loopStart(ls), loopStart(ls),
loopEnd(le), loopEnd(le),
loop(lp), loop(lp),
brrEmphasis(be),
loopMode(lm), loopMode(lm),
hasSample(false) {} hasSample(false) {}
~DivSampleHistory(); ~DivSampleHistory();
@ -106,7 +108,7 @@ struct DivSample {
// - 10: VOX ADPCM // - 10: VOX ADPCM
// - 16: 16-bit PCM // - 16: 16-bit PCM
DivSampleDepth depth; DivSampleDepth depth;
bool loop; bool loop, brrEmphasis;
// valid values are: // valid values are:
// - 0: Forward loop // - 0: Forward loop
// - 1: Backward loop // - 1: Backward loop
@ -306,6 +308,7 @@ struct DivSample {
loopOffP(0), loopOffP(0),
depth(DIV_SAMPLE_DEPTH_16BIT), depth(DIV_SAMPLE_DEPTH_16BIT),
loop(false), loop(false),
brrEmphasis(true),
loopMode(DIV_SAMPLE_LOOP_FORWARD), loopMode(DIV_SAMPLE_LOOP_FORWARD),
data8(NULL), data8(NULL),
data16(NULL), data16(NULL),

View file

@ -190,6 +190,31 @@ void FurnaceGUI::drawSampleEdit() {
ImGui::EndCombo(); ImGui::EndCombo();
} }
bool isThereSNES=false;
for (int i=0; i<e->song.systemLen; i++) {
if (e->song.system[i]==DIV_SYSTEM_SNES) {
isThereSNES=true;
break;
}
}
if (sample->depth==DIV_SAMPLE_DEPTH_BRR || isThereSNES) {
bool be=sample->brrEmphasis;
if (ImGui::Checkbox("BRR emphasis",&be)) {
sample->prepareUndo(true);
sample->brrEmphasis=be;
e->renderSamplesP();
updateSampleTex=true;
MARK_MODIFIED;
}
if (ImGui::IsItemHovered()) {
if (sample->depth==DIV_SAMPLE_DEPTH_BRR) {
ImGui::SetTooltip("this is a BRR sample.\nenabling this option will muffle it (only affects non-SNES chips).");
} else {
ImGui::SetTooltip("enable this option to slightly boost high frequencies\nto compensate for the SNES' Gaussian filter's muffle.");
}
}
}
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::Text("C-4 (Hz)"); ImGui::Text("C-4 (Hz)");
ImGui::SameLine(); ImGui::SameLine();