Merge branch 'master' of https://github.com/tildearrow/furnace into x1_010_bank

This commit is contained in:
cam900 2023-04-20 08:47:24 +09:00
commit c69318b222
86 changed files with 1642 additions and 375 deletions

View file

@ -17,10 +17,24 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#define _USE_MATH_DEFINES
#include "cmdStream.h"
#include "dispatch.h"
#include "engine.h"
#include "../ta-log.h"
bool DivCSChannelState::doCall(unsigned int addr) {
if (callStackPos>=8) {
readPos=0;
return false;
}
callStack[callStackPos++]=readPos;
readPos=addr;
return true;
}
void DivCSPlayer::cleanup() {
delete b;
}
@ -29,18 +43,25 @@ bool DivCSPlayer::tick() {
bool ticked=false;
for (int i=0; i<e->getTotalChannelCount(); i++) {
bool sendVolume=false;
bool sendPitch=false;
if (chan[i].readPos==0) continue;
ticked=true;
chan[i].waitTicks--;
while (chan[i].waitTicks<=0) {
stream.seek(chan[i].readPos,SEEK_SET);
if (!stream.seek(chan[i].readPos,SEEK_SET)) {
logE("%d: access violation! $%x",i,chan[i].readPos);
chan[i].readPos=0;
break;
}
unsigned char next=stream.readC();
unsigned char command=0;
if (next<0xb3) { // note
e->dispatchCmd(DivCommand(DIV_CMD_NOTE_ON,i,next-60));
e->dispatchCmd(DivCommand(DIV_CMD_NOTE_ON,i,(int)next-60));
chan[i].note=(int)next-60;
chan[i].vibratoPos=0;
} else if (next>=0xd0 && next<=0xdf) {
command=fastCmds[next&15];
} else if (next>=0xe0 && next<=0xef) { // preset delay
@ -48,6 +69,7 @@ bool DivCSPlayer::tick() {
} else switch (next) {
case 0xb4: // note on null
e->dispatchCmd(DivCommand(DIV_CMD_NOTE_ON,i,DIV_NOTE_NULL));
chan[i].vibratoPos=0;
break;
case 0xb5: // note off
e->dispatchCmd(DivCommand(DIV_CMD_NOTE_OFF,i));
@ -66,17 +88,46 @@ bool DivCSPlayer::tick() {
case 0xf7:
command=stream.readC();
break;
case 0xf8:
logE("TODO: CALL");
case 0xf8: {
unsigned int callAddr=chan[i].readPos+2+stream.readS();
if (!chan[i].doCall(callAddr)) {
logE("%d: (callb16) stack error!",i);
}
break;
}
case 0xf6: {
unsigned int callAddr=chan[i].readPos+4+stream.readI();
if (!chan[i].doCall(callAddr)) {
logE("%d: (callb32) stack error!",i);
}
break;
}
case 0xf5: {
unsigned int callAddr=stream.readI();
if (!chan[i].doCall(callAddr)) {
logE("%d: (call) stack error!",i);
}
break;
}
case 0xf4: {
logE("%d: (callsym) not supported here!",i);
chan[i].readPos=0;
break;
}
case 0xf9:
logE("TODO: RET");
if (!chan[i].callStackPos) {
logE("%d: (ret) stack error!",i);
chan[i].readPos=0;
break;
}
chan[i].readPos=chan[i].callStack[--chan[i].callStackPos];
break;
case 0xfa:
logE("TODO: JMP");
chan[i].readPos=stream.readI();
break;
case 0xfb:
logE("TODO: RATE");
stream.readI();
break;
case 0xfc:
chan[i].waitTicks=(unsigned short)stream.readS();
@ -88,6 +139,11 @@ bool DivCSPlayer::tick() {
chan[i].waitTicks=1;
break;
case 0xff:
chan[i].readPos=0;
logI("%d: stop",i);
break;
default:
logE("%d: illegal instruction $%.2x! $%.x",i,next,chan[i].readPos);
chan[i].readPos=0;
break;
}
@ -101,15 +157,18 @@ bool DivCSPlayer::tick() {
case DIV_CMD_INSTRUMENT:
case DIV_CMD_HINT_VIBRATO_RANGE:
case DIV_CMD_HINT_VIBRATO_SHAPE:
case DIV_CMD_HINT_PITCH:
case DIV_CMD_HINT_VOLUME:
case DIV_CMD_HINT_ARP_TIME:
arg0=(unsigned char)stream.readC();
break;
case DIV_CMD_HINT_PITCH:
arg0=(signed char)stream.readC();
break;
case DIV_CMD_PANNING:
case DIV_CMD_HINT_VIBRATO:
case DIV_CMD_HINT_ARPEGGIO:
case DIV_CMD_HINT_PORTA:
arg0=(unsigned char)stream.readC();
arg0=(signed char)stream.readC();
arg1=(unsigned char)stream.readC();
break;
case DIV_CMD_PRE_PORTA:
@ -120,6 +179,14 @@ bool DivCSPlayer::tick() {
case DIV_CMD_HINT_VOL_SLIDE:
arg0=(short)stream.readS();
break;
case DIV_CMD_HINT_LEGATO:
arg0=(unsigned char)stream.readC();
if (arg0==0xff) {
arg0=DIV_NOTE_NULL;
} else {
arg0-=60;
}
break;
case DIV_CMD_SAMPLE_MODE:
case DIV_CMD_SAMPLE_FREQ:
case DIV_CMD_SAMPLE_BANK:
@ -218,6 +285,29 @@ bool DivCSPlayer::tick() {
case DIV_CMD_HINT_VOL_SLIDE:
chan[i].volSpeed=arg0;
break;
case DIV_CMD_HINT_PITCH:
chan[i].pitch=arg0;
sendPitch=true;
break;
case DIV_CMD_HINT_VIBRATO:
chan[i].vibratoDepth=arg0;
chan[i].vibratoRate=arg1;
sendPitch=true;
break;
case DIV_CMD_HINT_PORTA:
chan[i].portaTarget=arg0;
chan[i].portaSpeed=arg1;
break;
case DIV_CMD_HINT_LEGATO:
chan[i].note=arg0;
e->dispatchCmd(DivCommand(DIV_CMD_LEGATO,i,chan[i].note));
break;
case DIV_CMD_HINT_ARPEGGIO:
chan[i].arp=(((unsigned char)arg0)<<4)|(arg1&15);
break;
case DIV_CMD_HINT_ARP_TIME:
arpSpeed=arg0;
break;
default: // dispatch it
e->dispatchCmd(DivCommand((DivDispatchCmds)command,i,arg0,arg1));
break;
@ -238,6 +328,37 @@ bool DivCSPlayer::tick() {
e->dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8));
}
if (sendPitch || chan[i].vibratoDepth!=0) {
if (chan[i].vibratoDepth>0) {
chan[i].vibratoPos+=chan[i].vibratoRate;
if (chan[i].vibratoPos>=64) chan[i].vibratoPos-=64;
}
e->dispatchCmd(DivCommand(DIV_CMD_PITCH,i,chan[i].pitch+(vibTable[chan[i].vibratoPos&63]*chan[i].vibratoDepth)/15));
}
if (chan[i].portaSpeed) {
e->dispatchCmd(DivCommand(DIV_CMD_NOTE_PORTA,i,chan[i].portaSpeed*(e->song.linearPitch==2?e->song.pitchSlideSpeed:1),chan[i].portaTarget));
}
if (chan[i].arp && !chan[i].portaSpeed) {
if (chan[i].arpTicks==0) {
switch (chan[i].arpStage) {
case 0:
e->dispatchCmd(DivCommand(DIV_CMD_LEGATO,i,chan[i].note));
break;
case 1:
e->dispatchCmd(DivCommand(DIV_CMD_LEGATO,i,chan[i].note+(chan[i].arp>>4)));
break;
case 2:
e->dispatchCmd(DivCommand(DIV_CMD_LEGATO,i,chan[i].note+(chan[i].arp&15)));
break;
}
chan[i].arpStage++;
if (chan[i].arpStage>=3) chan[i].arpStage=0;
chan[i].arpTicks=arpSpeed;
}
chan[i].arpTicks--;
}
}
return ticked;
@ -273,6 +394,12 @@ bool DivCSPlayer::init() {
chan[i].volume=chan[i].volMax;
}
for (int i=0; i<64; i++) {
vibTable[i]=127*sin(((double)i/64.0)*(2*M_PI));
}
arpSpeed=1;
return true;
}

View file

@ -29,14 +29,41 @@ struct DivCSChannelState {
unsigned int readPos;
int waitTicks;
int note, pitch;
int volume, volMax, volSpeed;
int vibratoDepth, vibratoRate, vibratoPos;
int portaTarget, portaSpeed;
unsigned char arp, arpStage, arpTicks;
unsigned int callStack[8];
unsigned char callStackPos;
struct TraceEntry {
unsigned int addr;
unsigned char length;
unsigned char data[11];
} trace[32];
unsigned char tracePos;
bool doCall(unsigned int addr);
DivCSChannelState():
readPos(0),
waitTicks(0),
note(-1),
pitch(0),
volume(0x7f00),
volMax(0),
volSpeed(0) {}
volSpeed(0),
vibratoDepth(0),
vibratoRate(0),
vibratoPos(0),
portaTarget(0),
portaSpeed(0),
arp(0),
arpStage(0),
arpTicks(0),
callStackPos(0) {}
};
class DivCSPlayer {
@ -46,6 +73,9 @@ class DivCSPlayer {
DivCSChannelState chan[DIV_MAX_CHANS];
unsigned char fastDelays[16];
unsigned char fastCmds[16];
unsigned char arpSpeed;
short vibTable[64];
public:
void cleanup();
bool tick();

View file

@ -23,21 +23,54 @@
#include "../fileutils.h"
#include <fmt/printf.h>
bool DivConfig::save(const char* path) {
#define REDUNDANCY_NUM_ATTEMPTS 5
#define CHECK_BUF_SIZE 8192
bool DivConfig::save(const char* path, bool redundancy) {
if (redundancy) {
char oldPath[4096];
char newPath[4096];
if (fileExists(path)==1) {
logD("rotating config files...");
for (int i=4; i>=0; i--) {
if (i>0) {
snprintf(oldPath,4095,"%s.%d",path,i);
} else {
strncpy(oldPath,path,4095);
}
snprintf(newPath,4095,"%s.%d",path,i+1);
if (i>=4) {
logV("remove %s",oldPath);
deleteFile(oldPath);
} else {
logV("move %s to %s",oldPath,newPath);
moveFiles(oldPath,newPath);
}
}
}
}
logD("opening config for write: %s",path);
FILE* f=ps_fopen(path,"wb");
if (f==NULL) {
logW("could not write config file! %s",strerror(errno));
reportError(fmt::sprintf("could not write config file! %s",strerror(errno)));
return false;
}
for (auto& i: conf) {
String toWrite=fmt::sprintf("%s=%s\n",i.first,i.second);
if (fwrite(toWrite.c_str(),1,toWrite.size(),f)!=toWrite.size()) {
logW("could not write config file! %s",strerror(errno));
reportError(fmt::sprintf("could not write config file! %s",strerror(errno)));
logV("removing config file");
fclose(f);
deleteFile(path);
return false;
}
}
fclose(f);
logD("config file written successfully.");
return true;
}
@ -63,6 +96,7 @@ void DivConfig::parseLine(const char* line) {
String value="";
bool keyOrValue=false;
for (const char* i=line; *i; i++) {
if (*i=='\r') continue;
if (*i=='\n') continue;
if (keyOrValue) {
value+=*i;
@ -79,17 +113,94 @@ void DivConfig::parseLine(const char* line) {
}
}
bool DivConfig::loadFromFile(const char* path, bool createOnFail) {
bool DivConfig::loadFromFile(const char* path, bool createOnFail, bool redundancy) {
char line[4096];
FILE* f=ps_fopen(path,"rb");
if (f==NULL) {
if (createOnFail) {
logI("creating default config.");
return save(path);
} else {
return false;
logD("opening config for read: %s",path);
FILE* f=NULL;
if (redundancy) {
unsigned char* readBuf=new unsigned char[CHECK_BUF_SIZE];
size_t readBufLen=0;
for (int i=0; i<REDUNDANCY_NUM_ATTEMPTS; i++) {
bool viable=false;
if (i>0) {
snprintf(line,4095,"%s.%d",path,i);
} else {
strncpy(line,path,4095);
}
logV("trying: %s",line);
// try to open config
f=ps_fopen(line,"rb");
// check whether we could open it
if (f==NULL) {
logV("fopen(): %s",strerror(errno));
continue;
}
// check whether there's something
while (!feof(f)) {
readBufLen=fread(readBuf,1,CHECK_BUF_SIZE,f);
if (ferror(f)) {
logV("fread(): %s",strerror(errno));
break;
}
for (size_t j=0; j<readBufLen; j++) {
if (readBuf[j]!='\r' && readBuf[j]!='\n' && readBuf[j]!=' ') {
viable=true;
break;
}
}
if (viable) break;
}
// there's something
if (viable) {
if (fseek(f,0,SEEK_SET)==-1) {
logV("fseek(): %s",strerror(errno));
viable=false;
} else {
break;
}
}
// close it (because there's nothing)
fclose(f);
f=NULL;
}
delete[] readBuf;
// we couldn't read at all
if (f==NULL) {
logD("config does not exist");
if (createOnFail) {
logI("creating default config.");
reportError(fmt::sprintf("Creating default config: %s",strerror(errno)));
return save(path,redundancy);
} else {
reportError(fmt::sprintf("COULD NOT LOAD CONFIG %s",strerror(errno)));
return false;
}
}
} else {
f=ps_fopen(path,"rb");
if (f==NULL) {
logD("config does not exist");
if (createOnFail) {
logI("creating default config.");
reportError(fmt::sprintf("Creating default config: %s",strerror(errno)));
return save(path);
} else {
reportError(fmt::sprintf("COULD NOT LOAD CONFIG %s",strerror(errno)));
return false;
}
}
}
logI("loading config.");
while (!feof(f)) {
if (fgets(line,4095,f)==NULL) {
@ -97,6 +208,7 @@ bool DivConfig::loadFromFile(const char* path, bool createOnFail) {
}
parseLine(line);
}
logD("end of file (%s)",strerror(errno));
fclose(f);
return true;
}
@ -175,6 +287,33 @@ String DivConfig::getString(String key, String fallback) const {
return fallback;
}
std::vector<int> DivConfig::getIntList(String key, std::initializer_list<int> fallback) const {
String next;
std::vector<int> ret;
try {
String val=conf.at(key);
for (char i: val) {
if (i==',') {
int num=std::stoi(next);
ret.push_back(num);
next="";
} else {
next+=i;
}
}
if (!next.empty()) {
int num=std::stoi(next);
ret.push_back(num);
}
return ret;
} catch (std::out_of_range& e) {
} catch (std::invalid_argument& e) {
}
return fallback;
}
bool DivConfig::has(String key) const {
try {
String test=conf.at(key);
@ -212,6 +351,17 @@ void DivConfig::set(String key, String value) {
conf[key]=value;
}
void DivConfig::set(String key, const std::vector<int>& value) {
String val;
bool comma=false;
for (int i: value) {
if (comma) val+=',';
val+=fmt::sprintf("%d",i);
comma=true;
}
conf[key]=val;
}
bool DivConfig::remove(String key) {
return conf.erase(key);
}

View file

@ -22,6 +22,8 @@
#include "../ta-utils.h"
#include <map>
#include <vector>
#include <initializer_list>
class DivConfig {
std::map<String,String> conf;
@ -30,10 +32,10 @@ class DivConfig {
// config loading/saving
bool loadFromMemory(const char* buf);
bool loadFromBase64(const char* buf);
bool loadFromFile(const char* path, bool createOnFail=true);
bool loadFromFile(const char* path, bool createOnFail=true, bool redundancy=false);
String toString();
String toBase64();
bool save(const char* path);
bool save(const char* path, bool redundancy=false);
// get the map
const std::map<String,String>& configMap();
@ -44,6 +46,7 @@ class DivConfig {
float getFloat(String key, float fallback) const;
double getDouble(String key, double fallback) const;
String getString(String key, String fallback) const;
std::vector<int> getIntList(String key, std::initializer_list<int> fallback) const;
// check for existence
bool has(String key) const;
@ -55,6 +58,7 @@ class DivConfig {
void set(String key, double value);
void set(String key, const char* value);
void set(String key, String value);
void set(String key, const std::vector<int>& value);
// remove a config value
bool remove(String key);

View file

@ -110,12 +110,12 @@ void DivEngine::initConfDir() {
bool DivEngine::saveConf() {
configFile=configPath+String(CONFIG_FILE);
return conf.save(configFile.c_str());
return conf.save(configFile.c_str(),true);
}
bool DivEngine::loadConf() {
configFile=configPath+String(CONFIG_FILE);
return conf.loadFromFile(configFile.c_str());
return conf.loadFromFile(configFile.c_str(),true,true);
}
bool DivEngine::getConfBool(String key, bool fallback) {

View file

@ -229,6 +229,8 @@ enum DivDispatchCmds {
DIV_CMD_ES5506_ENVELOPE_K2RAMP, // (ramp, slowdown)
DIV_CMD_ES5506_PAUSE, // (value)
DIV_CMD_HINT_ARP_TIME, // (value)
DIV_ALWAYS_SET_VOLUME, // () -> alwaysSetVol
DIV_CMD_MAX

View file

@ -418,6 +418,7 @@ void writePackedCommandValues(SafeWriter* w, const DivCommand& c) {
case DIV_CMD_AMIGA_PM:
case DIV_CMD_MACRO_OFF:
case DIV_CMD_MACRO_ON:
case DIV_CMD_HINT_ARP_TIME:
w->writeC(1); // length
w->writeC(c.value);
break;
@ -2440,6 +2441,16 @@ void DivEngine::stop() {
}
}
}
// reset all chan oscs
for (int i=0; i<chans; i++) {
DivDispatchOscBuffer* buf=disCont[dispatchOfChan[i]].dispatch->getOscBuffer(dispatchChanOfChan[i]);
if (buf!=NULL) {
memset(buf->data,0,65536*sizeof(short));
buf->needle=0;
buf->readNeedle=0;
}
}
BUSY_END;
}
@ -2657,10 +2668,17 @@ void DivEngine::previewSampleNoLock(int sample, int note, int pStart, int pEnd)
if (rate<=0) rate=song.sample[sample]->centerRate;
}
if (rate<100) rate=100;
double rateOrig=rate;
sPreview.rateMul=1;
while (sPreview.rateMul<0x40000000 && rate<got.rate) {
sPreview.rateMul<<=1;
rate*=2.0;
}
blip_set_rates(samp_bb,rate,got.rate);
samp_prevSample=0;
sPreview.rate=rate;
sPreview.rate=rateOrig;
sPreview.pos=(sPreview.pBegin>=0)?sPreview.pBegin:0;
sPreview.posSub=0;
sPreview.sample=sample;
sPreview.wave=-1;
sPreview.dir=false;
@ -2685,10 +2703,17 @@ void DivEngine::previewWaveNoLock(int wave, int note) {
blip_clear(samp_bb);
double rate=song.wave[wave]->len*((song.tuning*0.0625)*pow(2.0,(double)(note+3)/12.0));
if (rate<100) rate=100;
double rateOrig=rate;
sPreview.rateMul=1;
while (sPreview.rateMul<0x40000000 && rate<got.rate) {
sPreview.rateMul<<=1;
rate*=2.0;
}
blip_set_rates(samp_bb,rate,got.rate);
samp_prevSample=0;
sPreview.rate=rate;
sPreview.rate=rateOrig;
sPreview.pos=0;
sPreview.posSub=0;
sPreview.sample=-1;
sPreview.wave=wave;
sPreview.dir=false;

View file

@ -53,8 +53,8 @@
#define EXTERN_BUSY_BEGIN_SOFT e->softLocked=true; e->isBusy.lock();
#define EXTERN_BUSY_END e->isBusy.unlock(); e->softLocked=false;
#define DIV_VERSION "dev145"
#define DIV_ENGINE_VERSION 145
#define DIV_VERSION "dev153"
#define DIV_ENGINE_VERSION 153
// for imports
#define DIV_VERSION_MOD 0xff01
#define DIV_VERSION_FC 0xff02
@ -414,6 +414,7 @@ class DivEngine {
int wave;
int pos;
int pBegin, pEnd;
int rateMul, posSub;
bool dir;
SamplePreview():
rate(0.0),
@ -422,6 +423,8 @@ class DivEngine {
pos(0),
pBegin(-1),
pEnd(-1),
rateMul(1),
posSub(0),
dir(false) {}
} sPreview;

View file

@ -696,6 +696,13 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
}
ds.wave.push_back(wave);
}
// sometimes there's a single length 0 wavetable in the file. I don't know why.
if (ds.waveLen==1) {
if (ds.wave[0]->len==0) {
ds.clearWavetables();
}
}
}
logV("%x",reader.tell());
@ -850,6 +857,7 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
sample->rate=22050;
if (ds.version>=0x0b) {
sample->rate=fileToDivRate(reader.readC());
sample->centerRate=sample->rate;
pitch=reader.readC();
vol=reader.readC();
}
@ -874,24 +882,7 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
// what the hell man...
cutStart=reader.readI();
cutEnd=reader.readI();
if (cutStart<0 || cutStart>length) {
logE("cutStart is out of range! (%d)",cutStart);
lastError="file is corrupt or unreadable at samples";
delete[] file;
return false;
}
if (cutEnd<0 || cutEnd>length) {
logE("cutEnd is out of range! (%d)",cutEnd);
lastError="file is corrupt or unreadable at samples";
delete[] file;
return false;
}
if (cutEnd<cutStart) {
logE("cutEnd %d is before cutStart %d. what's going on?",cutEnd,cutStart);
lastError="file is corrupt or unreadable at samples";
delete[] file;
return false;
}
logV("cutStart: %d cutEnd: %d",cutStart,cutEnd);
}
if (length>0) {
if (ds.version>0x08) {
@ -903,19 +894,6 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
data=new short[length];
reader.read(data,length*2);
}
if (ds.version>0x1b) {
if (cutStart!=0 || cutEnd!=length) {
// cut data
short* newData=new short[cutEnd-cutStart];
memcpy(newData,&data[cutStart],(cutEnd-cutStart)*sizeof(short));
delete[] data;
data=newData;
length=cutEnd-cutStart;
cutStart=0;
cutEnd=length;
}
}
#ifdef TA_BIG_ENDIAN
// convert to big-endian
@ -924,27 +902,76 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
}
#endif
if (pitch!=5) {
int scaledLen=ceil((double)length/samplePitches[pitch]);
if (scaledLen>0) {
// resample
logD("%d: scaling from %d...",i,pitch);
}
// render data
if (!sample->init((double)length/samplePitches[pitch])) {
logE("%d: error while initializing sample!",i);
}
unsigned int k=0;
float mult=(float)(vol)/50.0f;
for (double j=0; j<length; j+=samplePitches[pitch]) {
if (k>=sample->samples) {
break;
short* newData=new short[scaledLen];
memset(newData,0,scaledLen*sizeof(short));
int k=0;
float mult=(float)(vol)/50.0f;
for (double j=0; j<length; j+=samplePitches[pitch]) {
if (k>=scaledLen) {
break;
}
if (sample->depth==DIV_SAMPLE_DEPTH_8BIT) {
float next=(float)(data[(unsigned int)j]-0x80)*mult;
newData[k++]=fmin(fmax(next,-128),127);
} else {
float next=(float)data[(unsigned int)j]*mult;
newData[k++]=fmin(fmax(next,-32768),32767);
}
}
if (sample->depth==DIV_SAMPLE_DEPTH_8BIT) {
float next=(float)(data[(unsigned int)j]-0x80)*mult;
sample->data8[k++]=fmin(fmax(next,-128),127);
} else {
float next=(float)data[(unsigned int)j]*mult;
sample->data16[k++]=fmin(fmax(next,-32768),32767);
delete[] data;
data=newData;
}
logV("length: %d. scaledLen: %d.",length,scaledLen);
if (ds.version>=0x1b) {
if (cutStart<0 || cutStart>scaledLen) {
logE("cutStart is out of range! (%d, scaledLen: %d)",cutStart,scaledLen);
lastError="file is corrupt or unreadable at samples";
delete[] file;
return false;
}
if (cutEnd<0 || cutEnd>scaledLen) {
logE("cutEnd is out of range! (%d, scaledLen: %d)",cutEnd,scaledLen);
lastError="file is corrupt or unreadable at samples";
delete[] file;
return false;
}
if (cutEnd<cutStart) {
logE("cutEnd %d is before cutStart %d. what's going on?",cutEnd,cutStart);
lastError="file is corrupt or unreadable at samples";
delete[] file;
return false;
}
if (cutStart!=0 || cutEnd!=scaledLen) {
// cut data
short* newData=new short[cutEnd-cutStart];
memcpy(newData,&data[cutStart],(cutEnd-cutStart)*sizeof(short));
delete[] data;
data=newData;
scaledLen=cutEnd-cutStart;
cutStart=0;
cutEnd=scaledLen;
}
}
// copy data
if (!sample->init(scaledLen)) {
logE("%d: error while initializing sample!",i);
} else {
for (int i=0; i<scaledLen; i++) {
if (sample->depth==DIV_SAMPLE_DEPTH_8BIT) {
sample->data8[i]=data[i];
} else {
sample->data16[i]=data[i];
}
}
}
@ -2688,6 +2715,15 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
}
}
// SrgaPCM slide compat
if (ds.version<153) {
for (int i=0; i<ds.systemLen; i++) {
if (ds.system[i]==DIV_SYSTEM_SEGAPCM || ds.system[i]==DIV_SYSTEM_SEGAPCM_COMPAT) {
ds.systemFlags[i].set("oldSlides",true);
}
}
}
if (active) quitDispatch();
BUSY_BEGIN_SOFT;
saveLock.lock();
@ -5183,7 +5219,6 @@ SafeWriter* DivEngine::saveFur(bool notPrimary) {
for (int i=0; i<song.insLen; i++) {
DivInstrument* ins=song.ins[i];
insPtr.push_back(w->tell());
logV("writing instrument %d...",i);
ins->putInsData2(w,false);
}

View file

@ -214,7 +214,6 @@ bool DivInstrumentSNES::operator==(const DivInstrumentSNES& other) {
#undef _C
#define FEATURE_BEGIN(x) \
logV("- %s",x); \
w->write(x,2); \
size_t featStartSeek=w->tell(); \
w->writeS(0);
@ -2099,6 +2098,12 @@ void DivInstrument::readFeatureSM(SafeReader& reader, short version) {
amiga.noteMap[note].freq=reader.readS();
amiga.noteMap[note].map=reader.readS();
}
if (version<152) {
for (int note=0; note<120; note++) {
amiga.noteMap[note].freq=note;
}
}
}
READ_FEAT_END;
@ -2966,6 +2971,12 @@ DivDataErrors DivInstrument::readInsDataOld(SafeReader &reader, short version) {
for (int note=0; note<120; note++) {
amiga.noteMap[note].map=reader.readS();
}
if (version<152) {
for (int note=0; note<120; note++) {
amiga.noteMap[note].freq=note;
}
}
}
}

View file

@ -414,7 +414,7 @@ struct DivInstrumentAmiga {
if (note>119) note=119;
return noteMap[note].freq;
}
return -1;
return note;
}
DivInstrumentAmiga():
@ -423,8 +423,9 @@ struct DivInstrumentAmiga {
useSample(false),
useWave(false),
waveLen(31) {
for (SampleMap& elem: noteMap) {
elem=SampleMap();
for (int i=0; i<120; i++) {
noteMap[i].map=-1;
noteMap[i].freq=i;
}
}
};

View file

@ -560,7 +560,10 @@ int DivPlatformAmiga::dispatch(DivCommand c) {
}
}
} else {
if (c.value!=DIV_NOTE_NULL) chan[c.chan].sample=ins->amiga.getSample(c.value);
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].sample=ins->amiga.getSample(c.value);
c.value=ins->amiga.getFreq(c.value);
}
chan[c.chan].useWave=false;
}
if (c.value!=DIV_NOTE_NULL) {

View file

@ -292,8 +292,6 @@ void DivPlatformAY8910::tick(bool sysTick) {
if (chan[i].std.phaseReset.val==1) {
if (chan[i].nextPSGMode.dac) {
if (dumpWrites) addWrite(0xffff0002+(i<<8),0);
DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_AY);
chan[i].dac.sample=ins->amiga.getSample(chan[i].note);
if (chan[i].dac.sample<0 || chan[i].dac.sample>=parent->song.sampleLen) {
if (dumpWrites) {
rWrite(0x08+i,0);
@ -405,7 +403,10 @@ int DivPlatformAY8910::dispatch(DivCommand c) {
if (chan[c.chan].nextPSGMode.dac) {
if (skipRegisterWrites) break;
if (!parent->song.disableSampleMacro && (ins->type==DIV_INS_AMIGA || ins->amiga.useSample)) {
if (c.value!=DIV_NOTE_NULL) chan[c.chan].dac.sample=ins->amiga.getSample(c.value);
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].dac.sample=ins->amiga.getSample(c.value);
c.value=ins->amiga.getFreq(c.value);
}
if (chan[c.chan].dac.sample<0 || chan[c.chan].dac.sample>=parent->song.sampleLen) {
chan[c.chan].dac.sample=-1;
if (dumpWrites) addWrite(0xffff0002+(c.chan<<8),0);

View file

@ -280,8 +280,6 @@ void DivPlatformAY8930::tick(bool sysTick) {
if (chan[i].std.phaseReset.val==1) {
if (chan[i].nextPSGMode.dac) {
if (dumpWrites) addWrite(0xffff0002+(i<<8),0);
DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_AY8930);
chan[i].dac.sample=ins->amiga.getSample(chan[i].note);
if (chan[i].dac.sample<0 || chan[i].dac.sample>=parent->song.sampleLen) {
if (dumpWrites) {
rWrite(0x08+i,0);
@ -406,7 +404,10 @@ int DivPlatformAY8930::dispatch(DivCommand c) {
if (chan[c.chan].nextPSGMode.dac) {
if (skipRegisterWrites) break;
if (ins->type==DIV_INS_AMIGA || ins->amiga.useSample) {
if (c.value!=DIV_NOTE_NULL) chan[c.chan].dac.sample=ins->amiga.getSample(c.value);
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].dac.sample=ins->amiga.getSample(c.value);
c.value=ins->amiga.getFreq(c.value);
}
if (chan[c.chan].dac.sample<0 || chan[c.chan].dac.sample>=parent->song.sampleLen) {
chan[c.chan].dac.sample=-1;
if (dumpWrites) addWrite(0xffff0002+(c.chan<<8),0);

View file

@ -410,7 +410,7 @@ void DivPlatformES5506::tick(bool sysTick) {
if (chan[i].pcmChanged.changed) {
if (chan[i].pcmChanged.index) {
const int next=chan[i].pcm.next;
bool sampleVaild=false;
bool sampleValid=false;
if (((ins->amiga.useNoteMap) && (next>=0 && next<120)) ||
((!ins->amiga.useNoteMap) && (next>=0 && next<parent->song.sampleLen))) {
DivInstrumentAmiga::SampleMap& noteMapind=ins->amiga.noteMap[next];
@ -420,7 +420,7 @@ void DivPlatformES5506::tick(bool sysTick) {
}
if (sample>=0 && sample<parent->song.sampleLen) {
const unsigned int offES5506=sampleOffES5506[sample];
sampleVaild=true;
sampleValid=true;
chan[i].pcm.index=sample;
chan[i].pcm.isNoteMap=ins->amiga.useNoteMap;
DivSample* s=parent->getSample(sample);
@ -433,7 +433,6 @@ void DivPlatformES5506::tick(bool sysTick) {
off=(double)center/8363.0;
}
if (ins->amiga.useNoteMap) {
off*=(double)noteMapind.freq/((double)MAX(1,center)*pow(2.0,((double)next-48.0)/12.0));
chan[i].pcm.note=next;
}
// get loop mode
@ -459,7 +458,7 @@ void DivPlatformES5506::tick(bool sysTick) {
}
}
}
if (sampleVaild) {
if (sampleValid) {
if (!chan[i].keyOn) {
pageWrite(0x20|i,0x03,(chan[i].pcm.direction)?chan[i].pcm.end:chan[i].pcm.start);
}
@ -618,10 +617,6 @@ void DivPlatformES5506::tick(bool sysTick) {
} else {
off=(double)center/8363.0;
}
if (ins->amiga.useNoteMap) {
DivInstrumentAmiga::SampleMap& noteMapind=ins->amiga.noteMap[chan[i].pcm.note];
off*=(double)noteMapind.freq/((double)MAX(1,center)*pow(2.0,((double)chan[i].pcm.note-48.0)/12.0));
}
chan[i].pcm.loopStart=(chan[i].pcm.start+(s->loopStart<<11))&0xfffff800;
chan[i].pcm.loopEnd=(chan[i].pcm.start+((s->loopEnd-1)<<11))&0xffffff80;
chan[i].pcm.freqOffs=PITCH_OFFSET*off;
@ -750,24 +745,22 @@ int DivPlatformES5506::dispatch(DivCommand c) {
switch (c.cmd) {
case DIV_CMD_NOTE_ON: {
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_ES5506);
bool sampleVaild=false;
bool sampleValid=false;
if (((ins->amiga.useNoteMap) && (c.value>=0 && c.value<120)) ||
((!ins->amiga.useNoteMap) && (ins->amiga.initSample>=0 && ins->amiga.initSample<parent->song.sampleLen))) {
DivInstrumentAmiga::SampleMap& noteMapind=ins->amiga.noteMap[c.value];
int sample=ins->amiga.initSample;
if (ins->amiga.useNoteMap) {
sample=noteMapind.map;
}
int sample=ins->amiga.getSample(c.value);
c.value=ins->amiga.getFreq(c.value);
if (sample>=0 && sample<parent->song.sampleLen) {
sampleVaild=true;
sampleValid=true;
chan[c.chan].volMacroMax=ins->type==DIV_INS_AMIGA?64:0xfff;
chan[c.chan].panMacroMax=ins->type==DIV_INS_AMIGA?127:0xfff;
chan[c.chan].pcm.next=sample;
chan[c.chan].pcm.note=c.value;
chan[c.chan].pcm.next=ins->amiga.useNoteMap?c.value:sample;
chan[c.chan].filter=ins->es5506.filter;
chan[c.chan].envelope=ins->es5506.envelope;
}
}
if (!sampleVaild) {
if (!sampleValid) {
chan[c.chan].pcm.index=chan[c.chan].pcm.next=-1;
chan[c.chan].filter=DivInstrumentES5506::Filter();
chan[c.chan].envelope=DivInstrumentES5506::Envelope();

View file

@ -200,7 +200,10 @@ int DivPlatformGA20::dispatch(DivCommand c) {
case DIV_CMD_NOTE_ON: {
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA);
chan[c.chan].macroVolMul=ins->type==DIV_INS_AMIGA?64:255;
if (c.value!=DIV_NOTE_NULL) chan[c.chan].sample=ins->amiga.getSample(c.value);
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].sample=ins->amiga.getSample(c.value);
c.value=ins->amiga.getFreq(c.value);
}
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
}

View file

@ -681,7 +681,10 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
if (c.chan>=5 && chan[c.chan].dacMode) {
//if (skipRegisterWrites) break;
if (ins->type==DIV_INS_AMIGA) { // Furnace mode
if (c.value!=DIV_NOTE_NULL) chan[c.chan].dacSample=ins->amiga.getSample(c.value);
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].dacSample=ins->amiga.getSample(c.value);
c.value=ins->amiga.getFreq(c.value);
}
if (chan[c.chan].dacSample<0 || chan[c.chan].dacSample>=parent->song.sampleLen) {
chan[c.chan].dacSample=-1;
if (dumpWrites) addWrite(0xffff0002,0);

View file

@ -433,13 +433,8 @@ void DivPlatformGenesisExt::muteChannel(int ch, bool mute) {
DivInstrumentFM::Operator op=chan[2].state.op[ordch];
if (isOpMuted[ch-2] || !op.enable) {
rWrite(baseAddr+0x40,127);
immWrite(baseAddr+0x40,127);
} else if (KVS(2,ordch)) {
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-2].outVol&0x7f,127));
immWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-2].outVol&0x7f,127));
} else {
rWrite(baseAddr+0x40,op.tl);
immWrite(baseAddr+0x40,op.tl);
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-2].outVol&0x7f,127));
}
rWrite(chanOffs[2]+0xb4,(IS_EXTCH_MUTED?0:(opChan[ch-2].pan<<6))|(chan[2].state.fms&7)|((chan[2].state.ams&3)<<4));

View file

@ -34,7 +34,7 @@ const char* regCheatSheetK007232[]={
"CHX_StartM", "X*6+3",
"CHX_StartH", "X*6+4",
"CHX_Keyon", "X*6+5",
"SLEV", "C", // external IO
"SLEV", "C", // external IO (Volume for Mono speaker)
"Loop", "D",
// off-chip
"CHX_Volume", "X*2+10",
@ -157,8 +157,7 @@ void DivPlatformK007232::tick(bool sysTick) {
rWrite(0x10+i,(chan[i].lvol&0xf)|((chan[i].rvol&0xf)<<4));
chan[i].prevPan=newPan;
}
}
else {
} else {
const unsigned char prevVolume=lastVolume;
lastVolume=(lastVolume&~(0xf<<(i<<2)))|((chan[i].resVol&0xf)<<(i<<2));
if (prevVolume!=lastVolume) {
@ -274,7 +273,10 @@ int DivPlatformK007232::dispatch(DivCommand c) {
case DIV_CMD_NOTE_ON: {
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA);
chan[c.chan].macroVolMul=ins->type==DIV_INS_AMIGA?64:15;
if (c.value!=DIV_NOTE_NULL) chan[c.chan].sample=ins->amiga.getSample(c.value);
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].sample=ins->amiga.getSample(c.value);
c.value=ins->amiga.getFreq(c.value);
}
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
}
@ -477,6 +479,7 @@ void DivPlatformK007232::setFlags(const DivConfig& flags) {
rate=chipClock/4;
stereo=flags.getBool("stereo",false);
for (int i=0; i<2; i++) {
chan[i].volumeChanged=true;
oscBuf[i]->rate=rate;
}
}

View file

@ -261,13 +261,14 @@ int DivPlatformLynx::dispatch(DivCommand c) {
chan[c.chan].macroVolMul=ins->type==DIV_INS_AMIGA?64:127;
chan[c.chan].pcm=(ins->type==DIV_INS_AMIGA || ins->amiga.useSample);
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
if (chan[c.chan].pcm) {
chan[c.chan].sample=ins->amiga.getSample(c.value);
c.value=ins->amiga.getFreq(c.value);
chan[c.chan].sampleBaseFreq=NOTE_FREQUENCY(c.value);
if (c.value!=DIV_NOTE_NULL) chan[c.chan].sample=ins->amiga.getSample(c.value);
chan[c.chan].sampleAccum=0;
chan[c.chan].samplePos=0;
}
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
chan[c.chan].freqChanged=true;
chan[c.chan].note=c.value;
chan[c.chan].actualNote=c.value;

View file

@ -176,7 +176,10 @@ int DivPlatformMMC5::dispatch(DivCommand c) {
if (c.chan==2) { // PCM
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_STD);
if (ins->type==DIV_INS_AMIGA) {
if (c.value!=DIV_NOTE_NULL) dacSample=ins->amiga.getSample(c.value);
if (c.value!=DIV_NOTE_NULL) {
dacSample=ins->amiga.getSample(c.value);
c.value=ins->amiga.getFreq(c.value);
}
if (dacSample<0 || dacSample>=parent->song.sampleLen) {
dacSample=-1;
if (dumpWrites) addWrite(0xffff0002,0);

View file

@ -576,7 +576,7 @@ void DivPlatformNamcoWSG::setFlags(const DivConfig& flags) {
oscBuf[i]->rate=rate;
}
newNoise=flags.getBool("newNoise",true);
romMode=flags.getBool("romMode",true);
romMode=flags.getBool("romMode",false);
if (devType==30) romMode=false;
}

View file

@ -361,7 +361,10 @@ int DivPlatformNES::dispatch(DivCommand c) {
if (c.chan==4) { // PCM
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_STD);
if (ins->type==DIV_INS_AMIGA) {
if (c.value!=DIV_NOTE_NULL) dacSample=ins->amiga.getSample(c.value);
if (c.value!=DIV_NOTE_NULL) {
dacSample=ins->amiga.getSample(c.value);
c.value=ins->amiga.getFreq(c.value);
}
if (dacSample<0 || dacSample>=parent->song.sampleLen) {
dacSample=-1;
if (dumpWrites && !dpcmMode) addWrite(0xffff0002,0);

View file

@ -856,7 +856,10 @@ int DivPlatformOPL::dispatch(DivCommand c) {
chan[c.chan].outVol=chan[c.chan].vol;
immWrite(18,chan[c.chan].outVol);
}
if (c.value!=DIV_NOTE_NULL) chan[c.chan].sample=ins->amiga.getSample(c.value);
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].sample=ins->amiga.getSample(c.value);
c.value=ins->amiga.getFreq(c.value);
}
if (chan[c.chan].sample>=0 && chan[c.chan].sample<parent->song.sampleLen) {
DivSample* s=parent->getSample(chan[c.chan].sample);
immWrite(8,0);
@ -1555,7 +1558,7 @@ DivMacroInt* DivPlatformOPL::getChanMacroInt(int ch) {
}
DivDispatchOscBuffer* DivPlatformOPL::getOscBuffer(int ch) {
if (oplType==759) {
if (oplType==759 || chipType==8950) {
if (ch>=totalChans+1) return NULL;
} else {
if (ch>=totalChans) return NULL;

View file

@ -282,7 +282,10 @@ int DivPlatformPCE::dispatch(DivCommand c) {
if (ins->type==DIV_INS_AMIGA || ins->amiga.useSample) {
chan[c.chan].furnaceDac=true;
if (skipRegisterWrites) break;
if (c.value!=DIV_NOTE_NULL) chan[c.chan].dacSample=ins->amiga.getSample(c.value);
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].dacSample=ins->amiga.getSample(c.value);
c.value=ins->amiga.getFreq(c.value);
}
if (chan[c.chan].dacSample<0 || chan[c.chan].dacSample>=parent->song.sampleLen) {
chan[c.chan].dacSample=-1;
if (dumpWrites) addWrite(0xffff0002+(c.chan<<8),0);

View file

@ -271,7 +271,10 @@ int DivPlatformPCMDAC::dispatch(DivCommand c) {
}
}
} else {
if (c.value!=DIV_NOTE_NULL) chan[0].sample=ins->amiga.getSample(c.value);
if (c.value!=DIV_NOTE_NULL) {
chan[0].sample=ins->amiga.getSample(c.value);
c.value=ins->amiga.getFreq(c.value);
}
chan[0].useWave=false;
}
if (c.value!=DIV_NOTE_NULL) {

View file

@ -450,7 +450,10 @@ int DivPlatformQSound::dispatch(DivCommand c) {
case DIV_CMD_NOTE_ON: {
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA);
chan[c.chan].isNewQSound=(ins->type==DIV_INS_QSOUND);
if (c.value!=DIV_NOTE_NULL) chan[c.chan].sample=ins->amiga.getSample(c.value);
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].sample=ins->amiga.getSample(c.value);
c.value=ins->amiga.getFreq(c.value);
}
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].baseFreq=QS_NOTE_FREQUENCY(c.value);
}

View file

@ -151,8 +151,8 @@ void DivPlatformRF5C68::tick(bool sysTick) {
if (s->isLoopable()) {
loop=start+s->loopStart;
}
start=MIN(start,getSampleMemCapacity()-31);
loop=MIN(loop,getSampleMemCapacity()-31);
start=MIN(start,getSampleMemCapacity()-32);
loop=MIN(loop,getSampleMemCapacity()-32);
rWrite(8,keyoff); // force keyoff first
chWrite(i,6,start>>8);
chWrite(i,4,loop&0xff);
@ -182,7 +182,10 @@ int DivPlatformRF5C68::dispatch(DivCommand c) {
case DIV_CMD_NOTE_ON: {
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA);
chan[c.chan].macroVolMul=ins->type==DIV_INS_AMIGA?64:255;
if (c.value!=DIV_NOTE_NULL) chan[c.chan].sample=ins->amiga.getSample(c.value);
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].sample=ins->amiga.getSample(c.value);
c.value=ins->amiga.getFreq(c.value);
}
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value);
}
@ -422,7 +425,7 @@ void DivPlatformRF5C68::renderSamples(int sysID) {
}
int length=s->getLoopEndPosition(DIV_SAMPLE_DEPTH_8BIT);
int actualLength=MIN((int)(getSampleMemCapacity()-memPos)-31,length);
int actualLength=MIN((int)(getSampleMemCapacity()-memPos)-32,length);
if (actualLength>0) {
sampleOffRFC[i]=memPos;
for (int j=0; j<actualLength; j++) {
@ -432,8 +435,8 @@ void DivPlatformRF5C68::renderSamples(int sysID) {
sampleMem[memPos++]=(val>0)?(val|0x80):(0-val);
}
// write end of sample marker
memset(&sampleMem[memPos],0xff,31);
memPos+=31;
memset(&sampleMem[memPos],0xff,32);
memPos+=32;
}
if (actualLength<length) {
logW("out of RF5C68 PCM memory for sample %d!",i);

View file

@ -72,7 +72,7 @@ void DivPlatformSegaPCM::tick(bool sysTick) {
chan[i].handleArp();
} else if (chan[i].std.arp.had) {
if (!chan[i].inPorta) {
chan[i].baseFreq=(parent->calcArp(chan[i].note,chan[i].std.arp.val)<<6);
chan[i].baseFreq=(parent->calcArp(chan[i].note,chan[i].std.arp.val)<<7);
}
chan[i].freqChanged=true;
}
@ -106,21 +106,22 @@ void DivPlatformSegaPCM::tick(bool sysTick) {
}
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
chan[i].freq=chan[i].baseFreq+(chan[i].pitch>>1)-64;
chan[i].freq=chan[i].baseFreq+(chan[i].pitch)-128+(oldSlides?0:chan[i].pitch2);
if (!parent->song.oldArpStrategy) {
if (chan[i].fixedArp) {
chan[i].freq=(chan[i].baseNoteOverride<<6)+(chan[i].pitch>>1)-64+chan[i].pitch2;
chan[i].freq=(chan[i].baseNoteOverride<<7)+chan[i].pitch-128+(chan[i].pitch2<<(oldSlides?1:0));
} else {
chan[i].freq+=chan[i].arpOff<<6;
chan[i].freq+=chan[i].arpOff<<7;
}
}
if (oldSlides) chan[i].freq&=~1;
if (chan[i].furnacePCM) {
double off=1.0;
if (chan[i].pcm.sample>=0 && chan[i].pcm.sample<parent->song.sampleLen) {
DivSample* s=parent->getSample(chan[i].pcm.sample);
off=(double)s->centerRate/8363.0;
}
chan[i].pcm.freq=MIN(255,(15625+(off*parent->song.tuning*pow(2.0,double(chan[i].freq+256)/(64.0*12.0)))*255)/31250)+chan[i].pitch2;
chan[i].pcm.freq=MIN(255,(15625+(off*parent->song.tuning*pow(2.0,double(chan[i].freq+512)/(128.0*12.0)))*255)/31250)+(oldSlides?chan[i].pitch2:0);
rWrite(7+(i<<3),chan[i].pcm.freq);
}
chan[i].freqChanged=false;
@ -186,7 +187,10 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) {
if (ins->type==DIV_INS_AMIGA || ins->type==DIV_INS_SEGAPCM) {
chan[c.chan].macroVolMul=(ins->type==DIV_INS_AMIGA)?64:127;
chan[c.chan].isNewSegaPCM=(ins->type==DIV_INS_SEGAPCM);
if (c.value!=DIV_NOTE_NULL) chan[c.chan].pcm.sample=ins->amiga.getSample(c.value);
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].pcm.sample=ins->amiga.getSample(c.value);
c.value=ins->amiga.getFreq(c.value);
}
if (chan[c.chan].pcm.sample<0 || chan[c.chan].pcm.sample>=parent->song.sampleLen) {
chan[c.chan].pcm.sample=-1;
rWrite(0x86+(c.chan<<3),3);
@ -198,7 +202,7 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) {
}
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].note=c.value;
chan[c.chan].baseFreq=(c.value<<6);
chan[c.chan].baseFreq=(c.value<<7);
chan[c.chan].freqChanged=true;
}
chan[c.chan].furnacePCM=true;
@ -286,17 +290,18 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) {
break;
}
case DIV_CMD_NOTE_PORTA: {
int destFreq=(c.value2<<6);
int destFreq=(c.value2<<7);
int newFreq;
int mul=(oldSlides || parent->song.linearPitch!=2)?8:1;
bool return2=false;
if (destFreq>chan[c.chan].baseFreq) {
newFreq=chan[c.chan].baseFreq+c.value*4;
newFreq=chan[c.chan].baseFreq+c.value*mul;
if (newFreq>=destFreq) {
newFreq=destFreq;
return2=true;
}
} else {
newFreq=chan[c.chan].baseFreq-c.value*4;
newFreq=chan[c.chan].baseFreq-c.value*mul;
if (newFreq<=destFreq) {
newFreq=destFreq;
return2=true;
@ -311,7 +316,7 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) {
break;
}
case DIV_CMD_LEGATO: {
chan[c.chan].baseFreq=(c.value<<6);
chan[c.chan].baseFreq=(c.value<<7);
chan[c.chan].freqChanged=true;
break;
}
@ -334,7 +339,7 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) {
return 127;
break;
case DIV_CMD_PRE_PORTA:
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=(chan[c.chan].note<<6);
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=(chan[c.chan].note<<7);
chan[c.chan].inPorta=c.value;
break;
case DIV_CMD_PRE_NOTE:
@ -480,7 +485,11 @@ void DivPlatformSegaPCM::renderSamples(int sysID) {
if (memPos>=16777216) break;
sampleOffSegaPCM[i]=memPos;
for (unsigned int j=0; j<alignedSize; j++) {
sampleMem[memPos++]=((unsigned char)sample->data8[j]+0x80);
if (j>=sample->samples) {
sampleMem[memPos++]=0;
} else {
sampleMem[memPos++]=((unsigned char)sample->data8[j]+0x80);
}
sampleEndSegaPCM[i]=((memPos+0xff)>>8)-1;
if (memPos>=16777216) break;
}
@ -497,6 +506,8 @@ void DivPlatformSegaPCM::setFlags(const DivConfig& flags) {
for (int i=0; i<16; i++) {
oscBuf[i]->rate=rate;
}
oldSlides=flags.getBool("oldSlides",false);
}
int DivPlatformSegaPCM::getOutputCount() {

View file

@ -65,6 +65,7 @@ class DivPlatformSegaPCM: public DivDispatch {
segapcm_device pcm;
int delay;
int pcmL, pcmR, pcmCycles;
bool oldSlides;
unsigned char sampleBank;
unsigned char lastBusy;

View file

@ -336,7 +336,10 @@ int DivPlatformSNES::dispatch(DivCommand c) {
}
chan[c.chan].ws.init(ins,chan[c.chan].wtLen,15,chan[c.chan].insChanged);
} else {
if (c.value!=DIV_NOTE_NULL) chan[c.chan].sample=ins->amiga.getSample(c.value);
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].sample=ins->amiga.getSample(c.value);
c.value=ins->amiga.getFreq(c.value);
}
chan[c.chan].useWave=false;
}
if (chan[c.chan].useWave || chan[c.chan].sample<0 || chan[c.chan].sample>=parent->song.sampleLen) {

View file

@ -138,9 +138,7 @@ void DivPlatformSoundUnit::tick(bool sysTick) {
//DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_SU);
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,chan[i].switchRoles,2,chan[i].pitch2,chipClock,chan[i].switchRoles?CHIP_DIVIDER:CHIP_FREQBASE);
if (chan[i].pcm) {
DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_SU);
// TODO: sample map?
DivSample* sample=parent->getSample(ins->amiga.getSample(chan[i].note));
DivSample* sample=parent->getSample(chan[i].sample);
if (sample!=NULL) {
double off=0.25;
if (sample->centerRate<1) {
@ -209,6 +207,12 @@ int DivPlatformSoundUnit::dispatch(DivCommand c) {
writeControlUpper(c.chan);
}
chan[c.chan].pcm=(ins->type==DIV_INS_AMIGA || ins->amiga.useSample);
if (chan[c.chan].pcm) {
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].sample=ins->amiga.getSample(c.value);
c.value=ins->amiga.getFreq(c.value);
}
}
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].baseFreq=NOTE_SU(c.chan,c.value);
chan[c.chan].freqChanged=true;

View file

@ -26,7 +26,7 @@
class DivPlatformSoundUnit: public DivDispatch {
struct Channel: public SharedChannel<signed char> {
int cutoff, baseCutoff, res, control, hasOffset;
int cutoff, baseCutoff, res, control, hasOffset, sample;
signed char pan;
unsigned char duty;
bool noise, pcm, phaseReset, filterPhaseReset, switchRoles;
@ -43,6 +43,7 @@ class DivPlatformSoundUnit: public DivDispatch {
res(0),
control(0),
hasOffset(0),
sample(-1),
pan(0),
duty(63),
noise(false),

View file

@ -261,7 +261,10 @@ int DivPlatformSwan::dispatch(DivCommand c) {
dacPos=0;
dacPeriod=0;
if (ins->type==DIV_INS_AMIGA || ins->amiga.useSample) {
if (c.value!=DIV_NOTE_NULL) dacSample=ins->amiga.getSample(c.value);
if (c.value!=DIV_NOTE_NULL) {
dacSample=ins->amiga.getSample(c.value);
c.value=ins->amiga.getFreq(c.value);
}
if (dacSample<0 || dacSample>=parent->song.sampleLen) {
dacSample=-1;
if (dumpWrites) postWrite(0xffff0002,0);

View file

@ -235,7 +235,11 @@ int DivPlatformVERA::dispatch(DivCommand c) {
if (c.chan<16) {
rWriteLo(c.chan,2,chan[c.chan].vol);
} else {
if (c.value!=DIV_NOTE_NULL) chan[16].pcm.sample=parent->getIns(chan[16].ins,DIV_INS_VERA)->amiga.getSample(c.value);
if (c.value!=DIV_NOTE_NULL) {
DivInstrument* ins=parent->getIns(chan[16].ins,DIV_INS_VERA);
chan[16].pcm.sample=ins->amiga.getSample(c.value);
c.value=ins->amiga.getFreq(c.value);
}
if (chan[16].pcm.sample<0 || chan[16].pcm.sample>=parent->song.sampleLen) {
chan[16].pcm.sample=-1;
}

View file

@ -179,8 +179,6 @@ void DivPlatformVRC6::tick(bool sysTick) {
if (chan[i].std.phaseReset.val && chan[i].active) {
if ((i!=2) && (!chan[i].pcm)) {
if (dumpWrites) addWrite(0xffff0002+(i<<8),0);
DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_VRC6);
chan[i].dacSample=ins->amiga.getSample(chan[i].note);
if (chan[i].dacSample<0 || chan[i].dacSample>=parent->song.sampleLen) {
if (dumpWrites) {
chWrite(i,2,0x80);
@ -242,7 +240,10 @@ int DivPlatformVRC6::dispatch(DivCommand c) {
if (chan[c.chan].pcm) {
if (skipRegisterWrites) break;
if (ins->type==DIV_INS_AMIGA || ins->amiga.useSample) {
if (c.value!=DIV_NOTE_NULL) chan[c.chan].dacSample=ins->amiga.getSample(c.value);
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].dacSample=ins->amiga.getSample(c.value);
c.value=ins->amiga.getFreq(c.value);
}
if (chan[c.chan].dacSample<0 || chan[c.chan].dacSample>=parent->song.sampleLen) {
chan[c.chan].dacSample=-1;
if (dumpWrites) addWrite(0xffff0002+(c.chan<<8),0);

View file

@ -222,7 +222,8 @@ void DivPlatformX1_010::acquire(short** buf, size_t len) {
if (stereo) buf[1][h]=tempR;
for (int i=0; i<16; i++) {
oscBuf[i]->data[oscBuf[i]->needle++]=(x1_010.voice_out(i,0)+x1_010.voice_out(i,1))>>1;
int vo=(x1_010.voice_out(i,0)+x1_010.voice_out(i,1))<<3;
oscBuf[i]->data[oscBuf[i]->needle++]=CLAMP(vo,-32768,32767);
}
}
}
@ -540,7 +541,10 @@ int DivPlatformX1_010::dispatch(DivCommand c) {
if (chan[c.chan].furnacePCM) {
chan[c.chan].pcm=true;
chan[c.chan].macroInit(ins);
if (c.value!=DIV_NOTE_NULL) chan[c.chan].sample=ins->amiga.getSample(c.value);
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].sample=ins->amiga.getSample(c.value);
c.value=ins->amiga.getFreq(c.value);
}
if (chan[c.chan].sample>=0 && chan[c.chan].sample<parent->song.sampleLen) {
DivSample* s=parent->getSample(chan[c.chan].sample);
if (isBanked) {

View file

@ -542,13 +542,8 @@ void DivPlatformYM2203Ext::muteChannel(int ch, bool mute) {
DivInstrumentFM::Operator op=chan[2].state.op[ordch];
if (isOpMuted[ch-2] || !op.enable) {
rWrite(baseAddr+0x40,127);
immWrite(baseAddr+0x40,127);
} else if (KVS(2,ordch)) {
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-2].outVol&0x7f,127));
immWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-2].outVol&0x7f,127));
} else {
rWrite(baseAddr+0x40,op.tl);
immWrite(baseAddr+0x40,op.tl);
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-2].outVol&0x7f,127));
}
}

View file

@ -896,7 +896,10 @@ int DivPlatformYM2608::dispatch(DivCommand c) {
chan[c.chan].outVol=chan[c.chan].vol;
immWrite(0x10b,chan[c.chan].outVol);
}
if (c.value!=DIV_NOTE_NULL) chan[c.chan].sample=ins->amiga.getSample(c.value);
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].sample=ins->amiga.getSample(c.value);
c.value=ins->amiga.getFreq(c.value);
}
if (chan[c.chan].sample>=0 && chan[c.chan].sample<parent->song.sampleLen) {
DivSample* s=parent->getSample(chan[c.chan].sample);
immWrite(0x100,0x01); // reset

View file

@ -564,13 +564,8 @@ void DivPlatformYM2608Ext::muteChannel(int ch, bool mute) {
DivInstrumentFM::Operator op=chan[2].state.op[ordch];
if (isOpMuted[ch-2] || !op.enable) {
rWrite(baseAddr+0x40,127);
immWrite(baseAddr+0x40,127);
} else if (KVS(2,ordch)) {
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-2].outVol&0x7f,127));
immWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-2].outVol&0x7f,127));
} else {
rWrite(baseAddr+0x40,op.tl);
immWrite(baseAddr+0x40,op.tl);
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-2].outVol&0x7f,127));
}
rWrite(chanOffs[2]+0xb4,(IS_EXTCH_MUTED?0:(opChan[ch-2].pan<<6))|(chan[2].state.fms&7)|((chan[2].state.ams&3)<<4));

View file

@ -827,7 +827,10 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
chan[c.chan].outVol=chan[c.chan].vol;
immWrite(0x1b,chan[c.chan].outVol);
}
if (c.value!=DIV_NOTE_NULL) chan[c.chan].sample=ins->amiga.getSample(c.value);
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].sample=ins->amiga.getSample(c.value);
c.value=ins->amiga.getFreq(c.value);
}
if (chan[c.chan].sample>=0 && chan[c.chan].sample<parent->song.sampleLen) {
DivSample* s=parent->getSample(chan[c.chan].sample);
immWrite(0x12,(sampleOffB[chan[c.chan].sample]>>8)&0xff);

View file

@ -894,7 +894,10 @@ int DivPlatformYM2610B::dispatch(DivCommand c) {
chan[c.chan].outVol=chan[c.chan].vol;
immWrite(0x1b,chan[c.chan].outVol);
}
if (c.value!=DIV_NOTE_NULL) chan[c.chan].sample=ins->amiga.getSample(c.value);
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].sample=ins->amiga.getSample(c.value);
c.value=ins->amiga.getFreq(c.value);
}
if (chan[c.chan].sample>=0 && chan[c.chan].sample<parent->song.sampleLen) {
DivSample* s=parent->getSample(chan[c.chan].sample);
immWrite(0x12,(sampleOffB[chan[c.chan].sample]>>8)&0xff);

View file

@ -560,13 +560,8 @@ void DivPlatformYM2610BExt::muteChannel(int ch, bool mute) {
DivInstrumentFM::Operator op=chan[extChanOffs].state.op[ordch];
if (isOpMuted[ch-extChanOffs] || !op.enable) {
rWrite(baseAddr+0x40,127);
immWrite(baseAddr+0x40,127);
} else if (KVS(2,ordch)) {
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-extChanOffs].outVol&0x7f,127));
immWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-extChanOffs].outVol&0x7f,127));
} else {
rWrite(baseAddr+0x40,op.tl);
immWrite(baseAddr+0x40,op.tl);
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-extChanOffs].outVol&0x7f,127));
}
rWrite(chanOffs[extChanOffs]+0xb4,(IS_EXTCH_MUTED?0:(opChan[ch-extChanOffs].pan<<6))|(chan[extChanOffs].state.fms&7)|((chan[extChanOffs].state.ams&3)<<4));

View file

@ -560,13 +560,8 @@ void DivPlatformYM2610Ext::muteChannel(int ch, bool mute) {
DivInstrumentFM::Operator op=chan[extChanOffs].state.op[ordch];
if (isOpMuted[ch-extChanOffs] || !op.enable) {
rWrite(baseAddr+0x40,127);
immWrite(baseAddr+0x40,127);
} else if (KVS(2,ordch)) {
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-extChanOffs].outVol&0x7f,127));
immWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-extChanOffs].outVol&0x7f,127));
} else {
rWrite(baseAddr+0x40,op.tl);
immWrite(baseAddr+0x40,op.tl);
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-extChanOffs].outVol&0x7f,127));
}
rWrite(chanOffs[extChanOffs]+0xb4,(IS_EXTCH_MUTED?0:(opChan[ch-extChanOffs].pan<<6))|(chan[extChanOffs].state.fms&7)|((chan[extChanOffs].state.ams&3)<<4));

View file

@ -212,7 +212,10 @@ int DivPlatformYMZ280B::dispatch(DivCommand c) {
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA);
chan[c.chan].isNewYMZ=ins->type==DIV_INS_YMZ280B;
chan[c.chan].macroVolMul=ins->type==DIV_INS_AMIGA?64:255;
if (c.value!=DIV_NOTE_NULL) chan[c.chan].sample=ins->amiga.getSample(c.value);
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].sample=ins->amiga.getSample(c.value);
c.value=ins->amiga.getFreq(c.value);
}
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value);
}

View file

@ -32,11 +32,11 @@ const char** DivPlatformZXBeeperQuadTone::getRegisterSheet() {
void DivPlatformZXBeeperQuadTone::acquire(short** buf, size_t len) {
bool o=false;
for (size_t h=0; h<len; h++) {
if (curSample>=0 && curSample<parent->song.sampleLen) {
if (curSample>=0 && curSample<parent->song.sampleLen && !isMuted[4]) {
while (curSamplePeriod>=chan[4].freq) {
DivSample* s=parent->getSample(curSample);
if (s->samples>0) {
o=(!isMuted[4]&&s->data8[curSamplePos++]>0);
if (!isMuted[4]) o=(s->data8[curSamplePos++]>0);
if (curSamplePos>=s->samples) curSample=-1;
// (theoretical) 32KiB limit
if (curSamplePos>=32768*8) curSample=-1;
@ -59,6 +59,7 @@ void DivPlatformZXBeeperQuadTone::acquire(short** buf, size_t len) {
if ((outputClock&1)==0) {
chan[ch].sPosition+=(regPool[1+b]<<8)|regPool[0+b];
chan[ch].out=regPool[3+b]+((((chan[ch].sPosition>>8)&0xff)<regPool[2+b])?1:0);
if (isMuted[ch]) chan[ch].out=0;
}
if ((outputClock&3)==0) {
oscBuf[4]->data[oscBuf[4]->needle++]=0;
@ -66,6 +67,22 @@ void DivPlatformZXBeeperQuadTone::acquire(short** buf, size_t len) {
o=chan[ch].out&0x10;
oscBuf[ch]->data[oscBuf[ch]->needle++]=o?32767:0;
chan[ch].out<<=1;
// if muted, ztill run sample
if (curSample>=0 && curSample<parent->song.sampleLen && isMuted[4]) {
while (curSamplePeriod>=chan[4].freq) {
DivSample* s=parent->getSample(curSample);
if (s->samples>0) {
if (curSamplePos>=s->samples) curSample=-1;
// (theoretical) 32KiB limit
if (curSamplePos>=32768*8) curSample=-1;
} else {
curSample=-1;
}
curSamplePeriod-=chan[4].freq;
}
curSamplePeriod+=40;
}
}
outputClock=(outputClock+1)&7;
buf[0][h]=o?32767:0;
@ -169,10 +186,11 @@ int DivPlatformZXBeeperQuadTone::dispatch(DivCommand c) {
} else {
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA);
if (c.value!=DIV_NOTE_NULL) {
curSample=ins->amiga.getSample(c.value);
c.value=ins->amiga.getFreq(c.value);
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
chan[c.chan].freqChanged=true;
chan[c.chan].note=c.value;
curSample=ins->amiga.getSample(c.value);
// TODO support offset commands
curSamplePos=0;
curSamplePeriod=0;
@ -282,7 +300,7 @@ int DivPlatformZXBeeperQuadTone::dispatch(DivCommand c) {
void DivPlatformZXBeeperQuadTone::writeOutVol(int ch) {
if (ch>=4) return;
unsigned char val=(chan[ch].outVol>=1)?((chan[ch].outVol>=2)?31:7):0;
rWrite(3+ch*4,(!isMuted[ch]&&chan[ch].active)?val:0);
rWrite(3+ch*4,(chan[ch].active)?val:0);
}
void DivPlatformZXBeeperQuadTone::muteChannel(int ch, bool mute) {

View file

@ -229,6 +229,8 @@ const char* cmdName[]={
"ES5506_ENVELOPE_K2RAMP",
"ES5506_PAUSE",
"HINT_ARP_TIME",
"ALWAYS_SET_VOLUME"
};
@ -510,13 +512,13 @@ void DivEngine::processRow(int i, bool afterDelay) {
if (chan[i].stopOnOff) {
chan[i].portaNote=-1;
chan[i].portaSpeed=-1;
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed));
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,CLAMP(chan[i].portaNote,-128,127),MAX(chan[i].portaSpeed,0)));
chan[i].stopOnOff=false;
}
if (disCont[dispatchOfChan[i]].dispatch->keyOffAffectsPorta(dispatchChanOfChan[i])) {
chan[i].portaNote=-1;
chan[i].portaSpeed=-1;
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed));
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,CLAMP(chan[i].portaNote,-128,127),MAX(chan[i].portaSpeed,0)));
/*if (i==2 && sysOfChan[i]==DIV_SYSTEM_SMS) {
chan[i+1].portaNote=-1;
chan[i+1].portaSpeed=-1;
@ -533,13 +535,13 @@ void DivEngine::processRow(int i, bool afterDelay) {
if (chan[i].stopOnOff) {
chan[i].portaNote=-1;
chan[i].portaSpeed=-1;
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed));
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,CLAMP(chan[i].portaNote,-128,127),MAX(chan[i].portaSpeed,0)));
chan[i].stopOnOff=false;
}
if (disCont[dispatchOfChan[i]].dispatch->keyOffAffectsPorta(dispatchChanOfChan[i])) {
chan[i].portaNote=-1;
chan[i].portaSpeed=-1;
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed));
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,CLAMP(chan[i].portaNote,-128,127),MAX(chan[i].portaSpeed,0)));
/*if (i==2 && sysOfChan[i]==DIV_SYSTEM_SMS) {
chan[i+1].portaNote=-1;
chan[i+1].portaSpeed=-1;
@ -632,13 +634,13 @@ void DivEngine::processRow(int i, bool afterDelay) {
if (effectVal==0) {
chan[i].portaNote=-1;
chan[i].portaSpeed=-1;
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed));
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,CLAMP(chan[i].portaNote,-128,127),MAX(chan[i].portaSpeed,0)));
chan[i].inPorta=false;
if (!song.arpNonPorta) dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,false,0));
} else {
chan[i].portaNote=song.limitSlides?0x60:255;
chan[i].portaSpeed=effectVal;
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed));
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,CLAMP(chan[i].portaNote,-128,127),MAX(chan[i].portaSpeed,0)));
chan[i].portaStop=true;
chan[i].nowYouCanStop=false;
chan[i].stopOnOff=false;
@ -654,13 +656,13 @@ void DivEngine::processRow(int i, bool afterDelay) {
if (effectVal==0) {
chan[i].portaNote=-1;
chan[i].portaSpeed=-1;
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed));
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,CLAMP(chan[i].portaNote,-128,127),MAX(chan[i].portaSpeed,0)));
chan[i].inPorta=false;
if (!song.arpNonPorta) dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,false,0));
} else {
chan[i].portaNote=song.limitSlides?disCont[dispatchOfChan[i]].dispatch->getPortaFloor(dispatchChanOfChan[i]):-60;
chan[i].portaSpeed=effectVal;
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed));
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,CLAMP(chan[i].portaNote,-128,127),MAX(chan[i].portaSpeed,0)));
chan[i].portaStop=true;
chan[i].nowYouCanStop=false;
chan[i].stopOnOff=false;
@ -674,7 +676,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
if (effectVal==0) {
chan[i].portaNote=-1;
chan[i].portaSpeed=-1;
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed));
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,CLAMP(chan[i].portaNote,-128,127),MAX(chan[i].portaSpeed,0)));
chan[i].inPorta=false;
dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,false,0));
} else {
@ -688,7 +690,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
chan[i].inPorta=true;
chan[i].wasShorthandPorta=false;
}
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed));
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,CLAMP(chan[i].portaNote,-128,127),MAX(chan[i].portaSpeed,0)));
chan[i].portaStop=true;
if (chan[i].keyOn) chan[i].doNote=false;
chan[i].stopOnOff=song.stopPortaOnNoteOff; // what?!
@ -765,12 +767,13 @@ void DivEngine::processRow(int i, bool afterDelay) {
case 0xe0: // arp speed
if (effectVal>0) {
curSubSong->arpLen=effectVal;
dispatchCmd(DivCommand(DIV_CMD_HINT_ARP_TIME,i,curSubSong->arpLen));
}
break;
case 0xe1: // portamento up
chan[i].portaNote=chan[i].note+(effectVal&15);
chan[i].portaSpeed=(effectVal>>4)*4;
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed));
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,CLAMP(chan[i].portaNote,-128,127),MAX(chan[i].portaSpeed,0)));
chan[i].portaStop=true;
chan[i].nowYouCanStop=false;
chan[i].stopOnOff=song.stopPortaOnNoteOff; // what?!
@ -789,7 +792,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
case 0xe2: // portamento down
chan[i].portaNote=chan[i].note-(effectVal&15);
chan[i].portaSpeed=(effectVal>>4)*4;
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed));
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,CLAMP(chan[i].portaNote,-128,127),MAX(chan[i].portaSpeed,0)));
chan[i].portaStop=true;
chan[i].nowYouCanStop=false;
chan[i].stopOnOff=song.stopPortaOnNoteOff; // what?!
@ -947,13 +950,13 @@ void DivEngine::processRow(int i, bool afterDelay) {
if (chan[i].inPorta && chan[i].keyOn && !chan[i].shorthandPorta) {
if (song.e1e2StopOnSameNote && chan[i].wasShorthandPorta) {
chan[i].portaSpeed=-1;
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed));
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,CLAMP(chan[i].portaNote,-128,127),MAX(chan[i].portaSpeed,0)));
if (!song.brokenShortcutSlides) dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,false,0));
chan[i].wasShorthandPorta=false;
chan[i].inPorta=false;
} else {
chan[i].portaNote=chan[i].note;
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed));
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,CLAMP(chan[i].portaNote,-128,127),MAX(chan[i].portaSpeed,0)));
}
} else if (!chan[i].noteOnInhibit) {
dispatchCmd(DivCommand(DIV_CMD_NOTE_ON,i,chan[i].note,chan[i].volume>>8));
@ -966,7 +969,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
if (!chan[i].keyOn && chan[i].scheduledSlideReset) {
chan[i].portaNote=-1;
chan[i].portaSpeed=-1;
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed));
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,CLAMP(chan[i].portaNote,-128,127),MAX(chan[i].portaSpeed,0)));
chan[i].scheduledSlideReset=false;
chan[i].inPorta=false;
}
@ -1122,11 +1125,23 @@ void DivEngine::nextRow() {
bool wantPreNote=false;
if (disCont[dispatchOfChan[i]].dispatch!=NULL) {
wantPreNote=disCont[dispatchOfChan[i]].dispatch->getWantPreNote();
if (wantPreNote) dispatchCmd(DivCommand(DIV_CMD_PRE_NOTE,i,ticks));
if (wantPreNote) {
int addition=0;
for (int j=0; j<curPat[i].effectCols; j++) {
if (pat->data[curRow][4+(j<<1)]==0xed) {
if (pat->data[curRow][5+(j<<1)]>0) {
addition=pat->data[curRow][5+(j<<1)];
break;
}
}
}
dispatchCmd(DivCommand(DIV_CMD_PRE_NOTE,i,ticks+addition));
}
}
if (song.oneTickCut) {
bool doPrepareCut=true;
int addition=0;
for (int j=0; j<curPat[i].effectCols; j++) {
if (pat->data[curRow][4+(j<<1)]==0x03) {
@ -1139,8 +1154,14 @@ void DivEngine::nextRow() {
break;
}
}
if (pat->data[curRow][4+(j<<1)]==0xed) {
if (pat->data[curRow][5+(j<<1)]>0) {
addition=pat->data[curRow][5+(j<<1)];
break;
}
}
}
if (doPrepareCut && !wantPreNote && chan[i].cut<=0) chan[i].cut=ticks;
if (doPrepareCut && !wantPreNote && chan[i].cut<=0) chan[i].cut=ticks+addition;
}
}
}
@ -1311,7 +1332,7 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
if ((chan[i].keyOn || chan[i].keyOff) && chan[i].portaSpeed>0) {
if (dispatchCmd(DivCommand(DIV_CMD_NOTE_PORTA,i,chan[i].portaSpeed*(song.linearPitch==2?song.pitchSlideSpeed:1),chan[i].portaNote))==2 && chan[i].portaStop && song.targetResetsSlides) {
chan[i].portaSpeed=0;
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed));
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,CLAMP(chan[i].portaNote,-128,127),MAX(chan[i].portaSpeed,0)));
chan[i].oldNote=chan[i].note;
chan[i].note=chan[i].portaNote;
chan[i].inPorta=false;
@ -1330,13 +1351,13 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
if (chan[i].stopOnOff) {
chan[i].portaNote=-1;
chan[i].portaSpeed=-1;
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed));
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,CLAMP(chan[i].portaNote,-128,127),MAX(chan[i].portaSpeed,0)));
chan[i].stopOnOff=false;
}
if (disCont[dispatchOfChan[i]].dispatch->keyOffAffectsPorta(dispatchChanOfChan[i])) {
chan[i].portaNote=-1;
chan[i].portaSpeed=-1;
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,chan[i].portaNote,chan[i].portaSpeed));
dispatchCmd(DivCommand(DIV_CMD_HINT_PORTA,i,CLAMP(chan[i].portaNote,-128,127),MAX(chan[i].portaSpeed,0)));
/*if (i==2 && sysOfChan[i]==DIV_SYSTEM_SMS) {
chan[i+1].portaNote=-1;
chan[i+1].portaSpeed=-1;
@ -1409,6 +1430,15 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
ret=true;
shallStop=false;
shallStopSched=false;
// reset all chan oscs
for (int i=0; i<chans; i++) {
DivDispatchOscBuffer* buf=disCont[dispatchOfChan[i]].dispatch->getOscBuffer(dispatchChanOfChan[i]);
if (buf!=NULL) {
memset(buf->data,0,65536*sizeof(short));
buf->needle=0;
buf->readNeedle=0;
}
}
return ret;
}
@ -1529,10 +1559,13 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi
samp_temp=0;
} else {
samp_temp=s->data16[sPreview.pos];
if (sPreview.dir) {
sPreview.pos--;
} else {
sPreview.pos++;
if (--sPreview.posSub<=0) {
sPreview.posSub=sPreview.rateMul;
if (sPreview.dir) {
sPreview.pos--;
} else {
sPreview.pos++;
}
}
}
blip_add_delta(samp_bb,i,samp_temp-samp_prevSample);
@ -1637,8 +1670,11 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi
} else {
samp_temp=((MIN(wave->data[sPreview.pos],wave->max)<<14)/wave->max)-8192;
}
if (++sPreview.pos>=wave->len) {
sPreview.pos=0;
if (--sPreview.posSub<=0) {
sPreview.posSub=sPreview.rateMul;
if (++sPreview.pos>=wave->len) {
sPreview.pos=0;
}
}
blip_add_delta(samp_bb,i,samp_temp-samp_prevSample);
samp_prevSample=samp_temp;

View file

@ -212,7 +212,6 @@ void DivWaveSynth::setWidth(int val) {
void DivWaveSynth::changeWave1(int num) {
DivWavetable* w1=e->getWave(num);
logV("changeWave1 (%d)",width);
if (width<1) return;
for (int i=0; i<width; i++) {
if (w1->max<1 || w1->len<1) {