aboutsummaryrefslogtreecommitdiff
path: root/scumm
diff options
context:
space:
mode:
authorEugene Sandulenko2005-04-07 07:29:19 +0000
committerEugene Sandulenko2005-04-07 07:29:19 +0000
commit668c889c8e2c10511c0783aca38e6f63bfbe6a60 (patch)
tree6d136301cb25d3c29e0be14fc9fa63d87debc268 /scumm
parent19ff0b0085df624918ee9ab438f1c40bfec57c83 (diff)
downloadscummvm-rg350-668c889c8e2c10511c0783aca38e6f63bfbe6a60.tar.gz
scummvm-rg350-668c889c8e2c10511c0783aca38e6f63bfbe6a60.tar.bz2
scummvm-rg350-668c889c8e2c10511c0783aca38e6f63bfbe6a60.zip
Patch from Quietust
o Remove unused _system variables in player_v2a and player_v3a o Sound player for MM NES :) Now we're playing chiptunes. svn-id: r17425
Diffstat (limited to 'scumm')
-rw-r--r--scumm/module.mk1
-rw-r--r--scumm/player_nes.cpp1092
-rw-r--r--scumm/player_nes.h111
-rw-r--r--scumm/player_v2a.cpp1
-rw-r--r--scumm/player_v2a.h2
-rw-r--r--scumm/player_v3a.cpp1
-rw-r--r--scumm/player_v3a.h2
-rw-r--r--scumm/scumm.cpp5
8 files changed, 1208 insertions, 7 deletions
diff --git a/scumm/module.mk b/scumm/module.mk
index b5ab3b4d62..9dcc3f1b5d 100644
--- a/scumm/module.mk
+++ b/scumm/module.mk
@@ -25,6 +25,7 @@ MODULE_OBJS := \
scumm/palette.o \
scumm/player_mod.o \
scumm/player_v1.o \
+ scumm/player_nes.o \
scumm/player_v2.o \
scumm/player_v2a.o \
scumm/player_v3a.o \
diff --git a/scumm/player_nes.cpp b/scumm/player_nes.cpp
new file mode 100644
index 0000000000..263dd3abaf
--- /dev/null
+++ b/scumm/player_nes.cpp
@@ -0,0 +1,1092 @@
+
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2005 The ScummVM project
+ *
+ * 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
+ * aint32 with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * $Header$
+ *
+ */
+
+#include "stdafx.h"
+#include "base/engine.h"
+#include "scumm/player_nes.h"
+#include "scumm/scumm.h"
+#include "sound/mixer.h"
+
+namespace Scumm {
+
+static const byte channelMask[4] = {1, 2, 4, 8};
+
+static const uint16 freqTable[64] = {
+ 0x07F0, 0x077E, 0x0712, 0x06AE, 0x064E, 0x05F3, 0x059E, 0x054D,
+ 0x0501, 0x04B9, 0x0475, 0x0435, 0x03F8, 0x03BF, 0x0389, 0x0357,
+ 0x0327, 0x02F9, 0x02CF, 0x02A6, 0x0280, 0x025C, 0x023A, 0x021A,
+ 0x01FC, 0x01DF, 0x01C4, 0x01AB, 0x0193, 0x017C, 0x0167, 0x0152,
+ 0x013F, 0x012D, 0x011C, 0x010C, 0x00FD, 0x00EE, 0x00E1, 0x00D4,
+ 0x00C8, 0x00BD, 0x00B2, 0x00A8, 0x009F, 0x0096, 0x008D, 0x0085,
+ 0x007E, 0x0076, 0x0070, 0x0069, 0x0063, 0x005E, 0x0058, 0x0053,
+ 0x004F, 0x004A, 0x0046, 0x0042, 0x003E, 0x003A, 0x0037, 0x0034
+};
+
+static const byte instChannel[16] = {
+ 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 2, 2, 1, 3, 3, 3
+};
+static const byte startCmd[16] = {
+ 0x05, 0x03, 0x06, 0x08, 0x0B, 0x01, 0x01, 0x1A,
+ 0x16, 0x06, 0x04, 0x17, 0x02, 0x10, 0x0E, 0x0D
+};
+static const byte releaseCmd[16] = {
+ 0x0F, 0x00, 0x00, 0x09, 0x00, 0x14, 0x15, 0x00,
+ 0x00, 0x00, 0x1B, 0x1B, 0x0F, 0x0F, 0x0F, 0x0F
+};
+static const byte nextCmd[28] = {
+ 0xFF, 0xFF, 0xFF, 0xFF, 0x17, 0xFF, 0x07, 0xFF,
+ 0xFF, 0x0A, 0x09, 0x0C, 0x00, 0x00, 0x00, 0x00,
+ 0x11, 0x12, 0x11, 0x03, 0xFF, 0xFF, 0x18, 0x00,
+ 0x19, 0x00, 0x00, 0x00
+};
+static const byte nextDelay[28] = {
+ 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00,
+ 0x00, 0x05, 0x08, 0x03, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00,
+ 0x03, 0x00, 0x00, 0x00
+};
+
+namespace APUe {
+
+struct tAPU {
+ int Cycles;
+ int BufPos;
+ int SampleRate;
+} APU;
+
+const byte LengthCounts[32] = {
+ 0x0A,0xFE,
+ 0x14,0x02,
+ 0x28,0x04,
+ 0x50,0x06,
+ 0xA0,0x08,
+ 0x3C,0x0A,
+ 0x0E,0x0C,
+ 0x1A,0x0E,
+
+ 0x0C,0x10,
+ 0x18,0x12,
+ 0x30,0x14,
+ 0x60,0x16,
+ 0xC0,0x18,
+ 0x48,0x1A,
+ 0x10,0x1C,
+ 0x20,0x1E
+};
+
+static struct {
+ byte volume, envelope, wavehold, duty, swpspeed, swpdir, swpstep, swpenab;
+ uint32 freq; // short
+ byte Vol;
+ byte CurD;
+ byte Timer;
+ byte EnvCtr, Envelope, BendCtr;
+ bool Enabled, ValidFreq, Active;
+ bool EnvClk, SwpClk;
+ uint32 Cycles; // short
+ int32 Pos;
+} Square0, Square1;
+
+const int8 Duties[4][8] = {
+ {-4,+4,-4,-4,-4,-4,-4,-4},
+ {-4,+4,+4,-4,-4,-4,-4,-4},
+ {-4,+4,+4,+4,+4,-4,-4,-4},
+ {+4,-4,-4,+4,+4,+4,+4,+4}
+};
+
+inline void Square0_CheckActive(void) {
+ Square0.ValidFreq = (Square0.freq >= 0x8) && ((Square0.swpdir) || !((Square0.freq + (Square0.freq >> Square0.swpstep)) & 0x800));
+ Square0.Active = Square0.Timer && Square0.ValidFreq;
+ Square0.Pos = Square0.Active ? (Duties[Square0.duty][Square0.CurD] * Square0.Vol) : 0;
+}
+
+inline void Square0_Write(int Reg, byte Val) {
+ switch (Reg) {
+ case 0:
+ Square0.volume = Val & 0xF;
+ Square0.envelope = Val & 0x10;
+ Square0.wavehold = Val & 0x20;
+ Square0.duty = (Val >> 6) & 0x3;
+ Square0.Vol = Square0.envelope ? Square0.volume : Square0.Envelope;
+ break;
+
+ case 1:
+ Square0.swpstep = Val & 0x07;
+ Square0.swpdir = Val & 0x08;
+ Square0.swpspeed = (Val >> 4) & 0x7;
+ Square0.swpenab = Val & 0x80;
+ Square0.SwpClk = true;
+ break;
+
+ case 2:
+ Square0.freq &= 0x700;
+ Square0.freq |= Val;
+ break;
+
+ case 3:
+ Square0.freq &= 0xFF;
+ Square0.freq |= (Val & 0x7) << 8;
+
+ if (Square0.Enabled)
+ Square0.Timer = LengthCounts[(Val >> 3) & 0x1F];
+
+ Square0.CurD = 0;
+ Square0.EnvClk = true;
+ break;
+
+ case 4:
+ if (!(Square0.Enabled = Val ? true : false))
+ Square0.Timer = 0;
+ break;
+ }
+ Square0_CheckActive();
+}
+
+inline void Square0_Run(void) {
+ if (!--Square0.Cycles) {
+ Square0.Cycles = (Square0.freq + 1) << 1;
+ Square0.CurD = (Square0.CurD + 1) & 0x7;
+
+ if (Square0.Active)
+ Square0.Pos = Duties[Square0.duty][Square0.CurD] * Square0.Vol;
+ }
+}
+
+inline void Square0_QuarterFrame(void) {
+ if (Square0.EnvClk) {
+ Square0.EnvClk = false;
+ Square0.Envelope = 0xF;
+ Square0.EnvCtr = Square0.volume + 1;
+ } else if (!--Square0.EnvCtr) {
+ Square0.EnvCtr = Square0.volume + 1;
+
+ if (Square0.Envelope)
+ Square0.Envelope--;
+ else
+ Square0.Envelope = Square0.wavehold ? 0xF : 0x0;
+ }
+
+ Square0.Vol = Square0.envelope ? Square0.volume : Square0.Envelope;
+ Square0_CheckActive();
+}
+
+inline void Square0_HalfFrame(void) {
+ if (!--Square0.BendCtr) {
+ Square0.BendCtr = Square0.swpspeed + 1;
+
+ if (Square0.swpenab && Square0.swpstep && Square0.ValidFreq) {
+ int sweep = Square0.freq >> Square0.swpstep;
+ Square0.freq += Square0.swpdir ? ~sweep : sweep;
+ }
+ }
+
+ if (Square0.SwpClk) {
+ Square0.SwpClk = false;
+ Square0.BendCtr = Square0.swpspeed + 1;
+ }
+
+ if (Square0.Timer && !Square0.wavehold)
+ Square0.Timer--;
+
+ Square0_CheckActive();
+}
+
+inline void Square1_CheckActive(void) {
+ Square1.ValidFreq = (Square1.freq >= 0x8) && ((Square1.swpdir) || !((Square1.freq + (Square1.freq >> Square1.swpstep)) & 0x800));
+ Square1.Active = Square1.Timer && Square1.ValidFreq;
+ Square1.Pos = Square1.Active ? (Duties[Square1.duty][Square1.CurD] * Square1.Vol) : 0;
+}
+
+inline void Square1_Write(int Reg, byte Val) {
+ switch (Reg) {
+ case 0:
+ Square1.volume = Val & 0xF;
+ Square1.envelope = Val & 0x10;
+ Square1.wavehold = Val & 0x20;
+ Square1.duty = (Val >> 6) & 0x3;
+ Square1.Vol = Square1.envelope ? Square1.volume : Square1.Envelope;
+ break;
+
+ case 1:
+ Square1.swpstep = Val & 0x07;
+ Square1.swpdir = Val & 0x08;
+ Square1.swpspeed = (Val >> 4) & 0x7;
+ Square1.swpenab = Val & 0x80;
+ Square1.SwpClk = true;
+ break;
+
+ case 2:
+ Square1.freq &= 0x700;
+ Square1.freq |= Val;
+ break;
+
+ case 3:
+ Square1.freq &= 0xFF;
+ Square1.freq |= (Val & 0x7) << 8;
+
+ if (Square1.Enabled)
+ Square1.Timer = LengthCounts[(Val >> 3) & 0x1F];
+
+ Square1.CurD = 0;
+ Square1.EnvClk = true;
+ break;
+
+ case 4:
+ if (!(Square1.Enabled = Val ? true : false))
+ Square1.Timer = 0;
+ break;
+ }
+ Square1_CheckActive();
+}
+
+inline void Square1_Run(void) {
+ if (!--Square1.Cycles) {
+ Square1.Cycles = (Square1.freq + 1) << 1;
+ Square1.CurD = (Square1.CurD + 1) & 0x7;
+
+ if (Square1.Active)
+ Square1.Pos = Duties[Square1.duty][Square1.CurD] * Square1.Vol;
+ }
+}
+
+inline void Square1_QuarterFrame(void) {
+ if (Square1.EnvClk) {
+ Square1.EnvClk = false;
+ Square1.Envelope = 0xF;
+ Square1.EnvCtr = Square1.volume + 1;
+ } else if (!--Square1.EnvCtr) {
+ Square1.EnvCtr = Square1.volume + 1;
+
+ if (Square1.Envelope)
+ Square1.Envelope--;
+ else
+ Square1.Envelope = Square1.wavehold ? 0xF : 0x0;
+ }
+
+ Square1.Vol = Square1.envelope ? Square1.volume : Square1.Envelope;
+ Square1_CheckActive();
+}
+
+inline void Square1_HalfFrame(void) {
+ if (!--Square1.BendCtr) {
+ Square1.BendCtr = Square1.swpspeed + 1;
+
+ if (Square1.swpenab && Square1.swpstep && Square1.ValidFreq) {
+ int sweep = Square1.freq >> Square1.swpstep;
+ Square1.freq += Square1.swpdir ? -sweep : sweep;
+ }
+ }
+
+ if (Square1.SwpClk) {
+ Square1.SwpClk = false;
+ Square1.BendCtr = Square1.swpspeed + 1;
+ }
+
+ if (Square1.Timer && !Square1.wavehold)
+ Square1.Timer--;
+
+ Square1_CheckActive();
+}
+
+static struct {
+ byte linear, wavehold;
+ uint32 freq; // short
+ byte CurD;
+ byte Timer, LinCtr;
+ bool Enabled, Active;
+ bool LinClk;
+ uint32 Cycles; // short
+ int32 Pos;
+} Triangle;
+
+const int8 TriDuty[32] = {
+ -8,-7,-6,-5,-4,-3,-2,-1,
+ +0,+1,+2,+3,+4,+5,+6,+7,
+ +7,+6,+5,+4,+3,+2,+1,+0,
+ -1,-2,-3,-4,-5,-6,-7,-8
+};
+
+inline void Triangle_CheckActive(void) {
+ Triangle.Active = Triangle.Timer && Triangle.LinCtr;
+
+ if (Triangle.freq < 4)
+ Triangle.Pos = 0; // beyond hearing range
+ else
+ Triangle.Pos = TriDuty[Triangle.CurD] * 8;
+}
+
+inline void Triangle_Write(int Reg, byte Val) {
+ switch (Reg) {
+ case 0:
+ Triangle.linear = Val & 0x7F;
+ Triangle.wavehold = (Val >> 7) & 0x1;
+ break;
+
+ case 2:
+ Triangle.freq &= 0x700;
+ Triangle.freq |= Val;
+ break;
+
+ case 3:
+ Triangle.freq &= 0xFF;
+ Triangle.freq |= (Val & 0x7) << 8;
+
+ if (Triangle.Enabled)
+ Triangle.Timer = LengthCounts[(Val >> 3) & 0x1F];
+
+ Triangle.LinClk = true;
+ break;
+
+ case 4:
+ if (!(Triangle.Enabled = Val ? true : false))
+ Triangle.Timer = 0;
+ break;
+ }
+ Triangle_CheckActive();
+}
+
+inline void Triangle_Run(void) {
+ if (!--Triangle.Cycles) {
+ Triangle.Cycles = Triangle.freq + 1;
+
+ if (Triangle.Active) {
+ Triangle.CurD++;
+ Triangle.CurD &= 0x1F;
+
+ if (Triangle.freq < 4)
+ Triangle.Pos = 0; // beyond hearing range
+ else
+ Triangle.Pos = TriDuty[Triangle.CurD] * 8;
+ }
+ }
+}
+
+inline void Triangle_QuarterFrame(void) {
+ if (Triangle.LinClk)
+ Triangle.LinCtr = Triangle.linear;
+ else if (Triangle.LinCtr)
+ Triangle.LinCtr--;
+
+ if (!Triangle.wavehold)
+ Triangle.LinClk = false;
+
+ Triangle_CheckActive();
+}
+
+inline void Triangle_HalfFrame(void) {
+ if (Triangle.Timer && !Triangle.wavehold)
+ Triangle.Timer--;
+
+ Triangle_CheckActive();
+}
+
+static struct {
+ byte volume, envelope, wavehold, datatype;
+ uint32 freq; // short
+ uint32 CurD; // short
+ byte Vol;
+ byte Timer;
+ byte EnvCtr, Envelope;
+ bool Enabled;
+ bool EnvClk;
+ uint32 Cycles; // short
+ int32 Pos;
+} Noise;
+
+const uint32 NoiseFreq[16] = {
+ 0x004,0x008,0x010,0x020,0x040,0x060,0x080,0x0A0,
+ 0x0CA,0x0FE,0x17C,0x1FC,0x2FA,0x3F8,0x7F2,0xFE4
+};
+
+inline void Noise_Write(int Reg, byte Val) {
+ switch (Reg) {
+ case 0:
+ Noise.volume = Val & 0x0F;
+ Noise.envelope = Val & 0x10;
+ Noise.wavehold = Val & 0x20;
+ Noise.Vol = Noise.envelope ? Noise.volume : Noise.Envelope;
+
+ if (Noise.Timer)
+ Noise.Pos = ((Noise.CurD & 0x4000) ? -2 : 2) * Noise.Vol;
+ break;
+
+ case 2:
+ Noise.freq = Val & 0xF;
+ Noise.datatype = Val & 0x80;
+ break;
+
+ case 3:
+ if (Noise.Enabled)
+ Noise.Timer = LengthCounts[(Val >> 3) & 0x1F];
+
+ Noise.EnvClk = true;
+ break;
+
+ case 4:
+ if (!(Noise.Enabled = Val ? true : false))
+ Noise.Timer = 0;
+ break;
+ }
+}
+
+inline void Noise_Run(void) {
+ if (!--Noise.Cycles) {
+ Noise.Cycles = NoiseFreq[Noise.freq]; /* no + 1 here */
+
+ if (Noise.datatype)
+ Noise.CurD = (Noise.CurD << 1) | (((Noise.CurD >> 14) ^ (Noise.CurD >> 8)) & 0x1);
+ else
+ Noise.CurD = (Noise.CurD << 1) | (((Noise.CurD >> 14) ^ (Noise.CurD >> 13)) & 0x1);
+
+ if (Noise.Timer)
+ Noise.Pos = ((Noise.CurD & 0x4000) ? -2 : 2) * Noise.Vol;
+ }
+}
+
+inline void Noise_QuarterFrame(void) {
+ if (Noise.EnvClk) {
+ Noise.EnvClk = false;
+ Noise.Envelope = 0xF;
+ Noise.EnvCtr = Noise.volume + 1;
+ } else if (!--Noise.EnvCtr) {
+ Noise.EnvCtr = Noise.volume + 1;
+
+ if (Noise.Envelope)
+ Noise.Envelope--;
+ else
+ Noise.Envelope = Noise.wavehold ? 0xF : 0x0;
+ }
+
+ Noise.Vol = Noise.envelope ? Noise.volume : Noise.Envelope;
+
+ if (Noise.Timer)
+ Noise.Pos = ((Noise.CurD & 0x4000) ? -2 : 2) * Noise.Vol;
+}
+
+inline void Noise_HalfFrame(void) {
+ if (Noise.Timer && !Noise.wavehold)
+ Noise.Timer--;
+}
+
+static struct {
+ uint32 Cycles;
+ int Num;
+} Frame;
+
+inline void Frame_Run(void) {
+ if (!--Frame.Cycles) {
+ Frame.Cycles = 7457;
+
+ if (Frame.Num < 4) {
+ Square0_QuarterFrame();
+ Square1_QuarterFrame();
+ Triangle_QuarterFrame();
+ Noise_QuarterFrame();
+
+ if (!(Frame.Num & 1)) {
+ Square0_HalfFrame();
+ Square1_HalfFrame();
+ Triangle_HalfFrame();
+ Noise_HalfFrame();
+ }
+ }
+
+ if (Frame.Num & 1)
+ Frame.Cycles++;
+
+ Frame.Num++;
+
+ if (Frame.Num == 5)
+ Frame.Num = 0;
+ }
+}
+
+void APU_WriteReg(int Addr, byte Val) {
+ switch (Addr) {
+ case 0x000: Square0_Write(0,Val); break;
+ case 0x001: Square0_Write(1,Val); break;
+ case 0x002: Square0_Write(2,Val); break;
+ case 0x003: Square0_Write(3,Val); break;
+ case 0x004: Square1_Write(0,Val); break;
+ case 0x005: Square1_Write(1,Val); break;
+ case 0x006: Square1_Write(2,Val); break;
+ case 0x007: Square1_Write(3,Val); break;
+ case 0x008: Triangle_Write(0,Val); break;
+ case 0x009: Triangle_Write(1,Val); break;
+ case 0x00A: Triangle_Write(2,Val); break;
+ case 0x00B: Triangle_Write(3,Val); break;
+ case 0x00C: Noise_Write(0,Val); break;
+ case 0x00D: Noise_Write(1,Val); break;
+ case 0x00E: Noise_Write(2,Val); break;
+ case 0x00F: Noise_Write(3,Val); break;
+ case 0x015: Square0_Write(4,Val & 0x1);
+ Square1_Write(4,Val & 0x2);
+ Triangle_Write(4,Val & 0x4);
+ Noise_Write(4,Val & 0x8);
+ break;
+ }
+}
+
+byte APU_Read4015(void) {
+ byte result =
+ (( Square0.Timer) ? 0x01 : 0) |
+ (( Square1.Timer) ? 0x02 : 0) |
+ ((Triangle.Timer) ? 0x04 : 0) |
+ (( Noise.Timer) ? 0x08 : 0);
+ return result;
+}
+
+void APU_Reset (void) {
+ APU.BufPos = 0;
+
+ memset(&Frame, 0, sizeof(Frame));
+ memset(&Square0, 0, sizeof(Square0));
+ memset(&Square1, 0, sizeof(Square1));
+ memset(&Triangle, 0, sizeof(Triangle));
+ memset(&Noise, 0, sizeof(Noise));
+
+ Noise.CurD = 1;
+ APU.Cycles = 1;
+ Square0.Cycles = 1;
+ Square0.EnvCtr = 1;
+ Square0.BendCtr = 1;
+ Square1.Cycles = 1;
+ Square1.EnvCtr = 1;
+ Square1.BendCtr = 1;
+ Triangle.Cycles = 1;
+ Noise.Cycles = 1;
+ Noise.EnvCtr = 1;
+ Frame.Cycles = 1;
+}
+
+int16 sample_pos = 0;
+bool sample_ok = false;
+
+void APU_Run(void) {
+ static int sampcycles = 0, samppos = 0;
+ int NewBufPos = APU.SampleRate * ++APU.Cycles / 1789773;
+
+ if (NewBufPos == APU.SampleRate) /* we've generated 1 second, so we can reset our counters now */
+ APU.Cycles = NewBufPos = 0;
+
+ Frame_Run();
+ Square0_Run();
+ Square1_Run();
+ Triangle_Run();
+ Noise_Run();
+
+ samppos += Square0.Pos + Square1.Pos + Triangle.Pos + Noise.Pos;
+ sampcycles++;
+
+ if (NewBufPos != APU.BufPos) {
+ APU.BufPos = NewBufPos;
+ samppos = (samppos << 6) / sampcycles;
+ sample_pos = samppos;
+ sample_ok = true;
+ samppos = sampcycles = 0;
+ }
+}
+
+};
+
+Player_NES::Player_NES(ScummEngine *scumm) {
+ int i;
+ _vm = scumm;
+ _mixer = scumm->_mixer;
+ APUe::APU.SampleRate = _sample_rate = _mixer->getOutputRate();
+
+ _samples_per_frame = _sample_rate / 60;
+ _current_sample = 0;
+
+ for (i = 0; i < NUMSLOTS; i++) {
+ _slot[i].id = -1;
+ _slot[i].framesleft = 0;
+ _slot[i].type = 0;
+ _slot[i].offset = 0;
+ _slot[i].data = NULL;
+ }
+
+ for (i = 0; i < NUMCHANS; i++) {
+ _mchan[i].command = 0;
+ _mchan[i].framedelay = 0;
+ _mchan[i].pitch = 0;
+ _mchan[i].volume = 0;
+ _mchan[i].voldelta = 0;
+ _mchan[i].envflags = 0;
+ _mchan[i].cmdlock = 0;
+ }
+ isSFXplaying = wasSFXplaying = false;
+
+ auxData1 = auxData2 = NULL;
+ numNotes = 0;
+
+ APU_writeControl(0);
+
+ APUe::APU_Reset();
+
+ _mixer->setupPremix(this);
+}
+
+Player_NES::~Player_NES() {
+ _mixer->setupPremix(0);
+}
+
+void Player_NES::setMusicVolume (int vol) {
+}
+
+int Player_NES::readBuffer(int16 *buffer, const int numSamples) {
+ for (int n = 0; n < numSamples; n++) {
+ while (!APUe::sample_ok)
+ APUe::APU_Run();
+
+ APUe::sample_ok = false;
+ buffer[n] = APUe::sample_pos;
+ _current_sample++;
+
+ if (_current_sample == _samples_per_frame) {
+ _current_sample = 0;
+ sound_play();
+ }
+ }
+ return numSamples;
+}
+void Player_NES::stopAllSounds() {
+ for (int i = 0; i < NUMSLOTS; i++) {
+ _slot[i].framesleft = 0;
+ _slot[i].type = 0;
+ _slot[i].id = -1;
+ }
+
+ isSFXplaying = 0;
+ checkSilenceChannels(0);
+}
+
+void Player_NES::stopSound(int nr) {
+ if (nr == -1)
+ return;
+
+ for (int i = 0; i < NUMSLOTS; i++) {
+ if (_slot[i].id != nr)
+ continue;
+
+ isSFXplaying = 0;
+ _slot[i].framesleft = 0;
+ _slot[i].type = 0;
+ _slot[i].id = -1;
+ checkSilenceChannels(i);
+ }
+}
+
+void Player_NES::startSound(int nr) {
+ byte *data = _vm->getResourceAddress(rtSound, nr) + 2;
+ assert(data);
+
+ int soundType = data[1];
+ int chan = data[0];
+
+ if (chan == 4) {
+ if (_slot[2].framesleft)
+ return;
+ chan = 0;
+ }
+
+ if (soundType < _slot[chan].type)
+ return;
+
+ _slot[chan].type = soundType;
+ _slot[chan].id = nr;
+ _slot[chan].data = data;
+ _slot[chan].offset = 2;
+ _slot[chan].framesleft = 1;
+ checkSilenceChannels(chan);
+ if (chan == 2) {
+ numNotes = _slot[chan].data[2];
+ auxData1 = _slot[chan].data + 3;
+ auxData2 = auxData1 + numNotes;
+ _slot[chan].data = auxData2 + numNotes;
+ _slot[chan].offset = 0;
+
+ for (int i = 0; i < NUMCHANS; i++)
+ _mchan[i].cmdlock = 0;
+ }
+}
+
+void Player_NES::checkSilenceChannels(int chan) {
+ for (chan--; chan >= 0; chan--) {
+ if (_slot[chan].framesleft)
+ return;
+ }
+ APU_writeControl(0);
+}
+
+void Player_NES::sound_play() {
+ if (_slot[0].framesleft)
+ playSFX(0);
+ else if (_slot[1].framesleft)
+ playSFX(1);
+
+ playMusic();
+}
+
+void Player_NES::playSFX (int nr) {
+ if (--_slot[nr].framesleft)
+ return;
+
+ while (1) {
+ int a = _slot[nr].data[_slot[nr].offset++];
+ if (a < 16) {
+ a >>= 2;
+ APU_writeControl(APU_readStatus() | channelMask[a]);
+ isSFXplaying = true;
+ APU_writeChannel(a, 0, _slot[nr].data[_slot[nr].offset++]);
+ APU_writeChannel(a, 1, _slot[nr].data[_slot[nr].offset++]);
+ APU_writeChannel(a, 2, _slot[nr].data[_slot[nr].offset++]);
+ APU_writeChannel(a, 3, _slot[nr].data[_slot[nr].offset++]);
+ } else if (a == 0xFE) {
+ _slot[nr].offset = 2;
+ } else if (a == 0xFF) {
+ _slot[nr].id = -1;
+ _slot[nr].type = 0;
+ isSFXplaying = false;
+ APU_writeControl(0);
+
+ if (!nr && _slot[1].framesleft) {
+ _slot[1].framesleft = 1;
+ isSFXplaying = true;
+ }
+ return;
+ } else {
+ _slot[nr].framesleft = _slot[nr].data[_slot[nr].offset++];
+ return;
+ }
+ }
+}
+
+void Player_NES::playMusic() {
+ if (!_slot[2].framesleft)
+ return;
+
+ if (wasSFXplaying && !isSFXplaying)
+ for (int x = 1; x >= 0; x--)
+ if (_mchan[x].cmdlock) {
+ _mchan[x].command = _mchan[x].cmdlock;
+ _mchan[x].framedelay = 1;
+ }
+
+ wasSFXplaying = isSFXplaying;
+ if (!--_slot[2].framesleft) {
+top:
+ int b = _slot[2].data[_slot[2].offset++];
+ if (b == 0xFF) {
+ _slot[2].id = -1;
+ _slot[2].type = 0;
+ b = 0;
+ } else if (b == 0xFE) {
+ _slot[2].offset = 0;
+ goto top;
+ } else {
+ if (b < numNotes) {
+ int inst = auxData1[b];
+ int ch = instChannel[inst];
+ _mchan[ch].pitch = auxData2[b];
+ _mchan[ch].cmdlock = startCmd[inst];
+ _mchan[ch].command = startCmd[inst];
+ _mchan[ch].framedelay = 1;
+ goto top;
+ }
+ b -= numNotes;
+ if (b < 16) {
+ int inst = b;
+ int ch = instChannel[inst];
+ _mchan[ch].cmdlock = 0;
+ _mchan[ch].command = releaseCmd[inst];
+ _mchan[ch].framedelay = 1;
+ goto top;
+ }
+ b -= 16;
+ }
+ _slot[2].framesleft = b;
+ }
+
+ for (int x = NUMCHANS - 1; x >= 0; x--) {
+ if (_slot[0].framesleft || _slot[1].framesleft) {
+ _mchan[x].volume = 0;
+ _mchan[x].framedelay = 0;
+ continue;
+ }
+
+ if (_mchan[x].framedelay && !--_mchan[x].framedelay) {
+ switch (_mchan[x].command) {
+ case 0x00:
+ case 0x13:
+ _mchan[x].voldelta = -10;
+ break;
+
+ case 0x01:
+ case 0x03:
+ case 0x08:
+ case 0x16:
+ _mchan[x].envflags = 0x30;
+ _mchan[x].volume = 0x6F;
+ _mchan[x].voldelta = 0;
+
+ APU_writeChannel(x, 0, 0x00);
+ APU_writeChannel(x, 1, 0x7F);
+ APU_writeControl(APU_readStatus() | channelMask[x]);
+ APU_writeChannel(x, 2, freqTable[_mchan[x].pitch] & 0xFF);
+ APU_writeChannel(x, 3, freqTable[_mchan[x].pitch] >> 8);
+
+ chainCommand(x);
+ break;
+
+ case 0x02:
+ _mchan[x].envflags = 0xB0;
+ _mchan[x].volume = 0x6F;
+ _mchan[x].voldelta = 0;
+
+ APU_writeChannel(x, 0, 0x00);
+ APU_writeChannel(x, 1, 0x84);
+ APU_writeControl(APU_readStatus() | channelMask[x]);
+ APU_writeChannel(x, 2, freqTable[_mchan[x].pitch] & 0xFF);
+ APU_writeChannel(x, 3, freqTable[_mchan[x].pitch] >> 8);
+
+ chainCommand(x);
+ break;
+
+ case 0x04:
+ _mchan[x].envflags = 0x80;
+ _mchan[x].volume = 0x6F;
+ _mchan[x].voldelta = 0;
+
+ APU_writeChannel(x, 0, 0x00);
+ APU_writeChannel(x, 1, 0x7F);
+ APU_writeControl(APU_readStatus() | channelMask[x]);
+ APU_writeChannel(x, 2, freqTable[_mchan[x].pitch] & 0xFF);
+ APU_writeChannel(x, 3, freqTable[_mchan[x].pitch] >> 8);
+
+ chainCommand(x);
+ break;
+
+ case 0x05:
+ _mchan[x].envflags = 0xF0;
+ _mchan[x].volume = 0x6F;
+ _mchan[x].voldelta = -15;
+
+ APU_writeChannel(x, 1, 0x7F);
+ APU_writeControl(APU_readStatus() | channelMask[x]);
+ APU_writeChannel(x, 2, freqTable[_mchan[x].pitch] & 0xFF);
+ APU_writeChannel(x, 3, freqTable[_mchan[x].pitch] >> 8);
+
+ chainCommand(x);
+ break;
+
+ case 0x06:
+ _mchan[x].pitch += 0x18;
+ _mchan[x].envflags = 0x80;
+ _mchan[x].volume = 0x6F;
+ _mchan[x].voldelta = 0;
+
+ APU_writeChannel(x, 0, 0x00);
+ APU_writeChannel(x, 1, 0x7F);
+ APU_writeControl(APU_readStatus() | channelMask[x]);
+ APU_writeChannel(x, 2, freqTable[_mchan[x].pitch] & 0xFF);
+ APU_writeChannel(x, 3, freqTable[_mchan[x].pitch] >> 8);
+
+ chainCommand(x);
+ break;
+
+ case 0x07:
+ APU_writeChannel(x, 2, freqTable[_mchan[x].pitch - 0x0C] & 0xFF);
+ APU_writeChannel(x, 3, freqTable[_mchan[x].pitch - 0x0C] >> 8);
+
+ chainCommand(x);
+ break;
+
+ case 0x09:
+ _mchan[x].voldelta = -2;
+
+ APU_writeChannel(x, 1, 0x7F);
+ APU_writeChannel(x, 2, freqTable[_mchan[x].pitch] & 0xFF);
+ APU_writeChannel(x, 3, freqTable[_mchan[x].pitch] >> 8);
+
+ chainCommand(x);
+ break;
+
+ case 0x0A:
+ APU_writeChannel(x, 1, 0x86);
+ APU_writeChannel(x, 2, freqTable[_mchan[x].pitch] & 0xFF);
+ APU_writeChannel(x, 3, freqTable[_mchan[x].pitch] >> 8);
+
+ chainCommand(x);
+ break;
+
+ case 0x0B: case 0x1A:
+ _mchan[x].envflags = 0x70;
+ _mchan[x].volume = 0x6F;
+ _mchan[x].voldelta = 0;
+
+ APU_writeChannel(x, 0, 0x00);
+ APU_writeChannel(x, 1, 0x7F);
+ APU_writeControl(APU_readStatus() | channelMask[x]);
+ APU_writeChannel(x, 2, freqTable[_mchan[x].pitch] & 0xFF);
+ APU_writeChannel(x, 3, freqTable[_mchan[x].pitch] >> 8);
+
+ chainCommand(x);
+ break;
+
+ case 0x0C:
+ _mchan[x].envflags = 0xB0;
+
+ chainCommand(x);
+ break;
+
+ case 0x0D:
+ _mchan[x].envflags = 0x30;
+ _mchan[x].volume = 0x5F;
+ _mchan[x].voldelta = -22;
+
+ APU_writeChannel(x, 0, 0x00);
+ APU_writeControl(APU_readStatus() | channelMask[x]);
+ APU_writeChannel(x, 2, _mchan[x].pitch & 0xF);
+ APU_writeChannel(x, 3, 0xFF);
+
+ chainCommand(x);
+ break;
+
+ case 0x0E:
+ case 0x10:
+ _mchan[x].envflags = 0x30;
+ _mchan[x].volume = 0x5F;
+ _mchan[x].voldelta = -6;
+
+ APU_writeChannel(x, 0, 0x00);
+ APU_writeControl(APU_readStatus() | channelMask[x]);
+ APU_writeChannel(x, 2, _mchan[x].pitch & 0xF);
+ APU_writeChannel(x, 3, 0xFF);
+
+ chainCommand(x);
+ break;
+
+ case 0x0F:
+ chainCommand(x);
+ break;
+
+ case 0x11:
+ APU_writeChannel(x, 2, _mchan[x].pitch & 0xF);
+ APU_writeChannel(x, 3, 0xFF);
+
+ chainCommand(x);
+ break;
+
+ case 0x12:
+ APU_writeChannel(x, 2, (_mchan[x].pitch + 3) & 0xF);
+ APU_writeChannel(x, 3, 0xFF);
+
+ chainCommand(x);
+ break;
+
+ case 0x14:
+ _mchan[x].voldelta = -12;
+
+ APU_writeChannel(x, 1, 0x8C);
+
+ chainCommand(x);
+ break;
+
+ case 0x15:
+ _mchan[x].voldelta = -12;
+
+ APU_writeChannel(x, 1, 0x84);
+
+ chainCommand(x);
+ break;
+
+ case 0x17:
+ _mchan[x].pitch += 0x0C;
+ _mchan[x].envflags = 0x80;
+ _mchan[x].volume = 0x6F;
+ _mchan[x].voldelta = 0;
+
+ APU_writeChannel(x, 0, 0x00);
+ APU_writeChannel(x, 1, 0x7F);
+ APU_writeControl(APU_readStatus() | channelMask[x]);
+ APU_writeChannel(x, 2, freqTable[_mchan[x].pitch] & 0xFF);
+ APU_writeChannel(x, 3, freqTable[_mchan[x].pitch] >> 8);
+
+ chainCommand(x);
+ break;
+
+ case 0x18:
+ _mchan[x].envflags = 0x70;
+
+ chainCommand(x);
+ break;
+
+ case 0x19:
+ _mchan[x].envflags = 0xB0;
+
+ chainCommand(x);
+ break;
+
+ case 0x1B:
+ _mchan[x].envflags = 0x00;
+ _mchan[x].voldelta = -10;
+ break;
+ }
+ }
+
+ _mchan[x].volume += _mchan[x].voldelta;
+
+ if (_mchan[x].volume < 0)
+ _mchan[x].volume = 0;
+ if (_mchan[x].volume > MAXVOLUME)
+ _mchan[x].volume = MAXVOLUME;
+
+ APU_writeChannel(x, 0, (_mchan[x].volume >> 3) | _mchan[x].envflags);
+ }
+}
+
+void Player_NES::chainCommand(int c) {
+ int i = _mchan[c].command;
+ _mchan[c].command = nextCmd[i];
+ _mchan[c].framedelay = nextDelay[i];
+}
+
+int Player_NES::getSoundStatus(int nr) const {
+ for (int i = 0; i < NUMSLOTS; i++)
+ if (_slot[i].id == nr)
+ return 1;
+ return 0;
+}
+
+void Player_NES::APU_writeChannel(int chan, int offset, byte value) {
+ APUe::APU_WriteReg(0x000 + 4 * chan + offset, value);
+}
+void Player_NES::APU_writeControl(byte value) {
+ APUe::APU_WriteReg(0x015, value);
+}
+byte Player_NES::APU_readStatus() {
+ return APUe::APU_Read4015();
+}
+
+} // End of namespace Scumm
diff --git a/scumm/player_nes.h b/scumm/player_nes.h
new file mode 100644
index 0000000000..c3132b6fdc
--- /dev/null
+++ b/scumm/player_nes.h
@@ -0,0 +1,111 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2005 The ScummVM project
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * $Header$
+ *
+ */
+
+#ifndef PLAYER_NES_H
+#define PLAYER_NES_H
+
+#include "common/scummsys.h"
+#include "scumm/music.h"
+#include "sound/audiostream.h"
+
+class SoundMixer;
+
+namespace Scumm {
+
+class ScummEngine;
+
+/**
+ * Scumm NES sound/music driver.
+ */
+class Player_NES : public AudioStream, public MusicEngine {
+public:
+ Player_NES(ScummEngine *scumm);
+ virtual ~Player_NES();
+
+ virtual void setMusicVolume(int vol);
+ virtual void startSound(int sound);
+ virtual void stopSound(int sound);
+ virtual void stopAllSounds();
+ virtual int getSoundStatus(int sound) const;
+
+ // AudioStream API
+ int readBuffer(int16 *buffer, const int numSamples);
+ bool isStereo() const { return false; }
+ bool endOfData() const { return false; }
+ int getRate() const { return _sample_rate; }
+
+private:
+
+ void sound_play();
+ void playSFX(int nr);
+ void playMusic();
+ byte fetchSoundByte(int nr);
+ void chainCommand(int chan);
+ void checkSilenceChannels(int chan);
+
+ void APU_writeChannel(int chan, int offset, byte value);
+ void APU_writeControl(byte value);
+ byte APU_readStatus();
+
+ void do_mix(int16 *buf, uint len);
+
+ ScummEngine *_vm;
+ SoundMixer *_mixer;
+ int _sample_rate;
+ int _samples_per_frame;
+ int _current_sample;
+
+ static const int MAXVOLUME = 0x7F;
+ static const int NUMSLOTS = 3;
+ static const int NUMCHANS = 4;
+
+ struct slot {
+ int framesleft;
+ int id;
+ int type;
+ byte *data;
+ int offset;
+ } _slot[NUMSLOTS];
+
+ struct mchan {
+ int command;
+ int framedelay;
+ int pitch;
+ int volume;
+ int voldelta;
+ int envflags;
+ int cmdlock;
+ } _mchan[NUMCHANS];
+
+ bool isSFXplaying, wasSFXplaying;
+
+ byte *dataStart;
+ int numNotes;
+ byte *auxData1;
+ byte *auxData2;
+
+ byte *soundptr;
+};
+
+} // End of namespace Scumm
+
+#endif
diff --git a/scumm/player_v2a.cpp b/scumm/player_v2a.cpp
index e37327cff8..4381c5bcc7 100644
--- a/scumm/player_v2a.cpp
+++ b/scumm/player_v2a.cpp
@@ -1305,7 +1305,6 @@ static V2A_Sound *findSound (unsigned long crc) {
Player_V2A::Player_V2A(ScummEngine *scumm) {
int i;
_vm = scumm;
- _system = scumm->_system;
#ifdef __PALM_OS__
if (!CRCtable) CRCtable = (uint32 *)calloc(256, sizeof(uint32));
diff --git a/scumm/player_v2a.h b/scumm/player_v2a.h
index 2c28e64fac..9462e7c50f 100644
--- a/scumm/player_v2a.h
+++ b/scumm/player_v2a.h
@@ -27,7 +27,6 @@
#include "scumm/music.h"
#include "scumm/player_mod.h"
-class OSystem;
class SoundMixer;
namespace Scumm {
@@ -60,7 +59,6 @@ private:
V2A_Sound *sound;
};
- OSystem *_system;
ScummEngine *_vm;
Player_MOD *_mod;
soundSlot _slot[V2A_MAXSLOTS];
diff --git a/scumm/player_v3a.cpp b/scumm/player_v3a.cpp
index 1a25be9829..e33a258435 100644
--- a/scumm/player_v3a.cpp
+++ b/scumm/player_v3a.cpp
@@ -37,7 +37,6 @@ static const uint16 note_freqs[4][12] = {
Player_V3A::Player_V3A(ScummEngine *scumm) {
int i;
_vm = scumm;
- _system = scumm->_system;
for (i = 0; i < V3A_MAXMUS; i++) {
_mus[i].id = 0;
_mus[i].dur = 0;
diff --git a/scumm/player_v3a.h b/scumm/player_v3a.h
index a2c8861b9b..31fe4d8840 100644
--- a/scumm/player_v3a.h
+++ b/scumm/player_v3a.h
@@ -27,7 +27,6 @@
#include "scumm/music.h"
#include "scumm/player_mod.h"
-class OSystem;
class SoundMixer;
namespace Scumm {
@@ -75,7 +74,6 @@ private:
int16 _pitadjust;
};
- OSystem *_system;
ScummEngine *_vm;
Player_MOD *_mod;
diff --git a/scumm/scumm.cpp b/scumm/scumm.cpp
index 3c81ab5c81..f0106d6a4e 100644
--- a/scumm/scumm.cpp
+++ b/scumm/scumm.cpp
@@ -46,6 +46,7 @@
#include "scumm/insane/insane.h"
#include "scumm/intern.h"
#include "scumm/object.h"
+#include "scumm/player_nes.h"
#include "scumm/player_v1.h"
#include "scumm/player_v2.h"
#include "scumm/player_v2a.h"
@@ -1652,11 +1653,13 @@ void ScummEngine::setupMusic(int midi) {
// Init iMuse
if (_features & GF_DIGI_IMUSE) {
_musicEngine = _imuseDigital = new IMuseDigital(this, 10);
+ } else if (_features & GF_NES) {
+ _musicEngine = new Player_NES(this);
} else if ((_features & GF_AMIGA) && (_version == 2)) {
_musicEngine = new Player_V2A(this);
} else if ((_features & GF_AMIGA) && (_version == 3)) {
_musicEngine = new Player_V3A(this);
- } else if (((_features & GF_AMIGA) && (_version < 5)) || (_features & GF_NES)) {
+ } else if ((_features & GF_AMIGA) && (_version < 5)) {
_musicEngine = NULL;
} else if (((_midiDriver == MD_PCJR) || (_midiDriver == MD_PCSPK)) && ((_version > 2) && (_version < 5))) {
_musicEngine = new Player_V2(this, _midiDriver != MD_PCSPK);