forked from Apple-2-Tools/jace
Remove dead code
This commit is contained in:
parent
eb776d44af
commit
9118b83a43
@ -1,638 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2012 Brendan Robert (BLuRry) brendan.robert@gmail.com.
|
|
||||||
*
|
|
||||||
* This library is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU Lesser General Public
|
|
||||||
* License as published by the Free Software Foundation; either
|
|
||||||
* version 2.1 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This library 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
|
|
||||||
* Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public
|
|
||||||
* License along with this library; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
||||||
* MA 02110-1301 USA
|
|
||||||
*/
|
|
||||||
package jace.hardware.mockingboard;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import jace.hardware.CardMockingboard;
|
|
||||||
|
|
||||||
// Port of AY code from AppleWin -- not used buy kept for reference.
|
|
||||||
|
|
||||||
/***************************************************************************
|
|
||||||
*
|
|
||||||
* ay8910.c
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* Emulation of the AY-3-8910 / YM2149 sound chip.
|
|
||||||
*
|
|
||||||
* Based on various code snippets by Ville Hallik, Michael Cuddy,
|
|
||||||
* Tatsuyuki Satoh, Fabrice Frances, Nicola Salmoria.
|
|
||||||
*
|
|
||||||
***************************************************************************/
|
|
||||||
|
|
||||||
//
|
|
||||||
// From mame.txt (http://www.mame.net/readme.html)
|
|
||||||
//
|
|
||||||
// VI. Reuse of Source Code
|
|
||||||
// --------------------------
|
|
||||||
// This chapter might not apply to specific portions of MAME (e.g. CPU
|
|
||||||
// emulators) which bear different copyright notices.
|
|
||||||
// The source code cannot be used in a commercial product without the written
|
|
||||||
// authorization of the authors. Use in non-commercial products is allowed, and
|
|
||||||
// indeed encouraged. If you use portions of the MAME source code in your
|
|
||||||
// program, however, you must make the full source code freely available as
|
|
||||||
// well.
|
|
||||||
// Usage of the _information_ contained in the source code is free for any use.
|
|
||||||
// However, given the amount of time and energy it took to collect this
|
|
||||||
// information, if you find new information we would appreciate if you made it
|
|
||||||
// freely available as well.
|
|
||||||
//
|
|
||||||
|
|
||||||
public class AY8910_old {
|
|
||||||
static final int MAX_OUTPUT = 0x007fff;
|
|
||||||
static final int MAX_AY8910 = 2;
|
|
||||||
static final int CLOCK = 1789770;
|
|
||||||
static final int SAMPLE_RATE = 44100;
|
|
||||||
|
|
||||||
// See AY8910_set_clock() for definition of STEP
|
|
||||||
static final int STEP = 0x008000;
|
|
||||||
static int num = 0, ym_num = 0;
|
|
||||||
int SampleRate = 0;
|
|
||||||
|
|
||||||
/* register id's */
|
|
||||||
public enum Reg {
|
|
||||||
AFine(0, 255),
|
|
||||||
ACoarse(1, 15),
|
|
||||||
BFine(2, 255),
|
|
||||||
BCoarse(3, 15),
|
|
||||||
CFine(4, 255),
|
|
||||||
CCoarse(5, 15),
|
|
||||||
NoisePeriod(6, 31),
|
|
||||||
Enable(7, 255),
|
|
||||||
AVol(8, 31),
|
|
||||||
BVol(9, 31),
|
|
||||||
CVol(10, 31),
|
|
||||||
EnvFine(11, 255),
|
|
||||||
EnvCoarse(12, 255),
|
|
||||||
EnvShape(13, 15),
|
|
||||||
PortA(14, 255),
|
|
||||||
PortB(15, 255);
|
|
||||||
|
|
||||||
public final int registerNumber;
|
|
||||||
public final int max;
|
|
||||||
Reg(int number, int maxValue) {
|
|
||||||
registerNumber = number;
|
|
||||||
max=maxValue;
|
|
||||||
}
|
|
||||||
static Reg get(int number) {
|
|
||||||
for (Reg r:Reg.values())
|
|
||||||
if (r.registerNumber == number) return r;
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
static public Reg[] preferredOrder = new Reg[]{
|
|
||||||
Enable,EnvShape,EnvCoarse,EnvFine,NoisePeriod,AVol,BVol,CVol,
|
|
||||||
AFine,ACoarse,BFine,BCoarse,CFine,CCoarse};
|
|
||||||
}
|
|
||||||
|
|
||||||
public AY8910_old() {
|
|
||||||
chips = new ArrayList<PSG>();
|
|
||||||
for (int i=0; i < MAX_AY8910; i++) {
|
|
||||||
PSG chip = new PSG();
|
|
||||||
chips.add(chip);
|
|
||||||
}
|
|
||||||
initAll(CLOCK, SAMPLE_RATE);
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////
|
|
||||||
private List<PSG> chips;
|
|
||||||
|
|
||||||
static int[] VolTable;
|
|
||||||
static {
|
|
||||||
buildMixerTable();
|
|
||||||
}
|
|
||||||
|
|
||||||
private class PSG {
|
|
||||||
int Channel;
|
|
||||||
int register_latch;
|
|
||||||
Map<Reg, Integer> registers;
|
|
||||||
|
|
||||||
int lastEnable;
|
|
||||||
int UpdateStep;
|
|
||||||
int PeriodA,PeriodB,PeriodC,PeriodN,PeriodE;
|
|
||||||
int CountA,CountB,CountC,CountN,CountE;
|
|
||||||
int VolA,VolB,VolC,VolE;
|
|
||||||
int EnvelopeA,EnvelopeB,EnvelopeC;
|
|
||||||
int OutputA,OutputB,OutputC,OutputN;
|
|
||||||
int CountEnv;
|
|
||||||
int Hold,Alternate,Attack,Holding;
|
|
||||||
int RNG;
|
|
||||||
|
|
||||||
public PSG() {
|
|
||||||
registers = new HashMap<Reg, Integer>();
|
|
||||||
for (Reg r: Reg.values())
|
|
||||||
setReg(r, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void reset() {
|
|
||||||
register_latch = 0;
|
|
||||||
RNG = 1;
|
|
||||||
OutputA = 0;
|
|
||||||
OutputB = 0;
|
|
||||||
OutputC = 0;
|
|
||||||
OutputN = 0x00ff;
|
|
||||||
lastEnable = -1; /* force a write */
|
|
||||||
for (Reg r: Reg.values())
|
|
||||||
writeReg(r, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setClock(int clock) {
|
|
||||||
/* the step clock for the tone and noise generators is the chip clock */
|
|
||||||
/* divided by 8; for the envelope generator of the AY-3-8910, it is half */
|
|
||||||
/* that much (clock/16), but the envelope of the YM2149 goes twice as */
|
|
||||||
/* fast, therefore again clock/8. */
|
|
||||||
/* Here we calculate the number of steps which happen during one sample */
|
|
||||||
/* at the given sample rate. No. of events = sample rate / (clock/8). */
|
|
||||||
/* STEP is a multiplier used to turn the fraction into a fixed point */
|
|
||||||
/* number. */
|
|
||||||
double clk = clock;
|
|
||||||
double smprate = SampleRate;
|
|
||||||
UpdateStep = (int) ((STEP * smprate * 8.0 + clk/2.0) / clk);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setReg(Reg r, int value) {
|
|
||||||
registers.put(r,value);
|
|
||||||
}
|
|
||||||
public int getReg(Reg r) {
|
|
||||||
return registers.get(r);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void writeReg(Reg r, int value) {
|
|
||||||
value &= r.max;
|
|
||||||
setReg(r, value);
|
|
||||||
int old;
|
|
||||||
/* A note about the period of tones, noise and envelope: for speed reasons,*/
|
|
||||||
/* we count down from the period to 0, but careful studies of the chip */
|
|
||||||
/* output prove that it instead counts up from 0 until the counter becomes */
|
|
||||||
/* greater or equal to the period. This is an important difference when the*/
|
|
||||||
/* program is rapidly changing the period to modulate the sound. */
|
|
||||||
/* To compensate for the difference, when the period is changed we adjust */
|
|
||||||
/* our internal counter. */
|
|
||||||
/* Also, note that period = 0 is the same as period = 1. This is mentioned */
|
|
||||||
/* in the YM2203 data sheets. However, this does NOT apply to the Envelope */
|
|
||||||
/* period. In that case, period = 0 is half as period = 1. */
|
|
||||||
switch(r) {
|
|
||||||
case ACoarse:
|
|
||||||
case AFine:
|
|
||||||
old = PeriodA;
|
|
||||||
PeriodA = (getReg(Reg.AFine) + 256 * getReg(Reg.ACoarse)) * UpdateStep;
|
|
||||||
if (PeriodA == 0) PeriodA = UpdateStep;
|
|
||||||
CountA += PeriodA - old;
|
|
||||||
if (CountA <= 0) CountA = 1;
|
|
||||||
break;
|
|
||||||
case BCoarse:
|
|
||||||
case BFine:
|
|
||||||
old = PeriodB;
|
|
||||||
PeriodB = (getReg(Reg.BFine) + 256 * getReg(Reg.BCoarse)) * UpdateStep;
|
|
||||||
if (PeriodB == 0) PeriodB = UpdateStep;
|
|
||||||
CountB += PeriodB - old;
|
|
||||||
if (CountB <= 0) CountB = 1;
|
|
||||||
break;
|
|
||||||
case CCoarse:
|
|
||||||
case CFine:
|
|
||||||
setReg(Reg.CCoarse, getReg(Reg.CCoarse) & 0x0f);
|
|
||||||
old = PeriodC;
|
|
||||||
PeriodA = (getReg(Reg.CFine) + 256 * getReg(Reg.CCoarse)) * UpdateStep;
|
|
||||||
if (PeriodC == 0) PeriodC = UpdateStep;
|
|
||||||
CountC += PeriodC - old;
|
|
||||||
if (CountC <= 0) CountC = 1;
|
|
||||||
break;
|
|
||||||
case NoisePeriod:
|
|
||||||
old = PeriodN;
|
|
||||||
PeriodN = getReg(Reg.NoisePeriod) * UpdateStep;
|
|
||||||
if (PeriodN == 0) PeriodN = UpdateStep;
|
|
||||||
CountN += PeriodN - old;
|
|
||||||
if (CountN <= 0) CountN = 1;
|
|
||||||
break;
|
|
||||||
case Enable:
|
|
||||||
lastEnable = value;
|
|
||||||
break;
|
|
||||||
case AVol:
|
|
||||||
EnvelopeA = value & 0x10;
|
|
||||||
if (EnvelopeA > 0)
|
|
||||||
VolA = VolE;
|
|
||||||
else {
|
|
||||||
if (value > 0)
|
|
||||||
VolA = CardMockingboard.VolTable[value];
|
|
||||||
else
|
|
||||||
VolA = CardMockingboard.VolTable[0];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case BVol:
|
|
||||||
EnvelopeB = value & 0x10;
|
|
||||||
if (EnvelopeB > 0)
|
|
||||||
VolB = VolE;
|
|
||||||
else {
|
|
||||||
if (value > 0)
|
|
||||||
VolB = CardMockingboard.VolTable[value];
|
|
||||||
else
|
|
||||||
VolB = CardMockingboard.VolTable[0];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case CVol:
|
|
||||||
EnvelopeC = value & 0x10;
|
|
||||||
if (EnvelopeC > 0)
|
|
||||||
VolC = VolE;
|
|
||||||
else {
|
|
||||||
if (value > 0)
|
|
||||||
VolC = CardMockingboard.VolTable[value];
|
|
||||||
else
|
|
||||||
VolC = CardMockingboard.VolTable[0];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case EnvFine:
|
|
||||||
case EnvCoarse:
|
|
||||||
old = PeriodE;
|
|
||||||
PeriodE = ((getReg(Reg.EnvFine) + 256 * getReg(Reg.EnvCoarse))) * UpdateStep;
|
|
||||||
if (PeriodE == 0) PeriodE = UpdateStep / 2;
|
|
||||||
CountE += PeriodE - old;
|
|
||||||
if (CountE <= 0) CountE = 1;
|
|
||||||
if (PeriodE <= 0) PeriodE = 1;
|
|
||||||
break;
|
|
||||||
case EnvShape:
|
|
||||||
/* envelope shapes:
|
|
||||||
C AtAlH
|
|
||||||
0 0 x x \___
|
|
||||||
0 1 x x /|__
|
|
||||||
1 0 0 0 \\\\
|
|
||||||
1 0 0 1 \___
|
|
||||||
1 0 1 0 \/\/
|
|
||||||
__
|
|
||||||
1 0 1 1 \|
|
|
||||||
1 1 0 0 ////
|
|
||||||
___
|
|
||||||
1 1 0 1 /
|
|
||||||
1 1 1 0 /\/\
|
|
||||||
1 1 1 1 /|__
|
|
||||||
|
|
||||||
The envelope counter on the AY-3-8910 has 16 steps. On the YM2149 it
|
|
||||||
has twice the steps, happening twice as fast. Since the end result is
|
|
||||||
just a smoother curve, we always use the YM2149 behaviour.
|
|
||||||
*/
|
|
||||||
Attack = (value & 0x04) != 0 ? 0x1f : 0x00;
|
|
||||||
if ( (value & 0x08) == 0) {
|
|
||||||
/* if Continue = 0, map the shape to the equivalent one which has Continue = 1 */
|
|
||||||
Hold = 1;
|
|
||||||
Alternate = Attack;
|
|
||||||
} else {
|
|
||||||
Hold = value & 0x01;
|
|
||||||
Alternate = value & 0x02;
|
|
||||||
}
|
|
||||||
CountE = PeriodE;
|
|
||||||
CountEnv = 0x1f;
|
|
||||||
Holding = 0;
|
|
||||||
VolE = CardMockingboard.VolTable[CountEnv ^ Attack];
|
|
||||||
if (EnvelopeA != 0) VolA = VolE;
|
|
||||||
if (EnvelopeB != 0) VolB = VolE;
|
|
||||||
if (EnvelopeC != 0) VolC = VolE;
|
|
||||||
break;
|
|
||||||
case PortA:
|
|
||||||
case PortB:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void update(int[][] buffer, int length) {
|
|
||||||
int[] buf1, buf2, buf3;
|
|
||||||
int outn;
|
|
||||||
|
|
||||||
buf1 = buffer[0];
|
|
||||||
buf2 = buffer[1];
|
|
||||||
buf3 = buffer[2];
|
|
||||||
|
|
||||||
|
|
||||||
/* The 8910 has three outputs, each output is the mix of one of the three */
|
|
||||||
/* tone generators and of the (single) noise generator. The two are mixed */
|
|
||||||
/* BEFORE going into the DAC. The formula to mix each channel is: */
|
|
||||||
/* (ToneOn | ToneDisable) & (NoiseOn | NoiseDisable). */
|
|
||||||
/* Note that this means that if both tone and noise are disabled, the output */
|
|
||||||
/* is 1, not 0, and can be modulated changing the volume. */
|
|
||||||
|
|
||||||
|
|
||||||
/* If the channels are disabled, set their output to 1, and increase the */
|
|
||||||
/* counter, if necessary, so they will not be inverted during this update. */
|
|
||||||
/* Setting the output to 1 is necessary because a disabled channel is locked */
|
|
||||||
/* into the ON state (see above); and it has no effect if the volume is 0. */
|
|
||||||
/* If the volume is 0, increase the counter, but don't touch the output. */
|
|
||||||
if ( (getReg(Reg.Enable) & 0x01) != 0) {
|
|
||||||
if (CountA <= length*STEP) CountA += length*STEP;
|
|
||||||
OutputA = 1;
|
|
||||||
} else if (getReg(Reg.AVol) == 0) {
|
|
||||||
/* note that I do count += length, NOT count = length + 1. You might think */
|
|
||||||
/* it's the same since the volume is 0, but doing the latter could cause */
|
|
||||||
/* interferencies when the program is rapidly modulating the volume. */
|
|
||||||
if (CountA <= length*STEP) CountA += length*STEP;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( (getReg(Reg.Enable) & 0x02) != 0) {
|
|
||||||
if (CountB <= length*STEP) CountB += length*STEP;
|
|
||||||
OutputB = 1;
|
|
||||||
} else if (getReg(Reg.BVol) == 0) {
|
|
||||||
if (CountB <= length*STEP) CountB += length*STEP;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( (getReg(Reg.Enable) & 0x04) != 0) {
|
|
||||||
if (CountC <= length*STEP) CountC += length*STEP;
|
|
||||||
OutputC = 1;
|
|
||||||
} else if (getReg(Reg.CVol) == 0) {
|
|
||||||
if (CountC <= length*STEP) CountC += length*STEP;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* for the noise channel we must not touch OutputN - it's also not necessary */
|
|
||||||
/* since we use outn. */
|
|
||||||
if ((getReg(Reg.Enable) & 0x38) == 0x38) /* all off */
|
|
||||||
if (CountN <= length*STEP) CountN += length*STEP;
|
|
||||||
|
|
||||||
outn = (OutputN | getReg(Reg.Enable));
|
|
||||||
int index = 0;
|
|
||||||
//System.out.println("Length:"+length);
|
|
||||||
/* buffering loop */
|
|
||||||
while (length != 0) {
|
|
||||||
int vola,volb,volc;
|
|
||||||
int left;
|
|
||||||
|
|
||||||
/* vola, volb and volc keep track of how long each square wave stays */
|
|
||||||
/* in the 1 position during the sample period. */
|
|
||||||
vola = volb = volc = 0;
|
|
||||||
//System.out.println("STEP:"+STEP);
|
|
||||||
|
|
||||||
left = STEP;
|
|
||||||
do {
|
|
||||||
int nextevent;
|
|
||||||
|
|
||||||
if (CountN < left) nextevent = CountN;
|
|
||||||
else nextevent = left;
|
|
||||||
|
|
||||||
if ( (outn & 0x08) != 0) {
|
|
||||||
if (OutputA != 0) vola += CountA;
|
|
||||||
CountA -= nextevent;
|
|
||||||
/* PeriodA is the half period of the square wave. Here, in each */
|
|
||||||
/* loop I add PeriodA twice, so that at the end of the loop the */
|
|
||||||
/* square wave is in the same status (0 or 1) it was at the start. */
|
|
||||||
/* vola is also incremented by PeriodA, since the wave has been 1 */
|
|
||||||
/* exactly half of the time, regardless of the initial position. */
|
|
||||||
/* If we exit the loop in the middle, OutputA has to be inverted */
|
|
||||||
/* and vola incremented only if the exit status of the square */
|
|
||||||
/* wave is 1. */
|
|
||||||
while (CountA <= 0 && PeriodA > 0) {
|
|
||||||
CountA += PeriodA;
|
|
||||||
if (CountA > 0) {
|
|
||||||
OutputA ^= 1;
|
|
||||||
if (OutputA != 0) vola += PeriodA;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
CountA += PeriodA;
|
|
||||||
vola += PeriodA;
|
|
||||||
}
|
|
||||||
if (OutputA != 0) vola -= CountA;
|
|
||||||
} else {
|
|
||||||
CountA -= nextevent;
|
|
||||||
while (CountA <= 0 && PeriodA > 0) {
|
|
||||||
CountA += PeriodA;
|
|
||||||
if (CountA > 0) {
|
|
||||||
OutputA ^= 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
CountA += PeriodA;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((outn & 0x10) != 0) {
|
|
||||||
if (OutputB != 0) volb += CountB;
|
|
||||||
CountB -= nextevent;
|
|
||||||
while (CountB <= 0 && PeriodB > 0) {
|
|
||||||
CountB += PeriodB;
|
|
||||||
if (CountB > 0) {
|
|
||||||
OutputB ^= 1;
|
|
||||||
if (OutputB != 0) volb += PeriodB;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
CountB += PeriodB;
|
|
||||||
volb += PeriodB;
|
|
||||||
}
|
|
||||||
if (OutputB != 0) volb -= CountB;
|
|
||||||
} else {
|
|
||||||
CountB -= nextevent;
|
|
||||||
while (CountB <= 0 && PeriodB > 0) {
|
|
||||||
CountB += PeriodB;
|
|
||||||
if (CountB > 0) {
|
|
||||||
OutputB ^= 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
CountB += PeriodB;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( (outn & 0x20) != 0) {
|
|
||||||
if (OutputC != 0) volc += CountC;
|
|
||||||
CountC -= nextevent;
|
|
||||||
while (CountC <= 0 && PeriodC > 0) {
|
|
||||||
CountC += PeriodC;
|
|
||||||
if (CountC > 0) {
|
|
||||||
OutputC ^= 1;
|
|
||||||
if (OutputC != 0) volc += PeriodC;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
CountC += PeriodC;
|
|
||||||
volc += PeriodC;
|
|
||||||
}
|
|
||||||
if (OutputC != 0) volc -= CountC;
|
|
||||||
} else {
|
|
||||||
CountC -= nextevent;
|
|
||||||
while (CountC <= 0 && PeriodC > 0) {
|
|
||||||
CountC += PeriodC;
|
|
||||||
if (CountC > 0) {
|
|
||||||
OutputC ^= 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
CountC += PeriodC;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CountN -= nextevent;
|
|
||||||
if (CountN <= 0 && PeriodN > 0) {
|
|
||||||
/* Is noise output going to change? */
|
|
||||||
/* (bit0^bit1)? */
|
|
||||||
if (((RNG + 1) & 2) != 0) {
|
|
||||||
OutputN ^= 0x0FF;
|
|
||||||
outn = (OutputN | getReg(Reg.Enable));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* The Random Number Generator of the 8910 is a 17-bit shift */
|
|
||||||
/* register. The input to the shift register is bit0 XOR bit3 */
|
|
||||||
/* (bit0 is the output). This was verified on AY-3-8910 and YM2149 chips. */
|
|
||||||
|
|
||||||
/* The following is a fast way to compute bit17 = bit0^bit3. */
|
|
||||||
/* Instead of doing all the logic operations, we only check */
|
|
||||||
/* bit0, relying on the fact that after three shifts of the */
|
|
||||||
/* register, what now is bit3 will become bit0, and will */
|
|
||||||
/* invert, if necessary, bit14, which previously was bit17. */
|
|
||||||
if ((RNG & 1) != 0) RNG ^= 0x0024000; /* This version is called the "Galois configuration". */
|
|
||||||
RNG >>= 1;
|
|
||||||
CountN += PeriodN;
|
|
||||||
}
|
|
||||||
|
|
||||||
left -= nextevent;
|
|
||||||
} while (left > 0);
|
|
||||||
// System.out.println("End left loop");
|
|
||||||
|
|
||||||
/* update envelope */
|
|
||||||
if (Holding == 0) {
|
|
||||||
CountE -= STEP;
|
|
||||||
if (CountE <= 0) {
|
|
||||||
do {
|
|
||||||
CountEnv--;
|
|
||||||
CountE += PeriodE;
|
|
||||||
} while (CountE <= 0);
|
|
||||||
|
|
||||||
/* check envelope current position */
|
|
||||||
if (CountEnv < 0) {
|
|
||||||
if (Hold != 0) {
|
|
||||||
if (Alternate != 0)
|
|
||||||
Attack ^= 0x1f;
|
|
||||||
Holding = 1;
|
|
||||||
CountEnv = 0;
|
|
||||||
} else {
|
|
||||||
/* if CountEnv has looped an odd number of times (usually 1), */
|
|
||||||
/* invert the output. */
|
|
||||||
if ( (Alternate != 0) && ((CountEnv & 0x20) != 0))
|
|
||||||
Attack ^= 0x1f;
|
|
||||||
CountEnv &= 0x1f;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
VolE = VolTable[CountEnv ^ Attack];
|
|
||||||
/* reload volume */
|
|
||||||
if (EnvelopeA != 0) VolA = VolE;
|
|
||||||
if (EnvelopeB != 0) VolB = VolE;
|
|
||||||
if (EnvelopeC != 0) VolC = VolE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Output PCM wave [-32768...32767] instead of MAME's voltage level [0...32767]
|
|
||||||
// - This allows for better s/w mixing
|
|
||||||
buf1[index] = (vola * VolA) / STEP;
|
|
||||||
buf2[index] = (volb * VolB) / STEP;
|
|
||||||
buf3[index] = (volc * VolC) / STEP;
|
|
||||||
/*
|
|
||||||
if(VolA != 0) {
|
|
||||||
if (vola != 0) buf1[index] = (vola * VolA) / STEP;
|
|
||||||
else buf1[index] = -VolA;
|
|
||||||
} else {
|
|
||||||
buf1[index] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
|
|
||||||
if(VolB != 0) {
|
|
||||||
if (volb != 0) buf2[index] = (volb * VolB) / STEP;
|
|
||||||
else buf2[index] = -VolB;
|
|
||||||
} else
|
|
||||||
buf2[index] = 0;
|
|
||||||
|
|
||||||
//
|
|
||||||
|
|
||||||
if(VolC != 0) {
|
|
||||||
if (volc != 0) buf3[index] = (volc * VolC) / STEP;
|
|
||||||
else buf3[index] = -VolC;
|
|
||||||
} else
|
|
||||||
buf3[index] = 0;
|
|
||||||
*/
|
|
||||||
index++;
|
|
||||||
length--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public void writeReg(int chipNumber, int register, int value) {
|
|
||||||
Reg r = Reg.get(register);
|
|
||||||
writeReg(chipNumber, r, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void writeReg(int chipNumber, Reg register, int value) {
|
|
||||||
chips.get(chipNumber).writeReg(register, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// /length/ is the number of samples we require
|
|
||||||
// NB. This should be called at twice the 6522 IRQ rate or (eg) 60Hz if no IRQ.
|
|
||||||
public void update(int chipNumber,int[][] buffer,int length) {
|
|
||||||
chips.get(chipNumber).update(buffer, length);
|
|
||||||
}
|
|
||||||
|
|
||||||
int[][] buffers;
|
|
||||||
int bufferLength = -1;
|
|
||||||
public int[][] getBuffers(int length) {
|
|
||||||
if (buffers == null || bufferLength != length) {
|
|
||||||
buffers = new int[3][length];
|
|
||||||
bufferLength = length;
|
|
||||||
}
|
|
||||||
return buffers;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void playSound(int size, int[] left, int[] right) {
|
|
||||||
int[][] buffers = getBuffers(left.length);
|
|
||||||
update(0, buffers, size);
|
|
||||||
mixDown(left, buffers, size);
|
|
||||||
update(1, buffers, size);
|
|
||||||
mixDown(right, buffers, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void mixDown(int[] out, int[][] in, int size) {
|
|
||||||
for (int i=0; i < size; i++) {
|
|
||||||
int sample = (in[0][i] + in[1][i] + in[2][i]) / 3;
|
|
||||||
out[i] = sample;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setClock(int chipNumber,int clock) {
|
|
||||||
chips.get(chipNumber).setClock(clock);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void reset(int chipNumber) {
|
|
||||||
chips.get(chipNumber).reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void initAll(int nClock, int nSampleRate) {
|
|
||||||
SampleRate = nSampleRate;
|
|
||||||
for (PSG p:chips) {
|
|
||||||
p.setClock(nClock);
|
|
||||||
p.reset();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void initClock(int nClock) {
|
|
||||||
for (PSG p:chips) p.setClock(nClock);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void buildMixerTable() {
|
|
||||||
VolTable = new int[32];
|
|
||||||
int SampleRate;
|
|
||||||
|
|
||||||
/* calculate the volume->voltage conversion table */
|
|
||||||
/* The AY-3-8910 has 16 levels, in a logarithmic scale (3dB per step) */
|
|
||||||
/* The YM2149 still has 16 levels for the tone generators, but 32 for */
|
|
||||||
/* the envelope generator (1.5dB per step). */
|
|
||||||
double out = MAX_OUTPUT;
|
|
||||||
for (int i = 31;i > 0;i--) {
|
|
||||||
VolTable[i] = (int) (out + 0.5); /* round to nearest */ // [TC: unsigned int cast]
|
|
||||||
out /= 1.188502227; /* = 10 ^ (1.5/20) = 1.5dB */
|
|
||||||
}
|
|
||||||
VolTable[0] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user