Merge branch 'master' into preset1

This commit is contained in:
cam900 2022-06-06 19:09:12 +09:00
commit 35caab6f82
119 changed files with 1593 additions and 371 deletions

View File

@ -494,6 +494,7 @@ src/gui/doAction.cpp
src/gui/editing.cpp
src/gui/editControls.cpp
src/gui/effectList.cpp
src/gui/findReplace.cpp
src/gui/insEdit.cpp
src/gui/log.cpp
src/gui/mixer.cpp

19
TODO.md
View File

@ -1,19 +1,12 @@
# to-do for 0.6pre1
- additional YM2612 features
- CSM
- MSM6258 pitch and clock select
- the last compat flags
- newSegaPCM
- newVolumeScaling
- collapse/expand pattern and song
- Game Boy envelope macro/sequence
- rewrite the system name detection function anyway
- volume commands should work on Game Boy
- add another FM editor layout
- if macros have release, note off should release them
- add ability to move selection by dragging
- Apply button in settings
- find and replace
- add mono/poly note preview button
- (maybe) add default patch selection
- implement Defle slide bug when using E1xy/E2xy and repeating origin note (requires format change)
# to-do for 0.6pre2 (as this requires new data structures)
- Game Boy envelope macro/sequence
- volume commands should work on Game Boy

BIN
demos/beeper_torture.fur Normal file

Binary file not shown.

View File

@ -1,12 +1,12 @@
# Namco C163
This is Namco's one of NES mapper, with up to 8 wavetable channels. It has also 128 byte of internal RAM, both channel register and wavetables are stored here. Wavetables are variable size and freely allocable anywhere in RAM, it means it can be uses part of or continuously pre-loaded waveform and/or its sequences in RAM. But waveform RAM area becomes smaller as much as activating more channels; Channel register consumes 8 byte for each channels. You must avoid conflict with channel register area and waveform for avoid channel playback broken.
This is one of Namco's NES mappers, with up to 8 wavetable channels. It has also 128 byte of internal RAM, and both channel register and wavetables are stored here. Wavetables are variable size and freely allocable anywhere in RAM, it means it can use part of or continuously pre-loaded waveform and its sequences in RAM. But waveform RAM area becomes smaller as more channels are activated; as channel registers consumes 8 bytes for each channel. You must avoid conflict with channel register area and waveform for avoid broken channel playback.
It has can be outputs only single channel at clock; so it's sound quality is more crunchy as much as activating more channels.
It outputs only a single channel at clock; so its sound quality gets more crunchy as more channels are activated.
Furnace supports both load waveform into RAM and waveform playback simultaneously, and channel limit is dynamically changeable with effect commands.
You must load waveform to RAM first for playback or do something, its load behavior is changeable to auto-update when every waveform changes or manual update.
Both waveform playback and load command is works independently per each channel columns, (Global) commands are don't care about the channel columns for work commands and its load behavior is independent with per-channel column load commands.
Furnace supports loading waveforms into RAM and waveform playback simultaneously, and channel limit is dynamically changeable with effect commands.
You must load waveform to RAM first for playback, as its load behavior auto-updates when every waveform changes.
Both waveform playback and load command works independently per each channel columns, (Global) commands don't care about the channel columns for work commands and its load behavior is independent with per-channel column load commands.
# effects

View File

@ -2,6 +2,30 @@
40 years of one square beep - and still going! Single channel, no volume control...
# real output
so far this is the only system in Furnace which has a real hardware output option.
to enable it, select file > configure system... > PC Speaker > Use system beeper.
be noted that this will only work on Linux as Windows does not provide any user-space APIs to address the PC speaker directly!
you may configure the output method by going in Settings > Emulation > PC Speaker strategy:
- `evdev SND_TONE`: uses input events to control the beeper.
- requires write permission to `/dev/input/by-path/platform-pcspkr-event-spkr`.
- is not 100% frequency-accurate as `SND_TONE` demands frequencies, but Furnace uses raw timer periods...
- `KIOCSOUND on /dev/tty1`: sends the `KIOCSOUND` ioctl to control the beeper.
- may require running Furnace as root.
- `/dev/port`: writes to `/dev/port` to control the beeper.
- requires read/write permission to `/dev/port`.
- `KIOCSOUND on standard output`: sends the `KIOCSOUND` ioctl to control the beeper.
- requires running Furnace on a TTY.
- `outb()`: uses the low-level kernel port API to control the beeper.
- requires running Furnace as root, or granting it `CAP_SYS_RAWIO` to the Furnace executable: `sudo setcap cap_sys_rawio=ep ./furnace`.
real hardware output only works on BIOS/UEFI (non-Mac) x86-based machines! attempting to do this under any other device **will not work**, or may even brick the device (if using `/dev/port` or `outb()`)!
oh, and of course you also need the beeper to be present in your machine. some laptops connect the beeper output to the built-in speakers (or the audio output jack), and some other don't do this at all.
# effects
ha! effects...

View File

@ -19,6 +19,7 @@
#include <string.h>
#include "jack.h"
#include "../ta-log.h"
int taJACKonSampleRate(jack_nframes_t rate, void* inst) {
TAAudioJACK* in=(TAAudioJACK*)inst;
@ -74,7 +75,10 @@ bool TAAudioJACK::quit() {
if (running) {
running=false;
if (jack_deactivate(ac)) return false;
if (jack_deactivate(ac)) {
logE("could not deactivate!");
return false;
}
}
for (int i=0; i<desc.inChans; i++) {
@ -109,28 +113,91 @@ bool TAAudioJACK::setRun(bool run) {
return running;
}
if (run) {
if (jack_activate(ac)) return false;
if (jack_activate(ac)) {
logE("could not activate!");
return false;
}
for (int i=0; i<desc.outChans; i++) {
jack_connect(ac,(desc.name+String(":out")+std::to_string(i)).c_str(),(String("system:playback_")+std::to_string(i+1)).c_str());
}
running=true;
} else {
if (jack_deactivate(ac)) return true;
if (jack_deactivate(ac)) {
logE("could not deactivate!");
return true;
}
running=false;
}
return running;
}
String TAAudioJACK::printStatus(jack_status_t status) {
String ret;
if (status&JackFailure) {
ret+="failure. ";
}
if (status&JackInvalidOption) {
ret+="invalid option ";
}
if (status&JackNameNotUnique) {
ret+="name not unique ";
}
if (status&JackServerStarted) {
ret+="server started ";
}
if (status&JackServerFailed) {
ret+="server failed ";
}
if (status&JackServerError) {
ret+="server error ";
}
if (status&JackNoSuchClient) {
ret+="no such client ";
}
if (status&JackLoadFailure) {
ret+="load failure ";
}
if (status&JackInitFailure) {
ret+="init failure ";
}
if (status&JackShmFailure) {
ret+="shared memory failure ";
}
if (status&JackVersionError) {
ret+="version error ";
}
if (status&JackBackendError) {
ret+="backend error ";
}
if (status&JackClientZombie) {
ret+="client is zombie ";
}
if (ret.empty()) {
ret="no status";
} else {
if (ret[ret.size()-1]==' ') {
ret.resize(ret.size()-1);
}
}
return ret;
}
bool TAAudioJACK::init(TAAudioDesc& request, TAAudioDesc& response) {
if (initialized) return false;
if (jack_client_open==NULL) return false;
if (jack_client_open==NULL) {
logE("JACK not installed!");
return false;
}
desc=request;
desc.outFormat=TA_AUDIO_FORMAT_F32;
jack_status_t as;
ac=jack_client_open(desc.name.c_str(),JackNoStartServer,&as);
if (ac==NULL) return false;
if (ac==NULL) {
logE("error while opening client! (%s)",printStatus(as));
return false;
}
desc.name=String(jack_get_client_name(ac));
jack_set_sample_rate_callback(ac,taJACKonSampleRate,this);

View File

@ -29,6 +29,8 @@ class TAAudioJACK: public TAAudio {
float** iInBufs;
float** iOutBufs;
String printStatus(jack_status_t status);
public:
void onSampleRate(jack_nframes_t rate);
void onBufferSize(jack_nframes_t bufsize);

View File

@ -524,6 +524,11 @@ class DivDispatch {
// this is a special case definition. only use it for f-num/block-based chips.
#define NOTE_FNUM_BLOCK(x,bits) parent->calcBaseFreqFNumBlock(chipClock,CHIP_FREQBASE,x,bits)
// this is for volume scaling calculation.
#define VOL_SCALE_LINEAR_BROKEN(x,y,range) ((parent->song.newVolumeScaling)?(((x)*(y))/(range)):(CLAMP(((x)+(y))-(range),0,(range))))
#define VOL_SCALE_LINEAR(x,y,range) (((x)*(y))/(range))
#define VOL_SCALE_LOG(x,y,range) ((parent->song.newVolumeScaling)?(CLAMP(((x)+(y))-(range),0,(range))):(((x)*(y))/(range)))
// these are here for convenience.
// it is encouraged to use these, since you get an exact value this way.
// - NTSC colorburst: 3.58MHz

View File

@ -193,6 +193,9 @@ bool DivEngine::isExporting() {
#ifdef HAVE_SNDFILE
void DivEngine::runExportThread() {
size_t fadeOutSamples=got.rate*exportFadeOut;
size_t curFadeOutSample=0;
bool isFadingOut=false;
switch (exportMode) {
case DIV_EXPORT_MODE_ONE: {
SNDFILE* sf;
@ -220,15 +223,33 @@ void DivEngine::runExportThread() {
logI("rendering to file...");
while (playing) {
size_t total=0;
nextBuf(NULL,outBuf,0,2,EXPORT_BUFSIZE);
for (int i=0; i<EXPORT_BUFSIZE; i++) {
outBuf[2][i<<1]=MAX(-1.0f,MIN(1.0f,outBuf[0][i]));
outBuf[2][1+(i<<1)]=MAX(-1.0f,MIN(1.0f,outBuf[1][i]));
}
if (totalProcessed>EXPORT_BUFSIZE) {
logE("error: total processed is bigger than export bufsize! %d>%d",totalProcessed,EXPORT_BUFSIZE);
totalProcessed=EXPORT_BUFSIZE;
}
if (sf_writef_float(sf,outBuf[2],totalProcessed)!=(int)totalProcessed) {
for (int i=0; i<(int)totalProcessed; i++) {
total++;
if (isFadingOut) {
double mul=(1.0-((double)curFadeOutSample/(double)fadeOutSamples));
outBuf[2][i<<1]=MAX(-1.0f,MIN(1.0f,outBuf[0][i]))*mul;
outBuf[2][1+(i<<1)]=MAX(-1.0f,MIN(1.0f,outBuf[1][i]))*mul;
if (++curFadeOutSample>=fadeOutSamples) {
playing=false;
break;
}
} else {
outBuf[2][i<<1]=MAX(-1.0f,MIN(1.0f,outBuf[0][i]));
outBuf[2][1+(i<<1)]=MAX(-1.0f,MIN(1.0f,outBuf[1][i]));
if (lastLoopPos>-1 && i>=lastLoopPos && totalLoops>=exportLoopCount) {
logD("start fading out...");
isFadingOut=true;
}
}
}
if (sf_writef_float(sf,outBuf[2],total)!=(int)total) {
logE("error: failed to write entire buffer!");
break;
}
@ -286,7 +307,10 @@ void DivEngine::runExportThread() {
float* outBuf[2];
outBuf[0]=new float[EXPORT_BUFSIZE];
outBuf[1]=new float[EXPORT_BUFSIZE];
short* sysBuf=new short[EXPORT_BUFSIZE*2];
short* sysBuf[32];
for (int i=0; i<song.systemLen; i++) {
sysBuf[i]=new short[EXPORT_BUFSIZE*2];
}
// take control of audio output
deinitAudioBackend();
@ -295,20 +319,45 @@ void DivEngine::runExportThread() {
logI("rendering to files...");
while (playing) {
size_t total=0;
nextBuf(NULL,outBuf,0,2,EXPORT_BUFSIZE);
for (int i=0; i<song.systemLen; i++) {
for (int j=0; j<EXPORT_BUFSIZE; j++) {
if (!disCont[i].dispatch->isStereo()) {
sysBuf[j]=disCont[i].bbOut[0][j];
} else {
sysBuf[j<<1]=disCont[i].bbOut[0][j];
sysBuf[1+(j<<1)]=disCont[i].bbOut[1][j];
if (totalProcessed>EXPORT_BUFSIZE) {
logE("error: total processed is bigger than export bufsize! %d>%d",totalProcessed,EXPORT_BUFSIZE);
totalProcessed=EXPORT_BUFSIZE;
}
for (int j=0; j<(int)totalProcessed; j++) {
total++;
if (isFadingOut) {
double mul=(1.0-((double)curFadeOutSample/(double)fadeOutSamples));
for (int i=0; i<song.systemLen; i++) {
if (!disCont[i].dispatch->isStereo()) {
sysBuf[i][j]=(double)disCont[i].bbOut[0][j]*mul;
} else {
sysBuf[i][j<<1]=(double)disCont[i].bbOut[0][j]*mul;
sysBuf[i][1+(j<<1)]=(double)disCont[i].bbOut[1][j]*mul;
}
}
if (++curFadeOutSample>=fadeOutSamples) {
playing=false;
break;
}
} else {
for (int i=0; i<song.systemLen; i++) {
if (!disCont[i].dispatch->isStereo()) {
sysBuf[i][j]=disCont[i].bbOut[0][j];
} else {
sysBuf[i][j<<1]=disCont[i].bbOut[0][j];
sysBuf[i][1+(j<<1)]=disCont[i].bbOut[1][j];
}
}
if (lastLoopPos>-1 && j>=lastLoopPos && totalLoops>=exportLoopCount) {
logD("start fading out...");
isFadingOut=true;
}
}
if (totalProcessed>EXPORT_BUFSIZE) {
logE("error: total processed is bigger than export bufsize! (%d) %d>%d",i,totalProcessed,EXPORT_BUFSIZE);
}
if (sf_writef_short(sf[i],sysBuf,totalProcessed)!=(int)totalProcessed) {
}
for (int i=0; i<song.systemLen; i++) {
if (sf_writef_short(sf[i],sysBuf[i],total)!=(int)total) {
logE("error: failed to write entire buffer! (%d)",i);
break;
}
@ -317,9 +366,9 @@ void DivEngine::runExportThread() {
delete[] outBuf[0];
delete[] outBuf[1];
delete[] sysBuf;
for (int i=0; i<song.systemLen; i++) {
delete[] sysBuf[i];
if (sf_close(sf[i])!=0) {
logE("could not close audio file!");
}
@ -382,19 +431,43 @@ void DivEngine::runExportThread() {
}
curOrder=0;
remainingLoops=loopCount;
prevOrder=0;
curFadeOutSample=0;
isFadingOut=false;
if (exportFadeOut<=0.01) {
remainingLoops=loopCount;
} else {
remainingLoops=-1;
}
playSub(false);
while (playing) {
size_t total=0;
nextBuf(NULL,outBuf,0,2,EXPORT_BUFSIZE);
for (int j=0; j<EXPORT_BUFSIZE; j++) {
outBuf[2][j<<1]=MAX(-1.0f,MIN(1.0f,outBuf[0][j]));
outBuf[2][1+(j<<1)]=MAX(-1.0f,MIN(1.0f,outBuf[1][j]));
}
if (totalProcessed>EXPORT_BUFSIZE) {
logE("error: total processed is bigger than export bufsize! %d>%d",totalProcessed,EXPORT_BUFSIZE);
totalProcessed=EXPORT_BUFSIZE;
}
if (sf_writef_float(sf,outBuf[2],totalProcessed)!=(int)totalProcessed) {
for (int j=0; j<(int)totalProcessed; j++) {
total++;
if (isFadingOut) {
double mul=(1.0-((double)curFadeOutSample/(double)fadeOutSamples));
outBuf[2][j<<1]=MAX(-1.0f,MIN(1.0f,outBuf[0][j]))*mul;
outBuf[2][1+(j<<1)]=MAX(-1.0f,MIN(1.0f,outBuf[1][j]))*mul;
if (++curFadeOutSample>=fadeOutSamples) {
playing=false;
break;
}
} else {
outBuf[2][j<<1]=MAX(-1.0f,MIN(1.0f,outBuf[0][j]));
outBuf[2][1+(j<<1)]=MAX(-1.0f,MIN(1.0f,outBuf[1][j]));
if (lastLoopPos>-1 && j>=lastLoopPos && totalLoops>=exportLoopCount) {
logD("start fading out...");
isFadingOut=true;
}
}
}
if (sf_writef_float(sf,outBuf[2],total)!=(int)total) {
logE("error: failed to write entire buffer!");
break;
}
@ -448,13 +521,14 @@ void DivEngine::runExportThread() {
}
#endif
bool DivEngine::saveAudio(const char* path, int loops, DivAudioExportModes mode) {
bool DivEngine::saveAudio(const char* path, int loops, DivAudioExportModes mode, double fadeOutTime) {
#ifndef HAVE_SNDFILE
logE("Furnace was not compiled with libsndfile. cannot export!");
return false;
#else
exportPath=path;
exportMode=mode;
exportFadeOut=fadeOutTime;
if (exportMode!=DIV_EXPORT_MODE_ONE) {
// remove extension
String lowerCase=exportPath;
@ -471,7 +545,12 @@ bool DivEngine::saveAudio(const char* path, int loops, DivAudioExportModes mode)
stop();
repeatPattern=false;
setOrder(0);
remainingLoops=loops;
if (exportFadeOut<=0.01) {
remainingLoops=loops;
} else {
remainingLoops=-1;
}
exportLoopCount=loops;
exportThread=new std::thread(_runExportThread,this);
return true;
#endif
@ -780,6 +859,8 @@ void DivEngine::changeSong(size_t songIndex) {
curSubSongIndex=songIndex;
curOrder=0;
curRow=0;
prevOrder=0;
prevRow=0;
}
void DivEngine::swapChannelsP(int src, int dest) {
@ -827,12 +908,51 @@ bool DivEngine::removeSubSong(int index) {
return true;
}
void DivEngine::moveSubSongUp(size_t index) {
if (index<1 || index>=song.subsong.size()) return;
BUSY_BEGIN;
saveLock.lock();
if (index==curSubSongIndex) {
curSubSongIndex--;
} else if (index-1==curSubSongIndex) {
curSubSongIndex++;
}
DivSubSong* prev=song.subsong[index-1];
song.subsong[index-1]=song.subsong[index];
song.subsong[index]=prev;
saveLock.unlock();
BUSY_END;
}
void DivEngine::moveSubSongDown(size_t index) {
if (index>=song.subsong.size()-1) return;
BUSY_BEGIN;
saveLock.lock();
if (index==curSubSongIndex) {
curSubSongIndex++;
} else if (index+1==curSubSongIndex) {
curSubSongIndex--;
}
DivSubSong* prev=song.subsong[index+1];
song.subsong[index+1]=song.subsong[index];
song.subsong[index]=prev;
saveLock.unlock();
BUSY_END;
}
void DivEngine::clearSubSongs() {
BUSY_BEGIN;
saveLock.lock();
song.clearSongData();
changeSong(0);
curOrder=0;
prevOrder=0;
saveLock.unlock();
BUSY_END;
}
@ -1075,6 +1195,8 @@ void DivEngine::playSub(bool preserveDrift, int goalRow) {
int goal=curOrder;
curOrder=0;
curRow=0;
prevOrder=0;
prevRow=0;
stepPlay=0;
int prevDrift;
prevDrift=clockDrift;
@ -1088,6 +1210,8 @@ void DivEngine::playSub(bool preserveDrift, int goalRow) {
totalTicks=0;
totalSeconds=0;
totalTicksR=0;
totalLoops=0;
lastLoopPos=-1;
}
speedAB=false;
playing=true;
@ -1124,6 +1248,8 @@ void DivEngine::playSub(bool preserveDrift, int goalRow) {
if (!preserveDrift) {
ticks=1;
subticks=1;
prevOrder=curOrder;
prevRow=curRow;
}
skipping=false;
cmdStream.clear();
@ -1250,6 +1376,7 @@ unsigned int DivEngine::convertPanLinearToSplit(int val, unsigned char bits, int
void DivEngine::play() {
BUSY_BEGIN_SOFT;
curOrder=prevOrder;
sPreview.sample=-1;
sPreview.wave=-1;
sPreview.pos=0;
@ -1545,11 +1672,11 @@ int DivEngine::getMaxVolumeChan(int ch) {
}
unsigned char DivEngine::getOrder() {
return curOrder;
return prevOrder;
}
int DivEngine::getRow() {
return curRow;
return prevRow;
}
size_t DivEngine::getCurrentSubSong() {
@ -2462,7 +2589,7 @@ void DivEngine::autoNoteOn(int ch, int ins, int note, int vol) {
// 2. find a free channel
do {
if (isViable[finalChan] && chan[finalChan].midiNote==-1 && (insInst->type==DIV_INS_OPL || getChannelType(finalChan)==finalChanType || notInViableChannel)) {
if ((!midiPoly) || (isViable[finalChan] && chan[finalChan].midiNote==-1 && (insInst->type==DIV_INS_OPL || getChannelType(finalChan)==finalChanType || notInViableChannel))) {
chan[finalChan].midiNote=note;
chan[finalChan].midiAge=midiAgeCounter++;
pendingNotes.push(DivNoteEvent(finalChan,ins,note,vol,true));
@ -2518,10 +2645,15 @@ void DivEngine::autoNoteOffAll() {
}
}
void DivEngine::setAutoNotePoly(bool poly) {
midiPoly=poly;
}
void DivEngine::setOrder(unsigned char order) {
BUSY_BEGIN_SOFT;
curOrder=order;
if (order>=curSubSong->ordersLen) curOrder=0;
prevOrder=curOrder;
if (playing && !freelance) {
playSub(false);
}
@ -2715,6 +2847,8 @@ void DivEngine::quitDispatch() {
tempoAccum=0;
curRow=0;
curOrder=0;
prevRow=0;
prevOrder=0;
nextSpeed=3;
changeOrd=-1;
changePos=0;

View File

@ -303,7 +303,7 @@ class DivEngine {
bool systemsRegistered;
bool hasLoadedSomething;
int softLockCount;
int subticks, ticks, curRow, curOrder, remainingLoops, nextSpeed;
int subticks, ticks, curRow, curOrder, prevRow, prevOrder, remainingLoops, totalLoops, lastLoopPos, exportLoopCount, nextSpeed;
size_t curSubSongIndex;
double divider;
int cycles;
@ -318,6 +318,7 @@ class DivEngine {
DivChannelState chan[DIV_MAX_CHANS];
DivAudioEngines audioEngine;
DivAudioExportModes exportMode;
double exportFadeOut;
std::map<String,String> conf;
std::queue<DivNoteEvent> pendingNotes;
bool isMuted[DIV_MAX_CHANS];
@ -352,6 +353,7 @@ class DivEngine {
int reversePitchTable[4096];
int pitchTable[4096];
int midiBaseChan;
bool midiPoly;
size_t midiAgeCounter;
blip_buffer_t* samp_bb;
@ -462,7 +464,7 @@ class DivEngine {
// dump to VGM.
SafeWriter* saveVGM(bool* sysToExport=NULL, bool loop=true, int version=0x171);
// export to an audio file
bool saveAudio(const char* path, int loops, DivAudioExportModes mode);
bool saveAudio(const char* path, int loops, DivAudioExportModes mode, double fadeOutTime=0.0);
// wait for audio export to finish
void waitAudioFile();
// stop audio file export
@ -725,6 +727,9 @@ class DivEngine {
void autoNoteOn(int chan, int ins, int note, int vol=-1);
void autoNoteOff(int chan, int note, int vol=-1);
void autoNoteOffAll();
// set whether autoNoteIn is mono or poly
void setAutoNotePoly(bool poly);
// go to order
void setOrder(unsigned char order);
@ -825,6 +830,10 @@ class DivEngine {
// remove subsong
bool removeSubSong(int index);
// move subsong
void moveSubSongUp(size_t index);
void moveSubSongDown(size_t index);
// clear all subsong data
void clearSubSongs();
@ -927,7 +936,12 @@ class DivEngine {
ticks(0),
curRow(0),
curOrder(0),
prevRow(0),
prevOrder(0),
remainingLoops(-1),
totalLoops(0),
lastLoopPos(0),
exportLoopCount(0),
nextSpeed(3),
curSubSongIndex(0),
divider(60),
@ -951,7 +965,9 @@ class DivEngine {
haltOn(DIV_HALT_NONE),
audioEngine(DIV_AUDIO_NULL),
exportMode(DIV_EXPORT_MODE_ONE),
exportFadeOut(0.0),
midiBaseChan(0),
midiPoly(true),
midiAgeCounter(0),
samp_bb(NULL),
samp_bbInLen(0),

View File

@ -101,6 +101,7 @@ void DivMacroInt::init(DivInstrument* which) {
macroListLen=0;
subTick=1;
hasRelease=false;
released=false;
if (ins==NULL) return;
@ -236,7 +237,12 @@ void DivMacroInt::init(DivInstrument* which) {
}
for (size_t i=0; i<macroListLen; i++) {
macroList[i]->prepare(*macroSource[i],e);
if (macroSource[i]!=NULL) {
macroList[i]->prepare(*macroSource[i],e);
hasRelease=(macroSource[i]->rel>=0 && macroSource[i]->rel<macroSource[i]->len);
} else {
hasRelease=false;
}
}
}

View File

@ -96,6 +96,9 @@ class DivMacroInt {
ksr() {}
} op[4];
// state
bool hasRelease;
/**
* trigger macro release.
*/
@ -149,7 +152,8 @@ class DivMacroInt {
ex5(),
ex6(),
ex7(),
ex8() {
ex8(),
hasRelease(false) {
memset(macroList,0,128*sizeof(void*));
memset(macroSource,0,128*sizeof(void*));
}

View File

@ -427,6 +427,10 @@ bool DivPlatformAmiga::keyOffAffectsArp(int ch) {
return true;
}
DivMacroInt* DivPlatformAmiga::getChanMacroInt(int ch) {
return &chan[ch].std;
}
void DivPlatformAmiga::notifyInsChange(int ins) {
for (int i=0; i<4; i++) {
if (chan[i].ins==ins) {

View File

@ -99,6 +99,7 @@ class DivPlatformAmiga: public DivDispatch {
void muteChannel(int ch, bool mute);
bool isStereo();
bool keyOffAffectsArp(int ch);
DivMacroInt* getChanMacroInt(int ch);
void setFlags(unsigned int flags);
void notifyInsChange(int ins);
void notifyWaveChange(int wave);

View File

@ -252,12 +252,12 @@ void DivPlatformArcade::tick(bool sysTick) {
chan[i].std.next();
if (chan[i].std.vol.had) {
chan[i].outVol=(chan[i].vol*MIN(127,chan[i].std.vol.val))/127;
chan[i].outVol=VOL_SCALE_LOG(chan[i].vol,MIN(127,chan[i].std.vol.val),127);
for (int j=0; j<4; j++) {
unsigned short baseAddr=chanOffs[i]|opOffs[j];
DivInstrumentFM::Operator& op=chan[i].state.op[j];
if (isOutput[chan[i].state.alg][j]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -346,7 +346,7 @@ void DivPlatformArcade::tick(bool sysTick) {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (isOutput[chan[i].state.alg][j]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -400,7 +400,7 @@ void DivPlatformArcade::tick(bool sysTick) {
if (m.tl.had) {
op.tl=127-m.tl.val;
if (isOutput[chan[i].state.alg][j]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -496,7 +496,7 @@ int DivPlatformArcade::dispatch(DivCommand c) {
DivInstrumentFM::Operator op=chan[c.chan].state.op[i];
if (isOutput[chan[c.chan].state.alg][i]) {
if (!chan[c.chan].active || chan[c.chan].insChanged) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127));
}
} else {
if (chan[c.chan].insChanged) {
@ -553,7 +553,7 @@ int DivPlatformArcade::dispatch(DivCommand c) {
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
if (isOutput[chan[c.chan].state.alg][i]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -644,7 +644,7 @@ int DivPlatformArcade::dispatch(DivCommand c) {
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
op.tl=c.value2;
if (isOutput[chan[c.chan].state.alg][c.value]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -844,7 +844,7 @@ void DivPlatformArcade::forceIns() {
unsigned short baseAddr=chanOffs[i]|opOffs[j];
DivInstrumentFM::Operator op=chan[i].state.op[j];
if (isOutput[chan[i].state.alg][j]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -881,6 +881,10 @@ void* DivPlatformArcade::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformArcade::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformArcade::getOscBuffer(int ch) {
return oscBuf[ch];
}

View File

@ -113,6 +113,7 @@ class DivPlatformArcade: public DivDispatch, public DivPlatformOPMBase {
void forceIns();
void tick(bool sysTick=true);
void muteChannel(int ch, bool mute);
DivMacroInt* getChanMacroInt(int ch);
void notifyInsChange(int ins);
void setFlags(unsigned int flags);
bool isStereo();

View File

@ -509,6 +509,10 @@ void* DivPlatformAY8910::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformAY8910::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformAY8910::getOscBuffer(int ch) {
return oscBuf[ch];
}

View File

@ -105,6 +105,7 @@ class DivPlatformAY8910: public DivDispatch {
void setFlags(unsigned int flags);
bool isStereo();
bool keyOffAffectsArp(int ch);
DivMacroInt* getChanMacroInt(int ch);
bool getDCOffRequired();
void notifyInsDeletion(void* ins);
void poke(unsigned int addr, unsigned short val);

View File

@ -539,6 +539,10 @@ void* DivPlatformAY8930::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformAY8930::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformAY8930::getOscBuffer(int ch) {
return oscBuf[ch];
}

View File

@ -98,6 +98,7 @@ class DivPlatformAY8930: public DivDispatch {
void setFlags(unsigned int flags);
bool isStereo();
bool keyOffAffectsArp(int ch);
DivMacroInt* getChanMacroInt(int ch);
void notifyInsDeletion(void* ins);
void poke(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist);

View File

@ -281,6 +281,10 @@ void* DivPlatformBubSysWSG::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformBubSysWSG::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformBubSysWSG::getOscBuffer(int ch) {
return oscBuf[ch];
}

View File

@ -78,6 +78,7 @@ class DivPlatformBubSysWSG: public DivDispatch {
void muteChannel(int ch, bool mute);
bool isStereo();
bool keyOffAffectsArp(int ch);
DivMacroInt* getChanMacroInt(int ch);
void setFlags(unsigned int flags);
void notifyWaveChange(int wave);
void notifyInsDeletion(void* ins);

View File

@ -491,6 +491,10 @@ void* DivPlatformC64::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformC64::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformC64::getOscBuffer(int ch) {
return oscBuf[ch];
}

View File

@ -97,6 +97,7 @@ class DivPlatformC64: public DivDispatch {
void setFlags(unsigned int flags);
void notifyInsChange(int ins);
bool getDCOffRequired();
DivMacroInt* getChanMacroInt(int ch);
void notifyInsDeletion(void* ins);
void poke(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist);

View File

@ -139,7 +139,7 @@ void DivPlatformFDS::tick(bool sysTick) {
chan[i].std.next();
if (chan[i].std.vol.had) {
// ok, why are the volumes like that?
chan[i].outVol=MIN(32,chan[i].std.vol.val)-(32-MIN(32,chan[i].vol));
chan[i].outVol=VOL_SCALE_LINEAR_BROKEN(chan[i].vol,chan[i].std.vol.val,32);
if (chan[i].outVol<0) chan[i].outVol=0;
rWrite(0x4080,0x80|chan[i].outVol);
}
@ -436,6 +436,10 @@ void* DivPlatformFDS::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformFDS::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformFDS::getOscBuffer(int ch) {
return oscBuf;
}

View File

@ -88,6 +88,7 @@ class DivPlatformFDS: public DivDispatch {
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();

View File

@ -433,6 +433,10 @@ void* DivPlatformGB::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformGB::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformGB::getOscBuffer(int ch) {
return oscBuf[ch];
}

View File

@ -72,6 +72,7 @@ class DivPlatformGB: public DivDispatch {
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();

View File

@ -26,6 +26,7 @@ static unsigned char konOffs[6]={
0, 1, 2, 4, 5, 6
};
#define IS_REALLY_MUTED(x) (isMuted[x] && (x<5 || !softPCM || (isMuted[5] && isMuted[6])))
const char* DivPlatformGenesis::getEffectName(unsigned char effect) {
switch (effect) {
@ -141,10 +142,13 @@ void DivPlatformGenesis::processDAC() {
DivSample* s=parent->getSample(chan[i].dacSample);
if (!isMuted[i] && s->samples>0) {
if (parent->song.noOPN2Vol) {
sample+=s->data8[chan[i].dacDirection?(s->samples-chan[i].dacPos-1):chan[i].dacPos];
chan[i].dacOutput=s->data8[chan[i].dacDirection?(s->samples-chan[i].dacPos-1):chan[i].dacPos];
} else {
sample+=(s->data8[chan[i].dacDirection?(s->samples-chan[i].dacPos-1):chan[i].dacPos]*dacVolTable[chan[i].outVol])>>7;
chan[i].dacOutput=(s->data8[chan[i].dacDirection?(s->samples-chan[i].dacPos-1):chan[i].dacPos]*dacVolTable[chan[i].outVol])>>7;
}
sample+=chan[i].dacOutput;
} else {
chan[i].dacOutput=0;
}
chan[i].dacPeriod+=chan[i].dacRate;
if (chan[i].dacPeriod>=(chipClock/576)) {
@ -246,7 +250,20 @@ void DivPlatformGenesis::acquire_nuked(short* bufL, short* bufR, size_t start, s
OPN2_Clock(&fm,o); os[0]+=o[0]; os[1]+=o[1];
//OPN2_Write(&fm,0,0);
oscBuf[i]->data[oscBuf[i]->needle++]=fm.ch_out[i]<<7;
if (i==5) {
if (fm.dacen) {
if (softPCM) {
oscBuf[5]->data[oscBuf[5]->needle++]=chan[5].dacOutput<<7;
oscBuf[6]->data[oscBuf[6]->needle++]=chan[6].dacOutput<<7;
} else {
oscBuf[i]->data[oscBuf[i]->needle++]=fm.dacdata<<7;
}
} else {
oscBuf[i]->data[oscBuf[i]->needle++]=fm.ch_out[i]<<7;
}
} else {
oscBuf[i]->data[oscBuf[i]->needle++]=fm.ch_out[i]<<7;
}
}
os[0]=(os[0]<<5);
@ -290,7 +307,20 @@ void DivPlatformGenesis::acquire_ymfm(short* bufL, short* bufR, size_t start, si
//OPN2_Write(&fm,0,0);
for (int i=0; i<6; i++) {
oscBuf[i]->data[oscBuf[i]->needle++]=(fme->debug_channel(i)->debug_output(0)+fme->debug_channel(i)->debug_output(1))<<6;
if (i==5) {
if (fm_ymfm->debug_dac_enable()) {
if (softPCM) {
oscBuf[5]->data[oscBuf[5]->needle++]=chan[5].dacOutput<<7;
oscBuf[6]->data[oscBuf[6]->needle++]=chan[6].dacOutput<<7;
} else {
oscBuf[i]->data[oscBuf[i]->needle++]=fm_ymfm->debug_dac_data()<<7;
}
} else {
oscBuf[i]->data[oscBuf[i]->needle++]=(fme->debug_channel(i)->debug_output(0)+fme->debug_channel(i)->debug_output(1))<<6;
}
} else {
oscBuf[i]->data[oscBuf[i]->needle++]=(fme->debug_channel(i)->debug_output(0)+fme->debug_channel(i)->debug_output(1))<<6;
}
}
if (os[0]<-32768) os[0]=-32768;
@ -318,7 +348,7 @@ void DivPlatformGenesis::tick(bool sysTick) {
chan[i].std.next();
if (chan[i].std.vol.had) {
chan[i].outVol=(chan[i].vol*MIN(127,chan[i].std.vol.val))/127;
chan[i].outVol=VOL_SCALE_LOG(chan[i].vol,MIN(127,chan[i].std.vol.val),127);
for (int j=0; j<4; j++) {
unsigned short baseAddr=chanOffs[i]|opOffs[j];
DivInstrumentFM::Operator& op=chan[i].state.op[j];
@ -326,7 +356,7 @@ void DivPlatformGenesis::tick(bool sysTick) {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (isOutput[chan[i].state.alg][j]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -352,7 +382,7 @@ void DivPlatformGenesis::tick(bool sysTick) {
if (chan[i].std.panL.had) {
chan[i].pan=chan[i].std.panL.val&3;
rWrite(chanOffs[i]+ADDR_LRAF,(isMuted[i]?0:(chan[i].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4));
rWrite(chanOffs[i]+ADDR_LRAF,(IS_REALLY_MUTED(i)?0:(chan[i].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4));
}
if (chan[i].std.pitch.had) {
@ -381,7 +411,7 @@ void DivPlatformGenesis::tick(bool sysTick) {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (isOutput[chan[i].state.alg][j]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -394,11 +424,11 @@ void DivPlatformGenesis::tick(bool sysTick) {
}
if (chan[i].std.fms.had) {
chan[i].state.fms=chan[i].std.fms.val;
rWrite(chanOffs[i]+ADDR_LRAF,(isMuted[i]?0:(chan[i].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4));
rWrite(chanOffs[i]+ADDR_LRAF,(IS_REALLY_MUTED(i)?0:(chan[i].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4));
}
if (chan[i].std.ams.had) {
chan[i].state.ams=chan[i].std.ams.val;
rWrite(chanOffs[i]+ADDR_LRAF,(isMuted[i]?0:(chan[i].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4));
rWrite(chanOffs[i]+ADDR_LRAF,(IS_REALLY_MUTED(i)?0:(chan[i].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4));
}
for (int j=0; j<4; j++) {
unsigned short baseAddr=chanOffs[i]|opOffs[j];
@ -434,7 +464,7 @@ void DivPlatformGenesis::tick(bool sysTick) {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (isOutput[chan[i].state.alg][j]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -492,8 +522,7 @@ void DivPlatformGenesis::tick(bool sysTick) {
}
}
for (int i=0; i<8; i++) {
for (int i=0; i<7; i++) {
if (i==2 && extMode) continue;
if (chan[i].freqChanged) {
if (parent->song.linearPitch==2) {
@ -542,27 +571,45 @@ void DivPlatformGenesis::tick(bool sysTick) {
void DivPlatformGenesis::muteChannel(int ch, bool mute) {
isMuted[ch]=mute;
if (ch>5) return;
for (int j=0; j<4; j++) {
unsigned short baseAddr=chanOffs[ch]|opOffs[j];
DivInstrumentFM::Operator& op=chan[ch].state.op[j];
if (isMuted[ch]) {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (isOutput[chan[ch].state.alg][j]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[ch].outVol&0x7f))/127));
if (ch>6) return;
if (ch<6) {
for (int j=0; j<4; j++) {
unsigned short baseAddr=chanOffs[ch]|opOffs[j];
DivInstrumentFM::Operator& op=chan[ch].state.op[j];
if (isMuted[ch]) {
rWrite(baseAddr+ADDR_TL,127);
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
if (isOutput[chan[ch].state.alg][j]) {
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[ch].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
}
}
} else {
ch--;
}
rWrite(chanOffs[ch]+ADDR_LRAF,(isMuted[ch]?0:(chan[ch].pan<<6))|(chan[ch].state.fms&7)|((chan[ch].state.ams&3)<<4));
rWrite(chanOffs[ch]+ADDR_LRAF,(IS_REALLY_MUTED(ch)?0:(chan[ch].pan<<6))|(chan[ch].state.fms&7)|((chan[ch].state.ams&3)<<4));
}
int DivPlatformGenesis::dispatch(DivCommand c) {
switch (c.cmd) {
case DIV_CMD_NOTE_ON: {
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM);
if (c.chan==7 && extMode && softPCM) { // CSM
chan[c.chan].macroInit(ins);
chan[c.chan].insChanged=false;
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
chan[c.chan].portaPause=false;
chan[c.chan].note=c.value;
chan[c.chan].freqChanged=true;
}
chan[c.chan].keyOn=true;
chan[c.chan].active=true;
break;
}
if (c.chan>=5) {
if (ins->type==DIV_INS_AMIGA) {
chan[c.chan].dacMode=1;
@ -631,7 +678,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
} else {
if (isOutput[chan[c.chan].state.alg][i]) {
if (!chan[c.chan].active || chan[c.chan].insChanged) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127));
}
} else {
if (chan[c.chan].insChanged) {
@ -650,7 +697,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
}
if (chan[c.chan].insChanged) {
rWrite(chanOffs[c.chan]+ADDR_FB_ALG,(chan[c.chan].state.alg&7)|(chan[c.chan].state.fb<<3));
rWrite(chanOffs[c.chan]+ADDR_LRAF,(isMuted[c.chan]?0:(chan[c.chan].pan<<6))|(chan[c.chan].state.fms&7)|((chan[c.chan].state.ams&3)<<4));
rWrite(chanOffs[c.chan]+ADDR_LRAF,(IS_REALLY_MUTED(c.chan)?0:(chan[c.chan].pan<<6))|(chan[c.chan].state.fms&7)|((chan[c.chan].state.ams&3)<<4));
}
chan[c.chan].insChanged=false;
@ -665,7 +712,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
break;
}
case DIV_CMD_NOTE_OFF:
if (c.chan>=5) {
if (c.chan>=5 && c.chan<7) {
chan[c.chan].dacSample=-1;
if (dumpWrites) addWrite(0xffff0002,0);
if (parent->song.brokenDACMode) {
@ -703,7 +750,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (isOutput[chan[c.chan].state.alg][i]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -728,7 +775,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
} else {
chan[c.chan].pan=(c.value2>0)|((c.value>0)<<1);
}
rWrite(chanOffs[c.chan]+ADDR_LRAF,(isMuted[c.chan]?0:(chan[c.chan].pan<<6))|(chan[c.chan].state.fms&7)|((chan[c.chan].state.ams&3)<<4));
rWrite(chanOffs[c.chan]+ADDR_LRAF,(IS_REALLY_MUTED(c.chan)?0:(chan[c.chan].pan<<6))|(chan[c.chan].state.fms&7)|((chan[c.chan].state.ams&3)<<4));
break;
}
case DIV_CMD_PITCH: {
@ -760,6 +807,29 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
}
break;
}
if (c.chan==7) {
int destFreq=NOTE_PERIODIC(c.value2);
bool return2=false;
if (destFreq>chan[c.chan].baseFreq) {
chan[c.chan].baseFreq+=c.value;
if (chan[c.chan].baseFreq>=destFreq) {
chan[c.chan].baseFreq=destFreq;
return2=true;
}
} else {
chan[c.chan].baseFreq-=c.value;
if (chan[c.chan].baseFreq<=destFreq) {
chan[c.chan].baseFreq=destFreq;
return2=true;
}
}
chan[c.chan].freqChanged=true;
if (return2) {
chan[c.chan].inPorta=false;
return 2;
}
break;
}
if (c.chan>=5 && chan[c.chan].furnaceDac && chan[c.chan].dacMode) {
int destFreq=parent->calcBaseFreq(1,1,c.value2,false);
bool return2=false;
@ -806,7 +876,9 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
break;
}
case DIV_CMD_LEGATO: {
if (c.chan>=5 && chan[c.chan].furnaceDac && chan[c.chan].dacMode) {
if (c.chan==7) {
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
} else if (c.chan>=5 && chan[c.chan].furnaceDac && chan[c.chan].dacMode) {
chan[c.chan].baseFreq=parent->calcBaseFreq(1,1,c.value,false);
} else {
chan[c.chan].baseFreq=NOTE_FNUM_BLOCK(c.value,11);
@ -844,7 +916,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (isOutput[chan[c.chan].state.alg][c.value]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -1035,7 +1107,7 @@ void DivPlatformGenesis::forceIns() {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (isOutput[chan[i].state.alg][j]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -1048,7 +1120,7 @@ void DivPlatformGenesis::forceIns() {
rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15);
}
rWrite(chanOffs[i]+ADDR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3));
rWrite(chanOffs[i]+ADDR_LRAF,(isMuted[i]?0:(chan[i].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4));
rWrite(chanOffs[i]+ADDR_LRAF,(IS_REALLY_MUTED(i)?0:(chan[i].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4));
if (chan[i].active) {
chan[i].keyOn=true;
chan[i].freqChanged=true;
@ -1068,6 +1140,10 @@ void* DivPlatformGenesis::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformGenesis::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformGenesis::getOscBuffer(int ch) {
return oscBuf[ch];
}

View File

@ -55,6 +55,7 @@ class DivPlatformGenesis: public DivDispatch, public DivPlatformOPNBase {
bool dacReady;
bool dacDirection;
unsigned char sampleBank;
signed char dacOutput;
void macroInit(DivInstrument* which) {
std.init(which);
pitch2=0;
@ -89,7 +90,8 @@ class DivPlatformGenesis: public DivDispatch, public DivPlatformOPNBase {
dacDelay(0),
dacReady(true),
dacDirection(false),
sampleBank(0) {}
sampleBank(0),
dacOutput(0) {}
};
Channel chan[10];
DivDispatchOscBuffer* oscBuf[10];
@ -130,6 +132,7 @@ class DivPlatformGenesis: public DivDispatch, public DivPlatformOPNBase {
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();

View File

@ -50,7 +50,7 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) {
rWrite(baseAddr+0x40,127);
} else {
if (opChan[ch].insChanged) {
rWrite(baseAddr+0x40,127-(((127-op.tl)*(opChan[ch].vol&0x7f))/127));
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG(127-op.tl,opChan[ch].vol&0x7f,127));
}
}
if (opChan[ch].insChanged) {
@ -88,7 +88,7 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) {
if (isOpMuted[ch]) {
rWrite(baseAddr+0x40,127);
} else {
rWrite(baseAddr+0x40,127-(((127-op.tl)*(opChan[ch].vol&0x7f))/127));
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG(127-op.tl,opChan[ch].vol&0x7f,127));
}
break;
}
@ -194,7 +194,7 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) {
if (isOpMuted[ch]) {
rWrite(baseAddr+0x40,127);
} else if (isOutput[chan[2].state.alg][c.value]) {
rWrite(baseAddr+0x40,127-(((127-op.tl)*(opChan[ch].vol&0x7f))/127));
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG(127-op.tl,opChan[ch].vol&0x7f,127));
} else {
rWrite(baseAddr+0x40,op.tl);
}
@ -377,8 +377,8 @@ void DivPlatformGenesisExt::muteChannel(int ch, bool mute) {
rWrite(baseAddr+0x40,127);
immWrite(baseAddr+0x40,127);
} else if (isOutput[chan[2].state.alg][ordch]) {
rWrite(baseAddr+0x40,127-(((127-op.tl)*(opChan[ch-2].vol&0x7f))/127));
immWrite(baseAddr+0x40,127-(((127-op.tl)*(opChan[ch-2].vol&0x7f))/127));
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG(127-op.tl,opChan[ch-2].vol&0x7f,127));
immWrite(baseAddr+0x40,127-VOL_SCALE_LOG(127-op.tl,opChan[ch-2].vol&0x7f,127));
} else {
rWrite(baseAddr+0x40,op.tl);
immWrite(baseAddr+0x40,op.tl);
@ -442,9 +442,43 @@ void DivPlatformGenesisExt::tick(bool sysTick) {
opChan[i].keyOn=false;
}
}
if (extMode && softPCM) {
if (chan[7].freqChanged) {
chan[7].freq=parent->calcFreq(chan[7].baseFreq,chan[7].pitch,true,0,chan[7].pitch2,chipClock,CHIP_DIVIDER);
if (chan[7].freq<1) chan[7].freq=1;
if (chan[7].freq>1024) chan[7].freq=1024;
int wf=0x400-chan[7].freq;
immWrite(0x24,wf>>2);
immWrite(0x25,wf&3);
chan[7].freqChanged=false;
}
if (chan[7].keyOff || chan[7].keyOn) {
writeNoteOn=true;
for (int i=0; i<4; i++) {
writeMask|=opChan[i].active<<(4+i);
}
}
}
if (writeNoteOn) {
if (chan[7].active) { // CSM
writeMask^=0xf0;
}
immWrite(0x28,writeMask);
}
if (extMode && softPCM) {
if (chan[7].keyOn) {
immWrite(0x27,0x81);
chan[7].keyOn=false;
}
if (chan[7].keyOff) {
immWrite(0x27,0x40);
chan[7].keyOff=false;
}
}
}
void DivPlatformGenesisExt::forceIns() {
@ -456,7 +490,7 @@ void DivPlatformGenesisExt::forceIns() {
if (isOpMuted[j]) {
rWrite(baseAddr+0x40,127);
} else if (isOutput[chan[i].state.alg][j]) {
rWrite(baseAddr+0x40,127-(((127-op.tl)*(opChan[j].vol&0x7f))/127));
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG(127-op.tl,opChan[j].vol&0x7f,127));
} else {
rWrite(baseAddr+0x40,op.tl);
}
@ -465,7 +499,7 @@ void DivPlatformGenesisExt::forceIns() {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (isOutput[chan[i].state.alg][j]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -504,6 +538,12 @@ void* DivPlatformGenesisExt::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformGenesisExt::getChanMacroInt(int ch) {
if (ch>=6) return &chan[ch-3].std;
if (ch>=2) return NULL; // currently not implemented
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformGenesisExt::getOscBuffer(int ch) {
if (ch>=6) return oscBuf[ch-3];
if (ch<3) return oscBuf[ch];

View File

@ -55,6 +55,7 @@ class DivPlatformGenesisExt: public DivPlatformGenesis {
public:
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
void reset();
void forceIns();

View File

@ -424,6 +424,10 @@ void* DivPlatformLynx::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformLynx::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformLynx::getOscBuffer(int ch) {
return oscBuf[ch];
}

View File

@ -88,6 +88,7 @@ class DivPlatformLynx: public DivDispatch {
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();

View File

@ -107,7 +107,7 @@ void DivPlatformMMC5::tick(bool sysTick) {
chan[i].std.next();
if (chan[i].std.vol.had) {
// ok, why are the volumes like that?
chan[i].outVol=MIN(15,chan[i].std.vol.val)-(15-(chan[i].vol&15));
chan[i].outVol=VOL_SCALE_LINEAR_BROKEN(chan[i].vol&15,MIN(15,chan[i].std.vol.val),15);
if (chan[i].outVol<0) chan[i].outVol=0;
rWrite(0x5000+i*4,0x30|chan[i].outVol|((chan[i].duty&3)<<6));
}
@ -353,6 +353,10 @@ void* DivPlatformMMC5::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformMMC5::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformMMC5::getOscBuffer(int ch) {
return oscBuf[ch];
}

View File

@ -74,6 +74,7 @@ class DivPlatformMMC5: public DivDispatch {
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();

View File

@ -31,50 +31,73 @@ const char** DivPlatformMSM6258::getRegisterSheet() {
}
const char* DivPlatformMSM6258::getEffectName(unsigned char effect) {
switch (effect) {
case 0x20:
return "20xx: Set frequency divider (0-2)";
break;
case 0x21:
return "21xx: Select clock rate (0: full; 1: half)";
break;
}
return NULL;
}
void DivPlatformMSM6258::acquire(short* bufL, short* bufR, size_t start, size_t len) {
short* outs[2]={
&msmOut,
NULL
};
for (size_t h=start; h<start+len; h++) {
short* outs[2]={
&bufL[h],
NULL
};
if (!writes.empty()) {
QueuedWrite& w=writes.front();
switch (w.addr) {
case 0:
msm->ctrl_w(w.val);
break;
}
writes.pop();
}
if (sample>=0 && sample<parent->song.sampleLen) {
DivSample* s=parent->getSample(sample);
unsigned char nextData=(s->dataVOX[samplePos]>>4)|(s->dataVOX[samplePos]<<4);
if (msm->data_w(nextData)) {
samplePos++;
if (samplePos>=(int)s->lengthVOX) {
sample=-1;
samplePos=0;
msm->ctrl_w(1);
if (--msmClockCount<0) {
if (--msmDividerCount<=0) {
if (!writes.empty()) {
QueuedWrite& w=writes.front();
switch (w.addr) {
case 0:
msm->ctrl_w(w.val);
break;
case 2:
msmPan=w.val;
break;
case 8:
msmClock=w.val;
break;
case 12:
msmDivider=4-(w.val&3);
if (msmDivider<2) msmDivider=2;
break;
}
writes.pop();
}
if (sample>=0 && sample<parent->song.sampleLen) {
DivSample* s=parent->getSample(sample);
unsigned char nextData=(s->dataVOX[samplePos]>>4)|(s->dataVOX[samplePos]<<4);
if (msm->data_w(nextData)) {
samplePos++;
if (samplePos>=(int)s->lengthVOX) {
sample=-1;
samplePos=0;
msm->ctrl_w(1);
}
}
}
msm->sound_stream_update(outs,1);
msmDividerCount=msmDivider;
}
msmClockCount=msmClock;
}
msm->sound_stream_update(outs,1);
if (isMuted[0]) {
bufL[h]=0;
bufR[h]=0;
oscBuf[0]->data[oscBuf[0]->needle++]=0;
} else {
bufL[h]=(msmPan&2)?msmOut:0;
bufR[h]=(msmPan&1)?msmOut:0;
oscBuf[0]->data[oscBuf[0]->needle++]=msmPan?msmOut:0;
}
/*if (++updateOsc>=22) {
updateOsc=0;
// TODO: per-channel osc
for (int i=0; i<1; i++) {
oscBuf[i]->data[oscBuf[i]->needle++]=msm->m_voice[i].m_muted?0:(msm->m_voice[i].m_out<<6);
}
}*/
}
}
@ -176,6 +199,23 @@ int DivPlatformMSM6258::dispatch(DivCommand c) {
sampleBank=parent->song.sample.size()/12;
}
break;
case DIV_CMD_SAMPLE_FREQ:
rateSel=c.value&3;
rWrite(12,rateSel);
break;
case DIV_CMD_SAMPLE_MODE:
clockSel=c.value&1;
rWrite(8,clockSel);
break;
case DIV_CMD_PANNING: {
if (c.value==0 && c.value2==0) {
chan[c.chan].pan=3;
} else {
chan[c.chan].pan=(c.value2>0)|((c.value>0)<<1);
}
rWrite(2,chan[c.chan].pan);
break;
}
case DIV_CMD_LEGATO: {
break;
}
@ -205,12 +245,19 @@ void DivPlatformMSM6258::forceIns() {
for (int i=0; i<1; i++) {
chan[i].insChanged=true;
}
rWrite(12,rateSel);
rWrite(8,clockSel);
rWrite(2,chan[0].pan);
}
void* DivPlatformMSM6258::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformMSM6258::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformMSM6258::getOscBuffer(int ch) {
return oscBuf[ch];
}
@ -234,6 +281,14 @@ void DivPlatformMSM6258::poke(std::vector<DivRegWrite>& wlist) {
void DivPlatformMSM6258::reset() {
while (!writes.empty()) writes.pop();
msm->device_reset();
msmClock=chipClock;
msmDivider=2;
msmDividerCount=0;
msmClock=0;
msmClockCount=0;
msmPan=3;
rateSel=2;
clockSel=0;
if (dumpWrites) {
addWrite(0xffffffff,0);
}
@ -253,6 +308,10 @@ void DivPlatformMSM6258::reset() {
delay=0;
}
bool DivPlatformMSM6258::isStereo() {
return true;
}
bool DivPlatformMSM6258::keyOffAffectsArp(int ch) {
return false;
}
@ -307,15 +366,23 @@ void DivPlatformMSM6258::renderSamples() {
}
void DivPlatformMSM6258::setFlags(unsigned int flags) {
if (flags&1) {
chipClock=4096000;
} else {
chipClock=4000000;
switch (flags) {
case 3:
chipClock=8192000;
break;
case 2:
chipClock=8000000;
break;
case 1:
chipClock=4096000;
break;
default:
chipClock=4000000;
break;
}
rate=chipClock/256;
rate=chipClock/128;
for (int i=0; i<1; i++) {
isMuted[i]=false;
oscBuf[i]->rate=rate/256;
oscBuf[i]->rate=rate;
}
}

View File

@ -81,7 +81,9 @@ class DivPlatformMSM6258: public DivDispatch {
unsigned char* adpcmMem;
size_t adpcmMemLen;
unsigned char sampleBank;
unsigned char sampleBank, msmPan, msmDivider, rateSel, msmClock, clockSel;
signed char msmDividerCount, msmClockCount;
short msmOut;
int delay, updateOsc, sample, samplePos;
@ -91,6 +93,7 @@ class DivPlatformMSM6258: public DivDispatch {
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();
@ -98,6 +101,7 @@ class DivPlatformMSM6258: public DivDispatch {
void forceIns();
void tick(bool sysTick=true);
void muteChannel(int ch, bool mute);
bool isStereo();
bool keyOffAffectsArp(int ch);
void notifyInsChange(int ins);
void notifyInsDeletion(void* ins);

View File

@ -230,6 +230,10 @@ void* DivPlatformMSM6295::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformMSM6295::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformMSM6295::getOscBuffer(int ch) {
return oscBuf[ch];
}
@ -394,7 +398,6 @@ void DivPlatformMSM6295::setFlags(unsigned int flags) {
}
rate=chipClock/3;
for (int i=0; i<4; i++) {
isMuted[i]=false;
oscBuf[i]->rate=rate/22;
}
if (rateSel!=rateSelInit) {

View File

@ -81,6 +81,7 @@ class DivPlatformMSM6295: public DivDispatch, public vgsound_emu_mem_intf {
virtual void acquire(short* bufL, short* bufR, size_t start, size_t len) override;
virtual int dispatch(DivCommand c) override;
virtual void* getChanState(int chan) override;
virtual DivMacroInt* getChanMacroInt(int ch) override;
virtual DivDispatchOscBuffer* getOscBuffer(int chan) override;
virtual unsigned char* getRegisterPool() override;
virtual int getRegisterPoolSize() override;
@ -101,7 +102,7 @@ class DivPlatformMSM6295: public DivDispatch, public vgsound_emu_mem_intf {
virtual size_t getSampleMemCapacity(int index) override;
virtual size_t getSampleMemUsage(int index) override;
virtual void renderSamples() override;
virtual int init(DivEngine* parent, int channels, int sugRate, unsigned int flags) override;
virtual void quit() override;
DivPlatformMSM6295():

View File

@ -625,6 +625,10 @@ void* DivPlatformN163::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformN163::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformN163::getOscBuffer(int ch) {
return oscBuf[ch];
}

View File

@ -95,6 +95,7 @@ class DivPlatformN163: public DivDispatch {
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();

View File

@ -467,6 +467,10 @@ void* DivPlatformNamcoWSG::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformNamcoWSG::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformNamcoWSG::getOscBuffer(int ch) {
return oscBuf[ch];
}

View File

@ -79,6 +79,7 @@ class DivPlatformNamcoWSG: public DivDispatch {
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();

View File

@ -230,7 +230,7 @@ void DivPlatformNES::tick(bool sysTick) {
chan[i].std.next();
if (chan[i].std.vol.had) {
// ok, why are the volumes like that?
chan[i].outVol=MIN(15,chan[i].std.vol.val)-(15-(chan[i].vol&15));
chan[i].outVol=VOL_SCALE_LINEAR_BROKEN(chan[i].vol&15,MIN(15,chan[i].std.vol.val),15);
if (chan[i].outVol<0) chan[i].outVol=0;
if (i==2) { // triangle
rWrite(0x4000+i*4,(chan[i].outVol==0)?0:255);
@ -610,6 +610,10 @@ void* DivPlatformNES::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformNES::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformNES::getOscBuffer(int ch) {
return oscBuf[ch];
}

View File

@ -89,6 +89,7 @@ class DivPlatformNES: public DivDispatch {
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();

View File

@ -347,7 +347,7 @@ void DivPlatformOPL::tick(bool sysTick) {
chan[i].std.next();
if (chan[i].std.vol.had) {
chan[i].outVol=(chan[i].vol*MIN(63,chan[i].std.vol.val))/63;
chan[i].outVol=VOL_SCALE_LOG(chan[i].vol,MIN(63,chan[i].std.vol.val),63);
for (int j=0; j<ops; j++) {
unsigned char slot=slots[j][i];
if (slot==255) continue;
@ -358,7 +358,7 @@ void DivPlatformOPL::tick(bool sysTick) {
rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
} else {
if (isOutputL[ops==4][chan[i].state.alg][j] || i>melodicChans) {
rWrite(baseAddr+ADDR_KSL_TL,(63-(((63-op.tl)*(chan[i].outVol&0x3f))/63))|(op.ksl<<6));
rWrite(baseAddr+ADDR_KSL_TL,(63-VOL_SCALE_LOG(63-op.tl,chan[i].outVol&0x3f,63))|(op.ksl<<6));
} else {
rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6));
}
@ -485,7 +485,7 @@ void DivPlatformOPL::tick(bool sysTick) {
rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
} else {
if (isOutputL[ops==4][chan[i].state.alg][j] || i>melodicChans) {
rWrite(baseAddr+ADDR_KSL_TL,(63-(((63-op.tl)*(chan[i].outVol&0x3f))/63))|(op.ksl<<6));
rWrite(baseAddr+ADDR_KSL_TL,(63-VOL_SCALE_LOG(63-op.tl,chan[i].outVol&0x3f,63))|(op.ksl<<6));
} else {
rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6));
}
@ -694,7 +694,7 @@ void DivPlatformOPL::muteChannel(int ch, bool mute) {
rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
} else {
if (isOutputL[ops==4][chan[ch].state.alg][i] || ch>melodicChans) {
rWrite(baseAddr+ADDR_KSL_TL,(63-(((63-op.tl)*(chan[ch].outVol&0x3f))/63))|(op.ksl<<6));
rWrite(baseAddr+ADDR_KSL_TL,(63-VOL_SCALE_LOG(63-op.tl,chan[ch].outVol&0x3f,63))|(op.ksl<<6));
} else {
rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6));
}
@ -825,7 +825,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
if (isMuted[ch]) {
rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
} else {
rWrite(baseAddr+ADDR_KSL_TL,(63-(((63-op.tl)*(chan[ch].outVol&0x3f))/63))|(op.ksl<<6));
rWrite(baseAddr+ADDR_KSL_TL,(63-VOL_SCALE_LOG(63-op.tl,chan[ch].outVol&0x3f,63))|(op.ksl<<6));
}
rWrite(baseAddr+ADDR_AM_VIB_SUS_KSR_MULT,(op.am<<7)|(op.vib<<6)|(op.sus<<5)|(op.ksr<<4)|op.mult);
@ -860,7 +860,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
} else {
if (isOutputL[ops==4][chan[c.chan].state.alg][i] || c.chan>melodicChans) {
rWrite(baseAddr+ADDR_KSL_TL,(63-(((63-op.tl)*(chan[c.chan].outVol&0x3f))/63))|(op.ksl<<6));
rWrite(baseAddr+ADDR_KSL_TL,(63-VOL_SCALE_LOG(63-op.tl,chan[c.chan].outVol&0x3f,63))|(op.ksl<<6));
} else {
rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6));
}
@ -976,7 +976,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
} else {
if (isOutputL[ops==4][chan[c.chan].state.alg][i] || c.chan>melodicChans) {
rWrite(baseAddr+ADDR_KSL_TL,(63-(((63-op.tl)*(chan[c.chan].outVol&0x3f))/63))|(op.ksl<<6));
rWrite(baseAddr+ADDR_KSL_TL,(63-VOL_SCALE_LOG(63-op.tl,chan[c.chan].outVol&0x3f,63))|(op.ksl<<6));
} else {
rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6));
}
@ -1125,7 +1125,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
} else {
if (isOutputL[ops==4][chan[c.chan].state.alg][c.value] || c.chan>melodicChans) {
rWrite(baseAddr+ADDR_KSL_TL,(63-(((63-op.tl)*(chan[c.chan].outVol&0x3f))/63))|(op.ksl<<6));
rWrite(baseAddr+ADDR_KSL_TL,(63-VOL_SCALE_LOG(63-op.tl,chan[c.chan].outVol&0x3f,63))|(op.ksl<<6));
} else {
rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6));
}
@ -1355,7 +1355,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
} else {
if (isOutputL[ops==4][chan[c.chan].state.alg][i] || c.chan>melodicChans) {
rWrite(baseAddr+ADDR_KSL_TL,(63-(((63-op.tl)*(chan[c.chan].outVol&0x3f))/63))|(op.ksl<<6));
rWrite(baseAddr+ADDR_KSL_TL,(63-VOL_SCALE_LOG(63-op.tl,chan[c.chan].outVol&0x3f,63))|(op.ksl<<6));
} else {
rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6));
}
@ -1372,7 +1372,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
} else {
if (isOutputL[ops==4][chan[c.chan].state.alg][c.value] || c.chan>melodicChans) {
rWrite(baseAddr+ADDR_KSL_TL,(63-(((63-op.tl)*(chan[c.chan].outVol&0x3f))/63))|(op.ksl<<6));
rWrite(baseAddr+ADDR_KSL_TL,(63-VOL_SCALE_LOG(63-op.tl,chan[c.chan].outVol&0x3f,63))|(op.ksl<<6));
} else {
rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6));
}
@ -1446,7 +1446,7 @@ void DivPlatformOPL::forceIns() {
rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
} else {
if (isOutputL[ops==4][chan[i].state.alg][j] || i>melodicChans) {
rWrite(baseAddr+ADDR_KSL_TL,(63-(((63-op.tl)*(chan[i].outVol&0x3f))/63))|(op.ksl<<6));
rWrite(baseAddr+ADDR_KSL_TL,(63-VOL_SCALE_LOG(63-op.tl,chan[i].outVol&0x3f,63))|(op.ksl<<6));
} else {
rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6));
}
@ -1488,6 +1488,10 @@ void* DivPlatformOPL::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformOPL::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformOPL::getOscBuffer(int ch) {
if (ch>=18) return NULL;
return oscBuf[ch];

View File

@ -125,6 +125,7 @@ class DivPlatformOPL: public DivDispatch {
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();

View File

@ -149,9 +149,9 @@ void DivPlatformOPLL::tick(bool sysTick) {
chan[i].std.next();
if (chan[i].std.vol.had) {
chan[i].outVol=(chan[i].vol*MIN(15,chan[i].std.vol.val))/15;
chan[i].outVol=VOL_SCALE_LOG(chan[i].vol,MIN(15,chan[i].std.vol.val),15);
if (i<9) {
rWrite(0x30+i,((15-(chan[i].outVol*(15-chan[i].state.op[1].tl))/15)&15)|(chan[i].state.opllPreset<<4));
rWrite(0x30+i,((15-VOL_SCALE_LOG(chan[i].outVol,15-chan[i].state.op[1].tl,15))&15)|(chan[i].state.opllPreset<<4));
}
}
@ -174,7 +174,7 @@ void DivPlatformOPLL::tick(bool sysTick) {
if (chan[i].std.wave.had && chan[i].state.opllPreset!=16) {
chan[i].state.opllPreset=chan[i].std.wave.val;
if (i<9) {
rWrite(0x30+i,((15-(chan[i].outVol*(15-chan[i].state.op[1].tl))/15)&15)|(chan[i].state.opllPreset<<4));
rWrite(0x30+i,((15-VOL_SCALE_LOG(chan[i].outVol,15-chan[i].state.op[1].tl,15))&15)|(chan[i].state.opllPreset<<4));
}
}
@ -244,7 +244,7 @@ void DivPlatformOPLL::tick(bool sysTick) {
op.tl=((j==1)?15:63)-m.tl.val;
if (j==1) {
if (i<9) {
rWrite(0x30+i,((15-(chan[i].outVol*(15-chan[i].state.op[1].tl))/15)&15)|(chan[i].state.opllPreset<<4));
rWrite(0x30+i,((15-VOL_SCALE_LOG(chan[i].outVol,15-chan[i].state.op[1].tl,15))&15)|(chan[i].state.opllPreset<<4));
}
} else {
rWrite(0x02,(chan[i].state.op[0].ksl<<6)|(op.tl&63));
@ -384,21 +384,6 @@ int DivPlatformOPLL::toFreq(int freq) {
void DivPlatformOPLL::muteChannel(int ch, bool mute) {
isMuted[ch]=mute;
/*
for (int j=0; j<4; j++) {
unsigned short baseAddr=chanOffs[ch]|opOffs[j];
DivInstrumentFM::Operator& op=chan[ch].state.op[j];
if (isMuted[ch]) {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (isOutput[chan[ch].state.alg][j]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[ch].outVol&0x7f))/127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
}
}
rWrite(chanOffs[ch]+ADDR_LRAF,(isMuted[ch]?0:(chan[ch].pan<<6))|(chan[ch].state.fms&7)|((chan[ch].state.ams&3)<<4));*/
}
int DivPlatformOPLL::dispatch(DivCommand c) {
@ -483,7 +468,7 @@ int DivPlatformOPLL::dispatch(DivCommand c) {
}
}
if (c.chan<9) {
rWrite(0x30+c.chan,((15-(chan[c.chan].outVol*(15-chan[c.chan].state.op[1].tl))/15)&15)|(chan[c.chan].state.opllPreset<<4));
rWrite(0x30+c.chan,((15-VOL_SCALE_LOG(chan[c.chan].outVol,15-chan[c.chan].state.op[1].tl,15))&15)|(chan[c.chan].state.opllPreset<<4));
}
}
}
@ -553,7 +538,7 @@ int DivPlatformOPLL::dispatch(DivCommand c) {
break;
} else if (c.chan<6 || !drums) {
if (c.chan<9) {
rWrite(0x30+c.chan,((15-(chan[c.chan].outVol*(15-chan[c.chan].state.op[1].tl))/15)&15)|(chan[c.chan].state.opllPreset<<4));
rWrite(0x30+c.chan,((15-VOL_SCALE_LOG(chan[c.chan].outVol,15-chan[c.chan].state.op[1].tl,15))&15)|(chan[c.chan].state.opllPreset<<4));
}
}
break;
@ -647,7 +632,7 @@ int DivPlatformOPLL::dispatch(DivCommand c) {
DivInstrumentFM::Operator& car=chan[c.chan].state.op[1];
car.tl=c.value2&15;
if (c.chan<9) {
rWrite(0x30+c.chan,((15-(chan[c.chan].outVol*(15-chan[c.chan].state.op[1].tl))/15)&15)|(chan[c.chan].state.opllPreset<<4));
rWrite(0x30+c.chan,((15-VOL_SCALE_LOG(chan[c.chan].outVol,15-chan[c.chan].state.op[1].tl,15))&15)|(chan[c.chan].state.opllPreset<<4));
}
}
break;
@ -862,7 +847,7 @@ void DivPlatformOPLL::forceIns() {
rWrite(0x07,(car.sl<<4)|(car.rr));
}
if (i<9) {
rWrite(0x30+i,((15-(chan[i].outVol*(15-chan[i].state.op[1].tl))/15)&15)|(chan[i].state.opllPreset<<4));
rWrite(0x30+i,((15-VOL_SCALE_LOG(chan[i].outVol,15-chan[i].state.op[1].tl,15))&15)|(chan[i].state.opllPreset<<4));
}
if (!(i>=6 && properDrums)) {
if (chan[i].active) {
@ -911,6 +896,10 @@ void* DivPlatformOPLL::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformOPLL::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformOPLL::getOscBuffer(int ch) {
if (ch>=9) return NULL;
return oscBuf[ch];

View File

@ -102,6 +102,7 @@ class DivPlatformOPLL: public DivDispatch {
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();

View File

@ -154,7 +154,7 @@ void DivPlatformPCE::tick(bool sysTick) {
for (int i=0; i<6; i++) {
chan[i].std.next();
if (chan[i].std.vol.had) {
chan[i].outVol=((chan[i].vol&31)*MIN(31,chan[i].std.vol.val))>>5;
chan[i].outVol=VOL_SCALE_LOG(chan[i].vol&31,MIN(31,chan[i].std.vol.val),31);
if (chan[i].furnaceDac && chan[i].pcm) {
// ignore for now
} else {
@ -477,6 +477,10 @@ void* DivPlatformPCE::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformPCE::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformPCE::getOscBuffer(int ch) {
return oscBuf[ch];
}

View File

@ -89,6 +89,7 @@ class DivPlatformPCE: public DivDispatch {
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();

View File

@ -19,6 +19,7 @@
#include "pcspkr.h"
#include "../engine.h"
#include "../../ta-log.h"
#include <math.h>
#ifdef __linux__
@ -28,6 +29,8 @@
#include <unistd.h>
#include <linux/input.h>
#include <linux/kd.h>
#include <time.h>
#include <sys/io.h>
#endif
#define PCSPKR_DIVIDER 4
@ -38,6 +41,137 @@ const char* regCheatSheetPCSpeaker[]={
NULL
};
void _pcSpeakerThread(void* inst) {
((DivPlatformPCSpeaker*)inst)->pcSpeakerThread();
}
void DivPlatformPCSpeaker::pcSpeakerThread() {
std::unique_lock<std::mutex> unique(realOutSelfLock);
RealQueueVal r(0,0,0);
logD("starting PC speaker out thread");
while (!realOutQuit) {
realQueueLock.lock();
if (realQueue.empty()) {
realQueueLock.unlock();
realOutCond.wait(unique);
continue;
} else {
r=realQueue.front();
realQueue.pop();
}
realQueueLock.unlock();
#ifdef __linux__
static struct timespec ts, tSleep, rSleep;
if (clock_gettime(CLOCK_MONOTONIC,&ts)<0) {
logW("could not get time!");
tSleep.tv_sec=0;
tSleep.tv_nsec=0;
} else {
tSleep.tv_sec=r.tv_sec-ts.tv_sec;
tSleep.tv_nsec=r.tv_nsec-ts.tv_nsec;
if (tSleep.tv_nsec<0) {
tSleep.tv_sec--;
tSleep.tv_nsec+=1000000000;
}
}
if (tSleep.tv_nsec>0 || tSleep.tv_sec>0) {
nanosleep(&tSleep,&rSleep);
}
if (beepFD>=0) {
switch (realOutMethod) {
case 0: { // evdev
static struct input_event ie;
ie.time.tv_sec=r.tv_sec;
ie.time.tv_usec=r.tv_nsec/1000;
ie.type=EV_SND;
ie.code=SND_TONE;
if (r.val>0) {
ie.value=chipClock/r.val;
} else {
ie.value=0;
}
if (write(beepFD,&ie,sizeof(struct input_event))<0) {
logW("error while writing frequency! %s",strerror(errno));
} else {
//logV("writing freq: %d",r.val);
}
break;
}
case 1: // KIOCSOUND (on tty)
if (ioctl(beepFD,KIOCSOUND,r.val)<0) {
logW("ioctl error! %s",strerror(errno));
}
break;
case 2: { // /dev/port
unsigned char bOut;
bOut=0;
if (r.val==0) {
lseek(beepFD,0x61,SEEK_SET);
if (read(beepFD,&bOut,1)<1) {
logW("read from 0x61: %s",strerror(errno));
}
bOut&=(~3);
lseek(beepFD,0x61,SEEK_SET);
if (write(beepFD,&bOut,1)<1) {
logW("write to 0x61: %s",strerror(errno));
}
} else {
lseek(beepFD,0x43,SEEK_SET);
bOut=0xb6;
if (write(beepFD,&bOut,1)<1) {
logW("write to 0x43: %s",strerror(errno));
}
lseek(beepFD,0x42,SEEK_SET);
bOut=r.val&0xff;
if (write(beepFD,&bOut,1)<1) {
logW("write to 0x42: %s",strerror(errno));
}
lseek(beepFD,0x42,SEEK_SET);
bOut=r.val>>8;
if (write(beepFD,&bOut,1)<1) {
logW("write to 0x42: %s",strerror(errno));
}
lseek(beepFD,0x61,SEEK_SET);
if (read(beepFD,&bOut,1)<1) {
logW("read from 0x61: %s",strerror(errno));
}
bOut|=3;
lseek(beepFD,0x61,SEEK_SET);
if (write(beepFD,&bOut,1)<1) {
logW("write to 0x61: %s",strerror(errno));
}
}
break;
}
case 3: // KIOCSOUND (on stdout)
if (ioctl(beepFD,KIOCSOUND,r.val)<0) {
logW("ioctl error! %s",strerror(errno));
}
break;
case 4: // outb()
if (r.val==0) {
outb(inb(0x61)&(~3),0x61);
realOutEnabled=false;
} else {
outb(0xb6,0x43);
outb(r.val&0xff,0x42);
outb(r.val>>8,0x42);
if (!realOutEnabled) {
outb(inb(0x61)|3,0x61);
realOutEnabled=true;
}
}
break;
}
} else {
//logV("not writing because fd is less than 0");
}
#endif
}
logD("stopping PC speaker out thread");
}
const char** DivPlatformPCSpeaker::getRegisterSheet() {
return regCheatSheetPCSpeaker;
}
@ -126,25 +260,28 @@ void DivPlatformPCSpeaker::acquire_piezo(short* bufL, short* bufR, size_t start,
}
}
void DivPlatformPCSpeaker::beepFreq(int freq) {
void DivPlatformPCSpeaker::beepFreq(int freq, int delay) {
realQueueLock.lock();
#ifdef __linux__
static struct input_event ie;
if (beepFD>=0) {
gettimeofday(&ie.time,NULL);
ie.type=EV_SND;
ie.code=SND_TONE;
if (freq>0) {
ie.value=chipClock/freq;
} else {
ie.value=0;
}
if (write(beepFD,&ie,sizeof(struct input_event))<0) {
perror("error while writing frequency!");
} else {
//printf("writing freq: %d\n",freq);
struct timespec ts;
double addition=1000000000.0*(double)delay/(double)rate;
addition+=1500000000.0*((double)parent->getAudioDescGot().bufsize/parent->getAudioDescGot().rate);
if (clock_gettime(CLOCK_MONOTONIC,&ts)<0) {
ts.tv_sec=0;
ts.tv_nsec=0;
} else {
ts.tv_nsec+=addition;
while (ts.tv_nsec>=1000000000) {
ts.tv_sec++;
ts.tv_nsec-=1000000000;
}
}
realQueue.push(RealQueueVal(ts.tv_sec,ts.tv_nsec,freq));
#else
realQueue.push(RealQueueVal(0,0,freq));
#endif
realQueueLock.unlock();
realOutCond.notify_one();
}
void DivPlatformPCSpeaker::acquire_real(short* bufL, short* bufR, size_t start, size_t len) {
@ -152,7 +289,7 @@ void DivPlatformPCSpeaker::acquire_real(short* bufL, short* bufR, size_t start,
if (lastOn!=on || lastFreq!=freq) {
lastOn=on;
lastFreq=freq;
beepFreq((on && !isMuted[0])?freq:0);
beepFreq((on && !isMuted[0])?freq:0,start);
}
for (size_t i=start; i<start+len; i++) {
if (on) {
@ -348,6 +485,10 @@ void* DivPlatformPCSpeaker::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformPCSpeaker::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformPCSpeaker::getOscBuffer(int ch) {
return oscBuf;
}
@ -385,18 +526,51 @@ void DivPlatformPCSpeaker::reset() {
low=0;
band=0;
if (speakerType==3) {
//if (speakerType==3) {
#ifdef __linux__
if (beepFD==-1) {
beepFD=open("/dev/input/by-path/platform-pcspkr-event-spkr",O_WRONLY);
switch (realOutMethod) {
case 0: // evdev
beepFD=open("/dev/input/by-path/platform-pcspkr-event-spkr",O_WRONLY);
break;
case 1: // KIOCSOUND (on tty)
beepFD=open("/dev/tty1",O_WRONLY);
break;
case 2: // /dev/port
beepFD=open("/dev/port",O_WRONLY);
break;
case 3: // KIOCSOUND (on stdout)
beepFD=STDOUT_FILENO;
break;
case 4: // outb()
beepFD=-1;
if (ioperm(0x61,8,1)<0) {
logW("ioperm 0x61: %s",strerror(errno));
break;
}
if (ioperm(0x43,8,1)<0) {
logW("ioperm 0x43: %s",strerror(errno));
break;
}
if (ioperm(0x42,8,1)<0) {
logW("ioperm 0x42: %s",strerror(errno));
break;
}
beepFD=STDOUT_FILENO;
break;
}
if (beepFD<0) {
perror("error while opening PC speaker");
logW("error while opening PC speaker! %s",strerror(errno));
}
}
#endif
beepFreq(0);
} else {
/*} else {
beepFreq(0);
}*/
if (realOutThread==NULL) {
realOutThread=new std::thread(_pcSpeakerThread,this);
}
memset(regPool,0,2);
@ -436,6 +610,10 @@ int DivPlatformPCSpeaker::init(DivEngine* p, int channels, int sugRate, unsigned
dumpWrites=false;
skipRegisterWrites=false;
beepFD=-1;
realOutQuit=false;
realOutThread=NULL;
realOutMethod=parent->getConfInt("pcSpeakerOutMethod",0);
realOutEnabled=false;
for (int i=0; i<1; i++) {
isMuted[i]=false;
}
@ -450,8 +628,14 @@ void DivPlatformPCSpeaker::quit() {
if (speakerType==3) {
beepFreq(0);
}
if (realOutThread!=NULL) {
realOutQuit=true;
realOutCond.notify_one();
realOutThread->join();
delete realOutThread;
}
#ifdef __linux__
if (beepFD>=0) close(beepFD);
if (beepFD>=0 && realOutMethod<3) close(beepFD);
#endif
delete oscBuf;
}

View File

@ -22,6 +22,10 @@
#include "../dispatch.h"
#include "../macroInt.h"
#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>
class DivPlatformPCSpeaker: public DivDispatch {
struct Channel {
@ -57,9 +61,23 @@ class DivPlatformPCSpeaker: public DivDispatch {
};
Channel chan[1];
DivDispatchOscBuffer* oscBuf;
std::thread* realOutThread;
std::mutex realOutSelfLock;
std::condition_variable realOutCond;
bool realOutQuit;
struct RealQueueVal {
int tv_sec, tv_nsec;
unsigned short val;
RealQueueVal(int sec, int nsec, unsigned short v):
tv_sec(sec),
tv_nsec(nsec),
val(v) {}
};
std::queue<RealQueueVal> realQueue;
std::mutex realQueueLock;
bool isMuted[1];
bool on, flip, lastOn;
int pos, speakerType, beepFD;
bool on, flip, lastOn, realOutEnabled;
int pos, speakerType, beepFD, realOutMethod;
float low, band;
float low2, high2, band2;
float low3, band3;
@ -68,7 +86,7 @@ class DivPlatformPCSpeaker: public DivDispatch {
friend void putDispatchChan(void*,int,int);
void beepFreq(int freq);
void beepFreq(int freq, int delay=0);
void acquire_unfilt(short* bufL, short* bufR, size_t start, size_t len);
void acquire_cone(short* bufL, short* bufR, size_t start, size_t len);
@ -76,9 +94,11 @@ class DivPlatformPCSpeaker: public DivDispatch {
void acquire_real(short* bufL, short* bufR, size_t start, size_t len);
public:
void pcSpeakerThread();
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();

View File

@ -249,6 +249,10 @@ void* DivPlatformPET::getChanState(int ch) {
return &chan;
}
DivMacroInt* DivPlatformPET::getChanMacroInt(int ch) {
return &chan.std;
}
DivDispatchOscBuffer* DivPlatformPET::getOscBuffer(int ch) {
return oscBuf;
}

View File

@ -66,6 +66,7 @@ class DivPlatformPET: public DivDispatch {
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();

View File

@ -530,6 +530,10 @@ void* DivPlatformQSound::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformQSound::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformQSound::getOscBuffer(int ch) {
return oscBuf[ch];
}

View File

@ -78,6 +78,7 @@ class DivPlatformQSound: public DivDispatch {
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();

View File

@ -300,6 +300,10 @@ void* DivPlatformRF5C68::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformRF5C68::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformRF5C68::getOscBuffer(int ch) {
return oscBuf[ch];
}

View File

@ -75,6 +75,7 @@ class DivPlatformRF5C68: public DivDispatch {
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();

View File

@ -136,7 +136,7 @@ void DivPlatformSAA1099::tick(bool sysTick) {
for (int i=0; i<6; i++) {
chan[i].std.next();
if (chan[i].std.vol.had) {
chan[i].outVol=MIN(15,chan[i].std.vol.val)-(15-(chan[i].vol&15));
chan[i].outVol=VOL_SCALE_LINEAR_BROKEN(chan[i].vol&15,MIN(15,chan[i].std.vol.val),15);
if (chan[i].outVol<0) chan[i].outVol=0;
if (isMuted[i]) {
rWrite(i,0);
@ -401,6 +401,10 @@ void* DivPlatformSAA1099::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformSAA1099::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformSAA1099::getOscBuffer(int ch) {
return oscBuf[ch];
}

View File

@ -91,6 +91,7 @@ class DivPlatformSAA1099: public DivDispatch {
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();

View File

@ -310,6 +310,10 @@ void* DivPlatformSCC::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformSCC::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformSCC::getOscBuffer(int ch) {
return oscBuf[ch];
}

View File

@ -70,6 +70,7 @@ class DivPlatformSCC: public DivDispatch {
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();

View File

@ -85,10 +85,17 @@ void DivPlatformSegaPCM::tick(bool sysTick) {
for (int i=0; i<16; i++) {
chan[i].std.next();
// TODO: fix
/*if (chan[i].std.vol.had) {
chan[i].outVol=(chan[i].vol*MIN(127,chan[i].std.vol.val))/127;
}*/
if (parent->song.newSegaPCM) {
if (chan[i].std.vol.had) {
chan[i].outVol=(chan[i].vol*MIN(64,chan[i].std.vol.val))>>6;
chan[i].chVolL=(chan[i].outVol*chan[i].chPanL)/127;
chan[i].chVolR=(chan[i].outVol*chan[i].chPanR)/127;
if (dumpWrites) {
addWrite(0x10002+(i<<3),chan[i].chVolL);
addWrite(0x10003+(i<<3),chan[i].chVolR);
}
}
}
if (chan[i].std.arp.had) {
if (!chan[i].inPorta) {
@ -107,14 +114,24 @@ void DivPlatformSegaPCM::tick(bool sysTick) {
}
if (chan[i].std.panL.had) {
chan[i].chVolL=chan[i].std.panL.val&127;
if (parent->song.newSegaPCM) {
chan[i].chPanL=chan[i].std.panL.val&127;
chan[i].chVolL=(chan[i].outVol*chan[i].chPanL)/127;
} else {
chan[i].chVolL=chan[i].std.panL.val&127;
}
if (dumpWrites) {
addWrite(0x10002+(i<<3),chan[i].chVolL);
}
}
if (chan[i].std.panR.had) {
chan[i].chVolR=chan[i].std.panR.val&127;
if (parent->song.newSegaPCM) {
chan[i].chPanR=chan[i].std.panR.val&127;
chan[i].chVolR=(chan[i].outVol*chan[i].chPanR)/127;
} else {
chan[i].chVolR=chan[i].std.panR.val&127;
}
if (dumpWrites) {
addWrite(0x10003+(i<<3),chan[i].chVolR);
}
@ -261,8 +278,13 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) {
if (!chan[c.chan].std.vol.has) {
chan[c.chan].outVol=c.value;
}
chan[c.chan].chVolL=c.value;
chan[c.chan].chVolR=c.value;
if (parent->song.newSegaPCM) {
chan[c.chan].chVolL=(c.value*chan[c.chan].chPanL)/127;
chan[c.chan].chVolR=(c.value*chan[c.chan].chPanR)/127;
} else {
chan[c.chan].chVolL=c.value;
chan[c.chan].chVolR=c.value;
}
if (dumpWrites) {
addWrite(0x10002+(c.chan<<3),chan[c.chan].chVolL);
addWrite(0x10003+(c.chan<<3),chan[c.chan].chVolR);
@ -280,8 +302,15 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) {
chan[c.chan].ins=c.value;
break;
case DIV_CMD_PANNING: {
chan[c.chan].chVolL=c.value>>1;
chan[c.chan].chVolR=c.value2>>1;
if (parent->song.newSegaPCM) {
chan[c.chan].chPanL=c.value>>1;
chan[c.chan].chPanR=c.value2>>1;
chan[c.chan].chVolL=(chan[c.chan].outVol*chan[c.chan].chPanL)/127;
chan[c.chan].chVolR=(chan[c.chan].outVol*chan[c.chan].chPanR)/127;
} else {
chan[c.chan].chVolL=c.value>>1;
chan[c.chan].chVolR=c.value2>>1;
}
if (dumpWrites) {
addWrite(0x10002+(c.chan<<3),chan[c.chan].chVolL);
addWrite(0x10003+(c.chan<<3),chan[c.chan].chVolR);
@ -371,6 +400,10 @@ void* DivPlatformSegaPCM::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformSegaPCM::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformSegaPCM::getOscBuffer(int ch) {
return oscBuf[ch];
}

View File

@ -34,6 +34,7 @@ class DivPlatformSegaPCM: public DivDispatch {
bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, portaPause, furnacePCM;
int vol, outVol;
unsigned char chVolL, chVolR;
unsigned char chPanL, chPanR;
struct PCMChannel {
int sample;
@ -46,7 +47,29 @@ class DivPlatformSegaPCM: public DivDispatch {
std.init(which);
pitch2=0;
}
Channel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), pitch2(0), note(0), ins(-1), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), inPorta(false), portaPause(false), furnacePCM(false), vol(0), outVol(0), chVolL(127), chVolR(127) {}
Channel():
freqH(0),
freqL(0),
freq(0),
baseFreq(0),
pitch(0),
pitch2(0),
note(0),
ins(-1),
active(false),
insChanged(true),
freqChanged(false),
keyOn(false),
keyOff(false),
inPorta(false),
portaPause(false),
furnacePCM(false),
vol(0),
outVol(0),
chVolL(127),
chVolR(127),
chPanL(127),
chPanR(127) {}
};
Channel chan[16];
DivDispatchOscBuffer* oscBuf[16];
@ -78,6 +101,7 @@ class DivPlatformSegaPCM: public DivDispatch {
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();

View File

@ -75,14 +75,13 @@ void DivPlatformSMS::acquire_nuked(short* bufL, short* bufR, size_t start, size_
if (o<-32768) o=-32768;
if (o>32767) o=32767;
bufL[h]=o;
/*
for (int i=0; i<4; i++) {
if (isMuted[i]) {
oscBuf[i]->data[oscBuf[i]->needle++]=0;
} else {
oscBuf[i]->data[oscBuf[i]->needle++]=sn->get_channel_output(i);
oscBuf[i]->data[oscBuf[i]->needle++]=sn_nuked.vol_table[sn_nuked.volume_out[i]];
}
}*/
}
}
}
@ -359,6 +358,8 @@ int DivPlatformSMS::dispatch(DivCommand c) {
if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_STD));
}
chan[c.chan].inPorta=c.value;
// TODO: pre porta cancel arp compat flag
//if (chan[c.chan].inPorta) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note);
break;
case DIV_CMD_GET_VOLMAX:
return 15;
@ -391,6 +392,10 @@ void* DivPlatformSMS::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformSMS::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformSMS::getOscBuffer(int ch) {
return oscBuf[ch];
}

View File

@ -86,6 +86,7 @@ class DivPlatformSMS: public DivDispatch {
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
void reset();
void forceIns();

View File

@ -778,6 +778,10 @@ public:
// get the engine
fm_engine* debug_engine() { return &m_fm; }
// get DAC state
uint16_t debug_dac_data() { return m_dac_data; }
uint8_t debug_dac_enable() { return m_dac_enable; }
protected:
// simulate the DAC discontinuity
constexpr int32_t dac_discontinuity(int32_t value) const { return (value < 0) ? (value - 2) : (value + 3); }

View File

@ -484,6 +484,10 @@ void* DivPlatformSoundUnit::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformSoundUnit::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformSoundUnit::getOscBuffer(int ch) {
return oscBuf[ch];
}

View File

@ -112,6 +112,7 @@ class DivPlatformSoundUnit: public DivDispatch {
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();

View File

@ -464,6 +464,10 @@ void* DivPlatformSwan::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformSwan::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformSwan::getOscBuffer(int ch) {
return oscBuf[ch];
}

View File

@ -79,6 +79,7 @@ class DivPlatformSwan: public DivDispatch {
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();

View File

@ -88,7 +88,7 @@ void DivPlatformTIA::tick(bool sysTick) {
for (int i=0; i<2; i++) {
chan[i].std.next();
if (chan[i].std.vol.had) {
chan[i].outVol=MIN(15,chan[i].std.vol.val)-(15-(chan[i].vol&15));
chan[i].outVol=VOL_SCALE_LINEAR_BROKEN(chan[i].vol&15,MIN(15,chan[i].std.vol.val),15);
if (chan[i].outVol<0) chan[i].outVol=0;
if (isMuted[i]) {
rWrite(0x19+i,0);
@ -293,6 +293,10 @@ void* DivPlatformTIA::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformTIA::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformTIA::getOscBuffer(int ch) {
return oscBuf[ch];
}

View File

@ -52,6 +52,7 @@ class DivPlatformTIA: public DivDispatch {
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();

View File

@ -238,7 +238,7 @@ void DivPlatformTX81Z::tick(bool sysTick) {
chan[i].std.next();
if (chan[i].std.vol.had) {
chan[i].outVol=(chan[i].vol*MIN(127,chan[i].std.vol.val))/127;
chan[i].outVol=VOL_SCALE_LOG(chan[i].vol,MIN(127,chan[i].std.vol.val),127);
for (int j=0; j<4; j++) {
unsigned short baseAddr=chanOffs[i]|opOffs[j];
DivInstrumentFM::Operator& op=chan[i].state.op[j];
@ -246,7 +246,7 @@ void DivPlatformTX81Z::tick(bool sysTick) {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (isOutput[chan[i].state.alg][j]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -322,7 +322,7 @@ void DivPlatformTX81Z::tick(bool sysTick) {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (isOutput[chan[i].state.alg][j]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -375,7 +375,7 @@ void DivPlatformTX81Z::tick(bool sysTick) {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (isOutput[chan[i].state.alg][j]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -467,7 +467,7 @@ void DivPlatformTX81Z::muteChannel(int ch, bool mute) {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (isOutput[chan[ch].state.alg][i]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[ch].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[ch].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -497,7 +497,7 @@ int DivPlatformTX81Z::dispatch(DivCommand c) {
} else {
if (isOutput[chan[c.chan].state.alg][i]) {
if (!chan[c.chan].active || chan[c.chan].insChanged) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127));
}
} else {
if (chan[c.chan].insChanged) {
@ -562,7 +562,7 @@ int DivPlatformTX81Z::dispatch(DivCommand c) {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (isOutput[chan[c.chan].state.alg][i]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -662,7 +662,7 @@ int DivPlatformTX81Z::dispatch(DivCommand c) {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (isOutput[chan[c.chan].state.alg][c.value]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -953,7 +953,7 @@ void DivPlatformTX81Z::forceIns() {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (isOutput[chan[i].state.alg][j]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -995,6 +995,10 @@ void* DivPlatformTX81Z::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformTX81Z::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformTX81Z::getOscBuffer(int ch) {
return oscBuf[ch];
}

View File

@ -100,6 +100,7 @@ class DivPlatformTX81Z: public DivDispatch, public DivPlatformOPMBase {
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();

View File

@ -397,6 +397,10 @@ void* DivPlatformVERA::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformVERA::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformVERA::getOscBuffer(int ch) {
return oscBuf[ch];
}

View File

@ -66,6 +66,7 @@ class DivPlatformVERA: public DivDispatch {
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();

View File

@ -278,6 +278,10 @@ void* DivPlatformVIC20::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformVIC20::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformVIC20::getOscBuffer(int ch) {
return oscBuf[ch];
}

View File

@ -68,6 +68,7 @@ class DivPlatformVIC20: public DivDispatch {
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();

View File

@ -438,6 +438,10 @@ void* DivPlatformVRC6::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformVRC6::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformVRC6::getOscBuffer(int ch) {
return oscBuf[ch];
}

View File

@ -85,6 +85,7 @@ class DivPlatformVRC6: public DivDispatch {
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();

View File

@ -842,6 +842,10 @@ void* DivPlatformX1_010::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformX1_010::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformX1_010::getOscBuffer(int ch) {
return oscBuf[ch];
}

View File

@ -129,6 +129,7 @@ class DivPlatformX1_010: public DivDispatch {
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();

View File

@ -329,7 +329,7 @@ void DivPlatformYM2203::tick(bool sysTick) {
chan[i].std.next();
if (chan[i].std.vol.had) {
chan[i].outVol=(chan[i].vol*MIN(127,chan[i].std.vol.val))/127;
chan[i].outVol=VOL_SCALE_LOG(chan[i].vol,MIN(127,chan[i].std.vol.val),127);
for (int j=0; j<4; j++) {
unsigned short baseAddr=chanOffs[i]|opOffs[j];
DivInstrumentFM::Operator& op=chan[i].state.op[j];
@ -337,7 +337,7 @@ void DivPlatformYM2203::tick(bool sysTick) {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (isOutput[chan[i].state.alg][j]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -387,7 +387,7 @@ void DivPlatformYM2203::tick(bool sysTick) {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (isOutput[chan[i].state.alg][j]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -432,7 +432,7 @@ void DivPlatformYM2203::tick(bool sysTick) {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (isOutput[chan[i].state.alg][j]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -544,7 +544,7 @@ int DivPlatformYM2203::dispatch(DivCommand c) {
} else {
if (isOutput[chan[c.chan].state.alg][i]) {
if (!chan[c.chan].active || chan[c.chan].insChanged) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127));
}
} else {
if (chan[c.chan].insChanged) {
@ -603,7 +603,7 @@ int DivPlatformYM2203::dispatch(DivCommand c) {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (isOutput[chan[c.chan].state.alg][i]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -681,7 +681,7 @@ int DivPlatformYM2203::dispatch(DivCommand c) {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (isOutput[chan[c.chan].state.alg][c.value]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -873,7 +873,7 @@ void DivPlatformYM2203::muteChannel(int ch, bool mute) {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (isOutput[chan[ch].state.alg][j]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[ch].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[ch].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -890,7 +890,7 @@ void DivPlatformYM2203::forceIns() {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (isOutput[chan[i].state.alg][j]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -924,6 +924,11 @@ void* DivPlatformYM2203::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformYM2203::getChanMacroInt(int ch) {
if (ch>=3) return ay->getChanMacroInt(ch-3);
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformYM2203::getOscBuffer(int ch) {
return oscBuf[ch];
}

View File

@ -102,6 +102,7 @@ class DivPlatformYM2203: public DivDispatch, public DivPlatformOPNBase {
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();

View File

@ -42,7 +42,7 @@ int DivPlatformYM2203Ext::dispatch(DivCommand c) {
rWrite(baseAddr+0x40,127);
} else {
if (opChan[ch].insChanged) {
rWrite(baseAddr+0x40,127-(((127-op.tl)*(opChan[ch].vol&0x7f))/127));
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG(127-op.tl,opChan[ch].vol&0x7f,127));
}
}
if (opChan[ch].insChanged) {
@ -81,7 +81,7 @@ int DivPlatformYM2203Ext::dispatch(DivCommand c) {
if (isOpMuted[ch]) {
rWrite(baseAddr+0x40,127);
} else {
rWrite(baseAddr+0x40,127-(((127-op.tl)*(opChan[ch].vol&0x7f))/127));
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG(127-op.tl,opChan[ch].vol&0x7f,127));
}
break;
}
@ -413,7 +413,7 @@ void DivPlatformYM2203Ext::muteChannel(int ch, bool mute) {
if (isOpMuted[ch-2]) {
rWrite(baseAddr+0x40,127);
} else if (isOutput[ins->fm.alg][ordch]) {
rWrite(baseAddr+0x40,127-(((127-op.tl)*(opChan[ch-2].vol&0x7f))/127));
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG(127-op.tl,opChan[ch-2].vol&0x7f,127));
} else {
rWrite(baseAddr+0x40,op.tl);
}
@ -428,7 +428,7 @@ void DivPlatformYM2203Ext::forceIns() {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (isOutput[chan[i].state.alg][j]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -468,6 +468,12 @@ void* DivPlatformYM2203Ext::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformYM2203Ext::getChanMacroInt(int ch) {
if (ch>=6) return ay->getChanMacroInt(ch-6);
if (ch>=2) return NULL; // currently not implemented
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformYM2203Ext::getOscBuffer(int ch) {
if (ch>=6) return oscBuf[ch-3];
if (ch<3) return oscBuf[ch];

View File

@ -40,6 +40,7 @@ class DivPlatformYM2203Ext: public DivPlatformYM2203 {
public:
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
void reset();
void forceIns();

View File

@ -491,12 +491,12 @@ void DivPlatformYM2608::tick(bool sysTick) {
chan[i].std.next();
if (chan[i].std.vol.had) {
chan[i].outVol=(chan[i].vol*MIN(127,chan[i].std.vol.val))/127;
chan[i].outVol=VOL_SCALE_LOG(chan[i].vol,MIN(127,chan[i].std.vol.val),127);
for (int j=0; j<4; j++) {
unsigned short baseAddr=chanOffs[i]|opOffs[j];
DivInstrumentFM::Operator& op=chan[i].state.op[j];
if (isOutput[chan[i].state.alg][j]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -550,7 +550,7 @@ void DivPlatformYM2608::tick(bool sysTick) {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (isOutput[chan[i].state.alg][j]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -600,7 +600,7 @@ void DivPlatformYM2608::tick(bool sysTick) {
if (m.tl.had) {
op.tl=127-m.tl.val;
if (isOutput[chan[i].state.alg][j]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -827,7 +827,7 @@ int DivPlatformYM2608::dispatch(DivCommand c) {
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
if (isOutput[chan[c.chan].state.alg][i]) {
if (!chan[c.chan].active || chan[c.chan].insChanged) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127));
}
} else {
if (chan[c.chan].insChanged) {
@ -907,7 +907,7 @@ int DivPlatformYM2608::dispatch(DivCommand c) {
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
if (isOutput[chan[c.chan].state.alg][i]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -1011,7 +1011,7 @@ int DivPlatformYM2608::dispatch(DivCommand c) {
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
op.tl=c.value2;
if (isOutput[chan[c.chan].state.alg][c.value]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -1214,7 +1214,7 @@ void DivPlatformYM2608::forceIns() {
unsigned short baseAddr=chanOffs[i]|opOffs[j];
DivInstrumentFM::Operator& op=chan[i].state.op[j];
if (isOutput[chan[i].state.alg][j]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -1248,6 +1248,11 @@ void* DivPlatformYM2608::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformYM2608::getChanMacroInt(int ch) {
if (ch>=6 && ch<9) return ay->getChanMacroInt(ch-6);
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformYM2608::getOscBuffer(int ch) {
return oscBuf[ch];
}

View File

@ -117,6 +117,7 @@ class DivPlatformYM2608: public DivDispatch, public DivPlatformOPNBase {
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();

View File

@ -42,7 +42,7 @@ int DivPlatformYM2608Ext::dispatch(DivCommand c) {
rWrite(baseAddr+0x40,127);
} else {
if (opChan[ch].insChanged) {
rWrite(baseAddr+0x40,127-(((127-op.tl)*(opChan[ch].vol&0x7f))/127));
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG(127-op.tl,opChan[ch].vol&0x7f,127));
}
}
if (opChan[ch].insChanged) {
@ -81,7 +81,7 @@ int DivPlatformYM2608Ext::dispatch(DivCommand c) {
if (isOpMuted[ch]) {
rWrite(baseAddr+0x40,127);
} else {
rWrite(baseAddr+0x40,127-(((127-op.tl)*(opChan[ch].vol&0x7f))/127));
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG(127-op.tl,opChan[ch].vol&0x7f,127));
}
break;
}
@ -413,7 +413,7 @@ void DivPlatformYM2608Ext::muteChannel(int ch, bool mute) {
if (isOpMuted[ch-2]) {
rWrite(baseAddr+0x40,127);
} else if (isOutput[ins->fm.alg][ordch]) {
rWrite(baseAddr+0x40,127-(((127-op.tl)*(opChan[ch-2].vol&0x7f))/127));
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG(127-op.tl,opChan[ch-2].vol&0x7f,127));
} else {
rWrite(baseAddr+0x40,op.tl);
}
@ -428,7 +428,7 @@ void DivPlatformYM2608Ext::forceIns() {
if (isOpMuted[j]) {
rWrite(baseAddr+0x40,127);
} else if (isOutput[chan[i].state.alg][j]) {
rWrite(baseAddr+0x40,127-(((127-op.tl)*(opChan[j].vol&0x7f))/127));
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG(127-op.tl,opChan[j].vol&0x7f,127));
} else {
rWrite(baseAddr+0x40,op.tl);
}
@ -437,7 +437,7 @@ void DivPlatformYM2608Ext::forceIns() {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (isOutput[chan[i].state.alg][j]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -481,6 +481,13 @@ void* DivPlatformYM2608Ext::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformYM2608Ext::getChanMacroInt(int ch) {
if (ch>=9 && ch<12) return ay->getChanMacroInt(ch-9);
if (ch>=6) return &chan[ch-3].std;
if (ch>=2) return NULL; // currently not implemented
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformYM2608Ext::getOscBuffer(int ch) {
if (ch>=6) return oscBuf[ch-3];
if (ch<3) return oscBuf[ch];

View File

@ -40,6 +40,7 @@ class DivPlatformYM2608Ext: public DivPlatformYM2608 {
public:
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
void reset();
void forceIns();

View File

@ -531,12 +531,12 @@ void DivPlatformYM2610::tick(bool sysTick) {
chan[i].std.next();
if (chan[i].std.vol.had) {
chan[i].outVol=(chan[i].vol*MIN(127,chan[i].std.vol.val))/127;
chan[i].outVol=VOL_SCALE_LOG(chan[i].vol,MIN(127,chan[i].std.vol.val),127);
for (int j=0; j<4; j++) {
unsigned short baseAddr=chanOffs[i]|opOffs[j];
DivInstrumentFM::Operator& op=chan[i].state.op[j];
if (isOutput[chan[i].state.alg][j]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -590,7 +590,7 @@ void DivPlatformYM2610::tick(bool sysTick) {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (isOutput[chan[i].state.alg][j]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -640,7 +640,7 @@ void DivPlatformYM2610::tick(bool sysTick) {
if (m.tl.had) {
op.tl=127-m.tl.val;
if (isOutput[chan[i].state.alg][j]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -870,7 +870,7 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
if (isOutput[chan[c.chan].state.alg][i]) {
if (!chan[c.chan].active || chan[c.chan].insChanged) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127));
}
} else {
if (chan[c.chan].insChanged) {
@ -950,7 +950,7 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
if (isOutput[chan[c.chan].state.alg][i]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -1054,7 +1054,7 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
op.tl=c.value2;
if (isOutput[chan[c.chan].state.alg][c.value]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -1257,7 +1257,7 @@ void DivPlatformYM2610::forceIns() {
unsigned short baseAddr=chanOffs[i]|opOffs[j];
DivInstrumentFM::Operator& op=chan[i].state.op[j];
if (isOutput[chan[i].state.alg][j]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -1291,6 +1291,11 @@ void* DivPlatformYM2610::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformYM2610::getChanMacroInt(int ch) {
if (ch>=4 && ch<7) return ay->getChanMacroInt(ch-4);
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformYM2610::getOscBuffer(int ch) {
return oscBuf[ch];
}

View File

@ -135,6 +135,7 @@ class DivPlatformYM2610: public DivPlatformYM2610Base {
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();

View File

@ -513,12 +513,12 @@ void DivPlatformYM2610B::tick(bool sysTick) {
chan[i].std.next();
if (chan[i].std.vol.had) {
chan[i].outVol=(chan[i].vol*MIN(127,chan[i].std.vol.val))/127;
chan[i].outVol=VOL_SCALE_LOG(chan[i].vol,MIN(127,chan[i].std.vol.val),127);
for (int j=0; j<4; j++) {
unsigned short baseAddr=chanOffs[i]|opOffs[j];
DivInstrumentFM::Operator& op=chan[i].state.op[j];
if (isOutput[chan[i].state.alg][j]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -572,7 +572,7 @@ void DivPlatformYM2610B::tick(bool sysTick) {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (isOutput[chan[i].state.alg][j]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -622,7 +622,7 @@ void DivPlatformYM2610B::tick(bool sysTick) {
if (m.tl.had) {
op.tl=127-m.tl.val;
if (isOutput[chan[i].state.alg][j]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -851,7 +851,7 @@ int DivPlatformYM2610B::dispatch(DivCommand c) {
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
if (isOutput[chan[c.chan].state.alg][i]) {
if (!chan[c.chan].active || chan[c.chan].insChanged) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127));
}
} else {
if (chan[c.chan].insChanged) {
@ -931,7 +931,7 @@ int DivPlatformYM2610B::dispatch(DivCommand c) {
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
if (isOutput[chan[c.chan].state.alg][i]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -1035,7 +1035,7 @@ int DivPlatformYM2610B::dispatch(DivCommand c) {
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
op.tl=c.value2;
if (isOutput[chan[c.chan].state.alg][c.value]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[c.chan].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -1238,7 +1238,7 @@ void DivPlatformYM2610B::forceIns() {
unsigned short baseAddr=chanOffs[i]|opOffs[j];
DivInstrumentFM::Operator& op=chan[i].state.op[j];
if (isOutput[chan[i].state.alg][j]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -1272,6 +1272,11 @@ void* DivPlatformYM2610B::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformYM2610B::getChanMacroInt(int ch) {
if (ch>=6 && ch<9) return ay->getChanMacroInt(ch-6);
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformYM2610B::getOscBuffer(int ch) {
return oscBuf[ch];
}

View File

@ -103,6 +103,7 @@ class DivPlatformYM2610B: public DivPlatformYM2610Base {
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();

View File

@ -42,7 +42,7 @@ int DivPlatformYM2610BExt::dispatch(DivCommand c) {
rWrite(baseAddr+0x40,127);
} else {
if (opChan[ch].insChanged) {
rWrite(baseAddr+0x40,127-(((127-op.tl)*(opChan[ch].vol&0x7f))/127));
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG(127-op.tl,opChan[ch].vol&0x7f,127));
}
}
if (opChan[ch].insChanged) {
@ -81,7 +81,7 @@ int DivPlatformYM2610BExt::dispatch(DivCommand c) {
if (isOpMuted[ch]) {
rWrite(baseAddr+0x40,127);
} else {
rWrite(baseAddr+0x40,127-(((127-op.tl)*(opChan[ch].vol&0x7f))/127));
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG(127-op.tl,opChan[ch].vol&0x7f,127));
}
break;
}
@ -413,7 +413,7 @@ void DivPlatformYM2610BExt::muteChannel(int ch, bool mute) {
if (isOpMuted[ch-2]) {
rWrite(baseAddr+0x40,127);
} else if (isOutput[ins->fm.alg][ordch]) {
rWrite(baseAddr+0x40,127-(((127-op.tl)*(opChan[ch-2].vol&0x7f))/127));
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG(127-op.tl,opChan[ch-2].vol&0x7f,127));
} else {
rWrite(baseAddr+0x40,op.tl);
}
@ -428,7 +428,7 @@ void DivPlatformYM2610BExt::forceIns() {
if (isOpMuted[j]) {
rWrite(baseAddr+0x40,127);
} else if (isOutput[chan[i].state.alg][j]) {
rWrite(baseAddr+0x40,127-(((127-op.tl)*(opChan[j].vol&0x7f))/127));
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG(127-op.tl,opChan[j].vol&0x7f,127));
} else {
rWrite(baseAddr+0x40,op.tl);
}
@ -437,7 +437,7 @@ void DivPlatformYM2610BExt::forceIns() {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (isOutput[chan[i].state.alg][j]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -481,6 +481,13 @@ void* DivPlatformYM2610BExt::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformYM2610BExt::getChanMacroInt(int ch) {
if (ch>=9 && ch<12) return ay->getChanMacroInt(ch-9);
if (ch>=6) return &chan[ch-3].std;
if (ch>=2) return NULL; // currently not implemented
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformYM2610BExt::getOscBuffer(int ch) {
if (ch>=6) return oscBuf[ch-3];
if (ch<3) return oscBuf[ch];

View File

@ -40,6 +40,7 @@ class DivPlatformYM2610BExt: public DivPlatformYM2610B {
public:
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
void reset();
void forceIns();

View File

@ -42,7 +42,7 @@ int DivPlatformYM2610Ext::dispatch(DivCommand c) {
rWrite(baseAddr+0x40,127);
} else {
if (opChan[ch].insChanged) {
rWrite(baseAddr+0x40,127-(((127-op.tl)*(opChan[ch].vol&0x7f))/127));
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG(127-op.tl,opChan[ch].vol&0x7f,127));
}
}
if (opChan[ch].insChanged) {
@ -81,7 +81,7 @@ int DivPlatformYM2610Ext::dispatch(DivCommand c) {
if (isOpMuted[ch]) {
rWrite(baseAddr+0x40,127);
} else {
rWrite(baseAddr+0x40,127-(((127-op.tl)*(opChan[ch].vol&0x7f))/127));
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG(127-op.tl,opChan[ch].vol&0x7f,127));
}
break;
}
@ -413,7 +413,7 @@ void DivPlatformYM2610Ext::muteChannel(int ch, bool mute) {
if (isOpMuted[ch]) {
rWrite(baseAddr+0x40,127);
} else if (isOutput[ins->fm.alg][ordch]) {
rWrite(baseAddr+0x40,127-(((127-op.tl)*(opChan[ch].vol&0x7f))/127));
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG(127-op.tl,opChan[ch].vol&0x7f,127));
} else {
rWrite(baseAddr+0x40,op.tl);
}
@ -428,7 +428,7 @@ void DivPlatformYM2610Ext::forceIns() {
if (isOpMuted[j]) {
rWrite(baseAddr+0x40,127);
} else if (isOutput[chan[i].state.alg][j]) {
rWrite(baseAddr+0x40,127-(((127-op.tl)*(opChan[j].vol&0x7f))/127));
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG(127-op.tl,opChan[j].vol&0x7f,127));
} else {
rWrite(baseAddr+0x40,op.tl);
}
@ -437,7 +437,7 @@ void DivPlatformYM2610Ext::forceIns() {
rWrite(baseAddr+ADDR_TL,127);
} else {
if (isOutput[chan[i].state.alg][j]) {
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127));
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG(127-op.tl,chan[i].outVol&0x7f,127));
} else {
rWrite(baseAddr+ADDR_TL,op.tl);
}
@ -475,13 +475,19 @@ void DivPlatformYM2610Ext::forceIns() {
}
}
void* DivPlatformYM2610Ext::getChanState(int ch) {
if (ch>=5) return &chan[ch-3];
if (ch>=1) return &opChan[ch-1];
return &chan[ch];
}
DivMacroInt* DivPlatformYM2610Ext::getChanMacroInt(int ch) {
if (ch>=7 && ch<10) return ay->getChanMacroInt(ch-7);
if (ch>=5) return &chan[ch-3].std;
if (ch>=1) return NULL; // currently not implemented
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformYM2610Ext::getOscBuffer(int ch) {
if (ch>=5) return oscBuf[ch-3];
if (ch<2) return oscBuf[ch];

Some files were not shown because too many files have changed in this diff Show More