QSound: ADPCM?
This commit is contained in:
parent
525f512bb1
commit
7070522bbe
|
@ -245,6 +245,22 @@ const unsigned char q1_reg_map[Q1V_REG_COUNT][16] = {
|
||||||
{0x06,0x0e,0x16,0x1e,0x26,0x2e,0x36,0x3e,0x46,0x4e,0x56,0x5e,0x66,0x6e,0x76,0x7e},
|
{0x06,0x0e,0x16,0x1e,0x26,0x2e,0x36,0x3e,0x46,0x4e,0x56,0x5e,0x66,0x6e,0x76,0x7e},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const unsigned char q1a_start_map[3]={
|
||||||
|
0xca, 0xce, 0xd2
|
||||||
|
};
|
||||||
|
|
||||||
|
const unsigned char q1a_end_map[3]={
|
||||||
|
0xcb, 0xcf, 0xd3
|
||||||
|
};
|
||||||
|
|
||||||
|
const unsigned char q1a_bank_map[3]={
|
||||||
|
0xcc, 0xd0, 0xd4
|
||||||
|
};
|
||||||
|
|
||||||
|
const unsigned char q1a_vol_map[3]={
|
||||||
|
0xcd, 0xd1, 0xd5
|
||||||
|
};
|
||||||
|
|
||||||
const char** DivPlatformQSound::getRegisterSheet() {
|
const char** DivPlatformQSound::getRegisterSheet() {
|
||||||
return regCheatSheetQSound;
|
return regCheatSheetQSound;
|
||||||
}
|
}
|
||||||
|
@ -265,7 +281,7 @@ void DivPlatformQSound::acquire(short* bufL, short* bufR, size_t start, size_t l
|
||||||
}
|
}
|
||||||
|
|
||||||
void DivPlatformQSound::tick(bool sysTick) {
|
void DivPlatformQSound::tick(bool sysTick) {
|
||||||
for (int i=0; i<16; i++) {
|
for (int i=0; i<19; i++) {
|
||||||
chan[i].std.next();
|
chan[i].std.next();
|
||||||
if (chan[i].std.vol.had) {
|
if (chan[i].std.vol.had) {
|
||||||
if (chan[i].isNewQSound) {
|
if (chan[i].isNewQSound) {
|
||||||
|
@ -277,7 +293,11 @@ void DivPlatformQSound::tick(bool sysTick) {
|
||||||
}
|
}
|
||||||
// Check if enabled and write volume
|
// Check if enabled and write volume
|
||||||
if (chan[i].active) {
|
if (chan[i].active) {
|
||||||
rWrite(q1_reg_map[Q1V_VOL][i],chan[i].resVol);
|
if (i<16) {
|
||||||
|
rWrite(q1_reg_map[Q1V_VOL][i],chan[i].resVol);
|
||||||
|
} else {
|
||||||
|
rWrite(q1a_vol_map[i-16],chan[i].resVol);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
uint16_t qsound_bank = 0;
|
uint16_t qsound_bank = 0;
|
||||||
|
@ -286,8 +306,13 @@ void DivPlatformQSound::tick(bool sysTick) {
|
||||||
uint16_t qsound_end = 0;
|
uint16_t qsound_end = 0;
|
||||||
if (chan[i].sample>=0 && chan[i].sample<parent->song.sampleLen) {
|
if (chan[i].sample>=0 && chan[i].sample<parent->song.sampleLen) {
|
||||||
DivSample* s=parent->getSample(chan[i].sample);
|
DivSample* s=parent->getSample(chan[i].sample);
|
||||||
qsound_bank = 0x8000 | (offPCM[chan[i].sample] >> 16);
|
if (i<16) {
|
||||||
qsound_addr = offPCM[chan[i].sample] & 0xffff;
|
qsound_bank = 0x8000 | (offPCM[chan[i].sample] >> 16);
|
||||||
|
qsound_addr = offPCM[chan[i].sample] & 0xffff;
|
||||||
|
} else {
|
||||||
|
qsound_bank = 0x8000 | (offBS[chan[i].sample] >> 16);
|
||||||
|
qsound_addr = offBS[chan[i].sample] & 0xffff;
|
||||||
|
}
|
||||||
|
|
||||||
int loopStart=s->loopStart;
|
int loopStart=s->loopStart;
|
||||||
int length = s->loopEnd;
|
int length = s->loopEnd;
|
||||||
|
@ -295,10 +320,18 @@ void DivPlatformQSound::tick(bool sysTick) {
|
||||||
length = 65536 - 16;
|
length = 65536 - 16;
|
||||||
}
|
}
|
||||||
if (loopStart == -1 || loopStart >= length) {
|
if (loopStart == -1 || loopStart >= length) {
|
||||||
qsound_end = offPCM[chan[i].sample] + length + 15;
|
if (i<16) {
|
||||||
|
qsound_end = offPCM[chan[i].sample] + length + 15;
|
||||||
|
} else {
|
||||||
|
qsound_end = offBS[chan[i].sample] + length + 15;
|
||||||
|
}
|
||||||
qsound_loop = 15;
|
qsound_loop = 15;
|
||||||
} else {
|
} else {
|
||||||
qsound_end = offPCM[chan[i].sample] + length;
|
if (i<16) {
|
||||||
|
qsound_end = offPCM[chan[i].sample] + length;
|
||||||
|
} else {
|
||||||
|
qsound_end = offBS[chan[i].sample] + length;
|
||||||
|
}
|
||||||
qsound_loop = length - loopStart;
|
qsound_loop = length - loopStart;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -310,7 +343,9 @@ void DivPlatformQSound::tick(bool sysTick) {
|
||||||
}
|
}
|
||||||
if (chan[i].isNewQSound && chan[i].std.duty.had) {
|
if (chan[i].isNewQSound && chan[i].std.duty.had) {
|
||||||
chan[i].echo=CLAMP(chan[i].std.duty.val,0,32767);
|
chan[i].echo=CLAMP(chan[i].std.duty.val,0,32767);
|
||||||
immWrite(Q1_ECHO+i,chan[i].echo&0x7fff);
|
if (i<16) {
|
||||||
|
immWrite(Q1_ECHO+i,chan[i].echo&0x7fff);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (chan[i].isNewQSound && chan[i].std.ex1.had) {
|
if (chan[i].isNewQSound && chan[i].std.ex1.had) {
|
||||||
immWrite(Q1_ECHO_FEEDBACK,chan[i].std.ex1.val&0x3fff);
|
immWrite(Q1_ECHO_FEEDBACK,chan[i].std.ex1.val&0x3fff);
|
||||||
|
@ -355,11 +390,17 @@ void DivPlatformQSound::tick(bool sysTick) {
|
||||||
chan[i].freq=off*parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,2,chan[i].pitch2,440.0,4096.0);
|
chan[i].freq=off*parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,2,chan[i].pitch2,440.0,4096.0);
|
||||||
if (chan[i].freq>0xefff) chan[i].freq=0xefff;
|
if (chan[i].freq>0xefff) chan[i].freq=0xefff;
|
||||||
if (chan[i].keyOn) {
|
if (chan[i].keyOn) {
|
||||||
rWrite(q1_reg_map[Q1V_BANK][i], qsound_bank);
|
if (i<16) {
|
||||||
rWrite(q1_reg_map[Q1V_END][i], qsound_end);
|
rWrite(q1_reg_map[Q1V_BANK][i], qsound_bank);
|
||||||
rWrite(q1_reg_map[Q1V_LOOP][i], qsound_loop);
|
rWrite(q1_reg_map[Q1V_END][i], qsound_end);
|
||||||
rWrite(q1_reg_map[Q1V_START][i], qsound_addr);
|
rWrite(q1_reg_map[Q1V_LOOP][i], qsound_loop);
|
||||||
rWrite(q1_reg_map[Q1V_PHASE][i], 0x8000);
|
rWrite(q1_reg_map[Q1V_START][i], qsound_addr);
|
||||||
|
rWrite(q1_reg_map[Q1V_PHASE][i], 0x8000);
|
||||||
|
} else {
|
||||||
|
rWrite(q1a_bank_map[i-16], qsound_bank);
|
||||||
|
rWrite(q1a_end_map[i-16], qsound_end);
|
||||||
|
rWrite(q1a_start_map[i-16], qsound_addr);
|
||||||
|
}
|
||||||
//logV("ch %d bank=%04x, addr=%04x, end=%04x, loop=%04x!",i,qsound_bank,qsound_addr,qsound_end,qsound_loop);
|
//logV("ch %d bank=%04x, addr=%04x, end=%04x, loop=%04x!",i,qsound_bank,qsound_addr,qsound_end,qsound_loop);
|
||||||
// Write sample address. Enable volume
|
// Write sample address. Enable volume
|
||||||
if (!chan[i].std.vol.had) {
|
if (!chan[i].std.vol.had) {
|
||||||
|
@ -368,16 +409,26 @@ void DivPlatformQSound::tick(bool sysTick) {
|
||||||
} else {
|
} else {
|
||||||
chan[i].resVol=chan[i].vol<<4;
|
chan[i].resVol=chan[i].vol<<4;
|
||||||
}
|
}
|
||||||
rWrite(q1_reg_map[Q1V_VOL][i],chan[i].resVol);
|
if (i<16) {
|
||||||
|
rWrite(q1_reg_map[Q1V_VOL][i],chan[i].resVol);
|
||||||
|
} else {
|
||||||
|
rWrite(q1a_vol_map[i-16],chan[i].resVol);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (chan[i].keyOff) {
|
if (chan[i].keyOff) {
|
||||||
// Disable volume
|
// Disable volume
|
||||||
rWrite(q1_reg_map[Q1V_VOL][i],0);
|
if (i<16) {
|
||||||
rWrite(q1_reg_map[Q1V_FREQ][i],0);
|
rWrite(q1_reg_map[Q1V_VOL][i],0);
|
||||||
|
rWrite(q1_reg_map[Q1V_FREQ][i],0);
|
||||||
|
} else {
|
||||||
|
rWrite(q1a_vol_map[i-16],0);
|
||||||
|
}
|
||||||
} else if (chan[i].active) {
|
} else if (chan[i].active) {
|
||||||
//logV("ch %d frequency set to %04x, off=%f, note=%d, %04x!",i,chan[i].freq,off,chan[i].note,QS_NOTE_FREQUENCY(chan[i].note));
|
//logV("ch %d frequency set to %04x, off=%f, note=%d, %04x!",i,chan[i].freq,off,chan[i].note,QS_NOTE_FREQUENCY(chan[i].note));
|
||||||
rWrite(q1_reg_map[Q1V_FREQ][i],chan[i].freq);
|
if (i<16) {
|
||||||
|
rWrite(q1_reg_map[Q1V_FREQ][i],chan[i].freq);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (chan[i].keyOn) chan[i].keyOn=false;
|
if (chan[i].keyOn) chan[i].keyOn=false;
|
||||||
if (chan[i].keyOff) chan[i].keyOff=false;
|
if (chan[i].keyOff) chan[i].keyOff=false;
|
||||||
|
@ -441,8 +492,12 @@ int DivPlatformQSound::dispatch(DivCommand c) {
|
||||||
} else {
|
} else {
|
||||||
chan[c.chan].resVol=chan[c.chan].outVol<<4;
|
chan[c.chan].resVol=chan[c.chan].outVol<<4;
|
||||||
}
|
}
|
||||||
if (chan[c.chan].active && c.chan<16) {
|
if (chan[c.chan].active) {
|
||||||
rWrite(q1_reg_map[Q1V_VOL][c.chan],chan[c.chan].resVol);
|
if (c.chan<16) {
|
||||||
|
rWrite(q1_reg_map[Q1V_VOL][c.chan],chan[c.chan].resVol);
|
||||||
|
} else {
|
||||||
|
rWrite(q1a_vol_map[c.chan-16],chan[c.chan].resVol);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -459,6 +514,7 @@ int DivPlatformQSound::dispatch(DivCommand c) {
|
||||||
break;
|
break;
|
||||||
case DIV_CMD_QSOUND_ECHO_LEVEL:
|
case DIV_CMD_QSOUND_ECHO_LEVEL:
|
||||||
chan[c.chan].echo=c.value<<7;
|
chan[c.chan].echo=c.value<<7;
|
||||||
|
if (c.chan>=16) break;
|
||||||
immWrite(Q1_ECHO+c.chan,chan[c.chan].echo&0x7fff);
|
immWrite(Q1_ECHO+c.chan,chan[c.chan].echo&0x7fff);
|
||||||
break;
|
break;
|
||||||
case DIV_CMD_QSOUND_ECHO_FEEDBACK:
|
case DIV_CMD_QSOUND_ECHO_FEEDBACK:
|
||||||
|
@ -532,7 +588,8 @@ void DivPlatformQSound::muteChannel(int ch, bool mute) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void DivPlatformQSound::forceIns() {
|
void DivPlatformQSound::forceIns() {
|
||||||
for (int i=0; i<4; i++) {
|
// TODO: what?
|
||||||
|
for (int i=0; i<19; i++) {
|
||||||
chan[i].insChanged=true;
|
chan[i].insChanged=true;
|
||||||
chan[i].freqChanged=true;
|
chan[i].freqChanged=true;
|
||||||
chan[i].sample=-1;
|
chan[i].sample=-1;
|
||||||
|
@ -552,7 +609,7 @@ DivDispatchOscBuffer* DivPlatformQSound::getOscBuffer(int ch) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void DivPlatformQSound::reset() {
|
void DivPlatformQSound::reset() {
|
||||||
for (int i=0; i<16; i++) {
|
for (int i=0; i<19; i++) {
|
||||||
chan[i]=DivPlatformQSound::Channel();
|
chan[i]=DivPlatformQSound::Channel();
|
||||||
chan[i].std.setEngine(parent);
|
chan[i].std.setEngine(parent);
|
||||||
}
|
}
|
||||||
|
@ -582,8 +639,7 @@ void DivPlatformQSound::notifyInsChange(int ins) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void DivPlatformQSound::notifyWaveChange(int wave) {
|
void DivPlatformQSound::notifyWaveChange(int wave) {
|
||||||
// TODO when wavetables are added
|
// yeah won't add wavetables
|
||||||
// TODO they probably won't be added unless the samples reside in RAM
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DivPlatformQSound::notifyInsDeletion(void* ins) {
|
void DivPlatformQSound::notifyInsDeletion(void* ins) {
|
||||||
|
@ -633,27 +689,32 @@ int DivPlatformQSound::getRegisterPoolDepth() {
|
||||||
}
|
}
|
||||||
|
|
||||||
const void* DivPlatformQSound::getSampleMem(int index) {
|
const void* DivPlatformQSound::getSampleMem(int index) {
|
||||||
return index == 0 ? sampleMem : NULL;
|
return (index == 0 || index == 1) ? sampleMem : NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t DivPlatformQSound::getSampleMemCapacity(int index) {
|
size_t DivPlatformQSound::getSampleMemCapacity(int index) {
|
||||||
return index == 0 ? 16777216 : 0;
|
return (index == 0 || index == 1) ? 16777216 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t DivPlatformQSound::getSampleMemUsage(int index) {
|
size_t DivPlatformQSound::getSampleMemUsage(int index) {
|
||||||
return index == 0 ? sampleMemLen : 0;
|
return index == 0 ? sampleMemLen : index == 1 ? sampleMemLenBS : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DivPlatformQSound::isSampleLoaded(int index, int sample) {
|
bool DivPlatformQSound::isSampleLoaded(int index, int sample) {
|
||||||
if (index!=0) return false;
|
if (index<0 || index>1) return false;
|
||||||
if (sample<0 || sample>255) return false;
|
if (sample<0 || sample>255) return false;
|
||||||
|
if (index==1) return sampleLoadedBS[sample];
|
||||||
return sampleLoaded[sample];
|
return sampleLoaded[sample];
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: ADPCM... come on...
|
const char* DivPlatformQSound::getSampleMemName(int index) {
|
||||||
|
return index == 0 ? "PCM" : index == 1 ? "ADPCM" : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
void DivPlatformQSound::renderSamples(int sysID) {
|
void DivPlatformQSound::renderSamples(int sysID) {
|
||||||
memset(sampleMem,0,getSampleMemCapacity());
|
memset(sampleMem,0,getSampleMemCapacity());
|
||||||
memset(sampleLoaded,0,256*sizeof(bool));
|
memset(sampleLoaded,0,256*sizeof(bool));
|
||||||
|
memset(sampleLoadedBS,0,256*sizeof(bool));
|
||||||
|
|
||||||
size_t memPos=0;
|
size_t memPos=0;
|
||||||
for (int i=0; i<parent->song.sampleLen; i++) {
|
for (int i=0; i<parent->song.sampleLen; i++) {
|
||||||
|
@ -689,6 +750,40 @@ void DivPlatformQSound::renderSamples(int sysID) {
|
||||||
memPos+=length+16;
|
memPos+=length+16;
|
||||||
}
|
}
|
||||||
sampleMemLen=memPos+256;
|
sampleMemLen=memPos+256;
|
||||||
|
|
||||||
|
for (int i=0; i<parent->song.sampleLen; i++) {
|
||||||
|
DivSample* s=parent->song.sample[i];
|
||||||
|
if (!s->renderOn[1][sysID]) {
|
||||||
|
offBS[i]=0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int length=s->lengthQSoundA;
|
||||||
|
if (length>65536-16) {
|
||||||
|
length=65536-16;
|
||||||
|
}
|
||||||
|
if ((memPos&0xff0000)!=((memPos+length)&0xff0000)) {
|
||||||
|
memPos=(memPos+0xffff)&0xff0000;
|
||||||
|
}
|
||||||
|
if (memPos>=getSampleMemCapacity()) {
|
||||||
|
logW("out of QSound ADPCM memory for sample %d!",i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (memPos+length>=getSampleMemCapacity()) {
|
||||||
|
for (unsigned int i=0; i<getSampleMemCapacity()-(memPos+length); i++) {
|
||||||
|
sampleMem[(memPos+i)^0x8000]=s->dataQSoundA[i];
|
||||||
|
}
|
||||||
|
logW("out of QSound ADPCM memory for sample %d!",i);
|
||||||
|
} else {
|
||||||
|
for (int i=0; i<length; i++) {
|
||||||
|
sampleMem[(memPos+i)^0x8000]=s->dataQSoundA[i];
|
||||||
|
}
|
||||||
|
sampleLoaded[i]=true;
|
||||||
|
}
|
||||||
|
offBS[i]=memPos^0x8000;
|
||||||
|
memPos+=length+16;
|
||||||
|
}
|
||||||
|
sampleMemLenBS=memPos+256;
|
||||||
}
|
}
|
||||||
|
|
||||||
int DivPlatformQSound::init(DivEngine* p, int channels, int sugRate, const DivConfig& flags) {
|
int DivPlatformQSound::init(DivEngine* p, int channels, int sugRate, const DivConfig& flags) {
|
||||||
|
@ -706,6 +801,7 @@ int DivPlatformQSound::init(DivEngine* p, int channels, int sugRate, const DivCo
|
||||||
rate = qsound_start(&chip, chipClock);
|
rate = qsound_start(&chip, chipClock);
|
||||||
sampleMem=new unsigned char[getSampleMemCapacity()];
|
sampleMem=new unsigned char[getSampleMemCapacity()];
|
||||||
sampleMemLen=0;
|
sampleMemLen=0;
|
||||||
|
sampleMemLenBS=0;
|
||||||
chip.rom_data=sampleMem;
|
chip.rom_data=sampleMem;
|
||||||
chip.rom_mask=0xffffff;
|
chip.rom_mask=0xffffff;
|
||||||
reset();
|
reset();
|
||||||
|
|
|
@ -69,7 +69,9 @@ class DivPlatformQSound: public DivDispatch {
|
||||||
|
|
||||||
unsigned char* sampleMem;
|
unsigned char* sampleMem;
|
||||||
size_t sampleMemLen;
|
size_t sampleMemLen;
|
||||||
|
size_t sampleMemLenBS;
|
||||||
bool sampleLoaded[256];
|
bool sampleLoaded[256];
|
||||||
|
bool sampleLoadedBS[256];
|
||||||
struct qsound_chip chip;
|
struct qsound_chip chip;
|
||||||
unsigned short regPool[512];
|
unsigned short regPool[512];
|
||||||
|
|
||||||
|
@ -102,6 +104,7 @@ class DivPlatformQSound: public DivDispatch {
|
||||||
void poke(std::vector<DivRegWrite>& wlist);
|
void poke(std::vector<DivRegWrite>& wlist);
|
||||||
const char** getRegisterSheet();
|
const char** getRegisterSheet();
|
||||||
const void* getSampleMem(int index = 0);
|
const void* getSampleMem(int index = 0);
|
||||||
|
const char* getSampleMemName(int index=0);
|
||||||
size_t getSampleMemCapacity(int index = 0);
|
size_t getSampleMemCapacity(int index = 0);
|
||||||
size_t getSampleMemUsage(int index = 0);
|
size_t getSampleMemUsage(int index = 0);
|
||||||
bool isSampleLoaded(int index, int sample);
|
bool isSampleLoaded(int index, int sample);
|
||||||
|
|
Loading…
Reference in New Issue