Merge branch 'master' of https://github.com/tildearrow/furnace into es5506_alt
This commit is contained in:
commit
62f1ba73eb
33 changed files with 1426 additions and 499 deletions
|
|
@ -22,78 +22,417 @@
|
|||
|
||||
#include "brrUtils.h"
|
||||
|
||||
#define NEXT_SAMPLE buf[j]-(buf[j]>>3)
|
||||
|
||||
#define DO_ONE_DEC(r) \
|
||||
if (nextDec&8) nextDec|=0xfffffff0; \
|
||||
\
|
||||
if (r>=13) { /* invalid shift */ \
|
||||
nextDec=(nextDec<0)?0xfffff800:0; \
|
||||
} else { \
|
||||
nextDec<<=r; /* range */ \
|
||||
nextDec>>=1; \
|
||||
} \
|
||||
\
|
||||
switch (filter) { /* filter */ \
|
||||
case 0: \
|
||||
break; \
|
||||
case 1: \
|
||||
nextDec+=last1+((-last1)>>4); \
|
||||
break; \
|
||||
case 2: \
|
||||
nextDec+=last1*2+((-last1*3)>>5)-last2+(last2>>4); \
|
||||
break; \
|
||||
case 3: \
|
||||
nextDec+=last1*2+((-last1*13)>>6)-last2+((last2*3)>>4); \
|
||||
break; \
|
||||
} \
|
||||
\
|
||||
if (nextDec>32767) nextDec=32767; \
|
||||
if (nextDec<-32768) nextDec=-32768; \
|
||||
nextDec&=0x7fff; \
|
||||
if (nextDec&0x4000) nextDec|=0xffff8000; \
|
||||
\
|
||||
last2=last1; \
|
||||
last1=nextDec; \
|
||||
|
||||
long brrEncode(short* buf, unsigned char* out, long len, long loopStart) {
|
||||
if (len==0) return 0;
|
||||
|
||||
// encoding process:
|
||||
// 1. read next group of 16 samples
|
||||
// 2. is this the first block?
|
||||
// - if yes, don't filter. output and then go to 1
|
||||
// - if yes, don't filter. output and then go to 1
|
||||
// 3. is this the loop block?
|
||||
// - if yes, don't filter. output and then go to 1
|
||||
// - if yes, don't filter. output and then go to 1
|
||||
// 4. try encoding using 4 filters
|
||||
// - perform linear prediction
|
||||
// - calculate range
|
||||
// - decode and apply correction to achieve low error
|
||||
// 5. which one of these yields the least amount of error?
|
||||
// 6. output the one which does
|
||||
// 7. is this the last block?
|
||||
// - if yes, mark end and finish
|
||||
// - if yes, mark end and finish
|
||||
// 8. go to 1
|
||||
long total=0;
|
||||
unsigned char next[9];
|
||||
unsigned char next0[8];
|
||||
unsigned char next1[8];
|
||||
unsigned char next2[8];
|
||||
unsigned char next3[8];
|
||||
unsigned char filter=0;
|
||||
unsigned char range=0;
|
||||
unsigned char range0=0;
|
||||
unsigned char range1=0;
|
||||
unsigned char range2=0;
|
||||
unsigned char range3=0;
|
||||
unsigned char o=0;
|
||||
int pred1[16];
|
||||
int pred2[16];
|
||||
int pred3[16];
|
||||
short o1=0;
|
||||
short o2=0;
|
||||
short o0=0;
|
||||
short o1f1=0;
|
||||
short o1f2=0;
|
||||
short o1f3=0;
|
||||
//short o2f1=0;
|
||||
short o2f2=0;
|
||||
short o2f3=0;
|
||||
|
||||
int last1=0;
|
||||
int last2=0;
|
||||
int nextDec=0;
|
||||
int maxError[4];
|
||||
int avgError[4];
|
||||
|
||||
len&=~15;
|
||||
loopStart&=~15;
|
||||
for (long i=0; i<len; i+=16) {
|
||||
// don't filter on the first or loop block
|
||||
if (i && i!=loopStart) {
|
||||
range0=0;
|
||||
// encode with no filter
|
||||
for (int j=0; j<16; j++) {
|
||||
short s=NEXT_SAMPLE;
|
||||
if (s<0) s=-s;
|
||||
while (range0<12 && s>((8<<range0)-1)) range0++;
|
||||
}
|
||||
for (int j=0; j<16; j++) {
|
||||
short s=NEXT_SAMPLE;
|
||||
o0=s>>range0;
|
||||
if (range0) if (s&(1<<(range1>>1))) o0++;
|
||||
if (o0>7) o0=7;
|
||||
if (o0<-8) o0=-8;
|
||||
if (range0>=12) if (o0<-7) o0=-7;
|
||||
o=o0&15;
|
||||
if (j&1) {
|
||||
next0[j>>1]|=o;
|
||||
} else {
|
||||
next0[j>>1]=o<<4;
|
||||
}
|
||||
}
|
||||
|
||||
// encode with filter
|
||||
if (i && i!=loopStart) {
|
||||
// 1: x = o0 - o1 * 15/16
|
||||
// 2: x = o0 + o2 * 15/16 - o1 * 61/32
|
||||
// 3: x = o0 + o2 * 13/16 - o1 * 115/64
|
||||
range1=0;
|
||||
range2=0;
|
||||
range3=0;
|
||||
//o2f1=o2;
|
||||
o2f2=o2;
|
||||
o2f3=o2;
|
||||
o1f1=o1;
|
||||
o1f2=o1;
|
||||
o1f3=o1;
|
||||
// first pass
|
||||
for (int j=0; j<16; j++) {
|
||||
int s=NEXT_SAMPLE;
|
||||
|
||||
pred1[j]=s-(((int)o1*15)>>4);
|
||||
if (pred1[j]<-32768) pred1[j]=-32768;
|
||||
if (pred1[j]>32767) pred1[j]=32767;
|
||||
|
||||
pred2[j]=s+(((int)o2*15)>>4)-(((int)o1*61)>>5);
|
||||
if (pred2[j]<-32768) pred2[j]=-32768;
|
||||
if (pred2[j]>32767) pred2[j]=32767;
|
||||
|
||||
pred3[j]=s+(((int)o2*13)>>4)-(((int)o1*115)>>6);
|
||||
if (pred3[j]<-32768) pred3[j]=-32768;
|
||||
if (pred3[j]>32767) pred3[j]=32767;
|
||||
|
||||
o2=o1;
|
||||
o1=s;
|
||||
}
|
||||
// calculate range of values
|
||||
for (int j=0; j<16; j++) {
|
||||
short s=pred1[j];
|
||||
if (s<0) s=-s;
|
||||
while (range1<12 && s>((8<<range1)-1)) range1++;
|
||||
|
||||
s=pred2[j];
|
||||
if (s<0) s=-s;
|
||||
while (range2<12 && s>((8<<range2)-1)) range2++;
|
||||
|
||||
s=pred3[j];
|
||||
if (s<0) s=-s;
|
||||
while (range3<12 && s>((8<<range3)-1)) range3++;
|
||||
}
|
||||
// second pass
|
||||
int prevLast1=last1;
|
||||
int prevLast2=last2;
|
||||
filter=1;
|
||||
for (int j=0; j<16; j++) {
|
||||
int s=NEXT_SAMPLE;
|
||||
|
||||
pred1[j]=s-(((int)o1f1*15)>>4);
|
||||
if (pred1[j]<-32768) pred1[j]=-32768;
|
||||
if (pred1[j]>32767) pred1[j]=32767;
|
||||
|
||||
o0=pred1[j]>>range1;
|
||||
if (range1) if (pred1[j]&(1<<(range1>>1))) o0++;
|
||||
if (o0>7) o0=7;
|
||||
if (o0<-8) o0=-8;
|
||||
o=o0&15;
|
||||
if (j&1) {
|
||||
next1[j>>1]|=o;
|
||||
} else {
|
||||
next1[j>>1]=o<<4;
|
||||
}
|
||||
|
||||
nextDec=o;
|
||||
DO_ONE_DEC(range1);
|
||||
//o2f1=last2<<1;
|
||||
o1f1=last1<<1;
|
||||
}
|
||||
last1=prevLast1;
|
||||
last2=prevLast2;
|
||||
filter=2;
|
||||
for (int j=0; j<16; j++) {
|
||||
int s=NEXT_SAMPLE;
|
||||
pred2[j]=s+(((int)o2f2*15)>>4)-(((int)o1f2*61)>>5);
|
||||
if (pred2[j]<-32768) pred2[j]=-32768;
|
||||
if (pred2[j]>32767) pred2[j]=32767;
|
||||
|
||||
o0=pred2[j]>>range2;
|
||||
if (range2) if (pred2[j]&(1<<(range2>>1))) o0++;
|
||||
if (o0>7) o0=7;
|
||||
if (o0<-8) o0=-8;
|
||||
o=o0&15;
|
||||
if (j&1) {
|
||||
next2[j>>1]|=o;
|
||||
} else {
|
||||
next2[j>>1]=o<<4;
|
||||
}
|
||||
|
||||
nextDec=o;
|
||||
DO_ONE_DEC(range2);
|
||||
o2f2=last2<<1;
|
||||
o1f2=last1<<1;
|
||||
}
|
||||
last1=prevLast1;
|
||||
last2=prevLast2;
|
||||
filter=3;
|
||||
for (int j=0; j<16; j++) {
|
||||
int s=NEXT_SAMPLE;
|
||||
pred3[j]=s+(((int)o2f3*13)>>4)-(((int)o1f3*115)>>6);
|
||||
if (pred3[j]<-32768) pred3[j]=-32768;
|
||||
if (pred3[j]>32767) pred3[j]=32767;
|
||||
|
||||
o0=pred3[j]>>range3;
|
||||
if (range3) if (pred3[j]&(1<<(range3>>1))) o0++;
|
||||
if (o0>7) o0=7;
|
||||
if (o0<-8) o0=-8;
|
||||
o=o0&15;
|
||||
if (j&1) {
|
||||
next3[j>>1]|=o;
|
||||
} else {
|
||||
next3[j>>1]=o<<4;
|
||||
}
|
||||
|
||||
nextDec=o;
|
||||
DO_ONE_DEC(range3);
|
||||
o2f3=last2<<1;
|
||||
o1f3=last1<<1;
|
||||
}
|
||||
last1=prevLast1;
|
||||
last2=prevLast2;
|
||||
|
||||
// find best filter
|
||||
int error=0;
|
||||
|
||||
maxError[0]=0;
|
||||
maxError[1]=0;
|
||||
maxError[2]=0;
|
||||
maxError[3]=0;
|
||||
avgError[0]=0;
|
||||
avgError[1]=0;
|
||||
avgError[2]=0;
|
||||
avgError[3]=0;
|
||||
|
||||
// test filter 0
|
||||
filter=0;
|
||||
for (int j=0; j<16; j++) {
|
||||
int s=NEXT_SAMPLE;
|
||||
if (j&1) {
|
||||
nextDec=next0[j>>1]&15;
|
||||
} else {
|
||||
nextDec=next0[j>>1]>>4;
|
||||
}
|
||||
DO_ONE_DEC(range0);
|
||||
error=s-(nextDec<<1);
|
||||
if (error<0) error=-error;
|
||||
avgError[0]+=error;
|
||||
if (error>maxError[0]) maxError[0]=error;
|
||||
}
|
||||
last1=prevLast1;
|
||||
last2=prevLast2;
|
||||
|
||||
// test filter 1
|
||||
filter=1;
|
||||
for (int j=0; j<16; j++) {
|
||||
int s=NEXT_SAMPLE;
|
||||
if (j&1) {
|
||||
nextDec=next1[j>>1]&15;
|
||||
} else {
|
||||
nextDec=next1[j>>1]>>4;
|
||||
}
|
||||
DO_ONE_DEC(range1);
|
||||
error=s-(nextDec<<1);
|
||||
if (error<0) error=-error;
|
||||
avgError[1]+=error;
|
||||
if (error>maxError[1]) maxError[1]=error;
|
||||
}
|
||||
last1=prevLast1;
|
||||
last2=prevLast2;
|
||||
|
||||
// test filter 2
|
||||
filter=2;
|
||||
for (int j=0; j<16; j++) {
|
||||
int s=NEXT_SAMPLE;
|
||||
if (j&1) {
|
||||
nextDec=next2[j>>1]&15;
|
||||
} else {
|
||||
nextDec=next2[j>>1]>>4;
|
||||
}
|
||||
DO_ONE_DEC(range2);
|
||||
error=s-(nextDec<<1);
|
||||
if (error<0) error=-error;
|
||||
avgError[2]+=error;
|
||||
if (error>maxError[2]) maxError[2]=error;
|
||||
}
|
||||
last1=prevLast1;
|
||||
last2=prevLast2;
|
||||
|
||||
// test filter 3
|
||||
filter=3;
|
||||
for (int j=0; j<16; j++) {
|
||||
int s=NEXT_SAMPLE;
|
||||
if (j&1) {
|
||||
nextDec=next3[j>>1]&15;
|
||||
} else {
|
||||
nextDec=next3[j>>1]>>4;
|
||||
}
|
||||
DO_ONE_DEC(range3);
|
||||
error=s-(nextDec<<1);
|
||||
if (error<0) error=-error;
|
||||
avgError[3]+=error;
|
||||
if (error>maxError[3]) maxError[3]=error;
|
||||
}
|
||||
last1=prevLast1;
|
||||
last2=prevLast2;
|
||||
|
||||
// pick best filter
|
||||
int candError=0x7fffffff;
|
||||
for (int j=0; j<4; j++) {
|
||||
if (avgError[j]<candError) {
|
||||
candError=avgError[j];
|
||||
filter=j;
|
||||
}
|
||||
}
|
||||
//printf("block %ld: %8d %8d %8d %8d -> %d\n",i>>4,avgError[0],avgError[1],avgError[2],avgError[3],filter);
|
||||
} else {
|
||||
// don't filter on the first or loop block
|
||||
filter=0;
|
||||
}
|
||||
|
||||
range=0;
|
||||
for (int j=0; j<16; j++) {
|
||||
short s=buf[j]-(buf[j]>>13);
|
||||
if (s<0) s=-s;
|
||||
while (range<12 && s>((8<<range)-1)) range++;
|
||||
}
|
||||
next[0]=(range<<4)|(filter<<2)|((i+16>=len)?((loopStart>=0)?3:1):0);
|
||||
switch (filter) {
|
||||
case 0:
|
||||
for (int j=0; j<16; j++) {
|
||||
short s=buf[j]-(buf[j]>>13);
|
||||
o0=s>>range;
|
||||
if (o0>7) o0=7;
|
||||
if (o0<-8) o0=-8;
|
||||
if (range>=12) if (o0<-7) o0=-7;
|
||||
o=o0&15;
|
||||
if (j&1) {
|
||||
next[1+(j>>1)]|=o;
|
||||
} else {
|
||||
next[1+(j>>1)]=o<<4;
|
||||
}
|
||||
for (int j=0; j<8; j++) {
|
||||
nextDec=next0[j]>>4;
|
||||
DO_ONE_DEC(range0);
|
||||
nextDec=next0[j]&15;
|
||||
DO_ONE_DEC(range0);
|
||||
}
|
||||
o2=last2<<1;
|
||||
o1=last1<<1;
|
||||
|
||||
out[0]=(range0<<4)|(filter<<2)|((i+16>=len)?((loopStart>=0)?3:1):0);
|
||||
out[1]=next0[0];
|
||||
out[2]=next0[1];
|
||||
out[3]=next0[2];
|
||||
out[4]=next0[3];
|
||||
out[5]=next0[4];
|
||||
out[6]=next0[5];
|
||||
out[7]=next0[6];
|
||||
out[8]=next0[7];
|
||||
break;
|
||||
case 1:
|
||||
for (int j=0; j<8; j++) {
|
||||
nextDec=next1[j]>>4;
|
||||
DO_ONE_DEC(range1);
|
||||
nextDec=next1[j]&15;
|
||||
DO_ONE_DEC(range1);
|
||||
}
|
||||
o2=last2<<1;
|
||||
o1=last1<<1;
|
||||
out[0]=(range1<<4)|(filter<<2)|((i+16>=len)?((loopStart>=0)?3:1):0);
|
||||
out[1]=next1[0];
|
||||
out[2]=next1[1];
|
||||
out[3]=next1[2];
|
||||
out[4]=next1[3];
|
||||
out[5]=next1[4];
|
||||
out[6]=next1[5];
|
||||
out[7]=next1[6];
|
||||
out[8]=next1[7];
|
||||
break;
|
||||
case 2:
|
||||
for (int j=0; j<8; j++) {
|
||||
nextDec=next2[j]>>4;
|
||||
DO_ONE_DEC(range2);
|
||||
nextDec=next2[j]&15;
|
||||
DO_ONE_DEC(range2);
|
||||
}
|
||||
o2=last2<<1;
|
||||
o1=last1<<1;
|
||||
out[0]=(range2<<4)|(filter<<2)|((i+16>=len)?((loopStart>=0)?3:1):0);
|
||||
out[1]=next2[0];
|
||||
out[2]=next2[1];
|
||||
out[3]=next2[2];
|
||||
out[4]=next2[3];
|
||||
out[5]=next2[4];
|
||||
out[6]=next2[5];
|
||||
out[7]=next2[6];
|
||||
out[8]=next2[7];
|
||||
break;
|
||||
case 3:
|
||||
for (int j=0; j<8; j++) {
|
||||
nextDec=next3[j]>>4;
|
||||
DO_ONE_DEC(range3);
|
||||
nextDec=next3[j]&15;
|
||||
DO_ONE_DEC(range3);
|
||||
}
|
||||
o2=last2<<1;
|
||||
o1=last1<<1;
|
||||
out[0]=(range3<<4)|(filter<<2)|((i+16>=len)?((loopStart>=0)?3:1):0);
|
||||
out[1]=next3[0];
|
||||
out[2]=next3[1];
|
||||
out[3]=next3[2];
|
||||
out[4]=next3[3];
|
||||
out[5]=next3[4];
|
||||
out[6]=next3[5];
|
||||
out[7]=next3[6];
|
||||
out[8]=next3[7];
|
||||
break;
|
||||
}
|
||||
|
||||
out[0]=next[0];
|
||||
out[1]=next[1];
|
||||
out[2]=next[2];
|
||||
out[3]=next[3];
|
||||
out[4]=next[4];
|
||||
out[5]=next[5];
|
||||
out[6]=next[6];
|
||||
out[7]=next[7];
|
||||
out[8]=next[8];
|
||||
buf+=16;
|
||||
out+=9;
|
||||
total+=9;
|
||||
|
|
@ -135,9 +474,6 @@ long brrEncode(short* buf, unsigned char* out, long len, long loopStart) {
|
|||
*out=next<<1; \
|
||||
out++;
|
||||
|
||||
// TODO:
|
||||
// - what happens during overflow?
|
||||
// - what happens when range values 12 to 15 are used?
|
||||
long brrDecode(unsigned char* buf, short* out, long len) {
|
||||
if (len==0) return 0;
|
||||
|
||||
|
|
|
|||
|
|
@ -17,101 +17,13 @@
|
|||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "engine.h"
|
||||
#include "config.h"
|
||||
#include "../ta-log.h"
|
||||
#include "../fileutils.h"
|
||||
#include <fmt/printf.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "winStuff.h"
|
||||
#define CONFIG_FILE "\\furnace.cfg"
|
||||
#else
|
||||
#ifdef __HAIKU__
|
||||
#include <support/SupportDefs.h>
|
||||
#include <storage/FindDirectory.h>
|
||||
#endif
|
||||
#include <unistd.h>
|
||||
#include <pwd.h>
|
||||
#include <sys/stat.h>
|
||||
#define CONFIG_FILE "/furnace.cfg"
|
||||
#endif
|
||||
|
||||
#ifdef IS_MOBILE
|
||||
#ifdef HAVE_SDL2
|
||||
#include <SDL.h>
|
||||
#else
|
||||
#error "Furnace mobile requires SDL2!"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
void DivEngine::initConfDir() {
|
||||
#ifdef _WIN32
|
||||
// maybe move this function in here instead?
|
||||
configPath=getWinConfigPath();
|
||||
#elif defined(IS_MOBILE)
|
||||
configPath=SDL_GetPrefPath("tildearrow","furnace");
|
||||
#else
|
||||
#ifdef __HAIKU__
|
||||
char userSettingsDir[PATH_MAX];
|
||||
status_t findUserDir = find_directory(B_USER_SETTINGS_DIRECTORY,0,true,userSettingsDir,PATH_MAX);
|
||||
if (findUserDir==B_OK) {
|
||||
configPath=userSettingsDir;
|
||||
} else {
|
||||
logW("unable to find/create user settings directory (%s)!",strerror(findUserDir));
|
||||
configPath=".";
|
||||
return;
|
||||
}
|
||||
#else
|
||||
// TODO this should check XDG_CONFIG_HOME first
|
||||
char* home=getenv("HOME");
|
||||
if (home==NULL) {
|
||||
int uid=getuid();
|
||||
struct passwd* entry=getpwuid(uid);
|
||||
if (entry==NULL) {
|
||||
logW("unable to determine home directory (%s)!",strerror(errno));
|
||||
configPath=".";
|
||||
return;
|
||||
} else {
|
||||
configPath=entry->pw_dir;
|
||||
}
|
||||
} else {
|
||||
configPath=home;
|
||||
}
|
||||
#ifdef __APPLE__
|
||||
configPath+="/Library/Application Support";
|
||||
#else
|
||||
// FIXME this doesn't honour XDG_CONFIG_HOME *at all*
|
||||
configPath+="/.config";
|
||||
#endif // __APPLE__
|
||||
#endif // __HAIKU__
|
||||
#ifdef __APPLE__
|
||||
configPath+="/Furnace";
|
||||
#else
|
||||
configPath+="/furnace";
|
||||
#endif // __APPLE__
|
||||
struct stat st;
|
||||
std::string pathSep="/";
|
||||
configPath+=pathSep;
|
||||
size_t sepPos=configPath.find(pathSep,1);
|
||||
while (sepPos!=std::string::npos) {
|
||||
std::string subpath=configPath.substr(0,sepPos++);
|
||||
if (stat(subpath.c_str(),&st)!=0) {
|
||||
logI("creating config path element %s ...",subpath.c_str());
|
||||
if (mkdir(subpath.c_str(),0755)!=0) {
|
||||
logW("could not create config path element %s! (%s)",subpath.c_str(),strerror(errno));
|
||||
configPath=".";
|
||||
return;
|
||||
}
|
||||
}
|
||||
sepPos=configPath.find(pathSep,sepPos);
|
||||
}
|
||||
configPath.resize(configPath.length()-pathSep.length());
|
||||
#endif // _WIN32
|
||||
}
|
||||
|
||||
bool DivEngine::saveConf() {
|
||||
configFile=configPath+String(CONFIG_FILE);
|
||||
FILE* f=ps_fopen(configFile.c_str(),"wb");
|
||||
bool DivConfig::save(const char* path) {
|
||||
FILE* f=ps_fopen(path,"wb");
|
||||
if (f==NULL) {
|
||||
logW("could not write config file! %s",strerror(errno));
|
||||
return false;
|
||||
|
|
@ -128,43 +40,72 @@ bool DivEngine::saveConf() {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool DivEngine::loadConf() {
|
||||
String DivConfig::toString() {
|
||||
String ret;
|
||||
for (auto& i: conf) {
|
||||
ret+=fmt::sprintf("%s=%s\n",i.first,i.second);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void DivConfig::parseLine(const char* line) {
|
||||
String key="";
|
||||
String value="";
|
||||
bool keyOrValue=false;
|
||||
for (const char* i=line; *i; i++) {
|
||||
if (*i=='\n') continue;
|
||||
if (keyOrValue) {
|
||||
value+=*i;
|
||||
} else {
|
||||
if (*i=='=') {
|
||||
keyOrValue=true;
|
||||
} else {
|
||||
key+=*i;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (keyOrValue) {
|
||||
conf[key]=value;
|
||||
}
|
||||
}
|
||||
|
||||
bool DivConfig::loadFromFile(const char* path, bool createOnFail) {
|
||||
char line[4096];
|
||||
configFile=configPath+String(CONFIG_FILE);
|
||||
FILE* f=ps_fopen(configFile.c_str(),"rb");
|
||||
FILE* f=ps_fopen(path,"rb");
|
||||
if (f==NULL) {
|
||||
logI("creating default config.");
|
||||
return saveConf();
|
||||
if (createOnFail) {
|
||||
logI("creating default config.");
|
||||
return save(path);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
logI("loading config.");
|
||||
while (!feof(f)) {
|
||||
String key="";
|
||||
String value="";
|
||||
bool keyOrValue=false;
|
||||
if (fgets(line,4095,f)==NULL) {
|
||||
break;
|
||||
}
|
||||
for (char* i=line; *i; i++) {
|
||||
if (*i=='\n') continue;
|
||||
if (keyOrValue) {
|
||||
value+=*i;
|
||||
} else {
|
||||
if (*i=='=') {
|
||||
keyOrValue=true;
|
||||
} else {
|
||||
key+=*i;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (keyOrValue) {
|
||||
conf[key]=value;
|
||||
}
|
||||
parseLine(line);
|
||||
}
|
||||
fclose(f);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DivEngine::getConfBool(String key, bool fallback) {
|
||||
bool DivConfig::loadFromMemory(const char* buf) {
|
||||
String line;
|
||||
const char* readPos=buf;
|
||||
while (*readPos) {
|
||||
line+=*readPos;
|
||||
readPos++;
|
||||
if ((*readPos)=='\n' || (*readPos)==0) {
|
||||
parseLine(line.c_str());
|
||||
line="";
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DivConfig::getConfBool(String key, bool fallback) {
|
||||
try {
|
||||
String val=conf.at(key);
|
||||
if (val=="true") {
|
||||
|
|
@ -177,7 +118,7 @@ bool DivEngine::getConfBool(String key, bool fallback) {
|
|||
return fallback;
|
||||
}
|
||||
|
||||
int DivEngine::getConfInt(String key, int fallback) {
|
||||
int DivConfig::getConfInt(String key, int fallback) {
|
||||
try {
|
||||
String val=conf.at(key);
|
||||
int ret=std::stoi(val);
|
||||
|
|
@ -188,7 +129,7 @@ int DivEngine::getConfInt(String key, int fallback) {
|
|||
return fallback;
|
||||
}
|
||||
|
||||
float DivEngine::getConfFloat(String key, float fallback) {
|
||||
float DivConfig::getConfFloat(String key, float fallback) {
|
||||
try {
|
||||
String val=conf.at(key);
|
||||
float ret=std::stof(val);
|
||||
|
|
@ -199,7 +140,7 @@ float DivEngine::getConfFloat(String key, float fallback) {
|
|||
return fallback;
|
||||
}
|
||||
|
||||
double DivEngine::getConfDouble(String key, double fallback) {
|
||||
double DivConfig::getConfDouble(String key, double fallback) {
|
||||
try {
|
||||
String val=conf.at(key);
|
||||
double ret=std::stod(val);
|
||||
|
|
@ -210,7 +151,7 @@ double DivEngine::getConfDouble(String key, double fallback) {
|
|||
return fallback;
|
||||
}
|
||||
|
||||
String DivEngine::getConfString(String key, String fallback) {
|
||||
String DivConfig::getConfString(String key, String fallback) {
|
||||
try {
|
||||
String val=conf.at(key);
|
||||
return val;
|
||||
|
|
@ -219,7 +160,7 @@ String DivEngine::getConfString(String key, String fallback) {
|
|||
return fallback;
|
||||
}
|
||||
|
||||
void DivEngine::setConf(String key, bool value) {
|
||||
void DivConfig::setConf(String key, bool value) {
|
||||
if (value) {
|
||||
conf[key]="true";
|
||||
} else {
|
||||
|
|
@ -227,22 +168,22 @@ void DivEngine::setConf(String key, bool value) {
|
|||
}
|
||||
}
|
||||
|
||||
void DivEngine::setConf(String key, int value) {
|
||||
void DivConfig::setConf(String key, int value) {
|
||||
conf[key]=fmt::sprintf("%d",value);
|
||||
}
|
||||
|
||||
void DivEngine::setConf(String key, float value) {
|
||||
void DivConfig::setConf(String key, float value) {
|
||||
conf[key]=fmt::sprintf("%f",value);
|
||||
}
|
||||
|
||||
void DivEngine::setConf(String key, double value) {
|
||||
void DivConfig::setConf(String key, double value) {
|
||||
conf[key]=fmt::sprintf("%f",value);
|
||||
}
|
||||
|
||||
void DivEngine::setConf(String key, const char* value) {
|
||||
void DivConfig::setConf(String key, const char* value) {
|
||||
conf[key]=String(value);
|
||||
}
|
||||
|
||||
void DivEngine::setConf(String key, String value) {
|
||||
void DivConfig::setConf(String key, String value) {
|
||||
conf[key]=value;
|
||||
}
|
||||
|
|
|
|||
52
src/engine/config.h
Normal file
52
src/engine/config.h
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2022 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.
|
||||
*/
|
||||
|
||||
#ifndef _DIVCONFIG_H
|
||||
#define _DIVCONFIG_H
|
||||
|
||||
#include "../ta-utils.h"
|
||||
#include <map>
|
||||
|
||||
class DivConfig {
|
||||
std::map<String,String> conf;
|
||||
void parseLine(const char* line);
|
||||
public:
|
||||
// config loading/saving
|
||||
bool loadFromMemory(const char* buf);
|
||||
bool loadFromFile(const char* path, bool createOnFail=true);
|
||||
String toString();
|
||||
bool save(const char* path);
|
||||
|
||||
// get a config value
|
||||
bool getConfBool(String key, bool fallback);
|
||||
int getConfInt(String key, int fallback);
|
||||
float getConfFloat(String key, float fallback);
|
||||
double getConfDouble(String key, double fallback);
|
||||
String getConfString(String key, String fallback);
|
||||
|
||||
// set a config value
|
||||
void setConf(String key, bool value);
|
||||
void setConf(String key, int value);
|
||||
void setConf(String key, float value);
|
||||
void setConf(String key, double value);
|
||||
void setConf(String key, const char* value);
|
||||
void setConf(String key, String value);
|
||||
};
|
||||
|
||||
#endif
|
||||
162
src/engine/configEngine.cpp
Normal file
162
src/engine/configEngine.cpp
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2022 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 "engine.h"
|
||||
#include "../ta-log.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "winStuff.h"
|
||||
#define CONFIG_FILE "\\furnace.cfg"
|
||||
#else
|
||||
#ifdef __HAIKU__
|
||||
#include <support/SupportDefs.h>
|
||||
#include <storage/FindDirectory.h>
|
||||
#endif
|
||||
#include <unistd.h>
|
||||
#include <pwd.h>
|
||||
#include <sys/stat.h>
|
||||
#define CONFIG_FILE "/furnace.cfg"
|
||||
#endif
|
||||
|
||||
#ifdef IS_MOBILE
|
||||
#ifdef HAVE_SDL2
|
||||
#include <SDL.h>
|
||||
#else
|
||||
#error "Furnace mobile requires SDL2!"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
void DivEngine::initConfDir() {
|
||||
#ifdef _WIN32
|
||||
// maybe move this function in here instead?
|
||||
configPath=getWinConfigPath();
|
||||
#elif defined(IS_MOBILE)
|
||||
configPath=SDL_GetPrefPath("tildearrow","furnace");
|
||||
#else
|
||||
#ifdef __HAIKU__
|
||||
char userSettingsDir[PATH_MAX];
|
||||
status_t findUserDir = find_directory(B_USER_SETTINGS_DIRECTORY,0,true,userSettingsDir,PATH_MAX);
|
||||
if (findUserDir==B_OK) {
|
||||
configPath=userSettingsDir;
|
||||
} else {
|
||||
logW("unable to find/create user settings directory (%s)!",strerror(findUserDir));
|
||||
configPath=".";
|
||||
return;
|
||||
}
|
||||
#else
|
||||
// TODO this should check XDG_CONFIG_HOME first
|
||||
char* home=getenv("HOME");
|
||||
if (home==NULL) {
|
||||
int uid=getuid();
|
||||
struct passwd* entry=getpwuid(uid);
|
||||
if (entry==NULL) {
|
||||
logW("unable to determine home directory (%s)!",strerror(errno));
|
||||
configPath=".";
|
||||
return;
|
||||
} else {
|
||||
configPath=entry->pw_dir;
|
||||
}
|
||||
} else {
|
||||
configPath=home;
|
||||
}
|
||||
#ifdef __APPLE__
|
||||
configPath+="/Library/Application Support";
|
||||
#else
|
||||
// FIXME this doesn't honour XDG_CONFIG_HOME *at all*
|
||||
configPath+="/.config";
|
||||
#endif // __APPLE__
|
||||
#endif // __HAIKU__
|
||||
#ifdef __APPLE__
|
||||
configPath+="/Furnace";
|
||||
#else
|
||||
configPath+="/furnace";
|
||||
#endif // __APPLE__
|
||||
struct stat st;
|
||||
std::string pathSep="/";
|
||||
configPath+=pathSep;
|
||||
size_t sepPos=configPath.find(pathSep,1);
|
||||
while (sepPos!=std::string::npos) {
|
||||
std::string subpath=configPath.substr(0,sepPos++);
|
||||
if (stat(subpath.c_str(),&st)!=0) {
|
||||
logI("creating config path element %s ...",subpath.c_str());
|
||||
if (mkdir(subpath.c_str(),0755)!=0) {
|
||||
logW("could not create config path element %s! (%s)",subpath.c_str(),strerror(errno));
|
||||
configPath=".";
|
||||
return;
|
||||
}
|
||||
}
|
||||
sepPos=configPath.find(pathSep,sepPos);
|
||||
}
|
||||
configPath.resize(configPath.length()-pathSep.length());
|
||||
#endif // _WIN32
|
||||
}
|
||||
|
||||
bool DivEngine::saveConf() {
|
||||
configFile=configPath+String(CONFIG_FILE);
|
||||
return conf.save(configFile.c_str());
|
||||
}
|
||||
|
||||
bool DivEngine::loadConf() {
|
||||
configFile=configPath+String(CONFIG_FILE);
|
||||
return conf.loadFromFile(configFile.c_str());
|
||||
}
|
||||
|
||||
bool DivEngine::getConfBool(String key, bool fallback) {
|
||||
return conf.getConfBool(key,fallback);
|
||||
}
|
||||
|
||||
int DivEngine::getConfInt(String key, int fallback) {
|
||||
return conf.getConfInt(key,fallback);
|
||||
}
|
||||
|
||||
float DivEngine::getConfFloat(String key, float fallback) {
|
||||
return conf.getConfFloat(key,fallback);
|
||||
}
|
||||
|
||||
double DivEngine::getConfDouble(String key, double fallback) {
|
||||
return conf.getConfDouble(key,fallback);
|
||||
}
|
||||
|
||||
String DivEngine::getConfString(String key, String fallback) {
|
||||
return conf.getConfString(key,fallback);
|
||||
}
|
||||
|
||||
void DivEngine::setConf(String key, bool value) {
|
||||
conf.setConf(key,value);
|
||||
}
|
||||
|
||||
void DivEngine::setConf(String key, int value) {
|
||||
conf.setConf(key,value);
|
||||
}
|
||||
|
||||
void DivEngine::setConf(String key, float value) {
|
||||
conf.setConf(key,value);
|
||||
}
|
||||
|
||||
void DivEngine::setConf(String key, double value) {
|
||||
conf.setConf(key,value);
|
||||
}
|
||||
|
||||
void DivEngine::setConf(String key, const char* value) {
|
||||
conf.setConf(key,value);
|
||||
}
|
||||
|
||||
void DivEngine::setConf(String key, String value) {
|
||||
conf.setConf(key,value);
|
||||
}
|
||||
|
|
@ -401,6 +401,7 @@ void writePackedCommandValues(SafeWriter* w, const DivCommand& c) {
|
|||
SafeWriter* DivEngine::saveCommand(bool binary) {
|
||||
stop();
|
||||
repeatPattern=false;
|
||||
shallStop=false;
|
||||
setOrder(0);
|
||||
BUSY_BEGIN_SOFT;
|
||||
// determine loop point
|
||||
|
|
@ -1121,7 +1122,7 @@ void DivEngine::initSongWithDesc(const int* description) {
|
|||
song.system[index]=(DivSystem)description[i];
|
||||
song.systemVol[index]=description[i+1];
|
||||
song.systemPan[index]=description[i+2];
|
||||
song.systemFlags[index]=description[i+3];
|
||||
song.systemFlagsOld[index]=description[i+3];
|
||||
index++;
|
||||
chanCount+=getChannelCount(song.system[index]);
|
||||
if (chanCount>=DIV_MAX_CHANS) break;
|
||||
|
|
@ -1344,7 +1345,7 @@ void DivEngine::changeSystem(int index, DivSystem which, bool preserveOrder) {
|
|||
}
|
||||
|
||||
song.system[index]=which;
|
||||
song.systemFlags[index]=0;
|
||||
song.systemFlagsOld[index]=0;
|
||||
recalcChans();
|
||||
saveLock.unlock();
|
||||
BUSY_END;
|
||||
|
|
@ -1370,7 +1371,7 @@ bool DivEngine::addSystem(DivSystem which) {
|
|||
song.system[song.systemLen]=which;
|
||||
song.systemVol[song.systemLen]=64;
|
||||
song.systemPan[song.systemLen]=0;
|
||||
song.systemFlags[song.systemLen++]=0;
|
||||
song.systemFlagsOld[song.systemLen++]=0;
|
||||
recalcChans();
|
||||
saveLock.unlock();
|
||||
BUSY_END;
|
||||
|
|
@ -1414,7 +1415,7 @@ bool DivEngine::removeSystem(int index, bool preserveOrder) {
|
|||
song.system[i]=song.system[i+1];
|
||||
song.systemVol[i]=song.systemVol[i+1];
|
||||
song.systemPan[i]=song.systemPan[i+1];
|
||||
song.systemFlags[i]=song.systemFlags[i+1];
|
||||
song.systemFlagsOld[i]=song.systemFlagsOld[i+1];
|
||||
}
|
||||
recalcChans();
|
||||
saveLock.unlock();
|
||||
|
|
@ -1540,9 +1541,9 @@ bool DivEngine::swapSystem(int src, int dest, bool preserveOrder) {
|
|||
song.systemPan[dest]^=song.systemPan[src];
|
||||
song.systemPan[src]^=song.systemPan[dest];
|
||||
|
||||
song.systemFlags[src]^=song.systemFlags[dest];
|
||||
song.systemFlags[dest]^=song.systemFlags[src];
|
||||
song.systemFlags[src]^=song.systemFlags[dest];
|
||||
song.systemFlagsOld[src]^=song.systemFlagsOld[dest];
|
||||
song.systemFlagsOld[dest]^=song.systemFlagsOld[src];
|
||||
song.systemFlagsOld[src]^=song.systemFlagsOld[dest];
|
||||
|
||||
recalcChans();
|
||||
saveLock.unlock();
|
||||
|
|
@ -1873,6 +1874,7 @@ void DivEngine::play() {
|
|||
sPreview.wave=-1;
|
||||
sPreview.pos=0;
|
||||
sPreview.dir=false;
|
||||
shallStop=false;
|
||||
if (stepPlay==0) {
|
||||
freelance=false;
|
||||
playSub(false);
|
||||
|
|
@ -2031,6 +2033,7 @@ void DivEngine::reset() {
|
|||
speed1=curSubSong->speed1;
|
||||
speed2=curSubSong->speed2;
|
||||
firstTick=false;
|
||||
shallStop=false;
|
||||
nextSpeed=speed1;
|
||||
divider=60;
|
||||
if (curSubSong->customTempo) {
|
||||
|
|
@ -3469,9 +3472,9 @@ void DivEngine::setOrder(unsigned char order) {
|
|||
void DivEngine::setSysFlags(int system, unsigned int flags, bool restart) {
|
||||
BUSY_BEGIN_SOFT;
|
||||
saveLock.lock();
|
||||
song.systemFlags[system]=flags;
|
||||
song.systemFlagsOld[system]=flags;
|
||||
saveLock.unlock();
|
||||
disCont[system].dispatch->setFlags(song.systemFlags[system]);
|
||||
disCont[system].dispatch->setFlags(song.systemFlagsOld[system]);
|
||||
disCont[system].setRates(got.rate);
|
||||
if (restart && isPlaying()) {
|
||||
playSub(false);
|
||||
|
|
@ -3629,7 +3632,7 @@ void DivEngine::rescanAudioDevices() {
|
|||
void DivEngine::initDispatch() {
|
||||
BUSY_BEGIN;
|
||||
for (int i=0; i<song.systemLen; i++) {
|
||||
disCont[i].init(song.system[i],this,getChannelCount(song.system[i]),got.rate,song.systemFlags[i]);
|
||||
disCont[i].init(song.system[i],this,getChannelCount(song.system[i]),got.rate,song.systemFlagsOld[i]);
|
||||
disCont[i].setRates(got.rate);
|
||||
disCont[i].setQuality(lowQuality);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
#ifndef _ENGINE_H
|
||||
#define _ENGINE_H
|
||||
#include "config.h"
|
||||
#include "instrument.h"
|
||||
#include "song.h"
|
||||
#include "dispatch.h"
|
||||
|
|
@ -46,8 +47,8 @@
|
|||
#define BUSY_BEGIN_SOFT softLocked=true; isBusy.lock();
|
||||
#define BUSY_END isBusy.unlock(); softLocked=false;
|
||||
|
||||
#define DIV_VERSION "dev117"
|
||||
#define DIV_ENGINE_VERSION 117
|
||||
#define DIV_VERSION "dev118"
|
||||
#define DIV_ENGINE_VERSION 118
|
||||
// for imports
|
||||
#define DIV_VERSION_MOD 0xff01
|
||||
#define DIV_VERSION_FC 0xff02
|
||||
|
|
@ -327,6 +328,7 @@ class DivEngine {
|
|||
bool lowQuality;
|
||||
bool playing;
|
||||
bool freelance;
|
||||
bool shallStop;
|
||||
bool speedAB;
|
||||
bool endOfSong;
|
||||
bool consoleMode;
|
||||
|
|
@ -365,7 +367,7 @@ class DivEngine {
|
|||
DivAudioEngines audioEngine;
|
||||
DivAudioExportModes exportMode;
|
||||
double exportFadeOut;
|
||||
std::map<String,String> conf;
|
||||
DivConfig conf;
|
||||
std::deque<DivNoteEvent> pendingNotes;
|
||||
// bitfield
|
||||
unsigned char walked[8192];
|
||||
|
|
@ -998,6 +1000,7 @@ class DivEngine {
|
|||
lowQuality(false),
|
||||
playing(false),
|
||||
freelance(false),
|
||||
shallStop(false),
|
||||
speedAB(false),
|
||||
endOfSong(false),
|
||||
consoleMode(false),
|
||||
|
|
|
|||
|
|
@ -1235,7 +1235,7 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
|
||||
// system props
|
||||
for (int i=0; i<32; i++) {
|
||||
ds.systemFlags[i]=reader.readI();
|
||||
ds.systemFlagsOld[i]=reader.readI();
|
||||
}
|
||||
|
||||
// handle compound systems
|
||||
|
|
@ -2373,7 +2373,7 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) {
|
|||
ds.systemLen=(chCount+3)/4;
|
||||
for(int i=0; i<ds.systemLen; i++) {
|
||||
ds.system[i]=DIV_SYSTEM_AMIGA;
|
||||
ds.systemFlags[i]=1|(80<<8)|(bypassLimits?4:0)|((ds.systemLen>1 || bypassLimits)?2:0); // PAL
|
||||
ds.systemFlagsOld[i]=1|(80<<8)|(bypassLimits?4:0)|((ds.systemLen>1 || bypassLimits)?2:0); // PAL
|
||||
}
|
||||
for(int i=0; i<chCount; i++) {
|
||||
ds.subsong[0]->chanShow[i]=true;
|
||||
|
|
@ -2579,7 +2579,7 @@ bool DivEngine::loadFC(unsigned char* file, size_t len) {
|
|||
ds.system[0]=DIV_SYSTEM_AMIGA;
|
||||
ds.systemVol[0]=64;
|
||||
ds.systemPan[0]=0;
|
||||
ds.systemFlags[0]=1|(80<<8); // PAL
|
||||
ds.systemFlagsOld[0]=1|(80<<8); // PAL
|
||||
ds.systemName="Amiga";
|
||||
|
||||
seqLen=reader.readI_BE();
|
||||
|
|
@ -3219,11 +3219,11 @@ bool DivEngine::loadFTM(unsigned char* file, size_t len) {
|
|||
}
|
||||
if (expansions&16) {
|
||||
ds.system[systemID]=DIV_SYSTEM_N163;
|
||||
ds.systemFlags[systemID++]=n163Chans;
|
||||
ds.systemFlagsOld[systemID++]=n163Chans;
|
||||
}
|
||||
if (expansions&32) {
|
||||
ds.system[systemID]=DIV_SYSTEM_AY8910;
|
||||
ds.systemFlags[systemID++]=38; // Sunsoft 5B
|
||||
ds.systemFlagsOld[systemID++]=38; // Sunsoft 5B
|
||||
}
|
||||
ds.systemLen=systemID;
|
||||
|
||||
|
|
@ -3734,7 +3734,7 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
|
|||
}
|
||||
|
||||
for (int i=0; i<32; i++) {
|
||||
w->writeI(song.systemFlags[i]);
|
||||
w->writeI(song.systemFlagsOld[i]);
|
||||
}
|
||||
|
||||
// song name
|
||||
|
|
|
|||
|
|
@ -559,13 +559,12 @@ void DivInstrument::putInsData(SafeWriter* w) {
|
|||
w->writeC(es5506.envelope.k2Slow);
|
||||
|
||||
// SNES
|
||||
// @tildearrow please update this
|
||||
w->writeC(snes.useEnv);
|
||||
w->writeC(0);
|
||||
w->writeC(0);
|
||||
w->writeC(snes.gainMode);
|
||||
w->writeC(snes.gain);
|
||||
w->writeC(snes.a);
|
||||
w->writeC(snes.d);
|
||||
w->writeC(snes.s);
|
||||
w->writeC((snes.s&7)|(snes.sus?8:0));
|
||||
w->writeC(snes.r);
|
||||
|
||||
// macro speed/delay
|
||||
|
|
@ -1259,11 +1258,19 @@ DivDataErrors DivInstrument::readInsData(SafeReader& reader, short version) {
|
|||
// SNES
|
||||
if (version>=109) {
|
||||
snes.useEnv=reader.readC();
|
||||
reader.readC();
|
||||
reader.readC();
|
||||
if (version<118) {
|
||||
// why why why
|
||||
reader.readC();
|
||||
reader.readC();
|
||||
} else {
|
||||
snes.gainMode=(DivInstrumentSNES::GainMode)reader.readC();
|
||||
snes.gain=reader.readC();
|
||||
}
|
||||
snes.a=reader.readC();
|
||||
snes.d=reader.readC();
|
||||
snes.s=reader.readC();
|
||||
snes.sus=snes.s&8;
|
||||
snes.s&=7;
|
||||
snes.r=reader.readC();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -610,12 +610,13 @@ struct DivInstrumentSNES {
|
|||
GAIN_MODE_INC_LINEAR=6,
|
||||
GAIN_MODE_INC_INVLOG=7
|
||||
};
|
||||
bool useEnv;
|
||||
bool useEnv, sus;
|
||||
GainMode gainMode;
|
||||
unsigned char gain;
|
||||
unsigned char a, d, s, r;
|
||||
DivInstrumentSNES():
|
||||
useEnv(true),
|
||||
sus(false),
|
||||
gainMode(GAIN_MODE_DIRECT),
|
||||
gain(127),
|
||||
a(15),
|
||||
|
|
|
|||
|
|
@ -218,6 +218,10 @@ void DivPlatformArcade::tick(bool sysTick) {
|
|||
immWrite(0x18,chan[i].std.ex3.val);
|
||||
}
|
||||
|
||||
if (chan[i].std.ex1.had || chan[i].std.ex2.had || chan[i].std.ex3.had) {
|
||||
immWrite(0x01,0x00); // LFO On
|
||||
}
|
||||
|
||||
if (chan[i].std.alg.had) {
|
||||
chan[i].state.alg=chan[i].std.alg.val;
|
||||
if (isMuted[i]) {
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ void DivPlatformPCMDAC::acquire(short* bufL, short* bufR, size_t start, size_t l
|
|||
chan.audPos%=chan.audLen;
|
||||
chan.audDir=false;
|
||||
}
|
||||
output=(chan.ws.output[chan.audPos]^0x80)<<8;
|
||||
output=(chan.ws.output[chan.audPos]-0x80)<<8;
|
||||
} else {
|
||||
DivSample* s=parent->getSample(chan.sample);
|
||||
if (s->samples>0) {
|
||||
|
|
@ -195,7 +195,7 @@ int DivPlatformPCMDAC::dispatch(DivCommand c) {
|
|||
DivInstrument* ins=parent->getIns(chan.ins,DIV_INS_AMIGA);
|
||||
if (ins->amiga.useWave) {
|
||||
chan.useWave=true;
|
||||
chan.audLen=ins->amiga.waveLen;
|
||||
chan.audLen=ins->amiga.waveLen+1;
|
||||
if (chan.insChanged) {
|
||||
if (chan.wave<0) {
|
||||
chan.wave=0;
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ void DivPlatformSNES::acquire(short* bufL, short* bufR, size_t start, size_t len
|
|||
bufL[h]=out[0];
|
||||
bufR[h]=out[1];
|
||||
for (int i=0; i<8; i++) {
|
||||
int next=chOut[i*2]+chOut[i*2+1];
|
||||
int next=(3*(chOut[i*2]+chOut[i*2+1]))>>2;
|
||||
if (next<-32768) next=-32768;
|
||||
if (next>32767) next=32767;
|
||||
next=(next*254)/MAX(1,globalVolL+globalVolR);
|
||||
|
|
@ -106,18 +106,9 @@ void DivPlatformSNES::tick(bool sysTick) {
|
|||
}
|
||||
if (chan[i].std.arp.had) {
|
||||
if (!chan[i].inPorta) {
|
||||
if (chan[i].std.arp.mode) {
|
||||
chan[i].baseFreq=NOTE_FREQUENCY(chan[i].std.arp.val);
|
||||
} else {
|
||||
chan[i].baseFreq=NOTE_FREQUENCY(chan[i].note+chan[i].std.arp.val);
|
||||
}
|
||||
chan[i].baseFreq=NOTE_FREQUENCY(parent->calcArp(chan[i].note,chan[i].std.arp.val));
|
||||
}
|
||||
chan[i].freqChanged=true;
|
||||
} else {
|
||||
if (chan[i].std.arp.mode && chan[i].std.arp.finished) {
|
||||
chan[i].baseFreq=NOTE_FREQUENCY(chan[i].note);
|
||||
chan[i].freqChanged=true;
|
||||
}
|
||||
}
|
||||
if (chan[i].std.duty.had) {
|
||||
noiseFreq=chan[i].std.duty.val;
|
||||
|
|
@ -217,7 +208,9 @@ void DivPlatformSNES::tick(bool sysTick) {
|
|||
chan[i].keyOn=false;
|
||||
}
|
||||
if (chan[i].keyOff) {
|
||||
koff|=(1<<i);
|
||||
if (!chan[i].state.sus) {
|
||||
koff|=(1<<i);
|
||||
}
|
||||
chan[i].keyOff=false;
|
||||
}
|
||||
if (chan[i].freqChanged) {
|
||||
|
|
@ -228,7 +221,7 @@ void DivPlatformSNES::tick(bool sysTick) {
|
|||
}
|
||||
}
|
||||
if (writeControl) {
|
||||
unsigned char control=noiseFreq&0x1f;
|
||||
unsigned char control=(noiseFreq&0x1f)|(echoOn?0:0x20);
|
||||
rWrite(0x6c,control);
|
||||
writeControl=false;
|
||||
}
|
||||
|
|
@ -291,9 +284,9 @@ int DivPlatformSNES::dispatch(DivCommand c) {
|
|||
if (chan[c.chan].insChanged) {
|
||||
if (chan[c.chan].wave<0) {
|
||||
chan[c.chan].wave=0;
|
||||
chan[c.chan].ws.setWidth(chan[c.chan].wtLen);
|
||||
chan[c.chan].ws.changeWave1(chan[c.chan].wave);
|
||||
}
|
||||
chan[c.chan].ws.setWidth(chan[c.chan].wtLen);
|
||||
chan[c.chan].ws.changeWave1(chan[c.chan].wave);
|
||||
}
|
||||
chan[c.chan].ws.init(ins,chan[c.chan].wtLen,15,chan[c.chan].insChanged);
|
||||
} else {
|
||||
|
|
@ -306,47 +299,39 @@ int DivPlatformSNES::dispatch(DivCommand c) {
|
|||
if (chan[c.chan].insChanged) {
|
||||
chan[c.chan].state=ins->snes;
|
||||
}
|
||||
if (chan[c.chan].state.useEnv) {
|
||||
chWrite(c.chan,5,chan[c.chan].state.a|(chan[c.chan].state.d<<4)|0x80);
|
||||
chWrite(c.chan,6,chan[c.chan].state.r|(chan[c.chan].state.s<<5));
|
||||
} else {
|
||||
chWrite(c.chan,5,0);
|
||||
switch (chan[c.chan].state.gainMode) {
|
||||
case DivInstrumentSNES::GAIN_MODE_DIRECT:
|
||||
chWrite(c.chan,7,chan[c.chan].state.gain&127);
|
||||
break;
|
||||
case DivInstrumentSNES::GAIN_MODE_DEC_LINEAR:
|
||||
chWrite(c.chan,7,0x80|(chan[c.chan].state.gain&31));
|
||||
break;
|
||||
case DivInstrumentSNES::GAIN_MODE_INC_LINEAR:
|
||||
chWrite(c.chan,7,0xc0|(chan[c.chan].state.gain&31));
|
||||
break;
|
||||
case DivInstrumentSNES::GAIN_MODE_DEC_LOG:
|
||||
chWrite(c.chan,7,0xa0|(chan[c.chan].state.gain&31));
|
||||
break;
|
||||
case DivInstrumentSNES::GAIN_MODE_INC_INVLOG:
|
||||
chWrite(c.chan,7,0xe0|(chan[c.chan].state.gain&31));
|
||||
break;
|
||||
}
|
||||
}
|
||||
chan[c.chan].active=true;
|
||||
if (chan[c.chan].insChanged || chan[c.chan].state.sus) {
|
||||
writeEnv(c.chan);
|
||||
}
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].baseFreq=round(NOTE_FREQUENCY(c.value));
|
||||
chan[c.chan].freqChanged=true;
|
||||
chan[c.chan].note=c.value;
|
||||
}
|
||||
chan[c.chan].active=true;
|
||||
chan[c.chan].keyOn=true;
|
||||
chan[c.chan].macroInit(ins);
|
||||
chan[c.chan].insChanged=false;
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_NOTE_OFF:
|
||||
chan[c.chan].sample=-1;
|
||||
chan[c.chan].active=false;
|
||||
chan[c.chan].keyOff=true;
|
||||
chan[c.chan].macroInit(NULL);
|
||||
chan[c.chan].keyOn=false;
|
||||
if (chan[c.chan].state.sus) {
|
||||
writeEnv(c.chan);
|
||||
} else {
|
||||
chan[c.chan].macroInit(NULL);
|
||||
}
|
||||
break;
|
||||
case DIV_CMD_NOTE_OFF_ENV:
|
||||
chan[c.chan].active=false;
|
||||
chan[c.chan].keyOff=true;
|
||||
chan[c.chan].keyOn=false;
|
||||
if (chan[c.chan].state.sus) {
|
||||
writeEnv(c.chan);
|
||||
}
|
||||
chan[c.chan].std.release();
|
||||
break;
|
||||
case DIV_CMD_ENV_RELEASE:
|
||||
chan[c.chan].std.release();
|
||||
break;
|
||||
|
|
@ -359,7 +344,10 @@ int DivPlatformSNES::dispatch(DivCommand c) {
|
|||
case DIV_CMD_VOLUME:
|
||||
if (chan[c.chan].vol!=c.value) {
|
||||
chan[c.chan].vol=c.value;
|
||||
writeOutVol(c.chan);
|
||||
if (!chan[c.chan].std.vol.has) {
|
||||
chan[c.chan].outVol=c.value;
|
||||
writeOutVol(c.chan);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case DIV_CMD_GET_VOLUME:
|
||||
|
|
@ -374,6 +362,11 @@ int DivPlatformSNES::dispatch(DivCommand c) {
|
|||
chan[c.chan].pitch=c.value;
|
||||
chan[c.chan].freqChanged=true;
|
||||
break;
|
||||
case DIV_CMD_WAVE:
|
||||
if (!chan[c.chan].useWave) break;
|
||||
chan[c.chan].wave=c.value;
|
||||
chan[c.chan].ws.changeWave1(chan[c.chan].wave);
|
||||
break;
|
||||
case DIV_CMD_NOTE_PORTA: {
|
||||
int destFreq=round(NOTE_FREQUENCY(c.value2));
|
||||
bool return2=false;
|
||||
|
|
@ -410,10 +403,117 @@ int DivPlatformSNES::dispatch(DivCommand c) {
|
|||
chan[c.chan].inPorta=c.value;
|
||||
break;
|
||||
case DIV_CMD_SAMPLE_POS:
|
||||
// may have to remove this
|
||||
chan[c.chan].audPos=c.value;
|
||||
chan[c.chan].setPos=true;
|
||||
break;
|
||||
// TODO SNES-specific commands
|
||||
case DIV_CMD_STD_NOISE_MODE:
|
||||
chan[c.chan].noise=c.value;
|
||||
writeNoise=true;
|
||||
break;
|
||||
case DIV_CMD_SNES_PITCH_MOD:
|
||||
chan[c.chan].pitchMod=c.value;
|
||||
writePitchMod=true;
|
||||
break;
|
||||
case DIV_CMD_SNES_INVERT:
|
||||
chan[c.chan].invertL=(c.value>>4);
|
||||
chan[c.chan].invertR=c.chan&15;
|
||||
writeOutVol(c.chan);
|
||||
break;
|
||||
case DIV_CMD_SNES_GAIN_MODE:
|
||||
if (c.value) {
|
||||
chan[c.chan].state.useEnv=false;
|
||||
switch (c.value) {
|
||||
case 1:
|
||||
chan[c.chan].state.gainMode=DivInstrumentSNES::GAIN_MODE_DIRECT;
|
||||
break;
|
||||
case 2:
|
||||
chan[c.chan].state.gainMode=DivInstrumentSNES::GAIN_MODE_DEC_LINEAR;
|
||||
break;
|
||||
case 3:
|
||||
chan[c.chan].state.gainMode=DivInstrumentSNES::GAIN_MODE_DEC_LOG;
|
||||
break;
|
||||
case 4:
|
||||
chan[c.chan].state.gainMode=DivInstrumentSNES::GAIN_MODE_INC_LINEAR;
|
||||
break;
|
||||
case 5:
|
||||
chan[c.chan].state.gainMode=DivInstrumentSNES::GAIN_MODE_INC_INVLOG;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
chan[c.chan].state.useEnv=true;
|
||||
}
|
||||
writeEnv(c.chan);
|
||||
break;
|
||||
case DIV_CMD_SNES_GAIN:
|
||||
if (chan[c.chan].state.gainMode==DivInstrumentSNES::GAIN_MODE_DIRECT) {
|
||||
chan[c.chan].state.gain=c.value&0x7f;
|
||||
} else {
|
||||
chan[c.chan].state.gain=c.value&0x1f;
|
||||
}
|
||||
if (!chan[c.chan].state.useEnv) writeEnv(c.chan);
|
||||
break;
|
||||
case DIV_CMD_STD_NOISE_FREQ:
|
||||
noiseFreq=c.value&0x1f;
|
||||
writeControl=true;
|
||||
break;
|
||||
case DIV_CMD_FM_AR:
|
||||
chan[c.chan].state.a=c.value&15;
|
||||
if (chan[c.chan].state.useEnv) writeEnv(c.chan);
|
||||
break;
|
||||
case DIV_CMD_FM_DR:
|
||||
chan[c.chan].state.d=c.value&7;
|
||||
if (chan[c.chan].state.useEnv) writeEnv(c.chan);
|
||||
break;
|
||||
case DIV_CMD_FM_SL:
|
||||
chan[c.chan].state.s=c.value&7;
|
||||
if (chan[c.chan].state.useEnv) writeEnv(c.chan);
|
||||
break;
|
||||
case DIV_CMD_FM_RR:
|
||||
chan[c.chan].state.r=c.value&0x1f;
|
||||
if (chan[c.chan].state.useEnv) writeEnv(c.chan);
|
||||
break;
|
||||
case DIV_CMD_SNES_ECHO:
|
||||
chan[c.chan].echo=c.value;
|
||||
writeEcho=true;
|
||||
break;
|
||||
case DIV_CMD_SNES_ECHO_DELAY: {
|
||||
echoDelay=c.value&15;
|
||||
unsigned char esa=0xf8-(echoDelay<<3);
|
||||
if (echoOn) {
|
||||
rWrite(0x6d,esa);
|
||||
rWrite(0x7d,echoDelay);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_SNES_ECHO_ENABLE:
|
||||
echoOn=c.value;
|
||||
initEcho();
|
||||
break;
|
||||
case DIV_CMD_SNES_ECHO_FEEDBACK:
|
||||
echoFeedback=c.value;
|
||||
if (echoOn) {
|
||||
rWrite(0x0d,echoFeedback);
|
||||
}
|
||||
break;
|
||||
case DIV_CMD_SNES_ECHO_FIR:
|
||||
echoFIR[c.value&7]=c.value2;
|
||||
if (echoOn) {
|
||||
rWrite(0x0f+((c.value&7)<<4),echoFIR[c.value&7]);
|
||||
}
|
||||
break;
|
||||
case DIV_CMD_SNES_ECHO_VOL_LEFT:
|
||||
echoVolL=c.value;
|
||||
if (echoOn) {
|
||||
rWrite(0x2c,echoVolL);
|
||||
}
|
||||
break;
|
||||
case DIV_CMD_SNES_ECHO_VOL_RIGHT:
|
||||
echoVolR=c.value;
|
||||
if (echoOn) {
|
||||
rWrite(0x3c,echoVolR);
|
||||
}
|
||||
break;
|
||||
case DIV_CMD_GET_VOLMAX:
|
||||
return 127;
|
||||
break;
|
||||
|
|
@ -450,6 +550,36 @@ void DivPlatformSNES::writeOutVol(int ch) {
|
|||
chWrite(ch,1,outR);
|
||||
}
|
||||
|
||||
void DivPlatformSNES::writeEnv(int ch) {
|
||||
if (chan[ch].state.useEnv) {
|
||||
chWrite(ch,5,chan[ch].state.a|(chan[ch].state.d<<4)|0x80);
|
||||
if (chan[ch].state.sus && chan[ch].active) {
|
||||
chWrite(ch,6,chan[ch].state.s<<5);
|
||||
} else {
|
||||
chWrite(ch,6,chan[ch].state.r|(chan[ch].state.s<<5));
|
||||
}
|
||||
} else {
|
||||
chWrite(ch,5,0);
|
||||
switch (chan[ch].state.gainMode) {
|
||||
case DivInstrumentSNES::GAIN_MODE_DIRECT:
|
||||
chWrite(ch,7,chan[ch].state.gain&127);
|
||||
break;
|
||||
case DivInstrumentSNES::GAIN_MODE_DEC_LINEAR:
|
||||
chWrite(ch,7,0x80|(chan[ch].state.gain&31));
|
||||
break;
|
||||
case DivInstrumentSNES::GAIN_MODE_INC_LINEAR:
|
||||
chWrite(ch,7,0xc0|(chan[ch].state.gain&31));
|
||||
break;
|
||||
case DivInstrumentSNES::GAIN_MODE_DEC_LOG:
|
||||
chWrite(ch,7,0xa0|(chan[ch].state.gain&31));
|
||||
break;
|
||||
case DivInstrumentSNES::GAIN_MODE_INC_INVLOG:
|
||||
chWrite(ch,7,0xe0|(chan[ch].state.gain&31));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformSNES::muteChannel(int ch, bool mute) {
|
||||
isMuted[ch]=mute;
|
||||
writeOutVol(ch);
|
||||
|
|
@ -469,6 +599,7 @@ void DivPlatformSNES::forceIns() {
|
|||
writeNoise=true;
|
||||
writePitchMod=true;
|
||||
writeEcho=true;
|
||||
initEcho();
|
||||
}
|
||||
|
||||
void* DivPlatformSNES::getChanState(int ch) {
|
||||
|
|
@ -497,7 +628,28 @@ int DivPlatformSNES::getRegisterPoolSize() {
|
|||
return 128;
|
||||
}
|
||||
|
||||
void DivPlatformSNES::initEcho() {
|
||||
unsigned char esa=0xf8-(echoDelay<<3);
|
||||
if (echoOn) {
|
||||
rWrite(0x6d,esa);
|
||||
rWrite(0x7d,echoDelay);
|
||||
rWrite(0x0d,echoFeedback);
|
||||
rWrite(0x2c,echoVolL);
|
||||
rWrite(0x3c,echoVolR);
|
||||
for (int i=0; i<8; i++) {
|
||||
rWrite(0x0f+(i<<4),echoFIR[i]);
|
||||
}
|
||||
} else {
|
||||
rWrite(0x6d,0);
|
||||
rWrite(0x7d,0);
|
||||
rWrite(0x2c,0);
|
||||
rWrite(0x3c,0);
|
||||
}
|
||||
writeControl=true;
|
||||
}
|
||||
|
||||
void DivPlatformSNES::reset() {
|
||||
memcpy(sampleMem,copyOfSampleMem,65536);
|
||||
dsp.init(sampleMem);
|
||||
dsp.set_output(NULL,0);
|
||||
memset(regPool,0,128);
|
||||
|
|
@ -521,6 +673,22 @@ void DivPlatformSNES::reset() {
|
|||
writeNoise=false;
|
||||
writePitchMod=false;
|
||||
writeEcho=false;
|
||||
|
||||
echoDelay=0;
|
||||
echoFeedback=0;
|
||||
echoFIR[0]=127;
|
||||
echoFIR[1]=0;
|
||||
echoFIR[2]=0;
|
||||
echoFIR[3]=0;
|
||||
echoFIR[4]=0;
|
||||
echoFIR[5]=0;
|
||||
echoFIR[6]=0;
|
||||
echoFIR[7]=0;
|
||||
echoVolL=127;
|
||||
echoVolR=127;
|
||||
echoOn=false;
|
||||
|
||||
initEcho();
|
||||
}
|
||||
|
||||
bool DivPlatformSNES::isStereo() {
|
||||
|
|
@ -574,7 +742,7 @@ size_t DivPlatformSNES::getSampleMemUsage(int index) {
|
|||
}
|
||||
|
||||
void DivPlatformSNES::renderSamples() {
|
||||
memset(sampleMem,0,getSampleMemCapacity());
|
||||
memset(copyOfSampleMem,0,getSampleMemCapacity());
|
||||
memset(sampleOff,0,256*sizeof(unsigned int));
|
||||
|
||||
// skip past sample table and wavetable buffer
|
||||
|
|
@ -585,17 +753,18 @@ void DivPlatformSNES::renderSamples() {
|
|||
int actualLength=MIN((int)(getSampleMemCapacity()-memPos)/9*9,length);
|
||||
if (actualLength>0) {
|
||||
sampleOff[i]=memPos;
|
||||
memcpy(&sampleMem[memPos],s->dataBRR,actualLength);
|
||||
memcpy(©OfSampleMem[memPos],s->dataBRR,actualLength);
|
||||
memPos+=actualLength;
|
||||
}
|
||||
if (actualLength<length) {
|
||||
// terminate the sample
|
||||
sampleMem[memPos-9]=1;
|
||||
copyOfSampleMem[memPos-9]=1;
|
||||
logW("out of BRR memory for sample %d!",i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
sampleMemLen=memPos;
|
||||
memcpy(sampleMem,copyOfSampleMem,65536);
|
||||
}
|
||||
|
||||
void DivPlatformSNES::setFlags(unsigned int flags) {
|
||||
|
|
@ -607,13 +776,14 @@ int DivPlatformSNES::init(DivEngine* p, int channels, int sugRate, unsigned int
|
|||
parent=p;
|
||||
dumpWrites=false;
|
||||
skipRegisterWrites=false;
|
||||
for (int i=0; i<8; i++) {
|
||||
oscBuf[i]=new DivDispatchOscBuffer;
|
||||
isMuted[i]=false;
|
||||
}
|
||||
sampleMemLen=0;
|
||||
chipClock=1024000;
|
||||
rate=chipClock/32;
|
||||
for (int i=0; i<8; i++) {
|
||||
oscBuf[i]=new DivDispatchOscBuffer;
|
||||
oscBuf[i]->rate=rate;
|
||||
isMuted[i]=false;
|
||||
}
|
||||
setFlags(flags);
|
||||
reset();
|
||||
return 8;
|
||||
|
|
|
|||
|
|
@ -77,11 +77,15 @@ class DivPlatformSNES: public DivDispatch {
|
|||
bool isMuted[8];
|
||||
int globalVolL, globalVolR;
|
||||
unsigned char noiseFreq;
|
||||
signed char echoVolL, echoVolR, echoFeedback;
|
||||
signed char echoFIR[8];
|
||||
unsigned char echoDelay;
|
||||
size_t sampleTableBase;
|
||||
bool writeControl;
|
||||
bool writeNoise;
|
||||
bool writePitchMod;
|
||||
bool writeEcho;
|
||||
bool echoOn;
|
||||
|
||||
struct QueuedWrite {
|
||||
unsigned char addr;
|
||||
|
|
@ -91,6 +95,7 @@ class DivPlatformSNES: public DivDispatch {
|
|||
std::queue<QueuedWrite> writes;
|
||||
|
||||
signed char sampleMem[65536];
|
||||
signed char copyOfSampleMem[65536];
|
||||
size_t sampleMemLen;
|
||||
unsigned int sampleOff[256];
|
||||
unsigned char regPool[0x80];
|
||||
|
|
@ -126,6 +131,8 @@ class DivPlatformSNES: public DivDispatch {
|
|||
private:
|
||||
void updateWave(int ch);
|
||||
void writeOutVol(int ch);
|
||||
void writeEnv(int ch);
|
||||
void initEcho();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -203,6 +203,7 @@ void DivPlatformYM2203::tick(bool sysTick) {
|
|||
ay->tick(sysTick);
|
||||
ay->flushWrites();
|
||||
for (DivRegWrite& i: ay->getRegisterWrites()) {
|
||||
if (i.addr>15) continue;
|
||||
immWrite(i.addr&15,i.val);
|
||||
}
|
||||
ay->getRegisterWrites().clear();
|
||||
|
|
@ -807,6 +808,7 @@ void DivPlatformYM2203::forceIns() {
|
|||
ay->forceIns();
|
||||
ay->flushWrites();
|
||||
for (DivRegWrite& i: ay->getRegisterWrites()) {
|
||||
if (i.addr>15) continue;
|
||||
immWrite(i.addr&15,i.val);
|
||||
}
|
||||
ay->getRegisterWrites().clear();
|
||||
|
|
|
|||
|
|
@ -448,6 +448,7 @@ void DivPlatformYM2203Ext::forceIns() {
|
|||
ay->forceIns();
|
||||
ay->flushWrites();
|
||||
for (DivRegWrite& i: ay->getRegisterWrites()) {
|
||||
if (i.addr>15) continue;
|
||||
immWrite(i.addr&15,i.val);
|
||||
}
|
||||
ay->getRegisterWrites().clear();
|
||||
|
|
|
|||
|
|
@ -654,6 +654,7 @@ void DivPlatformYM2608::tick(bool sysTick) {
|
|||
ay->tick(sysTick);
|
||||
ay->flushWrites();
|
||||
for (DivRegWrite& i: ay->getRegisterWrites()) {
|
||||
if (i.addr>15) continue;
|
||||
immWrite(i.addr&15,i.val);
|
||||
}
|
||||
ay->getRegisterWrites().clear();
|
||||
|
|
@ -1199,6 +1200,7 @@ void DivPlatformYM2608::forceIns() {
|
|||
ay->forceIns();
|
||||
ay->flushWrites();
|
||||
for (DivRegWrite& i: ay->getRegisterWrites()) {
|
||||
if (i.addr>15) continue;
|
||||
immWrite(i.addr&15,i.val);
|
||||
}
|
||||
ay->getRegisterWrites().clear();
|
||||
|
|
|
|||
|
|
@ -483,6 +483,7 @@ void DivPlatformYM2608Ext::forceIns() {
|
|||
ay->forceIns();
|
||||
ay->flushWrites();
|
||||
for (DivRegWrite& i: ay->getRegisterWrites()) {
|
||||
if (i.addr>15) continue;
|
||||
immWrite(i.addr&15,i.val);
|
||||
}
|
||||
ay->getRegisterWrites().clear();
|
||||
|
|
|
|||
|
|
@ -593,13 +593,14 @@ void DivPlatformYM2610::tick(bool sysTick) {
|
|||
ay->tick(sysTick);
|
||||
ay->flushWrites();
|
||||
for (DivRegWrite& i: ay->getRegisterWrites()) {
|
||||
if (i.addr>15) continue;
|
||||
immWrite(i.addr&15,i.val);
|
||||
}
|
||||
ay->getRegisterWrites().clear();
|
||||
}
|
||||
|
||||
int DivPlatformYM2610::dispatch(DivCommand c) {
|
||||
if (c.chan>=psgChanOffs && c.chan<7) {
|
||||
if (c.chan>=psgChanOffs && c.chan<adpcmAChanOffs) {
|
||||
c.chan-=psgChanOffs;
|
||||
return ay->dispatch(c);
|
||||
}
|
||||
|
|
@ -1169,6 +1170,7 @@ void DivPlatformYM2610::forceIns() {
|
|||
ay->forceIns();
|
||||
ay->flushWrites();
|
||||
for (DivRegWrite& i: ay->getRegisterWrites()) {
|
||||
if (i.addr>15) continue;
|
||||
immWrite(i.addr&15,i.val);
|
||||
}
|
||||
ay->getRegisterWrites().clear();
|
||||
|
|
|
|||
|
|
@ -656,6 +656,7 @@ void DivPlatformYM2610B::tick(bool sysTick) {
|
|||
ay->tick(sysTick);
|
||||
ay->flushWrites();
|
||||
for (DivRegWrite& i: ay->getRegisterWrites()) {
|
||||
if (i.addr>15) continue;
|
||||
immWrite(i.addr&15,i.val);
|
||||
}
|
||||
ay->getRegisterWrites().clear();
|
||||
|
|
@ -1232,6 +1233,7 @@ void DivPlatformYM2610B::forceIns() {
|
|||
ay->forceIns();
|
||||
ay->flushWrites();
|
||||
for (DivRegWrite& i: ay->getRegisterWrites()) {
|
||||
if (i.addr>15) continue;
|
||||
immWrite(i.addr&15,i.val);
|
||||
}
|
||||
ay->getRegisterWrites().clear();
|
||||
|
|
|
|||
|
|
@ -474,6 +474,7 @@ void DivPlatformYM2610BExt::forceIns() {
|
|||
ay->forceIns();
|
||||
ay->flushWrites();
|
||||
for (DivRegWrite& i: ay->getRegisterWrites()) {
|
||||
if (i.addr>15) continue;
|
||||
immWrite(i.addr&15,i.val);
|
||||
}
|
||||
ay->getRegisterWrites().clear();
|
||||
|
|
|
|||
|
|
@ -474,6 +474,7 @@ void DivPlatformYM2610Ext::forceIns() {
|
|||
ay->forceIns();
|
||||
ay->flushWrites();
|
||||
for (DivRegWrite& i: ay->getRegisterWrites()) {
|
||||
if (i.addr>15) continue;
|
||||
immWrite(i.addr&15,i.val);
|
||||
}
|
||||
ay->getRegisterWrites().clear();
|
||||
|
|
|
|||
|
|
@ -858,15 +858,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
|
|||
break;
|
||||
|
||||
case 0xff: // stop song
|
||||
freelance=false;
|
||||
playing=false;
|
||||
extValuePresent=false;
|
||||
stepPlay=0;
|
||||
remainingLoops=-1;
|
||||
sPreview.sample=-1;
|
||||
sPreview.wave=-1;
|
||||
sPreview.pos=0;
|
||||
sPreview.dir=false;
|
||||
shallStop=true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -1299,6 +1291,21 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
|
|||
|
||||
firstTick=false;
|
||||
|
||||
if (shallStop) {
|
||||
freelance=false;
|
||||
playing=false;
|
||||
extValuePresent=false;
|
||||
stepPlay=0;
|
||||
remainingLoops=-1;
|
||||
sPreview.sample=-1;
|
||||
sPreview.wave=-1;
|
||||
sPreview.pos=0;
|
||||
sPreview.dir=false;
|
||||
ret=true;
|
||||
shallStop=false;
|
||||
return ret;
|
||||
}
|
||||
|
||||
// system tick
|
||||
for (int i=0; i<song.systemLen; i++) disCont[i].dispatch->tick(subticks==tickMult);
|
||||
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
#define DIV_MAX_CHANS 128
|
||||
|
||||
#include "../ta-utils.h"
|
||||
#include "config.h"
|
||||
#include "orders.h"
|
||||
#include "instrument.h"
|
||||
#include "pattern.h"
|
||||
|
|
@ -233,202 +234,10 @@ struct DivSong {
|
|||
unsigned char systemLen;
|
||||
signed char systemVol[32];
|
||||
signed char systemPan[32];
|
||||
// interpretation of these flags varies depending on system.
|
||||
// - most systems:
|
||||
// - bit 0: PAL
|
||||
// - NES:
|
||||
// - bit 0-1: system type
|
||||
// - 0: NTSC
|
||||
// - 1: PAL
|
||||
// - 2: Dendy
|
||||
// - SMS/SN76489:
|
||||
// - bit 0-1, 8-15: clock rate
|
||||
// - 0000: 3.58MHz (NTSC)
|
||||
// - 0001: 3.55MHz (PAL)
|
||||
// - 0002: 4MHz (Other)
|
||||
// - 0003: 1.79MHz (half NTSC)
|
||||
// - 0100: 3MHz
|
||||
// - 0101: 2MHz
|
||||
// - 0102: 447KHz (NTSC / 8)
|
||||
// - bit 2-3, 6-7: chip type
|
||||
// - 00: Sega VDP (16-bit noise)
|
||||
// - 04: real SN76489 (15-bit noise)
|
||||
// - 08: real SN76489 with Atari-like short noise buzz (15-bit noise)
|
||||
// - 0c: Game Gear (16-bit noise, stereo)
|
||||
// - 40: real SN76489A (17-bit noise)
|
||||
// - 44: real SN76496 (17-bit noise)
|
||||
// - 48: NCR 8496 (16-bit noise)
|
||||
// - 4c: Tandy PSSJ-3 (16-bit noise)
|
||||
// - 80: real SN94624 (15-bit noise)
|
||||
// - 84: real SN76494 (17-bit noise)
|
||||
// - bit 4: disable noise phase reset
|
||||
// - YM2612/YM3438:
|
||||
// - bit 0-30: clock rate
|
||||
// - 0: Genesis NTSC (7.67MHz)
|
||||
// - 1: Genesis PAL (7.61MHz)
|
||||
// - 2: FM Towns (8MHz)
|
||||
// - 3: AtGames Genesis (6.13MHz)
|
||||
// - 4: Sega System 32 (8.06MHz)
|
||||
// - bit 31: DAC distortion
|
||||
// - 0: disable
|
||||
// - 1: enable
|
||||
// - YM2151:
|
||||
// - bit 0-7: clock rate
|
||||
// - 0: 3.58MHz (NTSC)
|
||||
// - 1: 3.55MHz (PAL)
|
||||
// - 2: 4MHz
|
||||
// - YM2610(B):
|
||||
// - bit 0-7: clock rate
|
||||
// - 0: 8MHz (Neo Geo MVS)
|
||||
// - 1: 8.06MHz (Neo Geo AES)
|
||||
// - AY-3-8910/AY8930:
|
||||
// - bit 0-3: clock rate
|
||||
// - 0: 1.79MHz (MSX NTSC)
|
||||
// - 1: 1.77MHz (ZX Spectrum, MSX PAL, etc.)
|
||||
// - 2: 1.75MHz (ZX Spectrum)
|
||||
// - 3: 2MHz (Atari ST)
|
||||
// - 4: 1.5MHz (Vectrex)
|
||||
// - 5: 1MHz (Amstrad CPC)
|
||||
// - 6: 0.89MHz (Sunsoft 5B)
|
||||
// - 7: 1.67MHz
|
||||
// - 8: 0.83MHz (Sunsoft 5B on PAL)
|
||||
// - 9: 1.10MHz (Gamate/VIC-20 PAL)
|
||||
// - 10: 2.097152MHz (Game Boy)
|
||||
// - 11: 3.58MHz (Darky)
|
||||
// - 12: 3.6MHz (Darky)
|
||||
// - 13: 1.25MHz
|
||||
// - 14: 1.536MHz
|
||||
// - bit 4-5: chip type (ignored on AY8930)
|
||||
// - 0: AY-3-8910 or similar
|
||||
// - 1: YM2149
|
||||
// - 2: Sunsoft 5B
|
||||
// - 3: AY-3-8914
|
||||
// - bit 6: stereo (ignored on Sunsoft 5B)
|
||||
// - 0: mono
|
||||
// - 1: stereo ABC
|
||||
// - bit 7: clock divider pin (YM2149, AY8930)
|
||||
// - 0: high (disable divider)
|
||||
// - 1: low (internally divided to half)
|
||||
// - SAA1099:
|
||||
// - bit 0-1: clock rate
|
||||
// - 0: 8MHz (SAM Coupé)
|
||||
// - 1: 7.15MHz (Game Blaster, NTSC)
|
||||
// - 2: 7.09MHz (PAL)
|
||||
// - Amiga:
|
||||
// - bit 0: clock rate
|
||||
// - 0: 7.15MHz (NTSC)
|
||||
// - 1: 7.09MHz (PAL)
|
||||
// - bit 1: model
|
||||
// - 0: Amiga 500
|
||||
// - 1: Amiga 1200
|
||||
// - bit 8-14: stereo separation
|
||||
// - 0 is 0% while 127 is 100%
|
||||
// - PC Speaker:
|
||||
// - bit 0-1: speaker type
|
||||
// - 0: unfiltered
|
||||
// - 1: cone
|
||||
// - 2: piezo
|
||||
// - 3: real (TODO)
|
||||
// - QSound:
|
||||
// - bit 12-20: echo feedback
|
||||
// - Valid values are 0-255
|
||||
// - bit 0-11: echo delay length
|
||||
// - Valid values are 0-2725
|
||||
// - 0 is max length, 2725 is min length
|
||||
// - OPLL:
|
||||
// - bit 0-3: clock rate
|
||||
// - 0: NTSC (3.58MHz)
|
||||
// - 1: PAL (3.55MHz)
|
||||
// - 2: Other (4MHz)
|
||||
// - 3: half NTSC (1.79MHz)
|
||||
// - bit 4-7: patch set
|
||||
// - 0: YM2413
|
||||
// - 1: YMF281
|
||||
// - 2: YM2423
|
||||
// - 3: VRC7
|
||||
// - 4: custom (TODO)
|
||||
// - X1-010:
|
||||
// - bit 0-3: clock rate
|
||||
// - 0: 16MHz (Seta 1)
|
||||
// - 1: 16.67MHz (Seta 2)
|
||||
// - bit 4: stereo
|
||||
// - 0: mono
|
||||
// - 1: stereo
|
||||
// - YM2203:
|
||||
// - bit 0-4: clock rate
|
||||
// - 0: 3.58MHz (NTSC)
|
||||
// - 1: 3.55MHz (PAL)
|
||||
// - 2: 4MHz
|
||||
// - 3: 3MHz
|
||||
// - 4: 3.9936MHz (PC-88, PC-98)
|
||||
// - 5: 1.5MHz
|
||||
// - bit 5-6: output rate
|
||||
// - 0: FM: clock / 72, SSG: clock / 16
|
||||
// - 1: FM: clock / 36, SSG: clock / 8
|
||||
// - 2: FM: clock / 24, SSG: clock / 4
|
||||
// - YM2608:
|
||||
// - bit 0-4: clock rate
|
||||
// - 0: 8MHz
|
||||
// - 1: 7.987MHz (PC-88, PC-98)
|
||||
// - bit 5-6: output rate
|
||||
// - 0: FM: clock / 144, SSG: clock / 32
|
||||
// - 1: FM: clock / 72, SSG: clock / 16
|
||||
// - 2: FM: clock / 48, SSG: clock / 8
|
||||
// - YM3526, YM3812, Y8950:
|
||||
// - bit 0-7: clock rate
|
||||
// - 0: 3.58MHz (NTSC)
|
||||
// - 1: 3.55MHz (PAL)
|
||||
// - 2: 4MHz
|
||||
// - 3: 3MHz
|
||||
// - 4: 3.9936MHz (PC-88, PC-98)
|
||||
// - 5: 3.5MHz
|
||||
// - YMF262:
|
||||
// - bit 0-7: clock rate
|
||||
// - 0: 14.32MHz (NTSC)
|
||||
// - 1: 14.19MHz (PAL)
|
||||
// - 2: 14MHz
|
||||
// - 3: 16MHz
|
||||
// - 4: 15MHz
|
||||
// - YMF289B: (TODO)
|
||||
// - bit 0-7: clock rate
|
||||
// - 0: 33.8688MHz
|
||||
// - 1: 28.64MHz (NTSC)
|
||||
// - 2: 28.38MHz (PAL)
|
||||
// - MSM6295:
|
||||
// - bit 0-6: clock rate
|
||||
// - 0: 1MHz
|
||||
// - 1: 1.056MHz
|
||||
// - 2: 4MHz
|
||||
// - 3: 4.224MHz
|
||||
// - 4: 3.58MHz (NTSC)
|
||||
// - 5: 1.79MHz (Half NTSC)
|
||||
// - 6: 1.023MHz
|
||||
// - 7: 0.895MHz (Quarter NTSC)
|
||||
// - 8: 2MHz
|
||||
// - 9: 2.112MHz
|
||||
// - 10: 0.875MHz
|
||||
// - 11: 0.9375MHz
|
||||
// - 12: 1.5MHz
|
||||
// - 13: 3MHz
|
||||
// - 14: 1.193MHz
|
||||
// - bit 7: Output rate
|
||||
// - 0: clock / 132
|
||||
// - 1: clock / 165
|
||||
// - SCC/+:
|
||||
// - bit 0-6: clock rate
|
||||
// - 0: 1.79MHz (MSX NTSC)
|
||||
// - 1: 1.77MHz (PAL)
|
||||
// - 2: 1.5MHz
|
||||
// - 3: 2MHz
|
||||
// - YMZ280B:
|
||||
// - bit 0-7: clock rate
|
||||
// - 0: 16.9344MHz
|
||||
// - 1: 14.32MHz (NTSC)
|
||||
// - 2: 14.19MHz (PAL)
|
||||
// - 3: 16MHz
|
||||
// - 4: 16.67MHz
|
||||
// - 5: 14MHz
|
||||
unsigned int systemFlags[32];
|
||||
// this one will be removed soon...
|
||||
unsigned int systemFlagsOld[32];
|
||||
// ...and replaced with... this!
|
||||
DivConfig systemFlags[32];
|
||||
|
||||
// song information
|
||||
String name, author, systemName;
|
||||
|
|
@ -624,7 +433,7 @@ struct DivSong {
|
|||
system[i]=DIV_SYSTEM_NULL;
|
||||
systemVol[i]=64;
|
||||
systemPan[i]=0;
|
||||
systemFlags[i]=0;
|
||||
systemFlagsOld[i]=0;
|
||||
}
|
||||
subsong.push_back(new DivSubSong);
|
||||
system[0]=DIV_SYSTEM_YM2612;
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ String DivEngine::getSongSystemLegacyName(DivSong& ds, bool isMultiSystemAccepta
|
|||
return "help! what's going on!";
|
||||
case 1:
|
||||
if (ds.system[0]==DIV_SYSTEM_AY8910) {
|
||||
switch (ds.systemFlags[0]&0x3f) {
|
||||
switch (ds.systemFlagsOld[0]&0x3f) {
|
||||
case 0: // AY-3-8910, 1.79MHz
|
||||
case 1: // AY-3-8910, 1.77MHz
|
||||
case 2: // AY-3-8910, 1.75MHz
|
||||
|
|
@ -88,35 +88,35 @@ String DivEngine::getSongSystemLegacyName(DivSong& ds, bool isMultiSystemAccepta
|
|||
return "Intellivision (PAL)";
|
||||
|
||||
default:
|
||||
if ((ds.systemFlags[0]&0x30)==0x00) {
|
||||
if ((ds.systemFlagsOld[0]&0x30)==0x00) {
|
||||
return "AY-3-8910";
|
||||
} else if ((ds.systemFlags[0]&0x30)==0x10) {
|
||||
} else if ((ds.systemFlagsOld[0]&0x30)==0x10) {
|
||||
return "Yamaha YM2149";
|
||||
} else if ((ds.systemFlags[0]&0x30)==0x20) {
|
||||
} else if ((ds.systemFlagsOld[0]&0x30)==0x20) {
|
||||
return "Overclocked Sunsoft 5B";
|
||||
} else if ((ds.systemFlags[0]&0x30)==0x30) {
|
||||
} else if ((ds.systemFlagsOld[0]&0x30)==0x30) {
|
||||
return "Intellivision";
|
||||
}
|
||||
}
|
||||
} else if (ds.system[0]==DIV_SYSTEM_SMS) {
|
||||
switch (ds.systemFlags[0]&0x0f) {
|
||||
switch (ds.systemFlagsOld[0]&0x0f) {
|
||||
case 0: case 1:
|
||||
return "Sega Master System";
|
||||
case 6:
|
||||
return "BBC Micro";
|
||||
}
|
||||
} else if (ds.system[0]==DIV_SYSTEM_YM2612) {
|
||||
switch (ds.systemFlags[0]&3) {
|
||||
switch (ds.systemFlagsOld[0]&3) {
|
||||
case 2:
|
||||
return "FM Towns";
|
||||
}
|
||||
} else if (ds.system[0]==DIV_SYSTEM_YM2151) {
|
||||
switch (ds.systemFlags[0]&3) {
|
||||
switch (ds.systemFlagsOld[0]&3) {
|
||||
case 2:
|
||||
return "Sharp X68000";
|
||||
}
|
||||
} else if (ds.system[0]==DIV_SYSTEM_SAA1099) {
|
||||
switch (ds.systemFlags[0]&3) {
|
||||
switch (ds.systemFlagsOld[0]&3) {
|
||||
case 0:
|
||||
return "SAM Coupé";
|
||||
}
|
||||
|
|
@ -875,23 +875,11 @@ void DivEngine::registerSystems() {
|
|||
{DIV_INS_SNES, DIV_INS_SNES, DIV_INS_SNES, DIV_INS_SNES, DIV_INS_SNES, DIV_INS_SNES, DIV_INS_SNES, DIV_INS_SNES},
|
||||
{},
|
||||
{
|
||||
{0x10, {DIV_CMD_WAVE, "10xx: Set waveform"}},
|
||||
{0x11, {DIV_CMD_STD_NOISE_MODE, "11xx: Toggle noise mode"}},
|
||||
{0x12, {DIV_CMD_SNES_ECHO, "12xx: Toggle echo on this channel"}},
|
||||
{0x13, {DIV_CMD_SNES_PITCH_MOD, "13xx: Toggle pitch modulation"}},
|
||||
{0x14, {DIV_CMD_SNES_INVERT, "14xy: Toggle invert (x: left; y: right)"}},
|
||||
{0x15, {DIV_CMD_SNES_GAIN_MODE, "15xx: Set gain mode"}},
|
||||
{0x16, {DIV_CMD_SNES_GAIN, "16xx: Set gain"}},
|
||||
{0x18, {DIV_CMD_SNES_ECHO_ENABLE, "18xx: Enable echo buffer"}},
|
||||
{0x19, {DIV_CMD_SNES_ECHO_DELAY, "19xx: Set echo delay"}},
|
||||
{0x19, {DIV_CMD_SNES_ECHO_DELAY, "19xx: Set echo delay (0 to F)"}},
|
||||
{0x1a, {DIV_CMD_SNES_ECHO_VOL_LEFT, "1Axx: Set left echo volume"}},
|
||||
{0x1b, {DIV_CMD_SNES_ECHO_VOL_RIGHT, "1Bxx: Set right echo volume"}},
|
||||
{0x1c, {DIV_CMD_SNES_ECHO_FEEDBACK, "1Cxx: Set echo feedback"}},
|
||||
{0x1d, {DIV_CMD_STD_NOISE_FREQ, "1Dxx: Set noise frequency"}},
|
||||
{0x20, {DIV_CMD_FM_AR, "20xx: Set attack"}},
|
||||
{0x21, {DIV_CMD_FM_DR, "21xx: Set decay"}},
|
||||
{0x22, {DIV_CMD_FM_SL, "22xx: Set sustain"}},
|
||||
{0x23, {DIV_CMD_FM_RR, "23xx: Set release"}},
|
||||
{0x30, {DIV_CMD_SNES_ECHO_FIR, "30xx: Set echo filter coefficient 0",constVal<0>,effectVal}},
|
||||
{0x31, {DIV_CMD_SNES_ECHO_FIR, "31xx: Set echo filter coefficient 1",constVal<1>,effectVal}},
|
||||
{0x32, {DIV_CMD_SNES_ECHO_FIR, "32xx: Set echo filter coefficient 2",constVal<2>,effectVal}},
|
||||
|
|
@ -900,6 +888,20 @@ void DivEngine::registerSystems() {
|
|||
{0x35, {DIV_CMD_SNES_ECHO_FIR, "35xx: Set echo filter coefficient 5",constVal<5>,effectVal}},
|
||||
{0x36, {DIV_CMD_SNES_ECHO_FIR, "36xx: Set echo filter coefficient 6",constVal<6>,effectVal}},
|
||||
{0x37, {DIV_CMD_SNES_ECHO_FIR, "37xx: Set echo filter coefficient 7",constVal<7>,effectVal}},
|
||||
},
|
||||
{
|
||||
{0x10, {DIV_CMD_WAVE, "10xx: Set waveform"}},
|
||||
{0x11, {DIV_CMD_STD_NOISE_MODE, "11xx: Toggle noise mode"}},
|
||||
{0x12, {DIV_CMD_SNES_ECHO, "12xx: Toggle echo on this channel"}},
|
||||
{0x13, {DIV_CMD_SNES_PITCH_MOD, "13xx: Toggle pitch modulation"}},
|
||||
{0x14, {DIV_CMD_SNES_INVERT, "14xy: Toggle invert (x: left; y: right)"}},
|
||||
{0x15, {DIV_CMD_SNES_GAIN_MODE, "15xx: Set envelope mode (0: ADSR, 1: gain/direct, 2: dec, 3: exp, 4: inc, 5: bent)"}},
|
||||
{0x16, {DIV_CMD_SNES_GAIN, "16xx: Set gain (00 to 7F if direct; 00 to 1F otherwise)"}},
|
||||
{0x1d, {DIV_CMD_STD_NOISE_FREQ, "1Dxx: Set noise frequency (00 to 1F)"}},
|
||||
{0x20, {DIV_CMD_FM_AR, "20xx: Set attack (0 to F)"}},
|
||||
{0x21, {DIV_CMD_FM_DR, "21xx: Set decay (0 to 7)"}},
|
||||
{0x22, {DIV_CMD_FM_SL, "22xx: Set sustain (0 to 7)"}},
|
||||
{0x23, {DIV_CMD_FM_RR, "23xx: Set release (00 to 1F)"}},
|
||||
}
|
||||
);
|
||||
|
||||
|
|
@ -1193,7 +1195,7 @@ void DivEngine::registerSystems() {
|
|||
);
|
||||
|
||||
sysDefs[DIV_SYSTEM_SFX_BEEPER]=new DivSysDef(
|
||||
"ZX Spectrum Beeper", NULL, 0x9f, 0, 6, false, true, 0, false, 1U<<DIV_SAMPLE_DEPTH_1BIT,
|
||||
"ZX Spectrum Beeper", NULL, 0x9f, 0, 6, false, true, 0, false, 1U<<DIV_SAMPLE_DEPTH_8BIT,
|
||||
"the ZX Spectrum only had a basic beeper capable of...\n...a bunch of thin pulses and tons of other interesting stuff!\nFurnace provides a thin pulse system.",
|
||||
{"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6"},
|
||||
{"CH1", "CH2", "CH3", "CH4", "CH5", "CH6"},
|
||||
|
|
|
|||
|
|
@ -979,7 +979,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
|
|||
if (!hasSN) {
|
||||
hasSN=disCont[i].dispatch->chipClock;
|
||||
willExport[i]=true;
|
||||
switch ((song.systemFlags[i]>>2)&3) {
|
||||
switch ((song.systemFlagsOld[i]>>2)&3) {
|
||||
case 1: // real SN
|
||||
snNoiseConfig=3;
|
||||
snNoiseSize=15;
|
||||
|
|
@ -1094,7 +1094,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
|
|||
ayConfig=0x03;
|
||||
hasClockDivider=true;
|
||||
} else {
|
||||
switch ((song.systemFlags[i]>>4)&3) {
|
||||
switch ((song.systemFlagsOld[i]>>4)&3) {
|
||||
default:
|
||||
case 0: // AY8910
|
||||
ayConfig=0x00;
|
||||
|
|
@ -1113,10 +1113,10 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
|
|||
break;
|
||||
}
|
||||
}
|
||||
if (hasClockDivider && ((song.systemFlags[i]>>7)&1)) {
|
||||
if (hasClockDivider && ((song.systemFlagsOld[i]>>7)&1)) {
|
||||
ayFlags|=0x10;
|
||||
}
|
||||
if (hasStereo && ((song.systemFlags[i]>>6)&1)) {
|
||||
if (hasStereo && ((song.systemFlagsOld[i]>>6)&1)) {
|
||||
ayFlags|=0x80;
|
||||
}
|
||||
willExport[i]=true;
|
||||
|
|
@ -1358,7 +1358,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
|
|||
// chips even though the only difference is the output resolution
|
||||
// these system types are currently handled by reusing isSecond flag
|
||||
// also this system is not dual-able
|
||||
if ((song.systemFlags[i]>>4)==1) {
|
||||
if ((song.systemFlagsOld[i]>>4)==1) {
|
||||
if (!hasRFC1) {
|
||||
hasRFC1=disCont[i].dispatch->chipClock;
|
||||
isSecond[i]=true;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue