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:
- 129: Furnace dev129
- 128: Furnace dev128
- 127: Furnace dev127
- 126: Furnace dev126
- 125: Furnace dev125
@ -1120,9 +1122,11 @@ size | description
| - 16: 16-bit PCM
1 | loop direction (>=123) or reserved
| - 0: forward
| - 0: backward
| - 0: ping-pong
2 | reserved
| - 1: backward
| - 2: ping-pong
1 | flags (>=129) or reserved
| - 0: BRR emphasis
1 | reserved
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) {
long brrEncode(short* buf, unsigned char* out, long len, long loopStart, unsigned char emphasis) {
if (len==0) return 0;
// encoding process:
@ -158,7 +158,12 @@ long brrEncode(short* buf, unsigned char* out, long len, long loopStart) {
unsigned char filter=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 last2[4][13];
@ -172,9 +177,9 @@ long brrEncode(short* buf, unsigned char* out, long len, long loopStart) {
memset(possibleOut,0,4*13*8);
for (long i=0; i<len; i+=16) {
if (i+16>len) {
if (i+17>len) {
long p=i;
for (int j=0; j<16; j++) {
for (int j=0; j<17; j++) {
if (p>=len) {
if (loopStart<0 || loopStart>=len) {
in[j]=0;
@ -187,7 +192,22 @@ long brrEncode(short* buf, unsigned char* out, long len, long loopStart) {
}
}
} 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
@ -237,13 +257,27 @@ long brrEncode(short* buf, unsigned char* out, long len, long loopStart) {
// encode loop block
if (loopStart>=0) {
long p=loopStart;
for (int i=0; i<16; i++) {
for (int i=0; i<17; i++) {
if (p>=len) {
p=loopStart;
}
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)
for (int j=0; j<2; j++) {
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++;
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;
short* outOrig=out;
long total=0;
int last1=0;
@ -344,5 +380,20 @@ long brrDecode(unsigned char* buf, short* out, long len) {
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;
}

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 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.
* @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.
* @param buf input data.
* @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 emphasis apply filter to simulate Gaussian interpolation high frequency loss.
* @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
}

View File

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

View File

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

View File

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

View File

@ -190,6 +190,31 @@ void FurnaceGUI::drawSampleEdit() {
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::Text("C-4 (Hz)");
ImGui::SameLine();