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

This commit is contained in:
cam900 2022-12-17 15:00:44 +09:00
commit 429aed0ab1
86 changed files with 1718 additions and 283 deletions

View file

@ -445,6 +445,8 @@ src/engine/platform/sound/oki/okim6258.cpp
src/engine/platform/sound/snes/SPC_DSP.cpp
src/engine/platform/sound/ga20/iremga20.cpp
src/engine/platform/oplAInterface.cpp
src/engine/platform/ym2608Interface.cpp
src/engine/platform/ym2610Interface.cpp
@ -527,6 +529,7 @@ src/engine/platform/namcowsg.cpp
src/engine/platform/rf5c68.cpp
src/engine/platform/snes.cpp
src/engine/platform/k007232.cpp
src/engine/platform/ga20.cpp
src/engine/platform/pcmdac.cpp
src/engine/platform/dummy.cpp
)

Binary file not shown.

BIN
demos/pce/Ma-Da-Ra.fur Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -65,6 +65,9 @@ however, effects are continuous, which means you only need to type it once and t
- `F2xx`: single tick slide down.
- `F3xx`: fine volume slide up (64x slower than `0Axy`).
- `F4xx`: fine volume slide down (64x slower than `0Axy`).
- `F5xx`: disable macro.
- see macro table at the end of this document for possible values.
- `F6xx`: enable macro.
- `F8xx`: single tick volume slide up.
- `F9xx`: single tick volume slide down.
- `FAxy`: fast volume slide (4x faster than `0Axy`).
@ -73,3 +76,72 @@ however, effects are continuous, which means you only need to type it once and t
- `FFxx`: end of song/stop playback.
additionally each chip has its own effects. [click here for more details](../7-systems/README.md).
## macro table
ID | macro
---|-----------------------------
00 | volume
01 | arpeggio
02 | duty/noise
03 | waveform
04 | pitch
05 | extra 1
06 | extra 2
07 | extra 3
08 | extra A (ALG)
09 | extra B (FM)
0A | extra C (FMS)
0B | extra D (AMS)
0C | panning left
0D | panning right
0E | phase reset
0F | extra 4
10 | extra 5
11 | extra 6
12 | extra 7
13 | extra 8
---|-----------------------------
20 | **operator 1 macros** - AM
21 | AR
22 | DR
23 | MULT
24 | RR
25 | SL
26 | TL
27 | DT2
28 | RS
29 | DT
2A | D2R
2B | SSG-EG
2C | DAM
2D | DVB
2E | EGT
2F | KSL
30 | SUS
31 | VIB
32 | WS
33 | KSR
---|-----------------------------
40 | operator 2 macros
60 | operator 2 macros
80 | operator 2 macros
the interpretation of duty, wave and extra macros depends on chip/instrument type:
ex | FM | OPM | OPZ | OPLL | AY-3-8910 | AY8930 | Lynx | C64 | SAA1099 | X1-010 | Namco 163 | FDS | Sound Unit | ES5506 | MSM6258 | QSound | SNES | MSM5232 |
---|--------|-----------|-----------|-------|------------|------------|----------|------------|----------|------------|------------|-----------|------------|-----------|----------|--------------|-----------|-----------|
D | NoiseF | NoiseFreq | | | NoiseFreq | NoiseFreq | Duty/Int | Duty | | | Wave Pos | | Duty | Filt Mode | FreqDiv | Echo Level | NoiseFreq | GroupCtrl |
W | | LFO Shape | LFO Shape | Patch | Waveform | Waveform | | Waveform | Waveform | Waveform | Waveform | Waveform | Waveform | | | | Waveform | |
1 | | AMD | AMD | | | Duty | | FilterMode | Envelope | EnvMode | WaveLen | Mod Depth | Cutoff | Filter K1 | ClockDiv | EchoFeedback | Special | GroupAtk |
2 | | PMD | PMD | | Envelope | Envelope | | Resonance | | Envelope | WaveUpdate | Mod Speed | Resonance | Filter K2 | | Echo Length | Gain | GroupDec |
3 | | LFO Speed | LFO Speed | | AutoEnvNum | AutoEnvNum | | Special | | AutoEnvNum | WaveLoad W | | Control | Env Count | | | | Noise |
A | ALG | ALG | ALG | | AutoEnvDen | AutoEnvDen | | | | AutoEnvDen | WaveLoad P | | | Control | | | | |
B | FB | FB | FB | | | Noise AND | | | | | WaveLoad L | | | | | | | |
C | FMS | FMS | FMS | | | Noise OR | | | | | WaveLoad T | | | | | | | |
D | AMS | AMS | AMS | | | | | | | | | | | | | | | |
4 | OpMask | OpMask | | | | | | Test/Gate | | | | | PResetTime | EnvRampL | | | | |
5 | | | AMD2 | | | | | | | | | | | EnvRampR | | | | |
6 | | | PMD2 | | | | | | | | | | | EnvRampK1 | | | | |
7 | | | LFO2Speed | | | | | | | | | | | EnvRampK2 | | | | |
8 | | | LFO2Shape | | | | | | | | | | | Env Mode | | | | |

View file

@ -28,7 +28,7 @@
// common shared channel struct
template<typename T> struct SharedChannel {
int freq, baseFreq, pitch, pitch2;
int freq, baseFreq, baseFreqOverride, pitch, pitch2, arpOff;
int ins, note;
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, inPorta;
T vol, outVol;
@ -40,8 +40,10 @@ template<typename T> struct SharedChannel {
SharedChannel(T initVol):
freq(0),
baseFreq(0),
baseFreqOverride(-1),
pitch(0),
pitch2(0),
arpOff(0),
ins(-1),
note(0),
active(false),

View file

@ -222,6 +222,9 @@ enum DivDispatchCmds {
DIV_CMD_NES_LENGTH,
DIV_CMD_NES_COUNT_MODE,
DIV_CMD_MACRO_OFF, // (which)
DIV_CMD_MACRO_ON, // (which)
DIV_ALWAYS_SET_VOLUME, // () -> alwaysSetVol
DIV_CMD_MAX

View file

@ -73,6 +73,7 @@
#include "platform/snes.h"
#include "platform/vb.h"
#include "platform/k007232.h"
#include "platform/ga20.h"
#include "platform/pcmdac.h"
#include "platform/dummy.h"
#include "../ta-log.h"
@ -430,6 +431,9 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do
case DIV_SYSTEM_K007232:
dispatch=new DivPlatformK007232;
break;
case DIV_SYSTEM_GA20:
dispatch=new DivPlatformGA20;
break;
case DIV_SYSTEM_PCM_DAC:
dispatch=new DivPlatformPCMDAC;
break;

View file

@ -113,6 +113,10 @@ const char* DivEngine::getEffectDesc(unsigned char effect, int chan, bool notNul
return "F3xx: Fine volume slide up";
case 0xf4:
return "F4xx: Fine volume slide down";
case 0xf5:
return "F5xx: Disable macro (see manual)";
case 0xf6:
return "F6xx: Enable macro (see manual)";
case 0xf8:
return "F8xx: Single tick volume slide up";
case 0xf9:
@ -405,6 +409,8 @@ void writePackedCommandValues(SafeWriter* w, const DivCommand& c) {
case DIV_CMD_AMIGA_FILTER:
case DIV_CMD_AMIGA_AM:
case DIV_CMD_AMIGA_PM:
case DIV_CMD_MACRO_OFF:
case DIV_CMD_MACRO_ON:
w->writeC(1); // length
w->writeC(c.value);
break;

View file

@ -50,6 +50,10 @@ void DivMacroStruct::doMacro(DivInstrumentMacro& source, bool released, bool tic
had=false;
return;
}
if (masked) {
had=false;
return;
}
if (delay>0) {
delay--;
had=false;
@ -173,6 +177,66 @@ void DivMacroInt::next() {
}
}
#define CONSIDER(x,y) \
case y: \
x.masked=enabled; \
break;
#define CONSIDER_OP(oi,o) \
CONSIDER(op[oi].am,0+o) \
CONSIDER(op[oi].ar,1+o) \
CONSIDER(op[oi].dr,2+o) \
CONSIDER(op[oi].mult,3+o) \
CONSIDER(op[oi].rr,4+o) \
CONSIDER(op[oi].sl,5+o) \
CONSIDER(op[oi].tl,6+o) \
CONSIDER(op[oi].dt2,7+o) \
CONSIDER(op[oi].rs,8+o) \
CONSIDER(op[oi].dt,9+o) \
CONSIDER(op[oi].d2r,10+o) \
CONSIDER(op[oi].ssg,11+o) \
CONSIDER(op[oi].dam,12+o) \
CONSIDER(op[oi].dvb,13+o) \
CONSIDER(op[oi].egt,14+o) \
CONSIDER(op[oi].ksl,15+o) \
CONSIDER(op[oi].sus,16+o) \
CONSIDER(op[oi].vib,17+o) \
CONSIDER(op[oi].ws,18+o) \
CONSIDER(op[oi].ksr,19+o)
void DivMacroInt::mask(unsigned char id, bool enabled) {
switch (id) {
CONSIDER(vol,0)
CONSIDER(arp,1)
CONSIDER(duty,2)
CONSIDER(wave,3)
CONSIDER(pitch,4)
CONSIDER(ex1,5)
CONSIDER(ex2,6)
CONSIDER(ex3,7)
CONSIDER(alg,8)
CONSIDER(fb,9)
CONSIDER(fms,10)
CONSIDER(ams,11)
CONSIDER(panL,12)
CONSIDER(panR,13)
CONSIDER(phaseReset,14)
CONSIDER(ex4,15)
CONSIDER(ex5,16)
CONSIDER(ex6,17)
CONSIDER(ex7,18)
CONSIDER(ex8,19)
CONSIDER_OP(0,0x20)
CONSIDER_OP(2,0x40)
CONSIDER_OP(1,0x60)
CONSIDER_OP(3,0x80)
}
}
#undef CONSIDER_OP
#undef CONSIDER
void DivMacroInt::release() {
released=true;
}
@ -458,3 +522,5 @@ DivMacroStruct* DivMacroInt::structByName(const String& name) {
return NULL;
}
#undef CONSIDER

View file

@ -27,7 +27,7 @@ class DivEngine;
struct DivMacroStruct {
int pos, lastPos, lfoPos, delay;
int val;
bool has, had, actualHad, finished, will, linger, began;
bool has, had, actualHad, finished, will, linger, began, masked;
unsigned int mode, type;
void doMacro(DivInstrumentMacro& source, bool released, bool tick);
void init() {
@ -52,6 +52,7 @@ struct DivMacroStruct {
will(false),
linger(false),
began(true),
masked(false),
mode(0),
type(0) {}
};
@ -105,6 +106,11 @@ class DivMacroInt {
// state
bool hasRelease;
/**
* set mask on macro.
*/
void mask(unsigned char id, bool enabled);
/**
* trigger macro release.
*/

View file

@ -349,6 +349,12 @@ int DivPlatformAmiga::dispatch(DivCommand c) {
case DIV_CMD_GET_VOLMAX:
return 64;
break;
case DIV_CMD_MACRO_OFF:
chan[c.chan].std.mask(c.value,true);
break;
case DIV_CMD_MACRO_ON:
chan[c.chan].std.mask(c.value,false);
break;
case DIV_ALWAYS_SET_VOLUME:
return 1;
break;

View file

@ -726,6 +726,12 @@ int DivPlatformArcade::dispatch(DivCommand c) {
}
break;
}
case DIV_CMD_MACRO_OFF:
chan[c.chan].std.mask(c.value,true);
break;
case DIV_CMD_MACRO_ON:
chan[c.chan].std.mask(c.value,false);
break;
case DIV_ALWAYS_SET_VOLUME:
return 0;
break;

View file

@ -635,6 +635,12 @@ int DivPlatformAY8910::dispatch(DivCommand c) {
sampleBank=parent->song.sample.size()/12;
}
break;
case DIV_CMD_MACRO_OFF:
chan[c.chan].std.mask(c.value,true);
break;
case DIV_CMD_MACRO_ON:
chan[c.chan].std.mask(c.value,false);
break;
case DIV_ALWAYS_SET_VOLUME:
return 0;
break;

View file

@ -635,6 +635,12 @@ int DivPlatformAY8930::dispatch(DivCommand c) {
sampleBank=parent->song.sample.size()/12;
}
break;
case DIV_CMD_MACRO_OFF:
chan[c.chan].std.mask(c.value,true);
break;
case DIV_CMD_MACRO_ON:
chan[c.chan].std.mask(c.value,false);
break;
case DIV_ALWAYS_SET_VOLUME:
return 0;
break;

View file

@ -238,6 +238,12 @@ int DivPlatformBubSysWSG::dispatch(DivCommand c) {
case DIV_CMD_GET_VOLMAX:
return 15;
break;
case DIV_CMD_MACRO_OFF:
chan[c.chan].std.mask(c.value,true);
break;
case DIV_CMD_MACRO_ON:
chan[c.chan].std.mask(c.value,false);
break;
case DIV_ALWAYS_SET_VOLUME:
return 1;
break;

View file

@ -416,6 +416,12 @@ int DivPlatformC64::dispatch(DivCommand c) {
break;
}
break;
case DIV_CMD_MACRO_OFF:
chan[c.chan].std.mask(c.value,true);
break;
case DIV_CMD_MACRO_ON:
chan[c.chan].std.mask(c.value,false);
break;
case DIV_ALWAYS_SET_VOLUME:
return 1;
break;

View file

@ -379,6 +379,12 @@ int DivPlatformFDS::dispatch(DivCommand c) {
case DIV_CMD_GET_VOLMAX:
return 32;
break;
case DIV_CMD_MACRO_OFF:
chan[c.chan].std.mask(c.value,true);
break;
case DIV_CMD_MACRO_ON:
chan[c.chan].std.mask(c.value,false);
break;
case DIV_ALWAYS_SET_VOLUME:
return 1;
break;

View file

@ -0,0 +1,492 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "ga20.h"
#include "../engine.h"
#include "../../ta-log.h"
#include <math.h>
#define rWrite(a,v) {if(!skipRegisterWrites) {writes.emplace(a,v); if(dumpWrites) addWrite(a,v);}}
#define CHIP_DIVIDER 64
const char* regCheatSheetGA20[]={
// on-chip
"CHX_StartL", "X*8+0",
"CHX_StartH", "X*8+1",
"CHX_EndL", "X*8+2",
"CHX_EndH", "X*8+3",
"CHX_Freq", "X*8+4",
"CHX_Volume", "X*8+5",
"CHX_Control", "X*8+6",
"CHX_Status", "X*8+7",
NULL
};
const char** DivPlatformGA20::getRegisterSheet() {
return regCheatSheetGA20;
}
inline void DivPlatformGA20::chWrite(unsigned char ch, unsigned int addr, unsigned char val) {
if (!skipRegisterWrites) {
if ((ch<4) && (addr<8)) {
rWrite((ch<<3)+(addr&7),val);
}
}
}
void DivPlatformGA20::acquire(short* bufL, short* bufR, size_t start, size_t len) {
if (ga20BufLen<len) {
ga20BufLen=len;
for (int i=0; i<4; i++) {
delete[] ga20Buf[i];
ga20Buf[i]=new short[ga20BufLen];
}
}
for (size_t h=start; h<start+len; h++) {
if ((--delay)<=0) {
delay=MAX(0,delay);
if (!writes.empty()) {
QueuedWrite& w=writes.front();
ga20.write(w.addr,w.val);
regPool[w.addr]=w.val;
writes.pop();
delay=w.delay;
}
}
short *buffer[4] = {&ga20Buf[0][h],&ga20Buf[1][h],&ga20Buf[2][h],&ga20Buf[3][h]};
ga20.sound_stream_update(buffer, 1);
bufL[h]=(signed int)(ga20Buf[0][h]+ga20Buf[1][h]+ga20Buf[2][h]+ga20Buf[3][h])>>2;
for (int i=0; i<4; i++) {
oscBuf[i]->data[oscBuf[i]->needle++]=ga20Buf[i][h];
}
}
}
u8 DivPlatformGA20::read_byte(u32 address) {
if ((sampleMem!=NULL) && (address<getSampleMemCapacity())) {
return sampleMem[address&0xfffff];
}
return 0;
}
void DivPlatformGA20::tick(bool sysTick) {
for (int i=0; i<4; i++) {
chan[i].std.next();
if (chan[i].std.vol.had) {
const signed char macroVol=VOL_SCALE_LOG((chan[i].vol&0xff),(0xff*MIN(chan[i].macroVolMul,chan[i].std.vol.val))/chan[i].macroVolMul,0xff);
if ((!isMuted[i]) && (macroVol!=chan[i].outVol)) {
chan[i].outVol=macroVol;
chan[i].volumeChanged=true;
}
}
if (chan[i].std.arp.had) {
if (!chan[i].inPorta) {
chan[i].baseFreq=NOTE_PERIODIC(parent->calcArp(chan[i].note,chan[i].std.arp.val));
}
chan[i].freqChanged=true;
}
if (chan[i].std.pitch.had) {
if (chan[i].std.pitch.mode) {
chan[i].pitch2+=chan[i].std.pitch.val;
CLAMP_VAR(chan[i].pitch2,-32768,32767);
} else {
chan[i].pitch2=chan[i].std.pitch.val;
}
chan[i].freqChanged=true;
}
if (chan[i].std.phaseReset.had) {
if (chan[i].std.phaseReset.val==1 && chan[i].active) {
chan[i].audPos=0;
chan[i].setPos=true;
}
}
if (chan[i].volumeChanged) {
chan[i].resVol=(chan[i].active && isMuted[i])?0:chan[i].outVol&0xff;
chWrite(i,0x5,chan[i].resVol);
chan[i].volumeChanged=false;
}
if (chan[i].setPos) {
// force keyon
chan[i].keyOn=true;
chan[i].setPos=false;
} else {
chan[i].audPos=0;
}
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
double off=1.0;
int sample=chan[i].sample;
if (sample>=0 && sample<parent->song.sampleLen) {
DivSample* s=parent->getSample(sample);
if (s->centerRate<1) {
off=1.0;
} else {
off=8363.0/s->centerRate;
}
}
DivSample* s=parent->getSample(chan[i].sample);
chan[i].freq=0x100-(int)(off*parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2,chipClock,CHIP_DIVIDER));
if (chan[i].freq>255) chan[i].freq=255;
if (chan[i].freq<0) chan[i].freq=0;
if (chan[i].keyOn) {
unsigned int start=0;
unsigned int end=0;
if (chan[i].sample>=0 && chan[i].sample<parent->song.sampleLen) {
start=sampleOffGA20[chan[i].sample]&0xfffff;
end=start+s->length8;
}
if (chan[i].audPos>0) {
start=start+MIN(chan[i].audPos,MIN(getSampleMemCapacity()-1,s->length8));
}
start=MIN(start,getSampleMemCapacity()-1);
end=MIN(end,getSampleMemCapacity()-1);
// force keyoff first
chWrite(i,5,0);
chWrite(i,6,0);
// keyon
if (chan[i].prevFreq!=chan[i].freq) {
chWrite(i,4,chan[i].freq&0xff);
chan[i].prevFreq=chan[i].freq;
}
chWrite(i,0,start>>4);
chWrite(i,1,start>>12);
chWrite(i,2,end>>4);
chWrite(i,3,end>>12);
chWrite(i,5,chan[i].resVol);
chWrite(i,6,2);
if (!chan[i].std.vol.had) {
chan[i].outVol=chan[i].vol;
if (!isMuted[i]) {
chan[i].volumeChanged=true;
}
}
chan[i].keyOn=false;
}
if (chan[i].keyOff) {
chWrite(i,5,0);
chWrite(i,6,0);
chan[i].keyOff=false;
}
if (chan[i].freqChanged) {
if (chan[i].prevFreq!=chan[i].freq) {
chWrite(i,4,chan[i].freq&0xff);
chan[i].prevFreq=chan[i].freq;
}
chan[i].freqChanged=false;
}
}
}
}
int DivPlatformGA20::dispatch(DivCommand c) {
switch (c.cmd) {
case DIV_CMD_NOTE_ON: {
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA);
chan[c.chan].macroVolMul=ins->type==DIV_INS_AMIGA?64:255;
chan[c.chan].sample=ins->amiga.getSample(c.value);
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
}
if (chan[c.chan].sample<0 || chan[c.chan].sample>=parent->song.sampleLen) {
chan[c.chan].sample=-1;
}
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].freqChanged=true;
chan[c.chan].note=c.value;
}
chan[c.chan].active=true;
chan[c.chan].keyOn=true;
chan[c.chan].macroInit(ins);
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
if (!isMuted[c.chan]) {
chan[c.chan].volumeChanged=true;
}
}
break;
}
case DIV_CMD_NOTE_OFF:
chan[c.chan].sample=-1;
chan[c.chan].active=false;
chan[c.chan].keyOff=true;
chan[c.chan].macroInit(NULL);
break;
case DIV_CMD_NOTE_OFF_ENV:
case DIV_CMD_ENV_RELEASE:
chan[c.chan].std.release();
break;
case DIV_CMD_INSTRUMENT:
if (chan[c.chan].ins!=c.value || c.value2==1) {
chan[c.chan].ins=c.value;
}
break;
case DIV_CMD_VOLUME:
if (chan[c.chan].vol!=c.value) {
chan[c.chan].vol=c.value;
if (!chan[c.chan].std.vol.has) {
chan[c.chan].outVol=c.value;
if (!isMuted[c.chan]) {
chan[c.chan].volumeChanged=true;
}
}
}
break;
case DIV_CMD_GET_VOLUME:
if (chan[c.chan].std.vol.has) {
return chan[c.chan].vol;
}
return chan[c.chan].outVol;
break;
case DIV_CMD_PITCH:
chan[c.chan].pitch=c.value;
chan[c.chan].freqChanged=true;
break;
case DIV_CMD_NOTE_PORTA: {
const 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;
}
case DIV_CMD_LEGATO: {
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value+((chan[c.chan].std.arp.will && !chan[c.chan].std.arp.mode)?(chan[c.chan].std.arp.val-12):(0)));
chan[c.chan].freqChanged=true;
chan[c.chan].note=c.value;
break;
}
case DIV_CMD_PRE_PORTA:
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA));
}
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note);
chan[c.chan].inPorta=c.value;
break;
case DIV_CMD_SAMPLE_POS:
chan[c.chan].audPos=c.value;
chan[c.chan].setPos=true;
break;
case DIV_CMD_GET_VOLMAX:
return 255;
break;
case DIV_CMD_MACRO_OFF:
chan[c.chan].std.mask(c.value,true);
break;
case DIV_CMD_MACRO_ON:
chan[c.chan].std.mask(c.value,false);
break;
case DIV_ALWAYS_SET_VOLUME:
return 1;
break;
default:
break;
}
return 1;
}
void DivPlatformGA20::muteChannel(int ch, bool mute) {
isMuted[ch]=mute;
chan[ch].volumeChanged=true;
}
void DivPlatformGA20::forceIns() {
while (!writes.empty()) writes.pop();
for (int i=0; i<4; i++) {
chan[i].insChanged=true;
chan[i].volumeChanged=true;
chan[i].freqChanged=true;
chan[i].prevFreq=-1;
}
}
void* DivPlatformGA20::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformGA20::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformGA20::getOscBuffer(int ch) {
return oscBuf[ch];
}
void DivPlatformGA20::reset() {
while (!writes.empty()) {
writes.pop();
}
memset(regPool,0,32);
ga20.device_reset();
delay=0;
for (int i=0; i<4; i++) {
chan[i]=DivPlatformGA20::Channel();
chan[i].std.setEngine(parent);
// keyoff all channels
chWrite(i,5,0);
chWrite(i,6,0);
}
}
bool DivPlatformGA20::isStereo() {
return false;
}
void DivPlatformGA20::notifyInsChange(int ins) {
for (int i=0; i<4; i++) {
if (chan[i].ins==ins) {
chan[i].insChanged=true;
}
}
}
void DivPlatformGA20::notifyWaveChange(int wave) {
// TODO when wavetables are added
// TODO they probably won't be added unless the samples reside in RAM
}
void DivPlatformGA20::notifyInsDeletion(void* ins) {
for (int i=0; i<4; i++) {
chan[i].std.notifyInsDeletion((DivInstrument*)ins);
}
}
void DivPlatformGA20::setFlags(const DivConfig& flags) {
chipClock=COLOR_NTSC;
CHECK_CUSTOM_CLOCK;
rate=chipClock/4;
for (int i=0; i<4; i++) {
oscBuf[i]->rate=rate;
}
}
void DivPlatformGA20::poke(unsigned int addr, unsigned short val) {
rWrite(addr&0x1f,val);
}
void DivPlatformGA20::poke(std::vector<DivRegWrite>& wlist) {
for (DivRegWrite& i: wlist) rWrite(i.addr&0x1f,i.val);
}
unsigned char* DivPlatformGA20::getRegisterPool() {
return regPool;
}
int DivPlatformGA20::getRegisterPoolSize() {
return 32;
}
const void* DivPlatformGA20::getSampleMem(int index) {
return index == 0 ? sampleMem : NULL;
}
size_t DivPlatformGA20::getSampleMemCapacity(int index) {
return index == 0 ? 1048576 : 0;
}
size_t DivPlatformGA20::getSampleMemUsage(int index) {
return index == 0 ? sampleMemLen : 0;
}
bool DivPlatformGA20::isSampleLoaded(int index, int sample) {
if (index!=0) return false;
if (sample<0 || sample>255) return false;
return sampleLoaded[sample];
}
void DivPlatformGA20::renderSamples(int sysID) {
memset(sampleMem,0x00,getSampleMemCapacity());
memset(sampleOffGA20,0,256*sizeof(unsigned int));
memset(sampleLoaded,0,256*sizeof(bool));
size_t memPos=0;
for (int i=0; i<parent->song.sampleLen; i++) {
DivSample* s=parent->song.sample[i];
if (!s->renderOn[0][sysID]) {
sampleOffGA20[i]=0;
continue;
}
const int length=s->getLoopEndPosition(DIV_SAMPLE_DEPTH_8BIT);
int actualLength=MIN((int)(getSampleMemCapacity()-memPos)-1,length);
if (actualLength>0) {
sampleOffGA20[i]=memPos;
for (int j=0; j<actualLength; j++) {
// convert to 8 bit unsigned
unsigned char val=((unsigned char)(s->data8[j]))^0x80;
sampleMem[memPos++]=CLAMP(val,0x01,0xff);
}
// write end of sample marker
memset(&sampleMem[memPos],0x00,1);
memPos+=1;
}
if ((memPos+MAX(actualLength,0))>=(getSampleMemCapacity()-1)) {
logW("out of GA20 PCM memory for sample %d!",i);
break;
} else {
sampleLoaded[i]=true;
}
// allign to 16 byte
memPos=(memPos+0xf)&~0xf;
}
sampleMemLen=memPos;
}
int DivPlatformGA20::init(DivEngine* p, int channels, int sugRate, const DivConfig& flags) {
parent=p;
dumpWrites=false;
skipRegisterWrites=false;
for (int i=0; i<4; i++) {
isMuted[i]=false;
oscBuf[i]=new DivDispatchOscBuffer;
}
sampleMem=new unsigned char[getSampleMemCapacity()];
sampleMemLen=0;
delay=0;
setFlags(flags);
ga20BufLen=65536;
for (int i=0; i<4; i++) ga20Buf[i]=new short[ga20BufLen];
reset();
return 4;
}
void DivPlatformGA20::quit() {
delete[] sampleMem;
for (int i=0; i<4; i++) {
delete[] ga20Buf[i];
delete oscBuf[i];
}
}

109
src/engine/platform/ga20.h Normal file
View file

@ -0,0 +1,109 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef _GA20_H
#define _GA20_H
#include "../dispatch.h"
#include <queue>
#include "../macroInt.h"
#include "sound/ga20/iremga20.h"
class DivPlatformGA20: public DivDispatch, public iremga20_intf {
struct Channel: public SharedChannel<int> {
int prevFreq;
unsigned int audPos;
int sample;
bool volumeChanged, setPos;
int resVol;
int macroVolMul;
Channel():
SharedChannel<int>(255),
prevFreq(-1),
audPos(0),
sample(-1),
volumeChanged(false),
setPos(false),
resVol(255),
macroVolMul(64) {}
};
Channel chan[4];
DivDispatchOscBuffer* oscBuf[4];
bool isMuted[4];
struct QueuedWrite {
unsigned short addr;
unsigned char val;
unsigned short delay;
QueuedWrite(unsigned short a, unsigned char v, unsigned short d=1):
addr(a),
val(v),
delay(d) {}
};
std::queue<QueuedWrite> writes;
unsigned int sampleOffGA20[256];
bool sampleLoaded[256];
int delay;
short* ga20Buf[4];
size_t ga20BufLen;
unsigned char* sampleMem;
size_t sampleMemLen;
iremga20_device ga20;
unsigned char regPool[32];
friend void putDispatchChip(void*,int);
friend void putDispatchChan(void*,int,int);
void chWrite(unsigned char ch, unsigned int addr, unsigned char val);
public:
virtual u8 read_byte(u32 address) override;
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;
virtual void reset() override;
virtual void forceIns() override;
virtual void tick(bool sysTick=true) override;
virtual void muteChannel(int ch, bool mute) override;
virtual bool isStereo() override;
virtual void notifyInsChange(int ins) override;
virtual void notifyWaveChange(int wave) override;
virtual void notifyInsDeletion(void* ins) override;
virtual void setFlags(const DivConfig& flags) override;
virtual void poke(unsigned int addr, unsigned short val) override;
virtual void poke(std::vector<DivRegWrite>& wlist) override;
virtual const char** getRegisterSheet() override;
virtual const void* getSampleMem(int index = 0) override;
virtual size_t getSampleMemCapacity(int index = 0) override;
virtual size_t getSampleMemUsage(int index = 0) override;
virtual bool isSampleLoaded(int index, int sample) override;
virtual void renderSamples(int chipID) override;
virtual int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags) override;
virtual void quit() override;
DivPlatformGA20():
DivDispatch(),
iremga20_intf(),
ga20(*this) {}
};
#endif

View file

@ -529,6 +529,12 @@ int DivPlatformGB::dispatch(DivCommand c) {
case DIV_CMD_GET_VOLMAX:
return 15;
break;
case DIV_CMD_MACRO_OFF:
chan[c.chan].std.mask(c.value,true);
break;
case DIV_CMD_MACRO_ON:
chan[c.chan].std.mask(c.value,false);
break;
case DIV_ALWAYS_SET_VOLUME:
return 1;
break;

View file

@ -1062,6 +1062,12 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
if (c.chan>=6) break;
chan[c.chan].hardReset=c.value;
break;
case DIV_CMD_MACRO_OFF:
chan[c.chan].std.mask(c.value,true);
break;
case DIV_CMD_MACRO_ON:
chan[c.chan].std.mask(c.value,false);
break;
case DIV_ALWAYS_SET_VOLUME:
return 0;
break;

View file

@ -387,6 +387,12 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) {
case DIV_CMD_GET_VOLMAX:
return 127;
break;
case DIV_CMD_MACRO_OFF:
opChan[ch].std.mask(c.value,true);
break;
case DIV_CMD_MACRO_ON:
opChan[ch].std.mask(c.value,false);
break;
case DIV_ALWAYS_SET_VOLUME:
return 0;
break;

View file

@ -377,7 +377,13 @@ int DivPlatformK007232::dispatch(DivCommand c) {
chan[c.chan].setPos=true;
break;
case DIV_CMD_GET_VOLMAX:
return 255;
return 15;
break;
case DIV_CMD_MACRO_OFF:
chan[c.chan].std.mask(c.value,true);
break;
case DIV_CMD_MACRO_ON:
chan[c.chan].std.mask(c.value,false);
break;
case DIV_ALWAYS_SET_VOLUME:
return 1;
@ -397,9 +403,13 @@ void DivPlatformK007232::forceIns() {
while (!writes.empty()) writes.pop();
for (int i=0; i<2; i++) {
chan[i].insChanged=true;
chan[i].volumeChanged=true;
chan[i].freqChanged=true;
chan[i].sample=-1;
chan[i].prevFreq=-1;
chan[i].prevBank=-1;
}
lastLoop=0;
lastVolume=0;
}
void* DivPlatformK007232::getChanState(int ch) {
@ -422,6 +432,7 @@ void DivPlatformK007232::reset() {
k007232.reset();
lastLoop=0;
lastVolume=0;
delay=0;
for (int i=0; i<2; i++) {
chan[i]=DivPlatformK007232::Channel();
chan[i].std.setEngine(parent);
@ -534,12 +545,13 @@ void DivPlatformK007232::renderSamples(int sysID) {
memset(&sampleMem[memPos],0xc0,1);
memPos+=1;
}
if (actualLength<length) {
if ((memPos+MAX(actualLength,0))>=(getSampleMemCapacity()-1)) {
logW("out of K007232 PCM memory for sample %d!",i);
break;
}
} else {
sampleLoaded[i]=true;
}
}
sampleMemLen=memPos;
}

View file

@ -92,7 +92,6 @@ class DivPlatformK007232: public DivDispatch, public k007232_intf {
void tick(bool sysTick=true);
void muteChannel(int ch, bool mute);
bool isStereo();
void setChipModel(int type);
void notifyInsChange(int ins);
void notifyWaveChange(int wave);
void notifyInsDeletion(void* ins);

View file

@ -371,6 +371,12 @@ int DivPlatformLynx::dispatch(DivCommand c) {
case DIV_CMD_GET_VOLMAX:
return 127;
break;
case DIV_CMD_MACRO_OFF:
chan[c.chan].std.mask(c.value,true);
break;
case DIV_CMD_MACRO_ON:
chan[c.chan].std.mask(c.value,false);
break;
case DIV_ALWAYS_SET_VOLUME:
return 0;
break;

View file

@ -316,6 +316,12 @@ int DivPlatformMMC5::dispatch(DivCommand c) {
case DIV_CMD_GET_VOLMAX:
return 15;
break;
case DIV_CMD_MACRO_OFF:
chan[c.chan].std.mask(c.value,true);
break;
case DIV_CMD_MACRO_ON:
chan[c.chan].std.mask(c.value,false);
break;
case DIV_ALWAYS_SET_VOLUME:
return 1;
break;

View file

@ -274,6 +274,12 @@ int DivPlatformMSM5232::dispatch(DivCommand c) {
case DIV_CMD_GET_VOLMAX:
return 127;
break;
case DIV_CMD_MACRO_OFF:
chan[c.chan].std.mask(c.value,true);
break;
case DIV_CMD_MACRO_ON:
chan[c.chan].std.mask(c.value,false);
break;
case DIV_ALWAYS_SET_VOLUME:
return 1;
break;

View file

@ -241,6 +241,12 @@ int DivPlatformMSM6258::dispatch(DivCommand c) {
case DIV_CMD_LEGATO: {
break;
}
case DIV_CMD_MACRO_OFF:
chan[c.chan].std.mask(c.value,true);
break;
case DIV_CMD_MACRO_ON:
chan[c.chan].std.mask(c.value,false);
break;
case DIV_ALWAYS_SET_VOLUME:
return 0;
break;

View file

@ -221,6 +221,12 @@ int DivPlatformMSM6295::dispatch(DivCommand c) {
case DIV_CMD_LEGATO: {
break;
}
case DIV_CMD_MACRO_OFF:
chan[c.chan].std.mask(c.value,true);
break;
case DIV_CMD_MACRO_ON:
chan[c.chan].std.mask(c.value,false);
break;
case DIV_ALWAYS_SET_VOLUME:
return 0;
break;

View file

@ -514,6 +514,12 @@ int DivPlatformN163::dispatch(DivCommand c) {
case DIV_CMD_GET_VOLMAX:
return 15;
break;
case DIV_CMD_MACRO_OFF:
chan[c.chan].std.mask(c.value,true);
break;
case DIV_CMD_MACRO_ON:
chan[c.chan].std.mask(c.value,false);
break;
case DIV_ALWAYS_SET_VOLUME:
return 1;
break;

View file

@ -427,6 +427,12 @@ int DivPlatformNamcoWSG::dispatch(DivCommand c) {
case DIV_CMD_GET_VOLMAX:
return 15;
break;
case DIV_CMD_MACRO_OFF:
chan[c.chan].std.mask(c.value,true);
break;
case DIV_CMD_MACRO_ON:
chan[c.chan].std.mask(c.value,false);
break;
case DIV_ALWAYS_SET_VOLUME:
return 1;
break;

View file

@ -564,6 +564,12 @@ int DivPlatformNES::dispatch(DivCommand c) {
case DIV_CMD_GET_VOLMAX:
return 15;
break;
case DIV_CMD_MACRO_OFF:
chan[c.chan].std.mask(c.value,true);
break;
case DIV_CMD_MACRO_ON:
chan[c.chan].std.mask(c.value,false);
break;
case DIV_ALWAYS_SET_VOLUME:
return 1;
break;

View file

@ -1352,6 +1352,12 @@ int DivPlatformOPL::dispatch(DivCommand c) {
if (c.chan==adpcmChan) break;
chan[c.chan].hardReset=c.value;
break;
case DIV_CMD_MACRO_OFF:
chan[c.chan].std.mask(c.value,true);
break;
case DIV_CMD_MACRO_ON:
chan[c.chan].std.mask(c.value,false);
break;
case DIV_ALWAYS_SET_VOLUME:
return 0;
break;

View file

@ -753,6 +753,12 @@ int DivPlatformOPLL::dispatch(DivCommand c) {
drumState=0;
}
break;
case DIV_CMD_MACRO_OFF:
chan[c.chan].std.mask(c.value,true);
break;
case DIV_CMD_MACRO_ON:
chan[c.chan].std.mask(c.value,false);
break;
case DIV_ALWAYS_SET_VOLUME:
return 0;
break;

View file

@ -456,6 +456,12 @@ int DivPlatformPCE::dispatch(DivCommand c) {
case DIV_CMD_GET_VOLMAX:
return 31;
break;
case DIV_CMD_MACRO_OFF:
chan[c.chan].std.mask(c.value,true);
break;
case DIV_CMD_MACRO_ON:
chan[c.chan].std.mask(c.value,false);
break;
case DIV_ALWAYS_SET_VOLUME:
return 1;
break;

View file

@ -29,92 +29,92 @@ void DivPlatformPCMDAC::acquire(short* bufL, short* bufR, size_t start, size_t l
const int depthScale=(15-outDepth);
int output=0;
for (size_t h=start; h<start+len; h++) {
if (!chan.active || isMuted) {
if (!chan[0].active || isMuted) {
bufL[h]=0;
bufR[h]=0;
oscBuf->data[oscBuf->needle++]=0;
continue;
}
if (chan.useWave || (chan.sample>=0 && chan.sample<parent->song.sampleLen)) {
chan.audPos+=((!chan.useWave) && chan.audDir)?-(chan.freq>>16):(chan.freq>>16);
chan.audSub+=(chan.freq&0xffff);
if (chan.audSub>=0x10000) {
chan.audSub-=0x10000;
chan.audPos+=((!chan.useWave) && chan.audDir)?-1:1;
if (chan[0].useWave || (chan[0].sample>=0 && chan[0].sample<parent->song.sampleLen)) {
chan[0].audPos+=((!chan[0].useWave) && chan[0].audDir)?-(chan[0].freq>>16):(chan[0].freq>>16);
chan[0].audSub+=(chan[0].freq&0xffff);
if (chan[0].audSub>=0x10000) {
chan[0].audSub-=0x10000;
chan[0].audPos+=((!chan[0].useWave) && chan[0].audDir)?-1:1;
}
if (chan.useWave) {
if (chan.audPos>=(int)chan.audLen) {
chan.audPos%=chan.audLen;
chan.audDir=false;
if (chan[0].useWave) {
if (chan[0].audPos>=(int)chan[0].audLen) {
chan[0].audPos%=chan[0].audLen;
chan[0].audDir=false;
}
output=(chan.ws.output[chan.audPos]-0x80)<<8;
output=(chan[0].ws.output[chan[0].audPos]-0x80)<<8;
} else {
DivSample* s=parent->getSample(chan.sample);
DivSample* s=parent->getSample(chan[0].sample);
if (s->samples>0) {
if (chan.audDir) {
if (chan[0].audDir) {
if (s->isLoopable()) {
switch (s->loopMode) {
case DIV_SAMPLE_LOOP_FORWARD:
case DIV_SAMPLE_LOOP_PINGPONG:
if (chan.audPos<s->loopStart) {
chan.audPos=s->loopStart+(s->loopStart-chan.audPos);
chan.audDir=false;
if (chan[0].audPos<s->loopStart) {
chan[0].audPos=s->loopStart+(s->loopStart-chan[0].audPos);
chan[0].audDir=false;
}
break;
case DIV_SAMPLE_LOOP_BACKWARD:
if (chan.audPos<s->loopStart) {
chan.audPos=s->loopEnd-1-(s->loopStart-chan.audPos);
chan.audDir=true;
if (chan[0].audPos<s->loopStart) {
chan[0].audPos=s->loopEnd-1-(s->loopStart-chan[0].audPos);
chan[0].audDir=true;
}
break;
default:
if (chan.audPos<0) {
chan.sample=-1;
if (chan[0].audPos<0) {
chan[0].sample=-1;
}
break;
}
} else if (chan.audPos>=(int)s->samples) {
chan.sample=-1;
} else if (chan[0].audPos>=(int)s->samples) {
chan[0].sample=-1;
}
} else {
if (s->isLoopable()) {
switch (s->loopMode) {
case DIV_SAMPLE_LOOP_FORWARD:
if (chan.audPos>=s->loopEnd) {
chan.audPos=(chan.audPos+s->loopStart)-s->loopEnd;
chan.audDir=false;
if (chan[0].audPos>=s->loopEnd) {
chan[0].audPos=(chan[0].audPos+s->loopStart)-s->loopEnd;
chan[0].audDir=false;
}
break;
case DIV_SAMPLE_LOOP_BACKWARD:
case DIV_SAMPLE_LOOP_PINGPONG:
if (chan.audPos>=s->loopEnd) {
chan.audPos=s->loopEnd-1-(s->loopEnd-1-chan.audPos);
chan.audDir=true;
if (chan[0].audPos>=s->loopEnd) {
chan[0].audPos=s->loopEnd-1-(s->loopEnd-1-chan[0].audPos);
chan[0].audDir=true;
}
break;
default:
if (chan.audPos>=(int)s->samples) {
chan.sample=-1;
if (chan[0].audPos>=(int)s->samples) {
chan[0].sample=-1;
}
break;
}
} else if (chan.audPos>=(int)s->samples) {
chan.sample=-1;
} else if (chan[0].audPos>=(int)s->samples) {
chan[0].sample=-1;
}
}
if (chan.audPos>=0 && chan.audPos<(int)s->samples) {
output=s->data16[chan.audPos];
if (chan[0].audPos>=0 && chan[0].audPos<(int)s->samples) {
output=s->data16[chan[0].audPos];
}
} else {
chan.sample=-1;
chan[0].sample=-1;
}
}
}
output=output*chan.vol*chan.envVol/16384;
output=output*chan[0].vol*chan[0].envVol/16384;
oscBuf->data[oscBuf->needle++]=output;
if (outStereo) {
bufL[h]=((output*chan.panL)>>(depthScale+8))<<depthScale;
bufR[h]=((output*chan.panR)>>(depthScale+8))<<depthScale;
bufL[h]=((output*chan[0].panL)>>(depthScale+8))<<depthScale;
bufR[h]=((output*chan[0].panR)>>(depthScale+8))<<depthScale;
} else {
output=(output>>depthScale)<<depthScale;
bufL[h]=output;
@ -124,204 +124,210 @@ void DivPlatformPCMDAC::acquire(short* bufL, short* bufR, size_t start, size_t l
}
void DivPlatformPCMDAC::tick(bool sysTick) {
chan.std.next();
if (chan.std.vol.had) {
chan.envVol=chan.std.vol.val;
chan[0].std.next();
if (chan[0].std.vol.had) {
chan[0].envVol=chan[0].std.vol.val;
}
if (chan.std.arp.had) {
if (!chan.inPorta) {
chan.baseFreq=NOTE_FREQUENCY(parent->calcArp(chan.note,chan.std.arp.val));
if (chan[0].std.arp.had) {
if (!chan[0].inPorta) {
chan[0].baseFreq=NOTE_FREQUENCY(parent->calcArp(chan[0].note,chan[0].std.arp.val));
}
chan.freqChanged=true;
chan[0].freqChanged=true;
}
if (chan.useWave && chan.std.wave.had) {
if (chan.wave!=chan.std.wave.val || chan.ws.activeChanged()) {
chan.wave=chan.std.wave.val;
chan.ws.changeWave1(chan.wave);
if (!chan.keyOff) chan.keyOn=true;
if (chan[0].useWave && chan[0].std.wave.had) {
if (chan[0].wave!=chan[0].std.wave.val || chan[0].ws.activeChanged()) {
chan[0].wave=chan[0].std.wave.val;
chan[0].ws.changeWave1(chan[0].wave);
if (!chan[0].keyOff) chan[0].keyOn=true;
}
}
if (chan.useWave && chan.active) {
chan.ws.tick();
if (chan[0].useWave && chan[0].active) {
chan[0].ws.tick();
}
if (chan.std.pitch.had) {
if (chan.std.pitch.mode) {
chan.pitch2+=chan.std.pitch.val;
CLAMP_VAR(chan.pitch2,-32768,32767);
if (chan[0].std.pitch.had) {
if (chan[0].std.pitch.mode) {
chan[0].pitch2+=chan[0].std.pitch.val;
CLAMP_VAR(chan[0].pitch2,-32768,32767);
} else {
chan.pitch2=chan.std.pitch.val;
chan[0].pitch2=chan[0].std.pitch.val;
}
chan.freqChanged=true;
chan[0].freqChanged=true;
}
if (chan.std.panL.had) {
int val=chan.std.panL.val&0x7f;
chan.panL=val*2;
if (chan[0].std.panL.had) {
int val=chan[0].std.panL.val&0x7f;
chan[0].panL=val*2;
}
if (chan.std.panR.had) {
int val=chan.std.panR.val&0x7f;
chan.panR=val*2;
if (chan[0].std.panR.had) {
int val=chan[0].std.panR.val&0x7f;
chan[0].panR=val*2;
}
if (chan.std.phaseReset.had) {
if (chan.std.phaseReset.val==1) {
chan.audDir=false;
chan.audPos=0;
if (chan[0].std.phaseReset.had) {
if (chan[0].std.phaseReset.val==1) {
chan[0].audDir=false;
chan[0].audPos=0;
}
}
if (chan.freqChanged || chan.keyOn || chan.keyOff) {
//DivInstrument* ins=parent->getIns(chan.ins,DIV_INS_AMIGA);
if (chan[0].freqChanged || chan[0].keyOn || chan[0].keyOff) {
//DivInstrument* ins=parent->getIns(chan[0].ins,DIV_INS_AMIGA);
double off=1.0;
if (!chan.useWave && chan.sample>=0 && chan.sample<parent->song.sampleLen) {
DivSample* s=parent->getSample(chan.sample);
if (!chan[0].useWave && chan[0].sample>=0 && chan[0].sample<parent->song.sampleLen) {
DivSample* s=parent->getSample(chan[0].sample);
off=(s->centerRate>=1)?((double)s->centerRate/8363.0):1.0;
}
chan.freq=off*parent->calcFreq(chan.baseFreq,chan.pitch,false,2,chan.pitch2,chipClock,CHIP_FREQBASE);
if (chan.freq>16777215) chan.freq=16777215;
if (chan.keyOn) {
if (!chan.std.vol.had) {
chan.envVol=64;
chan[0].freq=off*parent->calcFreq(chan[0].baseFreq,chan[0].pitch,false,2,chan[0].pitch2,chipClock,CHIP_FREQBASE);
if (chan[0].freq>16777215) chan[0].freq=16777215;
if (chan[0].keyOn) {
if (!chan[0].std.vol.had) {
chan[0].envVol=64;
}
chan.keyOn=false;
chan[0].keyOn=false;
}
if (chan.keyOff) {
chan.keyOff=false;
if (chan[0].keyOff) {
chan[0].keyOff=false;
}
chan.freqChanged=false;
chan[0].freqChanged=false;
}
}
int DivPlatformPCMDAC::dispatch(DivCommand c) {
switch (c.cmd) {
case DIV_CMD_NOTE_ON: {
DivInstrument* ins=parent->getIns(chan.ins,DIV_INS_AMIGA);
DivInstrument* ins=parent->getIns(chan[0].ins,DIV_INS_AMIGA);
if (ins->amiga.useWave) {
chan.useWave=true;
chan.audLen=ins->amiga.waveLen+1;
if (chan.insChanged) {
if (chan.wave<0) {
chan.wave=0;
chan.ws.setWidth(chan.audLen);
chan.ws.changeWave1(chan.wave);
chan[0].useWave=true;
chan[0].audLen=ins->amiga.waveLen+1;
if (chan[0].insChanged) {
if (chan[0].wave<0) {
chan[0].wave=0;
chan[0].ws.setWidth(chan[0].audLen);
chan[0].ws.changeWave1(chan[0].wave);
}
}
} else {
chan.sample=ins->amiga.getSample(c.value);
chan.useWave=false;
chan[0].sample=ins->amiga.getSample(c.value);
chan[0].useWave=false;
}
if (c.value!=DIV_NOTE_NULL) {
chan.baseFreq=round(NOTE_FREQUENCY(c.value));
chan[0].baseFreq=round(NOTE_FREQUENCY(c.value));
}
if (chan.useWave || chan.sample<0 || chan.sample>=parent->song.sampleLen) {
chan.sample=-1;
if (chan[0].useWave || chan[0].sample<0 || chan[0].sample>=parent->song.sampleLen) {
chan[0].sample=-1;
}
if (chan.setPos) {
chan.setPos=false;
if (chan[0].setPos) {
chan[0].setPos=false;
} else {
chan.audDir=false;
chan.audPos=0;
chan[0].audDir=false;
chan[0].audPos=0;
}
chan.audSub=0;
chan[0].audSub=0;
if (c.value!=DIV_NOTE_NULL) {
chan.freqChanged=true;
chan.note=c.value;
chan[0].freqChanged=true;
chan[0].note=c.value;
}
chan.active=true;
chan.keyOn=true;
chan.macroInit(ins);
if (!parent->song.brokenOutVol && !chan.std.vol.will) {
chan.envVol=64;
chan[0].active=true;
chan[0].keyOn=true;
chan[0].macroInit(ins);
if (!parent->song.brokenOutVol && !chan[0].std.vol.will) {
chan[0].envVol=64;
}
if (chan.useWave) {
chan.ws.init(ins,chan.audLen,255,chan.insChanged);
if (chan[0].useWave) {
chan[0].ws.init(ins,chan[0].audLen,255,chan[0].insChanged);
}
chan.insChanged=false;
chan[0].insChanged=false;
break;
}
case DIV_CMD_NOTE_OFF:
chan.sample=-1;
chan.active=false;
chan.keyOff=true;
chan.macroInit(NULL);
chan[0].sample=-1;
chan[0].active=false;
chan[0].keyOff=true;
chan[0].macroInit(NULL);
break;
case DIV_CMD_NOTE_OFF_ENV:
case DIV_CMD_ENV_RELEASE:
chan.std.release();
chan[0].std.release();
break;
case DIV_CMD_INSTRUMENT:
if (chan.ins!=c.value || c.value2==1) {
chan.ins=c.value;
chan.insChanged=true;
if (chan[0].ins!=c.value || c.value2==1) {
chan[0].ins=c.value;
chan[0].insChanged=true;
}
break;
case DIV_CMD_VOLUME:
if (chan.vol!=c.value) {
chan.vol=c.value;
if (!chan.std.vol.has) {
chan.envVol=64;
if (chan[0].vol!=c.value) {
chan[0].vol=c.value;
if (!chan[0].std.vol.has) {
chan[0].envVol=64;
}
}
break;
case DIV_CMD_GET_VOLUME:
return chan.vol;
return chan[0].vol;
break;
case DIV_CMD_PANNING:
chan.panL=c.value;
chan.panR=c.value2;
chan[0].panL=c.value;
chan[0].panR=c.value2;
break;
case DIV_CMD_PITCH:
chan.pitch=c.value;
chan.freqChanged=true;
chan[0].pitch=c.value;
chan[0].freqChanged=true;
break;
case DIV_CMD_WAVE:
if (!chan.useWave) break;
chan.wave=c.value;
chan.keyOn=true;
chan.ws.changeWave1(chan.wave);
if (!chan[0].useWave) break;
chan[0].wave=c.value;
chan[0].keyOn=true;
chan[0].ws.changeWave1(chan[0].wave);
break;
case DIV_CMD_NOTE_PORTA: {
DivInstrument* ins=parent->getIns(chan.ins,DIV_INS_AMIGA);
chan.sample=ins->amiga.getSample(c.value2);
DivInstrument* ins=parent->getIns(chan[0].ins,DIV_INS_AMIGA);
chan[0].sample=ins->amiga.getSample(c.value2);
int destFreq=round(NOTE_FREQUENCY(c.value2));
bool return2=false;
if (destFreq>chan.baseFreq) {
chan.baseFreq+=c.value;
if (chan.baseFreq>=destFreq) {
chan.baseFreq=destFreq;
if (destFreq>chan[0].baseFreq) {
chan[0].baseFreq+=c.value;
if (chan[0].baseFreq>=destFreq) {
chan[0].baseFreq=destFreq;
return2=true;
}
} else {
chan.baseFreq-=c.value;
if (chan.baseFreq<=destFreq) {
chan.baseFreq=destFreq;
chan[0].baseFreq-=c.value;
if (chan[0].baseFreq<=destFreq) {
chan[0].baseFreq=destFreq;
return2=true;
}
}
chan.freqChanged=true;
chan[0].freqChanged=true;
if (return2) {
chan.inPorta=false;
chan[0].inPorta=false;
return 2;
}
break;
}
case DIV_CMD_LEGATO: {
chan.baseFreq=round(NOTE_FREQUENCY(c.value+((chan.std.arp.will && !chan.std.arp.mode)?(chan.std.arp.val):(0))));
chan.freqChanged=true;
chan.note=c.value;
chan[0].baseFreq=round(NOTE_FREQUENCY(c.value+((chan[0].std.arp.will && !chan[0].std.arp.mode)?(chan[0].std.arp.val):(0))));
chan[0].freqChanged=true;
chan[0].note=c.value;
break;
}
case DIV_CMD_PRE_PORTA:
if (chan.active && c.value2) {
if (parent->song.resetMacroOnPorta) chan.macroInit(parent->getIns(chan.ins,DIV_INS_AMIGA));
if (chan[0].active && c.value2) {
if (parent->song.resetMacroOnPorta) chan[0].macroInit(parent->getIns(chan[0].ins,DIV_INS_AMIGA));
}
chan.inPorta=c.value;
chan[0].inPorta=c.value;
break;
case DIV_CMD_SAMPLE_POS:
if (chan.useWave) break;
chan.audPos=c.value;
chan.setPos=true;
if (chan[0].useWave) break;
chan[0].audPos=c.value;
chan[0].setPos=true;
break;
case DIV_CMD_GET_VOLMAX:
return 255;
break;
case DIV_CMD_MACRO_OFF:
chan[c.chan].std.mask(c.value,true);
break;
case DIV_CMD_MACRO_ON:
chan[c.chan].std.mask(c.value,false);
break;
case DIV_ALWAYS_SET_VOLUME:
return 1;
break;
@ -336,11 +342,11 @@ void DivPlatformPCMDAC::muteChannel(int ch, bool mute) {
}
void DivPlatformPCMDAC::forceIns() {
chan.insChanged=true;
chan.freqChanged=true;
chan.audDir=false;
chan.audPos=0;
chan.sample=-1;
chan[0].insChanged=true;
chan[0].freqChanged=true;
chan[0].audDir=false;
chan[0].audPos=0;
chan[0].sample=-1;
}
void* DivPlatformPCMDAC::getChanState(int ch) {
@ -352,10 +358,10 @@ DivDispatchOscBuffer* DivPlatformPCMDAC::getOscBuffer(int ch) {
}
void DivPlatformPCMDAC::reset() {
chan=DivPlatformPCMDAC::Channel();
chan.std.setEngine(parent);
chan.ws.setEngine(parent);
chan.ws.init(NULL,32,255);
chan[0]=DivPlatformPCMDAC::Channel();
chan[0].std.setEngine(parent);
chan[0].ws.setEngine(parent);
chan[0].ws.init(NULL,32,255);
}
bool DivPlatformPCMDAC::isStereo() {
@ -363,23 +369,23 @@ bool DivPlatformPCMDAC::isStereo() {
}
DivMacroInt* DivPlatformPCMDAC::getChanMacroInt(int ch) {
return &chan.std;
return &chan[0].std;
}
void DivPlatformPCMDAC::notifyInsChange(int ins) {
if (chan.ins==ins) {
chan.insChanged=true;
if (chan[0].ins==ins) {
chan[0].insChanged=true;
}
}
void DivPlatformPCMDAC::notifyWaveChange(int wave) {
if (chan.useWave && chan.wave==wave) {
chan.ws.changeWave1(wave);
if (chan[0].useWave && chan[0].wave==wave) {
chan[0].ws.changeWave1(wave);
}
}
void DivPlatformPCMDAC::notifyInsDeletion(void* ins) {
chan.std.notifyInsDeletion((DivInstrument*)ins);
chan[0].std.notifyInsDeletion((DivInstrument*)ins);
}
void DivPlatformPCMDAC::setFlags(const DivConfig& flags) {

View file

@ -51,7 +51,7 @@ class DivPlatformPCMDAC: public DivDispatch {
setPos(false),
envVol(64) {}
};
Channel chan;
Channel chan[1];
DivDispatchOscBuffer* oscBuf;
bool isMuted;
int outDepth;

View file

@ -464,6 +464,12 @@ int DivPlatformPCSpeaker::dispatch(DivCommand c) {
case DIV_CMD_GET_VOLMAX:
return 1;
break;
case DIV_CMD_MACRO_OFF:
chan[c.chan].std.mask(c.value,true);
break;
case DIV_CMD_MACRO_ON:
chan[c.chan].std.mask(c.value,false);
break;
case DIV_ALWAYS_SET_VOLUME:
return 1;
break;

View file

@ -44,12 +44,12 @@ void DivPlatformPET::rWrite(unsigned int addr, unsigned char val) {
case 9:
// simulate phase reset from switching between hw/sw shift registers
if ((regPool[9]==0)^(val==0)) {
chan.sreg=chan.wave;
chan[0].sreg=chan[0].wave;
}
break;
case 10:
chan.sreg=val;
if (hwSROutput) chan.cnt=2;
chan[0].sreg=val;
if (hwSROutput) chan[0].cnt=2;
break;
}
regPool[addr]=val;
@ -57,27 +57,27 @@ void DivPlatformPET::rWrite(unsigned int addr, unsigned char val) {
void DivPlatformPET::acquire(short* bufL, short* bufR, size_t start, size_t len) {
bool hwSROutput=((regPool[11]>>2)&7)==4;
if (chan.enable) {
if (chan[0].enable) {
int reload=regPool[8]*2+4;
if (!hwSROutput) {
reload+=regPool[9]*512;
}
for (size_t h=start; h<start+len; h++) {
if (SAMP_DIVIDER>chan.cnt) {
chan.out=(chan.sreg&1)*32767;
chan.sreg=(chan.sreg>>1)|((chan.sreg&1)<<7);
chan.cnt+=reload-SAMP_DIVIDER;
if (SAMP_DIVIDER>chan[0].cnt) {
chan[0].out=(chan[0].sreg&1)*32767;
chan[0].sreg=(chan[0].sreg>>1)|((chan[0].sreg&1)<<7);
chan[0].cnt+=reload-SAMP_DIVIDER;
} else {
chan.cnt-=SAMP_DIVIDER;
chan[0].cnt-=SAMP_DIVIDER;
}
bufL[h]=chan.out;
bufR[h]=chan.out;
oscBuf->data[oscBuf->needle++]=chan.out;
bufL[h]=chan[0].out;
bufR[h]=chan[0].out;
oscBuf->data[oscBuf->needle++]=chan[0].out;
}
// emulate driver writes to PCR
if (!hwSROutput) regPool[12]=chan.out?0xe0:0xc0;
if (!hwSROutput) regPool[12]=chan[0].out?0xe0:0xc0;
} else {
chan.out=0;
chan[0].out=0;
for (size_t h=start; h<start+len; h++) {
bufL[h]=0;
bufR[h]=0;
@ -87,152 +87,158 @@ void DivPlatformPET::acquire(short* bufL, short* bufR, size_t start, size_t len)
}
void DivPlatformPET::writeOutVol() {
if (chan.active && !isMuted && chan.outVol>0) {
chan.enable=true;
if (chan[0].active && !isMuted && chan[0].outVol>0) {
chan[0].enable=true;
rWrite(11,regPool[9]==0?16:0);
} else {
chan.enable=false;
chan[0].enable=false;
rWrite(11,0);
}
}
void DivPlatformPET::tick(bool sysTick) {
chan.std.next();
if (chan.std.vol.had) {
chan.outVol=chan.std.vol.val&chan.vol;
chan[0].std.next();
if (chan[0].std.vol.had) {
chan[0].outVol=chan[0].std.vol.val&chan[0].vol;
writeOutVol();
}
if (chan.std.arp.had) {
if (!chan.inPorta) {
chan.baseFreq=NOTE_PERIODIC(parent->calcArp(chan.note,chan.std.arp.val));
if (chan[0].std.arp.had) {
if (!chan[0].inPorta) {
chan[0].baseFreq=NOTE_PERIODIC(parent->calcArp(chan[0].note,chan[0].std.arp.val));
}
chan.freqChanged=true;
chan[0].freqChanged=true;
}
if (chan.std.wave.had) {
if (chan.wave!=chan.std.wave.val) {
chan.wave=chan.std.wave.val;
rWrite(10,chan.wave);
if (chan[0].std.wave.had) {
if (chan[0].wave!=chan[0].std.wave.val) {
chan[0].wave=chan[0].std.wave.val;
rWrite(10,chan[0].wave);
}
}
if (chan.std.pitch.had) {
if (chan.std.pitch.mode) {
chan.pitch2+=chan.std.pitch.val;
CLAMP_VAR(chan.pitch2,-32768,32767);
if (chan[0].std.pitch.had) {
if (chan[0].std.pitch.mode) {
chan[0].pitch2+=chan[0].std.pitch.val;
CLAMP_VAR(chan[0].pitch2,-32768,32767);
} else {
chan.pitch2=chan.std.pitch.val;
chan[0].pitch2=chan[0].std.pitch.val;
}
chan.freqChanged=true;
chan[0].freqChanged=true;
}
if (chan.freqChanged || chan.keyOn || chan.keyOff) {
chan.freq=parent->calcFreq(chan.baseFreq,chan.pitch,true,0,chan.pitch2,chipClock,CHIP_DIVIDER)-2;
if (chan.freq>65535) chan.freq=65535;
if (chan.freq<0) chan.freq=0;
rWrite(8,chan.freq&0xff);
rWrite(9,chan.freq>>8);
if (chan.keyOn) {
if (!chan.std.vol.will) {
chan.outVol=chan.vol;
if (chan[0].freqChanged || chan[0].keyOn || chan[0].keyOff) {
chan[0].freq=parent->calcFreq(chan[0].baseFreq,chan[0].pitch,true,0,chan[0].pitch2,chipClock,CHIP_DIVIDER)-2;
if (chan[0].freq>65535) chan[0].freq=65535;
if (chan[0].freq<0) chan[0].freq=0;
rWrite(8,chan[0].freq&0xff);
rWrite(9,chan[0].freq>>8);
if (chan[0].keyOn) {
if (!chan[0].std.vol.will) {
chan[0].outVol=chan[0].vol;
}
chan.keyOn=false;
chan[0].keyOn=false;
}
if (chan.keyOff) {
chan.keyOff=false;
if (chan[0].keyOff) {
chan[0].keyOff=false;
}
// update mode setting and channel enable
writeOutVol();
chan.freqChanged=false;
chan[0].freqChanged=false;
}
}
int DivPlatformPET::dispatch(DivCommand c) {
switch (c.cmd) {
case DIV_CMD_NOTE_ON: {
DivInstrument* ins=parent->getIns(chan.ins,DIV_INS_PET);
DivInstrument* ins=parent->getIns(chan[0].ins,DIV_INS_PET);
if (c.value!=DIV_NOTE_NULL) {
chan.baseFreq=NOTE_PERIODIC(c.value);
chan.freqChanged=true;
chan.note=c.value;
chan[0].baseFreq=NOTE_PERIODIC(c.value);
chan[0].freqChanged=true;
chan[0].note=c.value;
}
chan.active=true;
chan.keyOn=true;
chan.macroInit(ins);
if (!parent->song.brokenOutVol && !chan.std.vol.will) {
chan.outVol=chan.vol;
chan[0].active=true;
chan[0].keyOn=true;
chan[0].macroInit(ins);
if (!parent->song.brokenOutVol && !chan[0].std.vol.will) {
chan[0].outVol=chan[0].vol;
}
break;
}
case DIV_CMD_NOTE_OFF:
chan.active=false;
chan.keyOff=true;
chan.macroInit(NULL);
chan[0].active=false;
chan[0].keyOff=true;
chan[0].macroInit(NULL);
break;
case DIV_CMD_NOTE_OFF_ENV:
case DIV_CMD_ENV_RELEASE:
chan.std.release();
chan[0].std.release();
break;
case DIV_CMD_INSTRUMENT:
if (chan.ins!=c.value || c.value2==1) {
chan.ins=c.value;
if (chan[0].ins!=c.value || c.value2==1) {
chan[0].ins=c.value;
}
break;
case DIV_CMD_VOLUME:
if (chan.vol!=c.value) {
chan.vol=c.value;
if (!chan.std.vol.had) {
chan.outVol=chan.vol;
if (chan[0].vol!=c.value) {
chan[0].vol=c.value;
if (!chan[0].std.vol.had) {
chan[0].outVol=chan[0].vol;
writeOutVol();
}
}
break;
case DIV_CMD_GET_VOLUME:
return chan.vol;
return chan[0].vol;
break;
case DIV_CMD_PITCH:
chan.pitch=c.value;
chan.freqChanged=true;
chan[0].pitch=c.value;
chan[0].freqChanged=true;
break;
case DIV_CMD_WAVE:
chan.wave=c.value;
rWrite(10,chan.wave);
chan[0].wave=c.value;
rWrite(10,chan[0].wave);
break;
case DIV_CMD_NOTE_PORTA: {
int destFreq=NOTE_PERIODIC(c.value2);
bool return2=false;
if (destFreq>chan.baseFreq) {
chan.baseFreq+=c.value;
if (chan.baseFreq>=destFreq) {
chan.baseFreq=destFreq;
if (destFreq>chan[0].baseFreq) {
chan[0].baseFreq+=c.value;
if (chan[0].baseFreq>=destFreq) {
chan[0].baseFreq=destFreq;
return2=true;
}
} else {
chan.baseFreq-=c.value;
if (chan.baseFreq<=destFreq) {
chan.baseFreq=destFreq;
chan[0].baseFreq-=c.value;
if (chan[0].baseFreq<=destFreq) {
chan[0].baseFreq=destFreq;
return2=true;
}
}
chan.freqChanged=true;
chan[0].freqChanged=true;
if (return2) {
chan.inPorta=false;
chan[0].inPorta=false;
return 2;
}
break;
}
case DIV_CMD_LEGATO:
chan.baseFreq=NOTE_PERIODIC(c.value+((chan.std.arp.will && !chan.std.arp.mode)?(chan.std.arp.val):(0)));
chan.freqChanged=true;
chan.note=c.value;
chan[0].baseFreq=NOTE_PERIODIC(c.value+((chan[0].std.arp.will && !chan[0].std.arp.mode)?(chan[0].std.arp.val):(0)));
chan[0].freqChanged=true;
chan[0].note=c.value;
break;
case DIV_CMD_PRE_PORTA:
if (chan.active && c.value2) {
if (parent->song.resetMacroOnPorta) chan.macroInit(parent->getIns(chan.ins,DIV_INS_PET));
if (chan[0].active && c.value2) {
if (parent->song.resetMacroOnPorta) chan[0].macroInit(parent->getIns(chan[0].ins,DIV_INS_PET));
}
if (!chan.inPorta && c.value && !parent->song.brokenPortaArp && chan.std.arp.will) chan.baseFreq=NOTE_PERIODIC(chan.note);
chan.inPorta=c.value;
if (!chan[0].inPorta && c.value && !parent->song.brokenPortaArp && chan[0].std.arp.will) chan[0].baseFreq=NOTE_PERIODIC(chan[0].note);
chan[0].inPorta=c.value;
break;
case DIV_CMD_GET_VOLMAX:
return 1;
break;
case DIV_CMD_MACRO_OFF:
chan[c.chan].std.mask(c.value,true);
break;
case DIV_CMD_MACRO_ON:
chan[c.chan].std.mask(c.value,false);
break;
case DIV_ALWAYS_SET_VOLUME:
return 1;
break;
@ -248,8 +254,8 @@ void DivPlatformPET::muteChannel(int ch, bool mute) {
}
void DivPlatformPET::forceIns() {
chan.insChanged=true;
chan.freqChanged=true;
chan[0].insChanged=true;
chan[0].freqChanged=true;
writeOutVol();
}
@ -258,7 +264,7 @@ void* DivPlatformPET::getChanState(int ch) {
}
DivMacroInt* DivPlatformPET::getChanMacroInt(int ch) {
return &chan.std;
return &chan[0].std;
}
DivDispatchOscBuffer* DivPlatformPET::getOscBuffer(int ch) {
@ -275,8 +281,8 @@ int DivPlatformPET::getRegisterPoolSize() {
void DivPlatformPET::reset() {
memset(regPool,0,16);
chan=Channel();
chan.std.setEngine(parent);
chan[0]=Channel();
chan[0].std.setEngine(parent);
}
bool DivPlatformPET::isStereo() {
@ -284,7 +290,7 @@ bool DivPlatformPET::isStereo() {
}
void DivPlatformPET::notifyInsDeletion(void* ins) {
chan.std.notifyInsDeletion((DivInstrument*)ins);
chan[0].std.notifyInsDeletion((DivInstrument*)ins);
}
void DivPlatformPET::poke(unsigned int addr, unsigned short val) {

View file

@ -37,7 +37,7 @@ class DivPlatformPET: public DivDispatch {
cnt(0),
out(0) {}
};
Channel chan;
Channel chan[1];
DivDispatchOscBuffer* oscBuf;
bool isMuted;

View file

@ -123,7 +123,7 @@ int DivPlatformPokeMini::dispatch(DivCommand c) {
vol=(chan[c.chan].outVol==2)?3:chan[c.chan].outVol;
chan[c.chan].active=true;
chan[c.chan].keyOn=true;
chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_BEEPER));
chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_POKEMINI));
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
}
@ -199,6 +199,12 @@ int DivPlatformPokeMini::dispatch(DivCommand c) {
case DIV_CMD_GET_VOLMAX:
return 2;
break;
case DIV_CMD_MACRO_OFF:
chan[c.chan].std.mask(c.value,true);
break;
case DIV_CMD_MACRO_ON:
chan[c.chan].std.mask(c.value,false);
break;
case DIV_ALWAYS_SET_VOLUME:
return 1;
break;

View file

@ -172,6 +172,12 @@ int DivPlatformPong::dispatch(DivCommand c) {
case DIV_CMD_GET_VOLMAX:
return 1;
break;
case DIV_CMD_MACRO_OFF:
chan[c.chan].std.mask(c.value,true);
break;
case DIV_CMD_MACRO_ON:
chan[c.chan].std.mask(c.value,false);
break;
case DIV_ALWAYS_SET_VOLUME:
return 1;
break;

View file

@ -573,6 +573,12 @@ int DivPlatformQSound::dispatch(DivCommand c) {
case DIV_CMD_GET_VOLMAX:
return 255;
break;
case DIV_CMD_MACRO_OFF:
chan[c.chan].std.mask(c.value,true);
break;
case DIV_CMD_MACRO_ON:
chan[c.chan].std.mask(c.value,false);
break;
case DIV_ALWAYS_SET_VOLUME:
return 1;
break;

View file

@ -273,6 +273,12 @@ int DivPlatformRF5C68::dispatch(DivCommand c) {
case DIV_CMD_GET_VOLMAX:
return 255;
break;
case DIV_CMD_MACRO_OFF:
chan[c.chan].std.mask(c.value,true);
break;
case DIV_CMD_MACRO_ON:
chan[c.chan].std.mask(c.value,false);
break;
case DIV_ALWAYS_SET_VOLUME:
return 1;
break;

View file

@ -308,6 +308,12 @@ int DivPlatformSAA1099::dispatch(DivCommand c) {
saaEnv[c.chan/3]=c.value;
rWrite(0x18+(c.chan/3),c.value);
break;
case DIV_CMD_MACRO_OFF:
chan[c.chan].std.mask(c.value,true);
break;
case DIV_CMD_MACRO_ON:
chan[c.chan].std.mask(c.value,false);
break;
case DIV_ALWAYS_SET_VOLUME:
return 0;
break;

View file

@ -255,6 +255,12 @@ int DivPlatformSCC::dispatch(DivCommand c) {
case DIV_CMD_GET_VOLMAX:
return 15;
break;
case DIV_CMD_MACRO_OFF:
chan[c.chan].std.mask(c.value,true);
break;
case DIV_CMD_MACRO_ON:
chan[c.chan].std.mask(c.value,false);
break;
case DIV_ALWAYS_SET_VOLUME:
return 1;
break;

View file

@ -358,6 +358,12 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) {
sampleBank=parent->song.sample.size()/12;
}
break;
case DIV_CMD_MACRO_OFF:
chan[c.chan].std.mask(c.value,true);
break;
case DIV_CMD_MACRO_ON:
chan[c.chan].std.mask(c.value,false);
break;
case DIV_ALWAYS_SET_VOLUME:
return 0;
break;

View file

@ -378,6 +378,12 @@ int DivPlatformSMS::dispatch(DivCommand c) {
case DIV_CMD_GET_VOLMAX:
return 15;
break;
case DIV_CMD_MACRO_OFF:
chan[c.chan].std.mask(c.value,true);
break;
case DIV_CMD_MACRO_ON:
chan[c.chan].std.mask(c.value,false);
break;
case DIV_ALWAYS_SET_VOLUME:
return 0;
break;

View file

@ -561,6 +561,12 @@ int DivPlatformSNES::dispatch(DivCommand c) {
case DIV_CMD_GET_VOLMAX:
return 127;
break;
case DIV_CMD_MACRO_OFF:
chan[c.chan].std.mask(c.value,true);
break;
case DIV_CMD_MACRO_ON:
chan[c.chan].std.mask(c.value,false);
break;
default:
break;
}

View file

@ -0,0 +1,175 @@
// license:BSD-3-Clause
// copyright-holders:Acho A. Tang,R. Belmont, Valley Bell
/*********************************************************
Irem GA20 PCM Sound Chip
80 pin QFP, label NANAO GA20 (Nanao Corporation was Irem's parent company)
TODO:
- It's not currently known whether this chip is stereo.
- Is sample position base(regs 0,1) used while sample is playing, or
latched at key on? We've always emulated it the latter way.
gunforc2 seems to be the only game updating the address regs sometimes
while a sample is playing, but it doesn't seem intentional.
- What is the 2nd sample address for? Is it end(cut-off) address, or
loop start address? Every game writes a value that's past sample end.
- All games write either 0 or 2 to reg #6, do other bits have any function?
Revisions:
04-15-2002 Acho A. Tang
- rewrote channel mixing
- added prelimenary volume and sample rate emulation
05-30-2002 Acho A. Tang
- applied hyperbolic gain control to volume and used
a musical-note style progression in sample rate
calculation(still very inaccurate)
02-18-2004 R. Belmont
- sample rate calculation reverse-engineered.
Thanks to Fujix, Yasuhiro Ogawa, the Guru, and Tormod
for real PCB samples that made this possible.
02-03-2007 R. Belmont
- Cleaned up faux x86 assembly.
09-25-2018 Valley Bell & co
- rewrote channel update to make data 0 act as sample terminator
DISCLAIMER
- This file is modified for suitable in furnace.
- modified by cam900
*********************************************************/
#include "iremga20.h"
#include <string.h>
//**************************************************************************
// LIVE DEVICE
//**************************************************************************
//-------------------------------------------------
// iremga20_device - constructor
//-------------------------------------------------
iremga20_device::iremga20_device(iremga20_intf &intf) :
m_regs{0},
m_channel{channel_def(), channel_def(), channel_def(), channel_def()},
m_intf(intf)
{
}
//-------------------------------------------------
// device_reset - device-specific reset
//-------------------------------------------------
void iremga20_device::device_reset()
{
memset(m_regs, 0, 0x20 * sizeof(u8));
for (int i = 0; i < 4; i++)
{
m_channel[i].rate = 0;
m_channel[i].pos = 0;
m_channel[i].counter = 0;
m_channel[i].end = 0;
m_channel[i].volume = 0;
m_channel[i].play = false;
}
}
//-------------------------------------------------
// sound_stream_update - handle a stream update
//-------------------------------------------------
void iremga20_device::sound_stream_update(short** outputs, int len)
{
for (int i = 0; i < len; i++)
{
for (int j = 0; j < 4; j++)
{
s32 sampleout = 0;
channel_def &ch = m_channel[j];
if (ch.play)
{
int sample = m_intf.read_byte(ch.pos);
if (sample == 0x00) // check for sample end marker
ch.play = false;
else
{
sampleout = (sample - 0x80) * (s32)ch.volume;
ch.counter--;
if (ch.counter <= ch.rate)
{
ch.pos++;
ch.counter = 0x100;
}
}
}
outputs[j][i] = sampleout;
}
}
}
void iremga20_device::write(u32 offset, u8 data)
{
offset &= 0x1f;
m_regs[offset] = data;
int ch = offset >> 3;
// channel regs:
// 0,1: start address
// 2,3: end? address
// 4: rate
// 5: volume
// 6: control
// 7: voice status (read-only)
switch (offset & 0x7)
{
case 4:
m_channel[ch].rate = data;
break;
case 5:
m_channel[ch].volume = (data * 256) / (data + 10);
break;
case 6:
// d1: key on/off
if (data & 2)
{
m_channel[ch].play = true;
m_channel[ch].pos = (m_regs[ch << 3 | 0] | m_regs[ch << 3 | 1] << 8) << 4;
m_channel[ch].end = (m_regs[ch << 3 | 2] | m_regs[ch << 3 | 3] << 8) << 4;
m_channel[ch].counter = 0x100;
}
else
m_channel[ch].play = false;
// other: unknown/unused
// possibilities are: loop flag, left/right speaker(stereo)
break;
}
}
u8 iremga20_device::read(u32 offset)
{
offset &= 0x1f;
int ch = offset >> 3;
switch (offset & 0x7)
{
case 7: // voice status. bit 0 is 1 if active. (routine around 0xccc in rtypeleo)
return m_channel[ch].play ? 1 : 0;
}
return 0;
}

View file

@ -0,0 +1,74 @@
// license:BSD-3-Clause
// copyright-holders:Acho A. Tang,R. Belmont
/*********************************************************
Irem GA20 PCM Sound Chip
DISCLAIMER
- This file is modified for suitable in furnace.
- modified by cam900
*********************************************************/
#ifndef MAME_SOUND_IREMGA20_H
#define MAME_SOUND_IREMGA20_H
#pragma once
//**************************************************************************
// TYPE DEFINITIONS
//**************************************************************************
using u8 = unsigned char;
using u32 = unsigned int;
using s32 = signed int;
class iremga20_intf
{
public:
virtual u8 read_byte(u32 address) { return 0; };
};
// ======================> iremga20_device
class iremga20_device
{
public:
iremga20_device(iremga20_intf &intf);
void write(u32 offset, u8 data);
u8 read(u32 offset);
// device-level overrides
void device_reset();
// sound stream update overrides
void sound_stream_update(short** outputs, int len);
private:
struct channel_def
{
channel_def() :
rate(0),
pos(0),
counter(0),
end(0),
volume(0),
play(0)
{
}
u32 rate;
u32 pos;
u32 counter;
u32 end;
u32 volume;
bool play;
};
u8 m_regs[0x20];
channel_def m_channel[4];
iremga20_intf &m_intf;
};
#endif // MAME_SOUND_IREMGA20_H

View file

@ -4,7 +4,7 @@
# modified version
this is a modified version of ymfm with a small bug fix.
this is a modified version which contains many fixes.
## Supported environments

View file

@ -174,7 +174,7 @@ public:
// system-wide registers
uint32_t test() const { return byte(0x01, 0, 8); }
uint32_t lfo_reset() const { return byte(0x01, 1, 1); }
uint32_t noise_frequency() const { return byte(0x0f, 0, 5) ^ 0x1f; }
uint32_t noise_frequency() const { return byte(0x0f, 0, 5); }
uint32_t noise_enable() const { return byte(0x0f, 7, 1); }
uint32_t timer_a_value() const { return word(0x10, 0, 8, 0x11, 0, 2); }
uint32_t timer_b_value() const { return byte(0x12, 0, 8); }

View file

@ -412,6 +412,12 @@ int DivPlatformSoundUnit::dispatch(DivCommand c) {
case DIV_CMD_GET_VOLMAX:
return 127;
break;
case DIV_CMD_MACRO_OFF:
chan[c.chan].std.mask(c.value,true);
break;
case DIV_CMD_MACRO_ON:
chan[c.chan].std.mask(c.value,false);
break;
case DIV_ALWAYS_SET_VOLUME:
return 1;
break;

View file

@ -406,6 +406,12 @@ int DivPlatformSwan::dispatch(DivCommand c) {
case DIV_CMD_GET_VOLMAX:
return 15;
break;
case DIV_CMD_MACRO_OFF:
chan[c.chan].std.mask(c.value,true);
break;
case DIV_CMD_MACRO_ON:
chan[c.chan].std.mask(c.value,false);
break;
case DIV_ALWAYS_SET_VOLUME:
return 1;
break;

View file

@ -262,6 +262,12 @@ int DivPlatformT6W28::dispatch(DivCommand c) {
case DIV_CMD_GET_VOLMAX:
return 15;
break;
case DIV_CMD_MACRO_OFF:
chan[c.chan].std.mask(c.value,true);
break;
case DIV_CMD_MACRO_ON:
chan[c.chan].std.mask(c.value,false);
break;
case DIV_ALWAYS_SET_VOLUME:
return 1;
break;

View file

@ -253,6 +253,12 @@ int DivPlatformTIA::dispatch(DivCommand c) {
rWrite(0x15+c.chan,chan[c.chan].shape);
chan[c.chan].freqChanged=true;
break;
case DIV_CMD_MACRO_OFF:
chan[c.chan].std.mask(c.value,true);
break;
case DIV_CMD_MACRO_ON:
chan[c.chan].std.mask(c.value,false);
break;
case DIV_ALWAYS_SET_VOLUME:
return 0;
break;

View file

@ -784,6 +784,12 @@ int DivPlatformTX81Z::dispatch(DivCommand c) {
}
break;
}
case DIV_CMD_MACRO_OFF:
chan[c.chan].std.mask(c.value,true);
break;
case DIV_CMD_MACRO_ON:
chan[c.chan].std.mask(c.value,false);
break;
case DIV_ALWAYS_SET_VOLUME:
return 0;
break;

View file

@ -380,6 +380,12 @@ int DivPlatformVB::dispatch(DivCommand c) {
case DIV_CMD_GET_VOLMAX:
return 15;
break;
case DIV_CMD_MACRO_OFF:
chan[c.chan].std.mask(c.value,true);
break;
case DIV_CMD_MACRO_ON:
chan[c.chan].std.mask(c.value,false);
break;
case DIV_ALWAYS_SET_VOLUME:
return 1;
break;

View file

@ -356,6 +356,12 @@ int DivPlatformVERA::dispatch(DivCommand c) {
return 15;
}
break;
case DIV_CMD_MACRO_OFF:
chan[c.chan].std.mask(c.value,true);
break;
case DIV_CMD_MACRO_ON:
chan[c.chan].std.mask(c.value,false);
break;
case DIV_ALWAYS_SET_VOLUME:
return 0;
break;

View file

@ -231,6 +231,12 @@ int DivPlatformVIC20::dispatch(DivCommand c) {
case DIV_CMD_GET_VOLMAX:
return 15;
break;
case DIV_CMD_MACRO_OFF:
chan[c.chan].std.mask(c.value,true);
break;
case DIV_CMD_MACRO_ON:
chan[c.chan].std.mask(c.value,false);
break;
case DIV_ALWAYS_SET_VOLUME:
return 1;
break;

View file

@ -403,6 +403,12 @@ int DivPlatformVRC6::dispatch(DivCommand c) {
if (c.chan==2) return 63; // sawtooth has 6 bit volume
return 15; // pulse has 4 bit volume
break;
case DIV_CMD_MACRO_OFF:
chan[c.chan].std.mask(c.value,true);
break;
case DIV_CMD_MACRO_ON:
chan[c.chan].std.mask(c.value,false);
break;
case DIV_ALWAYS_SET_VOLUME:
return 1;
break;

View file

@ -819,6 +819,12 @@ int DivPlatformX1_010::dispatch(DivCommand c) {
case DIV_CMD_GET_VOLMAX:
return 15;
break;
case DIV_CMD_MACRO_OFF:
chan[c.chan].std.mask(c.value,true);
break;
case DIV_CMD_MACRO_ON:
chan[c.chan].std.mask(c.value,false);
break;
case DIV_ALWAYS_SET_VOLUME:
return 1;
break;

View file

@ -729,6 +729,12 @@ int DivPlatformYM2203::dispatch(DivCommand c) {
case DIV_CMD_FM_HARD_RESET:
chan[c.chan].hardReset=c.value;
break;
case DIV_CMD_MACRO_OFF:
chan[c.chan].std.mask(c.value,true);
break;
case DIV_CMD_MACRO_ON:
chan[c.chan].std.mask(c.value,false);
break;
case DIV_ALWAYS_SET_VOLUME:
return 0;
break;

View file

@ -317,6 +317,12 @@ int DivPlatformYM2203Ext::dispatch(DivCommand c) {
case DIV_CMD_GET_VOLMAX:
return 127;
break;
case DIV_CMD_MACRO_OFF:
opChan[ch].std.mask(c.value,true);
break;
case DIV_CMD_MACRO_ON:
opChan[ch].std.mask(c.value,false);
break;
case DIV_ALWAYS_SET_VOLUME:
return 0;
break;

View file

@ -1123,6 +1123,12 @@ int DivPlatformYM2608::dispatch(DivCommand c) {
case DIV_CMD_FM_HARD_RESET:
chan[c.chan].hardReset=c.value;
break;
case DIV_CMD_MACRO_OFF:
chan[c.chan].std.mask(c.value,true);
break;
case DIV_CMD_MACRO_ON:
chan[c.chan].std.mask(c.value,false);
break;
case DIV_ALWAYS_SET_VOLUME:
return 0;
break;

View file

@ -334,6 +334,12 @@ int DivPlatformYM2608Ext::dispatch(DivCommand c) {
case DIV_CMD_GET_VOLMAX:
return 127;
break;
case DIV_CMD_MACRO_OFF:
opChan[ch].std.mask(c.value,true);
break;
case DIV_CMD_MACRO_ON:
opChan[ch].std.mask(c.value,false);
break;
case DIV_ALWAYS_SET_VOLUME:
return 0;
break;

View file

@ -1103,6 +1103,12 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
case DIV_CMD_FM_HARD_RESET:
chan[c.chan].hardReset=c.value;
break;
case DIV_CMD_MACRO_OFF:
chan[c.chan].std.mask(c.value,true);
break;
case DIV_CMD_MACRO_ON:
chan[c.chan].std.mask(c.value,false);
break;
case DIV_ALWAYS_SET_VOLUME:
return 0;
break;

View file

@ -1166,6 +1166,12 @@ int DivPlatformYM2610B::dispatch(DivCommand c) {
case DIV_CMD_FM_HARD_RESET:
chan[c.chan].hardReset=c.value;
break;
case DIV_CMD_MACRO_OFF:
chan[c.chan].std.mask(c.value,true);
break;
case DIV_CMD_MACRO_ON:
chan[c.chan].std.mask(c.value,false);
break;
case DIV_ALWAYS_SET_VOLUME:
return 0;
break;

View file

@ -330,6 +330,12 @@ int DivPlatformYM2610BExt::dispatch(DivCommand c) {
case DIV_CMD_GET_VOLMAX:
return 127;
break;
case DIV_CMD_MACRO_OFF:
opChan[ch].std.mask(c.value,true);
break;
case DIV_CMD_MACRO_ON:
opChan[ch].std.mask(c.value,false);
break;
case DIV_ALWAYS_SET_VOLUME:
return 0;
break;

View file

@ -330,6 +330,12 @@ int DivPlatformYM2610Ext::dispatch(DivCommand c) {
case DIV_CMD_GET_VOLMAX:
return 127;
break;
case DIV_CMD_MACRO_OFF:
opChan[ch].std.mask(c.value,true);
break;
case DIV_CMD_MACRO_ON:
opChan[ch].std.mask(c.value,false);
break;
case DIV_ALWAYS_SET_VOLUME:
return 0;
break;

View file

@ -311,6 +311,12 @@ int DivPlatformYMZ280B::dispatch(DivCommand c) {
case DIV_CMD_GET_VOLMAX:
return 255;
break;
case DIV_CMD_MACRO_OFF:
chan[c.chan].std.mask(c.value,true);
break;
case DIV_CMD_MACRO_ON:
chan[c.chan].std.mask(c.value,false);
break;
case DIV_ALWAYS_SET_VOLUME:
return 1;
break;

View file

@ -209,6 +209,12 @@ int DivPlatformZXBeeper::dispatch(DivCommand c) {
case DIV_CMD_GET_VOLMAX:
return 1;
break;
case DIV_CMD_MACRO_OFF:
chan[c.chan].std.mask(c.value,true);
break;
case DIV_CMD_MACRO_ON:
chan[c.chan].std.mask(c.value,false);
break;
case DIV_ALWAYS_SET_VOLUME:
return 1;
break;

View file

@ -218,9 +218,12 @@ const char* cmdName[]={
"SNES_ECHO_FEEDBACK",
"SNES_ECHO_FIR",
"DIV_CMD_NES_ENV_MODE",
"DIV_CMD_NES_LENGTH",
"DIV_CMD_NES_COUNT_MODE",
"NES_ENV_MODE",
"NES_LENGTH",
"NES_COUNT_MODE",
"MACRO_OFF",
"MACRO_ON",
"ALWAYS_SET_VOLUME"
};
@ -836,6 +839,12 @@ void DivEngine::processRow(int i, bool afterDelay) {
chan[i].volSpeed=-effectVal;
dispatchCmd(DivCommand(DIV_CMD_HINT_VOL_SLIDE,i,chan[i].volSpeed));
break;
case 0xf5: // disable macro
dispatchCmd(DivCommand(DIV_CMD_MACRO_OFF,i,effectVal&0xff));
break;
case 0xf6: // enable macro
dispatchCmd(DivCommand(DIV_CMD_MACRO_ON,i,effectVal&0xff));
break;
case 0xf8: // single volume ramp up
chan[i].volume=MIN(chan[i].volume+effectVal*256,chan[i].volMax);
dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8));

View file

@ -1750,7 +1750,7 @@ void DivEngine::registerSystems() {
{"CH1", "CH2", "CH3", "CH4"},
{DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM},
{DIV_INS_GA20, DIV_INS_GA20, DIV_INS_GA20, DIV_INS_GA20},
{DIV_INS_AMIGA, DIV_INS_AMIGA}
{DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA}
);
sysDefs[DIV_SYSTEM_DUMMY]=new DivSysDef(

View file

@ -547,6 +547,16 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write
w->writeS_BE(baseAddr2S|(0x580>>2));
w->writeC(0xff);
break;
case DIV_SYSTEM_GA20:
for (int i=0; i<3; i++) {
w->writeC(0xbf); // mute
w->writeC((baseAddr2|5)+(i*8));
w->writeC(0);
w->writeC(0xbf); // keyoff
w->writeC((baseAddr2|6)+(i*8));
w->writeC(0);
}
break;
default:
break;
}
@ -868,6 +878,11 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write
w->writeC(baseAddr2|(write.addr&0x7f));
w->writeC(write.val);
break;
case DIV_SYSTEM_GA20:
w->writeC(0xbf);
w->writeC(baseAddr2|(write.addr&0x7f));
w->writeC(write.val);
break;
default:
logW("write not handled!");
break;
@ -1015,6 +1030,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
DivDispatch* writeZ280[2]={NULL,NULL};
DivDispatch* writeRF5C68[2]={NULL,NULL};
DivDispatch* writeMSM6295[2]={NULL,NULL};
DivDispatch* writeGA20[2]={NULL,NULL};
for (int i=0; i<song.systemLen; i++) {
willExport[i]=false;
@ -1448,6 +1464,19 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
howManyChips++;
}
break;
case DIV_SYSTEM_GA20:
if (!hasGA20) {
hasGA20=disCont[i].dispatch->chipClock;
willExport[i]=true;
writeGA20[0]=disCont[i].dispatch;
} else if (!(hasGA20&0x40000000)) {
isSecond[i]=true;
willExport[i]=true;
writeGA20[1]=disCont[i].dispatch;
hasGA20|=0x40000000;
howManyChips++;
}
break;
case DIV_SYSTEM_T6W28:
if (!hasSN) {
hasSN=0xc0000000|disCont[i].dispatch->chipClock;
@ -1830,6 +1859,15 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
w->writeI(0);
w->write(writeMSM6295[i]->getSampleMem(),writeMSM6295[i]->getSampleMemUsage());
}
if (writeGA20[i]!=NULL && writeGA20[i]->getSampleMemUsage()>0) {
w->writeC(0x67);
w->writeC(0x66);
w->writeC(0x93);
w->writeI((writeGA20[i]->getSampleMemUsage()+8)|(i*0x80000000));
w->writeI(writeGA20[i]->getSampleMemCapacity());
w->writeI(0);
w->write(writeGA20[i]->getSampleMem(),writeGA20[i]->getSampleMemUsage());
}
}
// TODO

View file

@ -50,6 +50,7 @@
#include "../engine/platform/lynx.h"
#include "../engine/platform/pcmdac.h"
#include "../engine/platform/k007232.h"
#include "../engine/platform/ga20.h"
#include "../engine/platform/dummy.h"
#define COMMON_CHIP_DEBUG \
@ -521,6 +522,14 @@ void putDispatchChip(void* data, int type) {
ImGui::TextColored(ch->stereo?colorOn:colorOff,">> Stereo");
break;
}
case DIV_SYSTEM_GA20: {
DivPlatformGA20* ch=(DivPlatformGA20*)data;
ImGui::Text("> GA20");
COMMON_CHIP_DEBUG;
ImGui::Text("- delay: %.2x",ch->delay);
COMMON_CHIP_DEBUG_BOOL;
break;
}
default:
ImGui::Text("Unimplemented chip! Help!");
break;
@ -1024,6 +1033,20 @@ void putDispatchChan(void* data, int chanNum, int type) {
ImGui::TextColored(ch->setPos?colorOn:colorOff,">> SetPos");
break;
}
case DIV_SYSTEM_GA20: {
DivPlatformGA20::Channel* ch=(DivPlatformGA20::Channel*)data;
ImGui::Text("> GA20");
COMMON_CHAN_DEBUG;
ImGui::Text("- prevFreq: %d",ch->prevFreq);
ImGui::Text("* Sample: %d",ch->sample);
ImGui::Text(" - pos: %d",ch->audPos);
ImGui::Text("- resVol: %.2x",ch->resVol);
ImGui::Text("- macroVolMul: %.2x",ch->macroVolMul);
COMMON_CHAN_DEBUG_BOOL;
ImGui::TextColored(ch->volumeChanged?colorOn:colorOff,">> VolumeChanged");
ImGui::TextColored(ch->setPos?colorOn:colorOff,">> SetPos");
break;
}
default:
ImGui::Text("Unimplemented chip! Help!");
break;

View file

@ -1325,7 +1325,8 @@ void FurnaceGUI::doAction(int what) {
i==DIV_INS_SU ||
i==DIV_INS_SNES ||
i==DIV_INS_ES5506 ||
i==DIV_INS_K007232) {
i==DIV_INS_K007232 ||
i==DIV_INS_GA20) {
makeInsTypeList.push_back(i);
}
}

View file

@ -440,8 +440,8 @@ const FurnaceGUIColors fxColors[256]={
GUI_COLOR_PATTERN_EFFECT_PITCH, // F2
GUI_COLOR_PATTERN_EFFECT_VOLUME, // F3
GUI_COLOR_PATTERN_EFFECT_VOLUME, // F4
GUI_COLOR_PATTERN_EFFECT_INVALID, // F5
GUI_COLOR_PATTERN_EFFECT_INVALID, // F6
GUI_COLOR_PATTERN_EFFECT_MISC, // F5
GUI_COLOR_PATTERN_EFFECT_MISC, // F6
GUI_COLOR_PATTERN_EFFECT_INVALID, // F7
GUI_COLOR_PATTERN_EFFECT_VOLUME, // F8
GUI_COLOR_PATTERN_EFFECT_VOLUME, // F9
@ -984,6 +984,7 @@ const int availableSystems[]={
DIV_SYSTEM_SNES,
DIV_SYSTEM_MSM5232,
DIV_SYSTEM_K007232,
DIV_SYSTEM_GA20,
DIV_SYSTEM_PCM_DAC,
DIV_SYSTEM_PONG,
0 // don't remove this last one!
@ -1084,6 +1085,7 @@ const int chipsSample[]={
DIV_SYSTEM_RF5C68,
DIV_SYSTEM_SNES,
DIV_SYSTEM_K007232,
DIV_SYSTEM_GA20,
DIV_SYSTEM_PCM_DAC,
DIV_SYSTEM_ES5506,
0 // don't remove this last one!

View file

@ -4253,7 +4253,8 @@ void FurnaceGUI::drawInsEdit() {
ins->type==DIV_INS_SU ||
ins->type==DIV_INS_SNES ||
ins->type==DIV_INS_ES5506 ||
ins->type==DIV_INS_K007232) {
ins->type==DIV_INS_K007232 ||
ins->type==DIV_INS_GA20) {
if (ImGui::BeginTabItem((ins->type==DIV_INS_SU)?"Sound Unit":"Sample")) {
String sName;
if (ins->amiga.initSample<0 || ins->amiga.initSample>=e->song.sampleLen) {
@ -4967,7 +4968,8 @@ void FurnaceGUI::drawInsEdit() {
if (ins->type==DIV_INS_ADPCMA) {
volMax=31;
}
if (ins->type==DIV_INS_ADPCMB || ins->type==DIV_INS_YMZ280B || ins->type==DIV_INS_RF5C68) {
if (ins->type==DIV_INS_ADPCMB || ins->type==DIV_INS_YMZ280B || ins->type==DIV_INS_RF5C68 ||
ins->type==DIV_INS_GA20) {
volMax=255;
}
if (ins->type==DIV_INS_QSOUND) {
@ -5022,7 +5024,7 @@ void FurnaceGUI::drawInsEdit() {
}
if (ins->type==DIV_INS_TIA || ins->type==DIV_INS_AMIGA || ins->type==DIV_INS_SCC ||
ins->type==DIV_INS_PET || ins->type==DIV_INS_VIC || ins->type==DIV_INS_SEGAPCM ||
ins->type==DIV_INS_FM || ins->type==DIV_INS_K007232) {
ins->type==DIV_INS_FM || ins->type==DIV_INS_K007232 || ins->type==DIV_INS_GA20) {
dutyMax=0;
}
if (ins->type==DIV_INS_VBOY) {
@ -5127,6 +5129,7 @@ void FurnaceGUI::drawInsEdit() {
if (ins->type==DIV_INS_MSM6295) waveMax=0;
if (ins->type==DIV_INS_SEGAPCM) waveMax=0;
if (ins->type==DIV_INS_K007232) waveMax=0;
if (ins->type==DIV_INS_GA20) waveMax=0;
if (ins->type==DIV_INS_POKEMINI) waveMax=0;
if (ins->type==DIV_INS_SU) waveMax=7;
if (ins->type==DIV_INS_PET) {
@ -5327,7 +5330,8 @@ void FurnaceGUI::drawInsEdit() {
ins->type==DIV_INS_T6W28 ||
ins->type==DIV_INS_VBOY ||
(ins->type==DIV_INS_X1_010 && ins->amiga.useSample) ||
ins->type==DIV_INS_K007232) {
ins->type==DIV_INS_K007232 ||
ins->type==DIV_INS_GA20) {
macroList.push_back(FurnaceGUIMacroDesc("Phase Reset",&ins->std.phaseResetMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true));
}
if (ex1Max>0) {

View file

@ -1158,6 +1158,11 @@ void FurnaceGUI::initSystemPresets() {
CH(DIV_SYSTEM_K007232, 64, 0, "")
}
);
ENTRY(
"Irem GA20", {
CH(DIV_SYSTEM_GA20, 64, 0, "")
}
);
ENTRY(
"Generic PCM DAC", {
CH(DIV_SYSTEM_PCM_DAC, 64, 0, "")
@ -2204,6 +2209,12 @@ void FurnaceGUI::initSystemPresets() {
)
}
);
ENTRY(
"Irem M92/M107", {
CH(DIV_SYSTEM_YM2151, 64, 0, ""),
CH(DIV_SYSTEM_GA20, 64, 0, "")
}
);
CATEGORY_END;
CATEGORY_BEGIN("DefleMask-compatible","these configurations are compatible with DefleMask.\nselect this if you need to save as .dmf or work with that program.");

View file

@ -1581,6 +1581,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo
case DIV_SYSTEM_BUBSYS_WSG:
case DIV_SYSTEM_PET:
case DIV_SYSTEM_VBOY:
case DIV_SYSTEM_GA20:
ImGui::Text("nothing to configure");
break;
case DIV_SYSTEM_VERA:

View file

@ -194,6 +194,7 @@ TAParamResult pVersion(String) {
printf("- reSIDfp by Dag Lem, Antti Lankila and Leandro Nini (GPLv2)\n");
printf("- Stella by Stella Team (GPLv2)\n");
printf("- vgsound_emu (second version, modified version) by cam900 (zlib license)\n");
printf("- MAME GA20 core by Acho A. Tang, R. Belmont, Valley Bell (BSD 3-clause)\n");
return TA_PARAM_QUIT;
}

View file

@ -22,6 +22,10 @@
typedef HRESULT (*SPDA)(int);
int WINAPI WinMain(HINSTANCE inst, HINSTANCE prevInst, PSTR args, int state) {
if (AttachConsole(ATTACH_PARENT_PROCESS)==0) {
if (GetLastError()==ERROR_ACCESS_DENIED) FreeConsole();
}
int argc=0;
wchar_t** argw=CommandLineToArgvW(GetCommandLineW(),&argc);
char** argv=new char*[argc+1];