Merge branch 'tildearrow:master' into master
This commit is contained in:
commit
00031c4658
|
|
@ -455,6 +455,8 @@ src/engine/platform/sound/c64_fp/WaveformCalculator.cpp
|
|||
src/engine/platform/sound/c64_fp/WaveformGenerator.cpp
|
||||
src/engine/platform/sound/c64_fp/resample/SincResampler.cpp
|
||||
|
||||
src/engine/platform/sound/c64_d/dsid.c
|
||||
|
||||
src/engine/platform/sound/tia/AudioChannel.cpp
|
||||
src/engine/platform/sound/tia/Audio.cpp
|
||||
|
||||
|
|
@ -860,7 +862,7 @@ endif()
|
|||
string(REPLACE ";" " " WARNING_FLAGS_STRING "${WARNING_FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WARNING_FLAGS_STRING}")
|
||||
if (WARNINGS_ARE_ERRORS)
|
||||
message(WARNING
|
||||
message(STATUS
|
||||
"Treating all warnings in furnace's C++ code as errors! "
|
||||
"Please report any errors you encounter on the bug tracker."
|
||||
)
|
||||
|
|
|
|||
|
|
@ -13,3 +13,5 @@ currently Furnace does not support the PCM channel's stereo mode, though (except
|
|||
- `2`: triangle
|
||||
- `3`: noise
|
||||
- `22xx`: **set duty cycle.** range is `0` to `3F`.
|
||||
- `EExx`: **ZSM synchronization event.**
|
||||
- Where `xx` is the event payload. This has no effect in how the music is played in Furnace, but the ZSMKit library for the Commander X16 interprets these events inside ZSM files and optionally triggers a callback routine. This can be used, for instance, to cause game code to respond to beats or at certain points in the music.
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ Any offset values contained in the PCM data header block are relative to the beg
|
|||
|
||||
### PCM Sample Data
|
||||
|
||||
This is blob of PCM data with no internal formatting. Offsets into this blob are provided via the PCM header. The end of this blob will be the end of the ZSM file.
|
||||
This is a blob of PCM data with no internal formatting. Offsets into this blob are provided via the PCM header. The end of this blob will be the end of the ZSM file.
|
||||
|
||||
## EXTCMD Channel Scifications
|
||||
|
||||
|
|
@ -149,7 +149,7 @@ Players implementing this channel should implement detection routines during ini
|
|||
|
||||
An expansion HW write will contain the following data:
|
||||
|
||||
Chip ID|Nuber of writes (`N`)| `N` tuples of data
|
||||
Chip ID|Number of writes (`N`)| `N` tuples of data
|
||||
--|--|--
|
||||
one byte|one byte|N * tuple_size bytes
|
||||
|
||||
|
|
@ -162,7 +162,14 @@ There are currently no supported expansion HW IDs assigned.
|
|||
|
||||
The purpose of this channel is to provide for music synchronization cues that applications may use to perform operations in sync with the music (such as when the Goombas jump in New Super Mario Bros in time with the BOP! BOP! notes in the music). It is intended for the reference player to provide a sync channel callback, passing the data bytes to the callback function, and then to proceed with playback.
|
||||
|
||||
The data structure within this channel is not yet defined. It is our intention to work with the community in order to collaborate on a useful structure.
|
||||
The synchronization format currently defines this one event type:
|
||||
|
||||
Event Type|Description|Message Format
|
||||
--|--|--
|
||||
`0x00`|Generic sync message|`xx` (any value from `0x00`-`0xff`)
|
||||
|
||||
An example of an EXTCMD containing one sync event might look as follows: `0x40 0x82 0x00 0x05`
|
||||
|
||||
|
||||
#### 3: Custom
|
||||
|
||||
|
|
|
|||
|
|
@ -236,6 +236,8 @@ enum DivDispatchCmds {
|
|||
|
||||
DIV_CMD_NES_LINEAR_LENGTH,
|
||||
|
||||
DIV_CMD_EXTERNAL, // (value)
|
||||
|
||||
DIV_ALWAYS_SET_VOLUME, // () -> alwaysSetVol
|
||||
|
||||
DIV_CMD_MAX
|
||||
|
|
|
|||
|
|
@ -172,6 +172,7 @@ void DivDispatchContainer::fillBuf(size_t runtotal, size_t offset, size_t size)
|
|||
if (bbIn[i]==NULL) continue;
|
||||
if (bb[i]==NULL) continue;
|
||||
for (size_t j=0; j<runtotal; j++) {
|
||||
if (bbIn[i][j]==temp[i]) continue;
|
||||
temp[i]=bbIn[i][j];
|
||||
blip_add_delta_fast(bb[i],j,temp[i]-prevSample[i]);
|
||||
prevSample[i]=temp[i];
|
||||
|
|
@ -182,6 +183,7 @@ void DivDispatchContainer::fillBuf(size_t runtotal, size_t offset, size_t size)
|
|||
if (bbIn[i]==NULL) continue;
|
||||
if (bb[i]==NULL) continue;
|
||||
for (size_t j=0; j<runtotal; j++) {
|
||||
if (bbIn[i][j]==temp[i]) continue;
|
||||
temp[i]=bbIn[i][j];
|
||||
blip_add_delta(bb[i],j,temp[i]-prevSample[i]);
|
||||
prevSample[i]=temp[i];
|
||||
|
|
@ -273,12 +275,12 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do
|
|||
break;
|
||||
case DIV_SYSTEM_C64_6581:
|
||||
dispatch=new DivPlatformC64;
|
||||
((DivPlatformC64*)dispatch)->setFP(eng->getConfInt("c64Core",1)==1);
|
||||
((DivPlatformC64*)dispatch)->setCore(eng->getConfInt("c64Core",0));
|
||||
((DivPlatformC64*)dispatch)->setChipModel(true);
|
||||
break;
|
||||
case DIV_SYSTEM_C64_8580:
|
||||
dispatch=new DivPlatformC64;
|
||||
((DivPlatformC64*)dispatch)->setFP(eng->getConfInt("c64Core",1)==1);
|
||||
((DivPlatformC64*)dispatch)->setCore(eng->getConfInt("c64Core",0));
|
||||
((DivPlatformC64*)dispatch)->setChipModel(false);
|
||||
break;
|
||||
case DIV_SYSTEM_YM2151:
|
||||
|
|
|
|||
|
|
@ -65,34 +65,45 @@ const char** DivPlatformC64::getRegisterSheet() {
|
|||
}
|
||||
|
||||
void DivPlatformC64::acquire(short** buf, size_t len) {
|
||||
int dcOff=isFP?0:sid.get_dc(0);
|
||||
int dcOff=(sidCore)?0:sid->get_dc(0);
|
||||
for (size_t i=0; i<len; i++) {
|
||||
if (!writes.empty()) {
|
||||
QueuedWrite w=writes.front();
|
||||
if (isFP) {
|
||||
sid_fp.write(w.addr,w.val);
|
||||
if (sidCore==2) {
|
||||
dSID_write(sid_d,w.addr,w.val);
|
||||
} else if (sidCore==1) {
|
||||
sid_fp->write(w.addr,w.val);
|
||||
} else {
|
||||
sid.write(w.addr,w.val);
|
||||
};
|
||||
sid->write(w.addr,w.val);
|
||||
}
|
||||
regPool[w.addr&0x1f]=w.val;
|
||||
writes.pop();
|
||||
}
|
||||
if (isFP) {
|
||||
sid_fp.clock(4,&buf[0][i]);
|
||||
if (sidCore==2) {
|
||||
double o=dSID_render(sid_d);
|
||||
buf[0][i]=32767*CLAMP(o,-1.0,1.0);
|
||||
if (++writeOscBuf>=4) {
|
||||
writeOscBuf=0;
|
||||
oscBuf[0]->data[oscBuf[0]->needle++]=(sid_fp.lastChanOut[0]-dcOff)>>5;
|
||||
oscBuf[1]->data[oscBuf[1]->needle++]=(sid_fp.lastChanOut[1]-dcOff)>>5;
|
||||
oscBuf[2]->data[oscBuf[2]->needle++]=(sid_fp.lastChanOut[2]-dcOff)>>5;
|
||||
oscBuf[0]->data[oscBuf[0]->needle++]=sid_d->lastOut[0];
|
||||
oscBuf[1]->data[oscBuf[1]->needle++]=sid_d->lastOut[1];
|
||||
oscBuf[2]->data[oscBuf[2]->needle++]=sid_d->lastOut[2];
|
||||
}
|
||||
} else if (sidCore==1) {
|
||||
sid_fp->clock(4,&buf[0][i]);
|
||||
if (++writeOscBuf>=4) {
|
||||
writeOscBuf=0;
|
||||
oscBuf[0]->data[oscBuf[0]->needle++]=(sid_fp->lastChanOut[0]-dcOff)>>5;
|
||||
oscBuf[1]->data[oscBuf[1]->needle++]=(sid_fp->lastChanOut[1]-dcOff)>>5;
|
||||
oscBuf[2]->data[oscBuf[2]->needle++]=(sid_fp->lastChanOut[2]-dcOff)>>5;
|
||||
}
|
||||
} else {
|
||||
sid.clock();
|
||||
buf[0][i]=sid.output();
|
||||
sid->clock();
|
||||
buf[0][i]=sid->output();
|
||||
if (++writeOscBuf>=16) {
|
||||
writeOscBuf=0;
|
||||
oscBuf[0]->data[oscBuf[0]->needle++]=(sid.last_chan_out[0]-dcOff)>>5;
|
||||
oscBuf[1]->data[oscBuf[1]->needle++]=(sid.last_chan_out[1]-dcOff)>>5;
|
||||
oscBuf[2]->data[oscBuf[2]->needle++]=(sid.last_chan_out[2]-dcOff)>>5;
|
||||
oscBuf[0]->data[oscBuf[0]->needle++]=(sid->last_chan_out[0]-dcOff)>>5;
|
||||
oscBuf[1]->data[oscBuf[1]->needle++]=(sid->last_chan_out[1]-dcOff)>>5;
|
||||
oscBuf[2]->data[oscBuf[2]->needle++]=(sid->last_chan_out[2]-dcOff)>>5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -366,7 +377,7 @@ int DivPlatformC64::dispatch(DivCommand c) {
|
|||
break;
|
||||
case DIV_CMD_C64_CUTOFF:
|
||||
if (c.value>100) c.value=100;
|
||||
filtCut=c.value*2047/100;
|
||||
filtCut=(c.value+2)*2047/102;
|
||||
updateFilter();
|
||||
break;
|
||||
case DIV_CMD_C64_FINE_CUTOFF:
|
||||
|
|
@ -452,10 +463,17 @@ int DivPlatformC64::dispatch(DivCommand c) {
|
|||
|
||||
void DivPlatformC64::muteChannel(int ch, bool mute) {
|
||||
isMuted[ch]=mute;
|
||||
if (isFP) {
|
||||
sid_fp.mute(ch,mute);
|
||||
if (sidCore==2) {
|
||||
dSID_setMuteMask(
|
||||
sid_d,
|
||||
(isMuted[0]?0:1)|
|
||||
(isMuted[1]?0:2)|
|
||||
(isMuted[2]?0:4)
|
||||
);
|
||||
} else if (sidCore==1) {
|
||||
sid_fp->mute(ch,mute);
|
||||
} else {
|
||||
sid.set_is_muted(ch,mute);
|
||||
sid->set_is_muted(ch,mute);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -514,7 +532,7 @@ bool DivPlatformC64::getWantPreNote() {
|
|||
}
|
||||
|
||||
float DivPlatformC64::getPostAmp() {
|
||||
return isFP?3.0f:1.0f;
|
||||
return (sidCore==1)?3.0f:1.0f;
|
||||
}
|
||||
|
||||
void DivPlatformC64::reset() {
|
||||
|
|
@ -524,11 +542,20 @@ void DivPlatformC64::reset() {
|
|||
chan[i].std.setEngine(parent);
|
||||
}
|
||||
|
||||
if (isFP) {
|
||||
sid_fp.reset();
|
||||
sid_fp.clockSilent(16000);
|
||||
if (sidCore==2) {
|
||||
dSID_init(sid_d,chipClock,rate,sidIs6581?6581:8580,needInitTables);
|
||||
dSID_setMuteMask(
|
||||
sid_d,
|
||||
(isMuted[0]?0:1)|
|
||||
(isMuted[1]?0:2)|
|
||||
(isMuted[2]?0:4)
|
||||
);
|
||||
needInitTables=false;
|
||||
} else if (sidCore==1) {
|
||||
sid_fp->reset();
|
||||
sid_fp->clockSilent(16000);
|
||||
} else {
|
||||
sid.reset();
|
||||
sid->reset();
|
||||
}
|
||||
memset(regPool,0,32);
|
||||
|
||||
|
|
@ -554,23 +581,11 @@ void DivPlatformC64::poke(std::vector<DivRegWrite>& wlist) {
|
|||
}
|
||||
|
||||
void DivPlatformC64::setChipModel(bool is6581) {
|
||||
if (is6581) {
|
||||
if (isFP) {
|
||||
sid_fp.setChipModel(reSIDfp::MOS6581);
|
||||
} else {
|
||||
sid.set_chip_model(MOS6581);
|
||||
}
|
||||
} else {
|
||||
if (isFP) {
|
||||
sid_fp.setChipModel(reSIDfp::MOS8580);
|
||||
} else {
|
||||
sid.set_chip_model(MOS8580);
|
||||
}
|
||||
}
|
||||
sidIs6581=is6581;
|
||||
}
|
||||
|
||||
void DivPlatformC64::setFP(bool fp) {
|
||||
isFP=fp;
|
||||
void DivPlatformC64::setCore(unsigned char which) {
|
||||
sidCore=which;
|
||||
}
|
||||
|
||||
void DivPlatformC64::setFlags(const DivConfig& flags) {
|
||||
|
|
@ -591,9 +606,9 @@ void DivPlatformC64::setFlags(const DivConfig& flags) {
|
|||
for (int i=0; i<3; i++) {
|
||||
oscBuf[i]->rate=rate/16;
|
||||
}
|
||||
if (isFP) {
|
||||
if (sidCore>0) {
|
||||
rate/=4;
|
||||
sid_fp.setSamplingParameters(chipClock,reSIDfp::DECIMATE,rate,0);
|
||||
if (sidCore==1) sid_fp->setSamplingParameters(chipClock,reSIDfp::DECIMATE,rate,0);
|
||||
}
|
||||
keyPriority=flags.getBool("keyPriority",true);
|
||||
testAD=((flags.getInt("testAttack",0)&15)<<4)|(flags.getInt("testDecay",0)&15);
|
||||
|
|
@ -604,11 +619,45 @@ int DivPlatformC64::init(DivEngine* p, int channels, int sugRate, const DivConfi
|
|||
parent=p;
|
||||
dumpWrites=false;
|
||||
skipRegisterWrites=false;
|
||||
needInitTables=true;
|
||||
writeOscBuf=0;
|
||||
for (int i=0; i<3; i++) {
|
||||
isMuted[i]=false;
|
||||
oscBuf[i]=new DivDispatchOscBuffer;
|
||||
}
|
||||
|
||||
if (sidCore==2) {
|
||||
sid=NULL;
|
||||
sid_fp=NULL;
|
||||
sid_d=new struct SID_chip;
|
||||
} else if (sidCore==1) {
|
||||
sid=NULL;
|
||||
sid_fp=new reSIDfp::SID;
|
||||
sid_d=NULL;
|
||||
} else {
|
||||
sid=new SID;
|
||||
sid_fp=NULL;
|
||||
sid_d=NULL;
|
||||
}
|
||||
|
||||
if (sidIs6581) {
|
||||
if (sidCore==2) {
|
||||
// do nothing
|
||||
} else if (sidCore==1) {
|
||||
sid_fp->setChipModel(reSIDfp::MOS6581);
|
||||
} else {
|
||||
sid->set_chip_model(MOS6581);
|
||||
}
|
||||
} else {
|
||||
if (sidCore==2) {
|
||||
// do nothing
|
||||
} else if (sidCore==1) {
|
||||
sid_fp->setChipModel(reSIDfp::MOS8580);
|
||||
} else {
|
||||
sid->set_chip_model(MOS8580);
|
||||
}
|
||||
}
|
||||
|
||||
setFlags(flags);
|
||||
|
||||
reset();
|
||||
|
|
@ -620,6 +669,9 @@ void DivPlatformC64::quit() {
|
|||
for (int i=0; i<3; i++) {
|
||||
delete oscBuf[i];
|
||||
}
|
||||
if (sid!=NULL) delete sid;
|
||||
if (sid_fp!=NULL) delete sid_fp;
|
||||
if (sid_d!=NULL) delete sid_d;
|
||||
}
|
||||
|
||||
DivPlatformC64::~DivPlatformC64() {
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
#include <queue>
|
||||
#include "sound/c64/sid.h"
|
||||
#include "sound/c64_fp/SID.h"
|
||||
#include "sound/c64_d/dsid.h"
|
||||
|
||||
class DivPlatformC64: public DivDispatch {
|
||||
struct Channel: public SharedChannel<signed char> {
|
||||
|
|
@ -64,15 +65,16 @@ class DivPlatformC64: public DivDispatch {
|
|||
|
||||
unsigned char filtControl, filtRes, vol;
|
||||
unsigned char writeOscBuf;
|
||||
unsigned char sidCore;
|
||||
int filtCut, resetTime;
|
||||
bool isFP;
|
||||
|
||||
bool keyPriority;
|
||||
bool keyPriority, sidIs6581, needInitTables;
|
||||
unsigned char chanOrder[3];
|
||||
unsigned char testAD, testSR;
|
||||
|
||||
SID sid;
|
||||
reSIDfp::SID sid_fp;
|
||||
SID* sid;
|
||||
reSIDfp::SID* sid_fp;
|
||||
struct SID_chip* sid_d;
|
||||
unsigned char regPool[32];
|
||||
|
||||
friend void putDispatchChip(void*,int);
|
||||
|
|
@ -105,7 +107,7 @@ class DivPlatformC64: public DivDispatch {
|
|||
const char** getRegisterSheet();
|
||||
int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags);
|
||||
void setChipModel(bool is6581);
|
||||
void setFP(bool fp);
|
||||
void setCore(unsigned char which);
|
||||
void quit();
|
||||
~DivPlatformC64();
|
||||
};
|
||||
|
|
|
|||
19
src/engine/platform/sound/c64_d/LICENSE
Normal file
19
src/engine/platform/sound/c64_d/LICENSE
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
Copyright (c) 2021 DefleMask Team
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
11
src/engine/platform/sound/c64_d/README.md
Normal file
11
src/engine/platform/sound/c64_d/README.md
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
dSID
|
||||
===
|
||||
|
||||
This is the SID core used in DefleMask.
|
||||
|
||||
The project started as a very careful port from jsSID, comparing the wave
|
||||
output from both, ensuring they were exactly the same.
|
||||
|
||||
## License
|
||||
|
||||
MIT License
|
||||
367
src/engine/platform/sound/c64_d/dsid.c
Normal file
367
src/engine/platform/sound/c64_d/dsid.c
Normal file
|
|
@ -0,0 +1,367 @@
|
|||
#include "dsid.h"
|
||||
#include <stdio.h>
|
||||
#include <math.h> // INFINITY
|
||||
#include <stdlib.h>
|
||||
#include <string.h> // memset, memcpy
|
||||
|
||||
#define SID_OUT_SCALE (0x10000 * 3 * 16)
|
||||
|
||||
// CONTROL
|
||||
#define GAT 0x01
|
||||
#define SYN 0x02
|
||||
#define RNG 0x04
|
||||
#define TST 0x08
|
||||
#define TRI 0x10
|
||||
#define SAW 0x20
|
||||
#define PUL 0x40
|
||||
#define NOI 0x80
|
||||
|
||||
#define _HZ 0x10
|
||||
#define DECSUS 0x40
|
||||
#define ATK 0x80
|
||||
|
||||
// filter mode (high)
|
||||
#define LP 0x10
|
||||
#define BP 0x20
|
||||
#define HP 0x40
|
||||
#define OFF3 0x80
|
||||
|
||||
#define waveforms_add_sample(_id,_s) \
|
||||
sid->lastOut[_id]=(_s);
|
||||
|
||||
const int Aexp[256] = {
|
||||
1, 30, 30, 30, 30, 30, 30, 16, 16, 16, 16, 16, 16, 16, 16, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
|
||||
8, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
||||
4, 4, 4, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
|
||||
|
||||
double cmbWF(int chn, int *wfa, int index, int differ6581, struct SID_globals *g) {
|
||||
if (differ6581 && g->model == 6581)
|
||||
index &= 0x7FF;
|
||||
|
||||
return wfa[index];
|
||||
}
|
||||
|
||||
void cCmbWF(int *wfa, double bitmul, double bstr, double trh) {
|
||||
for (int i = 0; i < 4096; i++) {
|
||||
wfa[i] = 0;
|
||||
for (int j = 0; j < 12; j++) {
|
||||
double blvl = 0;
|
||||
for (int k = 0; k < 12; k++) {
|
||||
blvl += (bitmul / pow(bstr, abs(k - j))) * (((i >> k) & 1) - 0.5);
|
||||
}
|
||||
wfa[i] += (blvl >= trh) ? pow(2, j) : 0;
|
||||
}
|
||||
wfa[i] *= 12;
|
||||
}
|
||||
}
|
||||
|
||||
void dSID_init(struct SID_chip* sid, double clockRate, double samplingRate, int model, unsigned char init_wf) {
|
||||
if (model == 6581) {
|
||||
sid->g.model = 6581;
|
||||
} else {
|
||||
sid->g.model = 8580;
|
||||
}
|
||||
|
||||
memset(sid->M,0,MemLen);
|
||||
memset(sid->SIDct, 0, sizeof(sid->SIDct));
|
||||
for (int i = 0; i < 3; i++) {
|
||||
for (int j = 0; j < 3; j++) {
|
||||
sid->SIDct[i].ch[j].Ast = _HZ;
|
||||
sid->SIDct[i].ch[j].nLFSR = 0x7FFFF8;
|
||||
sid->SIDct[i].ch[j].prevwfout = 0;
|
||||
}
|
||||
sid->SIDct[i].ch[0].FSW = 1;
|
||||
sid->SIDct[i].ch[1].FSW = 2;
|
||||
sid->SIDct[i].ch[2].FSW = 4;
|
||||
}
|
||||
|
||||
sid->g.ctfr = -2.0 * 3.14 * (12500.0 / 256.0) / samplingRate,
|
||||
sid->g.ctf_ratio_6581 = -2.0 * 3.14 * (samplingRate / 44100.0) * (20000.0 / 256.0) / samplingRate;
|
||||
sid->g.ckr = clockRate / samplingRate;
|
||||
|
||||
const double bAprd[16] = {9, 32 * 1, 63 * 1, 95 * 1, 149 * 1, 220 * 1,
|
||||
267 * 1, 313 * 1, 392 * 1, 977 * 1, 1954 * 1, 3126 * 1,
|
||||
3907 * 1, 11720 * 1, 19532 * 1, 31251 * 1};
|
||||
const int bAstp[16] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
|
||||
memcpy(&sid->g.Aprd, &bAprd, sizeof(bAprd));
|
||||
memcpy(&sid->g.Astp, &bAstp, sizeof(bAstp));
|
||||
if (init_wf) {
|
||||
cCmbWF(sid->g.trsaw, 0.8, 2.4, 0.64);
|
||||
cCmbWF(sid->g.pusaw, 1.4, 1.9, 0.68);
|
||||
cCmbWF(sid->g.Pulsetrsaw, 0.8, 2.5, 0.64);
|
||||
|
||||
for (int i = 0; i < 2048; i++) {
|
||||
double ctf = (double) i / 8.0 + 0.2;
|
||||
if (model == 8580) {
|
||||
ctf = 1 - exp(ctf * sid->g.ctfr);
|
||||
} else {
|
||||
if (ctf < 24) {
|
||||
ctf = 2.0 * sin(771.78 / samplingRate);
|
||||
} else {
|
||||
ctf = (44100.0 / samplingRate) - 1.263 * (44100.0 / samplingRate) * exp(ctf * sid->g.ctf_ratio_6581);
|
||||
}
|
||||
}
|
||||
sid->g.ctf_table[i] = ctf;
|
||||
}
|
||||
}
|
||||
|
||||
double prd0 = sid->g.ckr > 9 ? sid->g.ckr : 9;
|
||||
sid->g.Aprd[0] = prd0;
|
||||
sid->g.Astp[0] = ceil(prd0 / 9);
|
||||
}
|
||||
|
||||
double dSID_render(struct SID_chip* sid) {
|
||||
double flin = 0, output = 0;
|
||||
double wfout = 0;
|
||||
for (int chn = 0; chn < 3; chn++) {
|
||||
struct SIDVOICE *voic = &((struct SIDMEM *) (sid->M))->v[chn];
|
||||
double pgt = (sid->SIDct->ch[chn].Ast & GAT);
|
||||
uint8_t ctrl = voic->control;
|
||||
uint8_t wf = ctrl & 0xF0;
|
||||
uint8_t test = ctrl & TST;
|
||||
uint8_t SR = voic->susres;
|
||||
double tmp = 0;
|
||||
if (pgt != (ctrl & GAT)) {
|
||||
if (pgt) {
|
||||
sid->SIDct->ch[chn].Ast &= 0xFF - (GAT | ATK | DECSUS);
|
||||
} else {
|
||||
sid->SIDct->ch[chn].Ast = (GAT | ATK | DECSUS);
|
||||
if ((SR & 0xF) > (sid->SIDct->ch[chn].pSR & 0xF))
|
||||
tmp = 1;
|
||||
}
|
||||
}
|
||||
sid->SIDct->ch[chn].pSR = SR;
|
||||
sid->SIDct->ch[chn].rcnt += sid->g.ckr;
|
||||
if (sid->SIDct->ch[chn].rcnt >= 0x8000)
|
||||
sid->SIDct->ch[chn].rcnt -= 0x8000;
|
||||
|
||||
static double step;
|
||||
double prd;
|
||||
|
||||
if (sid->SIDct->ch[chn].Ast & ATK) {
|
||||
step = voic->attack;
|
||||
prd = sid->g.Aprd[(int) step];
|
||||
} else if (sid->SIDct->ch[chn].Ast & DECSUS) {
|
||||
step = voic->decay;
|
||||
prd = sid->g.Aprd[(int) step];
|
||||
} else {
|
||||
step = SR & 0xF;
|
||||
prd = sid->g.Aprd[(int) step];
|
||||
}
|
||||
step = sid->g.Astp[(int) step];
|
||||
if (sid->SIDct->ch[chn].rcnt >= prd && sid->SIDct->ch[chn].rcnt < prd + sid->g.ckr &&
|
||||
tmp == 0) {
|
||||
sid->SIDct->ch[chn].rcnt -= prd;
|
||||
if ((sid->SIDct->ch[chn].Ast & ATK) ||
|
||||
++sid->SIDct->ch[chn].expcnt == Aexp[(int) sid->SIDct->ch[chn].envcnt]) {
|
||||
if (!(sid->SIDct->ch[chn].Ast & _HZ)) {
|
||||
if (sid->SIDct->ch[chn].Ast & ATK) {
|
||||
sid->SIDct->ch[chn].envcnt += step;
|
||||
if (sid->SIDct->ch[chn].envcnt >= 0xFF) {
|
||||
sid->SIDct->ch[chn].envcnt = 0xFF;
|
||||
sid->SIDct->ch[chn].Ast &= 0xFF - ATK;
|
||||
}
|
||||
} else if (!(sid->SIDct->ch[chn].Ast & DECSUS) ||
|
||||
sid->SIDct->ch[chn].envcnt > (SR >> 4) + (SR & 0xF0)) {
|
||||
sid->SIDct->ch[chn].envcnt -= step;
|
||||
if (sid->SIDct->ch[chn].envcnt <= 0 &&
|
||||
sid->SIDct->ch[chn].envcnt + step != 0) {
|
||||
sid->SIDct->ch[chn].envcnt = 0;
|
||||
sid->SIDct->ch[chn].Ast |= _HZ;
|
||||
}
|
||||
}
|
||||
}
|
||||
sid->SIDct->ch[chn].expcnt = 0;
|
||||
} else {
|
||||
}
|
||||
}
|
||||
sid->SIDct->ch[chn].envcnt = (int) sid->SIDct->ch[chn].envcnt & 0xFF;
|
||||
double aAdd = (voic->freq_low + voic->freq_high * 256) * sid->g.ckr;
|
||||
if (test || ((ctrl & SYN) && sid->SIDct->sMSBrise)) {
|
||||
sid->SIDct->ch[chn].pacc = 0;
|
||||
} else {
|
||||
sid->SIDct->ch[chn].pacc += aAdd;
|
||||
if (sid->SIDct->ch[chn].pacc > 0xFFFFFF)
|
||||
sid->SIDct->ch[chn].pacc -= 0x1000000;
|
||||
}
|
||||
double MSB = (int) sid->SIDct->ch[chn].pacc & 0x800000;
|
||||
sid->SIDct->sMSBrise = (MSB > ((int) sid->SIDct->ch[chn].pracc & 0x800000)) ? 1 : 0;
|
||||
|
||||
if (wf & NOI) {
|
||||
tmp = sid->SIDct->ch[chn].nLFSR;
|
||||
if ((((int) sid->SIDct->ch[chn].pacc & 0x100000) !=
|
||||
((int) sid->SIDct->ch[chn].pracc & 0x100000)) ||
|
||||
aAdd >= 0x100000) {
|
||||
step = ((int) tmp & 0x400000) ^ (((int) tmp & 0x20000) << 5);
|
||||
tmp = (((int) tmp << 1) + (step > 0 || test)) & 0x7FFFFF;
|
||||
sid->SIDct->ch[chn].nLFSR = tmp;
|
||||
}
|
||||
wfout = (wf & 0x70) ? 0
|
||||
: (((int) tmp & 0x100000) >> 5) + (((int) tmp & 0x40000) >> 4) +
|
||||
(((int) tmp & 0x4000) >> 1) + (((int) tmp & 0x800) << 1) +
|
||||
(((int) tmp & 0x200) << 2) + (((int) tmp & 0x20) << 5) +
|
||||
(((int) tmp & 0x04) << 7) + (((int) tmp & 0x01) << 8);
|
||||
} else if (wf & PUL) {
|
||||
double pw = (voic->pw_low + (voic->pw_high) * 256) * 16;
|
||||
tmp = (int) aAdd >> 9;
|
||||
if (0 < pw && pw < tmp)
|
||||
pw = tmp;
|
||||
tmp = (int) tmp ^ 0xFFFF;
|
||||
if (pw > tmp)
|
||||
pw = tmp;
|
||||
tmp = (int) sid->SIDct->ch[chn].pacc >> 8;
|
||||
if (wf == PUL) {
|
||||
int lel = ((int) aAdd >> 16);
|
||||
if (lel > 0) {
|
||||
step = 256.0 / (double) lel;
|
||||
} else {
|
||||
step = INFINITY;
|
||||
}
|
||||
if (test)
|
||||
wfout = 0xFFFF;
|
||||
else if (tmp < pw) {
|
||||
double lim = (0xFFFF - pw) * step;
|
||||
if (lim > 0xFFFF)
|
||||
lim = 0xFFFF;
|
||||
wfout = lim - (pw - tmp) * step;
|
||||
if (wfout < 0)
|
||||
wfout = 0;
|
||||
} else {
|
||||
double lim = pw * step;
|
||||
if (lim > 0xFFFF)
|
||||
lim = 0xFFFF;
|
||||
wfout = (0xFFFF - tmp) * step - lim;
|
||||
if (wfout >= 0)
|
||||
wfout = 0xFFFF;
|
||||
wfout = (int) wfout & 0xFFFF;
|
||||
}
|
||||
} else {
|
||||
wfout = (tmp >= pw || test) ? 0xFFFF : 0;
|
||||
if (wf & TRI) {
|
||||
if (wf & SAW) {
|
||||
wfout =
|
||||
(wfout) ? cmbWF(chn, sid->g.Pulsetrsaw, (int) tmp >> 4, 1, &sid->g) : 0;
|
||||
} else {
|
||||
tmp = (int) sid->SIDct->ch[chn].pacc ^ (ctrl & RNG ? sid->SIDct->sMSB : 0);
|
||||
wfout =
|
||||
(wfout)
|
||||
? cmbWF(chn, sid->g.pusaw,
|
||||
((int) tmp ^ ((int) tmp & 0x800000 ? 0xFFFFFF : 0)) >> 11,
|
||||
0, &sid->g)
|
||||
: 0;
|
||||
}
|
||||
} else if (wf & SAW)
|
||||
wfout = (wfout) ? cmbWF(chn, sid->g.pusaw, (int) tmp >> 4, 1, &sid->g) : 0;
|
||||
}
|
||||
} else if (wf & SAW) {
|
||||
wfout = (int) sid->SIDct->ch[chn].pacc >> 8;
|
||||
if (wf & TRI)
|
||||
wfout = cmbWF(chn, sid->g.trsaw, (int) wfout >> 4, 1, &sid->g);
|
||||
else {
|
||||
step = aAdd / 0x1200000;
|
||||
wfout += wfout * step;
|
||||
if (wfout > 0xFFFF)
|
||||
wfout = 0xFFFF - (wfout - 0x10000) / step;
|
||||
}
|
||||
} else if (wf & TRI) {
|
||||
tmp = (int) sid->SIDct->ch[chn].pacc ^ (ctrl & RNG ? sid->SIDct->sMSB : 0);
|
||||
wfout = ((int) tmp ^ ((int) tmp & 0x800000 ? 0xFFFFFF : 0)) >> 7;
|
||||
}
|
||||
if (wf)
|
||||
sid->SIDct->ch[chn].prevwfout = wfout;
|
||||
else {
|
||||
wfout = sid->SIDct->ch[chn].prevwfout;
|
||||
}
|
||||
sid->SIDct->ch[chn].pracc = sid->SIDct->ch[chn].pacc;
|
||||
sid->SIDct->sMSB = MSB;
|
||||
// double preflin = flin;
|
||||
if ((sid->mute_mask & (1 << chn))) {
|
||||
if (sid->M[0x17] & sid->SIDct->ch[chn].FSW) {
|
||||
double chnout = (wfout - 0x8000) * (sid->SIDct->ch[chn].envcnt / 256);
|
||||
flin += chnout;
|
||||
|
||||
// fake filter for solo waveform ahead
|
||||
// mostly copypasted from below
|
||||
double fakeflin = chnout;
|
||||
double fakeflout = 0;
|
||||
static double fakeplp[3] = {0};
|
||||
static double fakepbp[3] = {0};
|
||||
double ctf = sid->g.ctf_table[((sid->M[0x15]&7)|(sid->M[0x16]<<3))&0x7ff];
|
||||
double reso;
|
||||
if (sid->g.model == 8580) {
|
||||
reso = pow(2, ((double) (4 - (double) (sid->M[0x17] >> 4)) / 8));
|
||||
} else {
|
||||
reso = (sid->M[0x17] > 0x5F) ? 8.0 / (double) (sid->M[0x17] >> 4) : 1.41;
|
||||
}
|
||||
double tmp = fakeflin + fakepbp[chn] * reso + fakeplp[chn];
|
||||
if (sid->M[0x18] & HP)
|
||||
fakeflout -= tmp;
|
||||
tmp = fakepbp[chn] - tmp * ctf;
|
||||
fakepbp[chn] = tmp;
|
||||
if (sid->M[0x18] & BP)
|
||||
fakeflout -= tmp;
|
||||
tmp = fakeplp[chn] + tmp * ctf;
|
||||
fakeplp[chn] = tmp;
|
||||
if (sid->M[0x18] & LP)
|
||||
fakeflout += tmp;
|
||||
|
||||
double wf_out = (fakeflout / SID_OUT_SCALE) * (sid->M[0x18] & 0xF) * 65535;
|
||||
waveforms_add_sample(chn, wf_out);
|
||||
} else if ((chn % 3) != 2 || !(sid->M[0x18] & OFF3)) {
|
||||
double chnout = (wfout - 0x8000) * (sid->SIDct->ch[chn].envcnt / 256);
|
||||
output += chnout;
|
||||
|
||||
double wf_out = (chnout / SID_OUT_SCALE) * (sid->M[0x18] & 0xF) * 65535;
|
||||
waveforms_add_sample(chn, wf_out);
|
||||
}
|
||||
} else {
|
||||
waveforms_add_sample(chn, 0);
|
||||
}
|
||||
}
|
||||
int M1 = 0;
|
||||
if (M1 & 3)
|
||||
sid->M[0x1B] = (int) wfout >> 8;
|
||||
sid->M[0x1C] = sid->SIDct->ch[2].envcnt;
|
||||
|
||||
double ctf = sid->g.ctf_table[((sid->M[0x15]&7)|(sid->M[0x16]<<3))&0x7ff];
|
||||
double reso;
|
||||
if (sid->g.model == 8580) {
|
||||
reso = pow(2, ((double) (4 - (double) (sid->M[0x17] >> 4)) / 8));
|
||||
} else {
|
||||
reso = (sid->M[0x17] > 0x5F) ? 8.0 / (double) (sid->M[0x17] >> 4) : 1.41;
|
||||
}
|
||||
|
||||
double tmp = flin + sid->SIDct->pbp * reso + sid->SIDct->plp;
|
||||
if (sid->M[0x18] & HP)
|
||||
output -= tmp;
|
||||
tmp = sid->SIDct->pbp - tmp * ctf;
|
||||
sid->SIDct->pbp = tmp;
|
||||
if (sid->M[0x18] & BP)
|
||||
output -= tmp;
|
||||
tmp = sid->SIDct->plp + tmp * ctf;
|
||||
sid->SIDct->plp = tmp;
|
||||
if (sid->M[0x18] & LP)
|
||||
output += tmp;
|
||||
return (output / SID_OUT_SCALE) * (sid->M[0x18] & 0xF);
|
||||
}
|
||||
|
||||
void dSID_setMuteMask(struct SID_chip* sid, int mute_mask) {
|
||||
sid->mute_mask = mute_mask;
|
||||
}
|
||||
|
||||
float dSID_getVolume(struct SID_chip* sid, int channel) {
|
||||
if ((sid->M[0x18] & 0xF) == 0)
|
||||
return 0;
|
||||
return sid->SIDct[0].ch[channel].envcnt / 256.0f;
|
||||
}
|
||||
|
||||
void dSID_write(struct SID_chip* sid, unsigned char addr, unsigned char val) {
|
||||
sid->M[addr&0x1f]=val;
|
||||
}
|
||||
97
src/engine/platform/sound/c64_d/dsid.h
Normal file
97
src/engine/platform/sound/c64_d/dsid.h
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
#ifndef DSID_H
|
||||
#define DSID_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
struct SID_ctx_chan {
|
||||
double rcnt;
|
||||
double envcnt;
|
||||
double expcnt;
|
||||
double pacc;
|
||||
double pracc;
|
||||
int FSW;
|
||||
int nLFSR;
|
||||
double prevwfout;
|
||||
uint8_t pSR;
|
||||
int Ast;
|
||||
};
|
||||
|
||||
struct SID_ctx {
|
||||
int sMSBrise;
|
||||
int sMSB;
|
||||
double plp;
|
||||
double pbp;
|
||||
struct SID_ctx_chan ch[3];
|
||||
};
|
||||
|
||||
struct SIDVOICE {
|
||||
uint8_t freq_low;
|
||||
uint8_t freq_high;
|
||||
uint8_t pw_low;
|
||||
uint8_t pw_high : 4;
|
||||
uint8_t UNUSED : 4;
|
||||
uint8_t control;
|
||||
uint8_t decay : 4;
|
||||
uint8_t attack : 4;
|
||||
uint8_t susres;
|
||||
// uint8_t release : 4;
|
||||
// uint8_t sustain : 4;
|
||||
};
|
||||
|
||||
struct SIDMEM {
|
||||
struct SIDVOICE v[3];
|
||||
uint8_t UNUSED : 4;
|
||||
uint8_t cutoff_low : 4;
|
||||
uint8_t cutoff_high;
|
||||
uint8_t reso_rt : 4;
|
||||
uint8_t reso : 4;
|
||||
uint8_t volume : 4;
|
||||
uint8_t filter_mode : 4;
|
||||
uint8_t paddlex;
|
||||
uint8_t paddley;
|
||||
uint8_t osc3;
|
||||
uint8_t env3;
|
||||
};
|
||||
|
||||
struct SID_globals {
|
||||
double ckr;
|
||||
double ctfr;
|
||||
double ctf_ratio_6581;
|
||||
|
||||
double ctf_table[2048];
|
||||
|
||||
int trsaw[4096];
|
||||
int pusaw[4096];
|
||||
int Pulsetrsaw[4096];
|
||||
|
||||
double Aprd[16];
|
||||
int Astp[16];
|
||||
int model;
|
||||
};
|
||||
|
||||
#define MemLen 65536
|
||||
|
||||
struct SID_chip {
|
||||
struct SID_globals g;
|
||||
struct SID_ctx SIDct[3];
|
||||
uint8_t M[MemLen];
|
||||
int16_t lastOut[3];
|
||||
int mute_mask;
|
||||
};
|
||||
|
||||
double dSID_render(struct SID_chip* sid);
|
||||
void dSID_init(struct SID_chip* sid, double clockRate, double samplingRate, int model, unsigned char init_wf);
|
||||
float dSID_getVolume(struct SID_chip* sid, int channel);
|
||||
void dSID_setMuteMask(struct SID_chip* sid, int mute_mask);
|
||||
|
||||
void dSID_write(struct SID_chip* sid, unsigned char addr, unsigned char val);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
@ -37,6 +37,7 @@ extern "C" {
|
|||
#define rWritePCMRate(d) {regPool[65]=(d); pcm_write_rate(pcm,d);if (dumpWrites) addWrite(65,(d));}
|
||||
#define rWritePCMData(d) {regPool[66]=(d); pcm_write_fifo(pcm,d);}
|
||||
#define rWritePCMVol(d) rWritePCMCtrl((regPool[64]&(~0x8f))|((d)&15))
|
||||
#define rWriteZSMSync(d) {if (dumpWrites) addWrite(68,(d));}
|
||||
|
||||
const char* regCheatSheetVERA[]={
|
||||
"CHxFreq", "00+x*4",
|
||||
|
|
@ -46,6 +47,7 @@ const char* regCheatSheetVERA[]={
|
|||
"AUDIO_CTRL", "40",
|
||||
"AUDIO_RATE", "41",
|
||||
"AUDIO_DATA", "42",
|
||||
"ZSM_SYNC", "44",
|
||||
|
||||
NULL
|
||||
};
|
||||
|
|
@ -414,6 +416,9 @@ int DivPlatformVERA::dispatch(DivCommand c) {
|
|||
case DIV_CMD_MACRO_ON:
|
||||
chan[c.chan].std.mask(c.value,false);
|
||||
break;
|
||||
case DIV_CMD_EXTERNAL:
|
||||
rWriteZSMSync(c.value);
|
||||
break;
|
||||
case DIV_ALWAYS_SET_VOLUME:
|
||||
return 0;
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ class DivPlatformVERA: public DivDispatch {
|
|||
Channel chan[17];
|
||||
DivDispatchOscBuffer* oscBuf[17];
|
||||
bool isMuted[17];
|
||||
unsigned char regPool[67];
|
||||
unsigned char regPool[69];
|
||||
struct VERA_PSG* psg;
|
||||
struct VERA_PCM* pcm;
|
||||
|
||||
|
|
|
|||
|
|
@ -236,6 +236,8 @@ const char* cmdName[]={
|
|||
|
||||
"NES_LINEAR_LENGTH",
|
||||
|
||||
"EXTERNAL",
|
||||
|
||||
"ALWAYS_SET_VOLUME"
|
||||
};
|
||||
|
||||
|
|
@ -913,6 +915,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
|
|||
//printf("\x1b[1;36m%d: extern command %d\x1b[m\n",i,effectVal);
|
||||
extValue=effectVal;
|
||||
extValuePresent=true;
|
||||
dispatchCmd(DivCommand(DIV_CMD_EXTERNAL,effectVal));
|
||||
break;
|
||||
case 0xef: // global pitch
|
||||
globalPitch+=(signed char)(effectVal-0x80);
|
||||
|
|
@ -1980,14 +1983,10 @@ void DivEngine::nextBuf(float** in, float** out, int inChans, int outChans, unsi
|
|||
} else {
|
||||
// 3. run MIDI clock
|
||||
int midiTotal=MIN(cycles,runLeftG);
|
||||
for (int i=0; i<midiTotal; i++) {
|
||||
runMidiClock();
|
||||
}
|
||||
runMidiClock(midiTotal);
|
||||
|
||||
// 4. run MIDI timecode
|
||||
for (int i=0; i<midiTotal; i++) {
|
||||
runMidiTime();
|
||||
}
|
||||
runMidiTime(midiTotal);
|
||||
|
||||
// 5. tick the clock and fill buffers as needed
|
||||
if (cycles<runLeftG) {
|
||||
|
|
|
|||
|
|
@ -118,9 +118,13 @@ void DivZSM::writePSG(unsigned char a, unsigned char v) {
|
|||
// TODO: suppress writes to PSG voice that is not audible (volume=0)
|
||||
// ^ Let's leave these alone, ZSMKit has a feature that can benefit
|
||||
// from silent channels.
|
||||
if (a>=67) {
|
||||
if (a>=69) {
|
||||
logD("ZSM: ignoring VERA PSG write a=%02x v=%02x",a,v);
|
||||
return;
|
||||
} else if (a==68) {
|
||||
// Sync event
|
||||
numWrites++;
|
||||
return syncCache.push_back(v);
|
||||
} else if (a>=64) {
|
||||
return writePCM(a-64,v);
|
||||
}
|
||||
|
|
@ -259,7 +263,7 @@ SafeWriter* DivZSM::finish() {
|
|||
}
|
||||
|
||||
void DivZSM::flushWrites() {
|
||||
logD("ZSM: flushWrites.... numwrites=%d ticks=%d ymwrites=%d pcmMeta=%d pcmCache=%d pcmData=%d",numWrites,ticks,ymwrites.size(),pcmMeta.size(),pcmCache.size(),pcmData.size());
|
||||
logD("ZSM: flushWrites.... numwrites=%d ticks=%d ymwrites=%d pcmMeta=%d pcmCache=%d pcmData=%d syncCache=%d",numWrites,ticks,ymwrites.size(),pcmMeta.size(),pcmCache.size(),pcmData.size(),syncCache.size());
|
||||
if (numWrites==0) return;
|
||||
flushTicks(); // only flush ticks if there are writes pending.
|
||||
for (unsigned char i=0; i<64; i++) {
|
||||
|
|
@ -287,7 +291,7 @@ void DivZSM::flushWrites() {
|
|||
unsigned int pcmInst=0;
|
||||
int pcmOff=0;
|
||||
int pcmLen=0;
|
||||
int extCmdLen=pcmMeta.size()*2;
|
||||
int extCmd0Len=pcmMeta.size()*2;
|
||||
if (pcmCache.size()) {
|
||||
// collapse stereo data to mono if both channels are fully identical
|
||||
// which cuts PCM data size in half for center-panned PCM events
|
||||
|
|
@ -307,7 +311,7 @@ void DivZSM::flushWrites() {
|
|||
pcmCache[e+1]=pcmCache[(e<<1)+1];
|
||||
}
|
||||
pcmCache.resize(pcmCache.size()>>1);
|
||||
pcmCtrlDCCache &= ~0x10; // clear stereo bit
|
||||
pcmCtrlDCCache&=(unsigned char)~0x10; // clear stereo bit
|
||||
}
|
||||
}
|
||||
} else { // 8-bit
|
||||
|
|
@ -323,7 +327,7 @@ void DivZSM::flushWrites() {
|
|||
pcmCache[e]=pcmCache[e<<1];
|
||||
}
|
||||
pcmCache.resize(pcmCache.size()>>1);
|
||||
pcmCtrlDCCache &= ~0x10; // clear stereo bit
|
||||
pcmCtrlDCCache&=(unsigned char)~0x10; // clear stereo bit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -340,7 +344,7 @@ void DivZSM::flushWrites() {
|
|||
pcmData.insert(pcmData.end(),pcmCache.begin(),pcmCache.end());
|
||||
}
|
||||
pcmCache.clear();
|
||||
extCmdLen+=2;
|
||||
extCmd0Len+=2;
|
||||
// search for a matching PCM instrument definition
|
||||
for (S_pcmInst& inst: pcmInsts) {
|
||||
if (inst.offset==pcmOff && inst.length==pcmLen && inst.geometry==pcmCtrlDCCache)
|
||||
|
|
@ -355,14 +359,14 @@ void DivZSM::flushWrites() {
|
|||
pcmInsts.push_back(inst);
|
||||
}
|
||||
}
|
||||
if (extCmdLen>63) { // this would be bad, but will almost certainly never happen
|
||||
logE("ZSM: extCmd exceeded maximum length of 63: %d",extCmdLen);
|
||||
extCmdLen=0;
|
||||
if (extCmd0Len>63) { // this would be bad, but will almost certainly never happen
|
||||
logE("ZSM: extCmd 0 exceeded maximum length of 63: %d",extCmd0Len);
|
||||
extCmd0Len=0;
|
||||
pcmMeta.clear();
|
||||
}
|
||||
if (extCmdLen) { // we have some PCM events to write
|
||||
w->writeC(0x40);
|
||||
w->writeC((unsigned char)extCmdLen); // the high two bits are guaranteed to be zero, meaning this is a PCM command
|
||||
if (extCmd0Len) { // we have some PCM events to write
|
||||
w->writeC(ZSM_EXT);
|
||||
w->writeC(ZSM_EXT_PCM|(unsigned char)extCmd0Len);
|
||||
for (DivRegWrite& write: pcmMeta) {
|
||||
w->writeC(write.addr);
|
||||
w->writeC(write.val);
|
||||
|
|
@ -373,6 +377,18 @@ void DivZSM::flushWrites() {
|
|||
w->writeC((unsigned char)pcmInst&0xff);
|
||||
}
|
||||
}
|
||||
n=0;
|
||||
while (n<(long)syncCache.size()) { // we have one or more sync events to write
|
||||
int writes=syncCache.size()-n;
|
||||
w->writeC(ZSM_EXT);
|
||||
if (writes>ZSM_SYNC_MAX_WRITES) writes=ZSM_SYNC_MAX_WRITES;
|
||||
w->writeC(ZSM_EXT_SYNC|(writes<<1));
|
||||
for (; writes>0; writes--) {
|
||||
w->writeC(0x00); // 0x00 = Arbitrary sync message
|
||||
w->writeC(syncCache[n++]);
|
||||
}
|
||||
}
|
||||
syncCache.clear();
|
||||
numWrites=0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -30,9 +30,16 @@
|
|||
#define ZSM_YM_CMD 0x40
|
||||
#define ZSM_DELAY_CMD 0x80
|
||||
#define ZSM_YM_MAX_WRITES 63
|
||||
#define ZSM_SYNC_MAX_WRITES 31
|
||||
#define ZSM_DELAY_MAX 127
|
||||
#define ZSM_EOF ZSM_DELAY_CMD
|
||||
|
||||
#define ZSM_EXT ZSM_YM_CMD
|
||||
#define ZSM_EXT_PCM 0x00
|
||||
#define ZSM_EXT_CHIP 0x40
|
||||
#define ZSM_EXT_SYNC 0x80
|
||||
#define ZSM_EXT_CUSTOM 0xC0
|
||||
|
||||
enum YM_STATE { ym_PREV, ym_NEW, ym_STATES };
|
||||
enum PSG_STATE { psg_PREV, psg_NEW, psg_STATES };
|
||||
|
||||
|
|
@ -52,6 +59,7 @@ class DivZSM {
|
|||
std::vector<unsigned char> pcmData;
|
||||
std::vector<unsigned char> pcmCache;
|
||||
std::vector<S_pcmInst> pcmInsts;
|
||||
std::vector<unsigned char> syncCache;
|
||||
int loopOffset;
|
||||
int numWrites;
|
||||
int ticks;
|
||||
|
|
|
|||
|
|
@ -151,7 +151,7 @@ SafeWriter* DivEngine::saveZSM(unsigned int zsmrate, bool loop) {
|
|||
for (DivRegWrite& write: writes) {
|
||||
if (i==YM) zsm.writeYM(write.addr&0xff,write.val);
|
||||
if (i==VERA) {
|
||||
if (done && write.addr >= 64) continue; // don't process any PCM events on the loop lookahead
|
||||
if (done && write.addr>=64) continue; // don't process any PCM or sync events on the loop lookahead
|
||||
zsm.writePSG(write.addr&0xff,write.val);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -173,6 +173,7 @@ const char* aboutLine[]={
|
|||
"reSID by Dag Lem",
|
||||
"reSIDfp by Dag Lem, Antti Lankila",
|
||||
"and Leandro Nini",
|
||||
"dSID by DefleMask Team based on jsSID",
|
||||
"Stella by Stella Team",
|
||||
"QSound emulator by superctr and Valley Bell",
|
||||
"VICE VIC-20 sound core by Rami Rasanen and viznut",
|
||||
|
|
|
|||
|
|
@ -356,7 +356,6 @@ void putDispatchChip(void* data, int type) {
|
|||
ImGui::Text("- filtCut: %d",ch->filtCut);
|
||||
ImGui::Text("- resetTime: %d",ch->resetTime);
|
||||
COMMON_CHIP_DEBUG_BOOL;
|
||||
ImGui::TextColored(ch->isFP?colorOn:colorOff,">> IsFP");
|
||||
break;
|
||||
}
|
||||
case DIV_SYSTEM_ARCADE:
|
||||
|
|
|
|||
|
|
@ -6347,8 +6347,8 @@ bool FurnaceGUI::init() {
|
|||
|
||||
logD("starting render backend...");
|
||||
if (!rend->init(sdlWin)) {
|
||||
if (settings.renderBackend!="SDL" && !settings.renderBackend.empty()) {
|
||||
settings.renderBackend="";
|
||||
if (settings.renderBackend!="SDL") {
|
||||
settings.renderBackend="SDL";
|
||||
//e->setConf("renderBackend","");
|
||||
//e->saveConf();
|
||||
//lastError=fmt::sprintf("\r\nthe render backend has been set to a safe value. please restart Furnace.");
|
||||
|
|
|
|||
|
|
@ -75,16 +75,20 @@ enum FurnaceGUIRenderBackend {
|
|||
GUI_BACKEND_DX11
|
||||
};
|
||||
|
||||
#ifdef HAVE_RENDER_SDL
|
||||
#define GUI_BACKEND_DEFAULT GUI_BACKEND_SDL
|
||||
#define GUI_BACKEND_DEFAULT_NAME "SDL"
|
||||
#else
|
||||
#ifdef HAVE_RENDER_DX11
|
||||
#define GUI_BACKEND_DEFAULT GUI_BACKEND_DX11
|
||||
#define GUI_BACKEND_DEFAULT_NAME "DirectX 11"
|
||||
#else
|
||||
#ifdef HAVE_RENDER_GL
|
||||
#define GUI_BACKEND_DEFAULT GUI_BACKEND_GL
|
||||
#define GUI_BACKEND_DEFAULT_NAME "OpenGL"
|
||||
#else
|
||||
#ifdef HAVE_RENDER_SDL
|
||||
#define GUI_BACKEND_DEFAULT GUI_BACKEND_SDL
|
||||
#define GUI_BACKEND_DEFAULT_NAME "SDL"
|
||||
#else
|
||||
#error how did you manage to do that?
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
|
@ -1544,7 +1548,7 @@ class FurnaceGUI {
|
|||
snCore(0),
|
||||
nesCore(0),
|
||||
fdsCore(0),
|
||||
c64Core(1),
|
||||
c64Core(0),
|
||||
pokeyCore(1),
|
||||
opnCore(1),
|
||||
pcSpeakerOutMethod(0),
|
||||
|
|
|
|||
|
|
@ -116,7 +116,8 @@ const char* nesCores[]={
|
|||
|
||||
const char* c64Cores[]={
|
||||
"reSID",
|
||||
"reSIDfp"
|
||||
"reSIDfp",
|
||||
"dSID"
|
||||
};
|
||||
|
||||
const char* pokeyCores[]={
|
||||
|
|
@ -1254,7 +1255,7 @@ void FurnaceGUI::drawSettings() {
|
|||
|
||||
ImGui::Text("SID core");
|
||||
ImGui::SameLine();
|
||||
ImGui::Combo("##C64Core",&settings.c64Core,c64Cores,2);
|
||||
ImGui::Combo("##C64Core",&settings.c64Core,c64Cores,3);
|
||||
|
||||
ImGui::Text("POKEY core");
|
||||
ImGui::SameLine();
|
||||
|
|
@ -2645,7 +2646,7 @@ void FurnaceGUI::syncSettings() {
|
|||
settings.snCore=e->getConfInt("snCore",0);
|
||||
settings.nesCore=e->getConfInt("nesCore",0);
|
||||
settings.fdsCore=e->getConfInt("fdsCore",0);
|
||||
settings.c64Core=e->getConfInt("c64Core",1);
|
||||
settings.c64Core=e->getConfInt("c64Core",0);
|
||||
settings.pokeyCore=e->getConfInt("pokeyCore",1);
|
||||
settings.opnCore=e->getConfInt("opnCore",1);
|
||||
settings.pcSpeakerOutMethod=e->getConfInt("pcSpeakerOutMethod",0);
|
||||
|
|
@ -2771,7 +2772,7 @@ void FurnaceGUI::syncSettings() {
|
|||
settings.orderButtonPos=e->getConfInt("orderButtonPos",2);
|
||||
settings.compress=e->getConfInt("compress",1);
|
||||
settings.newPatternFormat=e->getConfInt("newPatternFormat",1);
|
||||
settings.renderBackend=e->getConfString("renderBackend","SDL");
|
||||
settings.renderBackend=e->getConfString("renderBackend",GUI_BACKEND_DEFAULT_NAME);
|
||||
settings.renderClearPos=e->getConfInt("renderClearPos",0);
|
||||
settings.insertBehavior=e->getConfInt("insertBehavior",1);
|
||||
settings.pullDeleteRow=e->getConfInt("pullDeleteRow",1);
|
||||
|
|
@ -2791,7 +2792,7 @@ void FurnaceGUI::syncSettings() {
|
|||
clampSetting(settings.snCore,0,1);
|
||||
clampSetting(settings.nesCore,0,1);
|
||||
clampSetting(settings.fdsCore,0,1);
|
||||
clampSetting(settings.c64Core,0,1);
|
||||
clampSetting(settings.c64Core,0,2);
|
||||
clampSetting(settings.pokeyCore,0,1);
|
||||
clampSetting(settings.opnCore,0,1);
|
||||
clampSetting(settings.pcSpeakerOutMethod,0,4);
|
||||
|
|
@ -2971,7 +2972,7 @@ void FurnaceGUI::commitSettings() {
|
|||
settings.snCore!=e->getConfInt("snCore",0) ||
|
||||
settings.nesCore!=e->getConfInt("nesCore",0) ||
|
||||
settings.fdsCore!=e->getConfInt("fdsCore",0) ||
|
||||
settings.c64Core!=e->getConfInt("c64Core",1) ||
|
||||
settings.c64Core!=e->getConfInt("c64Core",0) ||
|
||||
settings.pokeyCore!=e->getConfInt("pokeyCore",1) ||
|
||||
settings.opnCore!=e->getConfInt("opnCore",1)
|
||||
);
|
||||
|
|
|
|||
|
|
@ -198,6 +198,7 @@ TAParamResult pVersion(String) {
|
|||
printf("- NSFPlay by Brad Smith and Brezza (unknown open-source license)\n");
|
||||
printf("- reSID by Dag Lem (GPLv2)\n");
|
||||
printf("- reSIDfp by Dag Lem, Antti Lankila and Leandro Nini (GPLv2)\n");
|
||||
printf("- dSID by DefleMask Team (based on jsSID by Hermit) (MIT)\n");
|
||||
printf("- Stella by Stella Team (GPLv2)\n");
|
||||
printf("- vgsound_emu (second version, modified version) by cam900 (zlib license)\n");
|
||||
printf("- MAME GA20 core by Acho A. Tang, R. Belmont, Valley Bell (BSD 3-clause)\n");
|
||||
|
|
|
|||
Loading…
Reference in a new issue