diff options
author | Eugene Sandulenko | 2005-04-07 07:29:19 +0000 |
---|---|---|
committer | Eugene Sandulenko | 2005-04-07 07:29:19 +0000 |
commit | 668c889c8e2c10511c0783aca38e6f63bfbe6a60 (patch) | |
tree | 6d136301cb25d3c29e0be14fc9fa63d87debc268 | |
parent | 19ff0b0085df624918ee9ab438f1c40bfec57c83 (diff) | |
download | scummvm-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
-rw-r--r-- | scumm/module.mk | 1 | ||||
-rw-r--r-- | scumm/player_nes.cpp | 1092 | ||||
-rw-r--r-- | scumm/player_nes.h | 111 | ||||
-rw-r--r-- | scumm/player_v2a.cpp | 1 | ||||
-rw-r--r-- | scumm/player_v2a.h | 2 | ||||
-rw-r--r-- | scumm/player_v3a.cpp | 1 | ||||
-rw-r--r-- | scumm/player_v3a.h | 2 | ||||
-rw-r--r-- | scumm/scumm.cpp | 5 |
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); |