Initial commit

This commit is contained in:
2026-02-02 04:50:13 +01:00
commit 5b11698731
22592 changed files with 7677434 additions and 0 deletions

View File

@@ -0,0 +1,71 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/nuvie/sound/adplug/adplug_player.h"
#include "ultima/nuvie/sound/adplug/silent_opl.h"
namespace Ultima {
namespace Nuvie {
/***** CPlayer *****/
const unsigned short CPlayer::note_table[12] =
{363, 385, 408, 432, 458, 485, 514, 544, 577, 611, 647, 686};
const unsigned char CPlayer::op_table[9] =
{0x00, 0x01, 0x02, 0x08, 0x09, 0x0a, 0x10, 0x11, 0x12};
CPlayer::CPlayer(Copl *newopl)
: opl(newopl) {
}
CPlayer::~CPlayer() {
}
unsigned long CPlayer::songlength(int subsong) {
CSilentopl tempopl;
Copl *saveopl = opl;
float slength = 0.0f;
// save original OPL from being overwritten
opl = &tempopl;
// get song length
rewind(subsong);
while (update() && slength < 600000) // song length limit: 10 minutes
slength += 1000.0f / getrefresh();
rewind(subsong);
// restore original OPL and return
opl = saveopl;
return (unsigned long)slength;
}
void CPlayer::seek(unsigned long ms) {
float pos = 0.0f;
rewind();
while (pos < ms && update()) // seek to new position
pos += 1000 / getrefresh();
}
} // End of namespace Nuvie
} // End of namespace Ultima

View File

@@ -0,0 +1,99 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef NUVIE_SOUND_ADPLUG_ADPLUG_PLAYER
#define NUVIE_SOUND_ADPLUG_ADPLUG_PLAYER
#include "ultima/shared/std/string.h"
#include "ultima/nuvie/sound/adplug/opl.h"
namespace Common {
class Path;
}
namespace Ultima {
namespace Nuvie {
class CPlayer {
public:
CPlayer(Copl *newopl);
virtual ~CPlayer();
/***** Operational methods *****/
void seek(unsigned long ms);
virtual bool load(const Common::Path &filename) = 0; // loads file
virtual bool update() = 0; // executes replay code for 1 tick
virtual void rewind(int subsong = -1) = 0; // rewinds to specified subsong
virtual float getrefresh() = 0; // returns needed timer refresh rate
/***** Informational methods *****/
unsigned long songlength(int subsong = -1);
virtual Std::string gettype() = 0; // returns file type
virtual Std::string gettitle() { // returns song title
return Std::string();
}
virtual Std::string getauthor() { // returns song author name
return Std::string();
}
virtual Std::string getdesc() { // returns song description
return Std::string();
}
virtual unsigned int getpatterns() { // returns number of patterns
return 0;
}
virtual unsigned int getpattern() { // returns currently playing pattern
return 0;
}
virtual unsigned int getorders() { // returns size of orderlist
return 0;
}
virtual unsigned int getorder() { // returns currently playing song position
return 0;
}
virtual unsigned int getrow() { // returns currently playing row
return 0;
}
virtual unsigned int getspeed() { // returns current song speed
return 0;
}
virtual unsigned int getsubsongs() { // returns number of subsongs
return 1;
}
virtual unsigned int getinstruments() { // returns number of instruments
return 0;
}
virtual Std::string getinstrument(unsigned int n) { // returns n-th instrument name
return Std::string();
}
protected:
Copl *opl; // our OPL chip
static const unsigned short note_table[12]; // standard adlib note table
static const unsigned char op_table[9]; // the 9 operators as expected by the OPL2
};
} // End of namespace Nuvie
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,76 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/nuvie/sound/adplug/emu_opl.h"
#include "ultima/nuvie/sound/adplug/fm_opl.h"
namespace Ultima {
namespace Nuvie {
CEmuopl::CEmuopl(int rate, bool bit16, bool usestereo)
: use16bit(bit16), stereo(usestereo), oplRate(rate) {
YM3812Init(1, 3579545, rate);
}
CEmuopl::~CEmuopl() {
YM3812Shutdown();
}
void CEmuopl::update(short *buf, int samples) {
int i;
if (use16bit) {
YM3812UpdateOne(0, buf, samples);
if (stereo)
for (i = samples - 1; i >= 0; i--) {
buf[i * 2] = buf[i];
buf[i * 2 + 1] = buf[i];
}
} else {
short *tempbuf = new short[stereo ? samples * 2 : samples];
YM3812UpdateOne(0, tempbuf, samples);
if (stereo)
for (i = samples - 1; i >= 0; i--) {
tempbuf[i * 2] = tempbuf[i];
tempbuf[i * 2 + 1] = tempbuf[i];
}
for (i = 0; i < (stereo ? samples * 2 : samples); i++)
((char *)buf)[i] = (tempbuf[i] >> 8) ^ 0x80;
delete [] tempbuf;
}
}
void CEmuopl::write(int reg, int val) {
YM3812Write(0, 0, reg);
YM3812Write(0, 1, val);
}
void CEmuopl::init() {
YM3812ResetChip(0);
}
} // End of namespace Nuvie
} // End of namespace Ultima

View File

@@ -0,0 +1,54 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef NUVIE_SOUND_ADPLUG_EMU_OPL_H
#define NUVIE_SOUND_ADPLUG_EMU_OPL_H
#include "ultima/nuvie/sound/adplug/opl.h"
#include "audio/fmopl.h"
namespace Ultima {
namespace Nuvie {
class CEmuopl: public Copl {
public:
CEmuopl(int rate, bool bit16, bool usestereo); // rate = sample rate
~CEmuopl() override;
int getRate() {
return oplRate;
}
void update(short *buf, int samples); // fill buffer
// template methods
void write(int reg, int val) override;
void init() override;
private:
bool use16bit, stereo;
int oplRate;
};
} // End of namespace Nuvie
} // End of namespace Ultima
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,137 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef NUVIE_SOUND_ADPLUG_FM_OPL_H
#define NUVIE_SOUND_ADPLUG_FM_OPL_H
#include "common/scummsys.h"
namespace Ultima {
namespace Nuvie {
#define HAS_YM3812 1
#define HAS_YM3526 0
#define HAS_Y8950 0
/* --- select emulation chips --- */
#define BUILD_YM3812 (HAS_YM3812)
#define BUILD_YM3526 (HAS_YM3526)
#define BUILD_Y8950 (HAS_Y8950)
/* select output bits size of output : 8 or 16 */
#define OPL_SAMPLE_BITS 16
#if (OPL_SAMPLE_BITS==16)
typedef int16 OPLSAMPLE;
#endif
#if (OPL_SAMPLE_BITS==8)
typedef int8 OPLSAMPLE;
#endif
typedef void (*OPL_TIMERHANDLER)(int channel, double interval_Sec);
typedef void (*OPL_IRQHANDLER)(int param, int irq);
typedef void (*OPL_UPDATEHANDLER)(int param, int min_interval_us);
typedef void (*OPL_PORTHANDLER_W)(int param, unsigned char data);
typedef unsigned char (*OPL_PORTHANDLER_R)(int param);
#if BUILD_YM3812
int YM3812Init(int num, int clock, int rate);
void YM3812Shutdown(void);
void YM3812ResetChip(int which);
int YM3812Write(int which, int a, int v);
unsigned char YM3812Read(int which, int a);
int YM3812TimerOver(int which, int c);
void YM3812UpdateOne(int which, int16 *buffer, int length);
void YM3812SetTimerHandler(int which, OPL_TIMERHANDLER TimerHandler, int channelOffset);
void YM3812SetIRQHandler(int which, OPL_IRQHANDLER IRQHandler, int param);
void YM3812SetUpdateHandler(int which, OPL_UPDATEHANDLER UpdateHandler, int param);
#endif
#if BUILD_YM3526
/*
** Initialize YM3526 emulator(s).
**
** 'num' is the number of virtual YM3526's to allocate
** 'clock' is the chip clock in Hz
** 'rate' is sampling rate
*/
int YM3526Init(int num, int clock, int rate);
/* shutdown the YM3526 emulators*/
void YM3526Shutdown(void);
void YM3526ResetChip(int which);
int YM3526Write(int which, int a, int v);
unsigned char YM3526Read(int which, int a);
int YM3526TimerOver(int which, int c);
/*
** Generate samples for one of the YM3526's
**
** 'which' is the virtual YM3526 number
** '*buffer' is the output buffer pointer
** 'length' is the number of samples that should be generated
*/
void YM3526UpdateOne(int which, int16 *buffer, int length);
void YM3526SetTimerHandler(int which, OPL_TIMERHANDLER TimerHandler, int channelOffset);
void YM3526SetIRQHandler(int which, OPL_IRQHANDLER IRQHandler, int param);
void YM3526SetUpdateHandler(int which, OPL_UPDATEHANDLER UpdateHandler, int param);
#endif
} // End of namespace Nuvie
} // End of namespace Ultima
#if BUILD_Y8950
#include "ymdeltat.h"
namespace Ultima {
namespace Nuvie {
/* Y8950 port handlers */
void Y8950SetPortHandler(int which, OPL_PORTHANDLER_W PortHandler_w, OPL_PORTHANDLER_R PortHandler_r, int param);
void Y8950SetKeyboardHandler(int which, OPL_PORTHANDLER_W KeyboardHandler_w, OPL_PORTHANDLER_R KeyboardHandler_r, int param);
void Y8950SetDeltaTMemory(int which, void *deltat_rom, int deltat_rom_size);
int Y8950Init(int num, int clock, int rate);
void Y8950Shutdown(void);
void Y8950ResetChip(int which);
int Y8950Write(int which, int a, int v);
unsigned char Y8950Read(int which, int a);
int Y8950TimerOver(int which, int c);
void Y8950UpdateOne(int which, int16 *buffer, int length);
void Y8950SetTimerHandler(int which, OPL_TIMERHANDLER TimerHandler, int channelOffset);
void Y8950SetIRQHandler(int which, OPL_IRQHANDLER IRQHandler, int param);
void Y8950SetUpdateHandler(int which, OPL_UPDATEHANDLER UpdateHandler, int param);
} // End of namespace Nuvie
} // End of namespace Ultima
#endif
#endif

View File

@@ -0,0 +1,572 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/nuvie/core/nuvie_defs.h"
#include "ultima/nuvie/misc/u6_misc.h"
#include "ultima/nuvie/files/u6_lib_n.h"
#include "ultima/nuvie/core/game.h"
#include "ultima/nuvie/sound/origin_fx_adib_driver.h"
#include "ultima/nuvie/sound/adplug/mid.h"
namespace Ultima {
namespace Nuvie {
//#define TESTING
#ifdef TESTING
#define midiprintf debug
#else
void CmidPlayer::midiprintf(const char *format, ...) {
}
#endif
#define LUCAS_STYLE 1
#define CMF_STYLE 2
#define MIDI_STYLE 4
#define SIERRA_STYLE 8
// AdLib melodic and rhythm mode defines
#define ADLIB_MELODIC 1
#define ADLIB_RYTHM 0
// File types
#define FILE_LUCAS 1
#define FILE_MIDI 2
#define FILE_CMF 3
#define FILE_SIERRA 4
#define FILE_ADVSIERRA 5
#define FILE_OLDLUCAS 6
CPlayer *CmidPlayer::factory(Copl *newopl) {
return new CmidPlayer(newopl);
}
CmidPlayer::CmidPlayer(Copl *newopl)
: CPlayer(newopl), author(&emptystr), title(&emptystr), remarks(&emptystr),
emptystr('\0'), flen(0), data(0) {
origin_fx_driver = new OriginFXAdLibDriver(Game::get_game()->get_config(), newopl);
}
CmidPlayer::~CmidPlayer() {
if (data)
delete [] data;
delete origin_fx_driver;
}
unsigned char CmidPlayer::datalook(long pos_) {
if (pos_ < 0 || pos_ >= flen) return 0;
return data[pos_];
}
unsigned long CmidPlayer::getnexti(unsigned long num) {
unsigned long v = 0;
unsigned long i;
for (i = 0; i < num; i++) {
v += (datalook(pos) << (8 * i));
pos++;
}
return v;
}
unsigned long CmidPlayer::getnext(unsigned long num) {
unsigned long v = 0;
unsigned long i;
for (i = 0; i < num; i++) {
v <<= 8;
v += datalook(pos);
pos++;
}
return v;
}
unsigned long CmidPlayer::getval() {
int v = 0;
unsigned char b;
b = (unsigned char)getnext(1);
v = b & 0x7f;
while ((b & 0x80) != 0) {
b = (unsigned char)getnext(1);
v = (v << 7) + (b & 0x7F);
}
return v;
}
bool CmidPlayer::load(const Common::Path &filename) {
return false;
}
bool CmidPlayer::load(const Common::Path &filename, int song_index) {
U6Lib_n f;
f.open(filename, 4, NUVIE_GAME_MD);
//binistream *f = fp.open(filename); if(!f) return false;
int good;
flen = f.get_item_size(song_index);
data = new unsigned char [flen];
f.get_item(song_index, data);
//f->readString((char *)data, flen);
//f->readString((char *)s, 6);
good = 0;
subsongs = 0;
switch (data[0]) {
case 'A':
if (data[1] == 'D' && data[2] == 'L') good = FILE_LUCAS;
break;
case 'M':
if (data[1] == 'T' && data[2] == 'h' && data[3] == 'd') good = FILE_MIDI;
break;
case 'C':
if (data[1] == 'T' && data[2] == 'M' && data[3] == 'F') good = FILE_CMF;
break;
break;
default:
if (data[4] == 'A' && data[5] == 'D') good = FILE_OLDLUCAS;
break;
}
if (good != 0)
subsongs = 1;
else {
delete [] data;
data = nullptr;
return false;
}
type = good;
//f->seek(0);
rewind(0);
return true;
}
void CmidPlayer::interrupt_vector() {
origin_fx_driver->interrupt_vector();
}
bool CmidPlayer::update() {
const uint8 adlib_chan_tbl[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10,
10, 18, 11, 0, 12, 13, 17, 13, 16, 13, 14, 13, 13, 15,
13, 19, 0, 0, 0, 0, 21, 0, 0, 0, 26, 26, 25, 20, 20,
0, 0, 21, 21, 22, 23, 0, 0, 24, 0, 20, 0
};
const uint8 adlib_note_tbl[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48,
48, 48, 48, 0, 48, 42, 71, 42, 71, 47, 71, 47, 52, 79,
52, 77, 0, 0, 0, 0, 71, 0, 0, 0, 72, 79, 79, 64, 58,
0, 0, 89, 84, 48, 72, 0, 0, 36, 0, 96, 0
};
//long w,v,note,vel,ctrl,nv,x,l,lnum;
long w, v, note, vel, ctrl, x, l, lnum;
int i = 0, j, c;
//int on,onl,numchan;
int ret;
int current_status[16];
for (i = 0; i < 16; i++)
current_status[i] = 0;
if (doing == 1) {
// just get the first wait and ignore it :>
for (curtrack = 0; curtrack < 16; curtrack++)
if (track[curtrack].on) {
pos = track[curtrack].pos;
if (type != FILE_SIERRA && type != FILE_ADVSIERRA)
track[curtrack].iwait += getval();
else
track[curtrack].iwait += getnext(1);
track[curtrack].pos = pos;
}
doing = 0;
}
iwait = 0;
ret = 1;
while (iwait == 0 && ret == 1) {
for (curtrack = 0; curtrack < 16; curtrack++)
if (track[curtrack].on && track[curtrack].iwait == 0 &&
track[curtrack].pos < track[curtrack].tend) {
pos = track[curtrack].pos;
v = getnext(1);
// This is to do implied MIDI events. aka 'Running Status'
if (v < 0x80) {
v = track[curtrack].pv;
debug("Running status [%2X]", (unsigned int)v);
pos--;
} else {
if (v >= 0xf0 && v < 0xf9) {
track[curtrack].pv = 0; //reset running status.
} else if (v < 0xf0) {
track[curtrack].pv = (unsigned char)v;
}
// if v > 0xf9 then current running status is maintained.
}
c = v & 0x0f;
midiprintf("[%2X]", (unsigned int)v);
if (v == 0xfe)
midiprintf("pos=%d", (int)pos);
current_status[curtrack] = v;
switch (v & 0xf0) {
case 0x80: /*note off*/
midiprintf("Trk%02d: Note Off\n", curtrack);
note = getnext(1);
vel = getnext(1);
origin_fx_driver->play_note(c, note, 0);
break;
case 0x90: /*note on*/
// doing=0;
midiprintf("Trk%02d: Note On\n", curtrack);
note = getnext(1);
vel = getnext(1);
if (c == 9) {
if (adlib_chan_tbl[note] != 0) {
origin_fx_driver->play_note(adlib_chan_tbl[note] - 1, adlib_note_tbl[note], vel);
}
} else {
origin_fx_driver->play_note(c, note, vel);
}
break;
case 0xa0: /*key after touch */
note = getnext(1);
vel = getnext(1);
break;
case 0xb0: /*control change .. pitch bend? */
ctrl = getnext(1);
vel = getnext(1);
origin_fx_driver->control_mode_change(c, ctrl, vel);
break;
case 0xc0: /*patch change*/
x = getnext(1);
origin_fx_driver->program_change(c, x);
break;
case 0xd0: /*chanel touch*/
x = getnext(1);
break;
case 0xe0: /*pitch wheel*/
x = getnext(1);
l = getnext(1);
origin_fx_driver->pitch_bend(c, x, l);
break;
case 0xf0:
switch (v) {
case 0xf0:
case 0xf7: /*sysex*/
l = getval();
if (datalook(pos + l) == 0xf7)
i = 1;
midiprintf("{%d}", (int)l);
midiprintf("\n");
if (datalook(pos) == 0x7d &&
datalook(pos + 1) == 0x10 &&
datalook(pos + 2) < 16) {
adlib_style = LUCAS_STYLE | MIDI_STYLE;
for (i = 0; i < l; i++) {
midiprintf("%x ", datalook(pos + i));
if ((i - 3) % 10 == 0) midiprintf("\n");
}
midiprintf("\n");
getnext(1);
getnext(1);
c = getnext(1);
getnext(1);
// getnext(22); //temp
i = (getnext(1) << 4) + getnext(1);
//if ((i&1)==1) ch[c].ins[10]=1;
midiprintf("\n%d: ", c);
getnext(l - 26);
} else {
midiprintf("\n");
for (j = 0; j < l; j++)
midiprintf("%2X ", (unsigned int)getnext(1));
}
midiprintf("\n");
if (i == 1)
getnext(1);
break;
case 0xf1:
break;
case 0xf2:
getnext(2);
break;
case 0xf3:
getnext(1);
break;
case 0xf4:
break;
case 0xf5:
break;
case 0xf6: /*something*/
case 0xf8:
case 0xfa:
case 0xfb:
case 0xfc:
break;
case 0xfe:
i = getnext(1);
//debug("FE %02X pos=%d\n",i, (int)pos);//(unsigned int)getnext(1),(unsigned int)getnext(1));
getnext(2);
if (i == 0) {
//debug(" %02X",(unsigned int)getnext(1));
//getnext(1);
}
//debug("\n");
if (i != 3) {
origin_fx_driver->control_mode_change(c, 0x7b, 0);
}
break;
case 0xfd:
break;
case 0xff:
v = getnext(1);
l = getval();
midiprintf("\n");
midiprintf("{%X_%X}", (unsigned int)v, (int)l);
if (v == 0x51) {
lnum = getnext(l);
msqtr = lnum; /*set tempo*/
midiprintf("Set Tempo (qtr=%ld)", msqtr);
} else if (v == 0x3) {
midiprintf("Track Name: ");
for (i = 0; i < l; i++)
midiprintf("%c", (unsigned char)getnext(1));
} else if (v == 0x6) {
debugN("Marker: ");
for (i = 0; i < l; i++) {
//midiprintf ("%c",(unsigned char)getnext(1));
debugN("%c", (unsigned char)getnext(1));
}
debug("%s", "");
} else {
for (i = 0; i < l; i++)
midiprintf("%2X ", (unsigned int)getnext(1));
}
break;
}
break;
default:
midiprintf("! v = %d", (int)v); /* if we get down here, a error occurred */
break;
}
if (pos < track[curtrack].tend) {
if (type != FILE_SIERRA && type != FILE_ADVSIERRA)
w = getval();
else
w = getnext(1);
track[curtrack].iwait = w;
/*
if (w!=0)
{
midiprintf("\n<%d>",w);
f =
((float)w/(float)deltas)*((float)msqtr/(float)1000000);
if (doing==1) f=0; //not playing yet. don't wait yet
}
*/
} else
track[curtrack].iwait = 0;
track[curtrack].pos = pos;
}
/*
for(i=0;i<16;i++)
{
if(current_status[i] == 0)
debug("--");
else
debug("%02X", current_status[i]);
debug(" ");
}
debug("\n");
*/
ret = 0; //end of song.
iwait = 0;
for (curtrack = 0; curtrack < 16; curtrack++)
if (track[curtrack].on == 1 &&
track[curtrack].pos < track[curtrack].tend)
ret = 1; //not yet..
if (ret == 1) {
iwait = 0xffffff; // bigger than any wait can be!
for (curtrack = 0; curtrack < 16; curtrack++)
if (track[curtrack].on == 1 &&
track[curtrack].pos < track[curtrack].tend &&
track[curtrack].iwait < iwait)
iwait = track[curtrack].iwait;
}
}
if (iwait != 0 && ret == 1) {
for (curtrack = 0; curtrack < 16; curtrack++)
if (track[curtrack].on)
track[curtrack].iwait -= iwait;
fwait = 1.0f / (((float)iwait / (float)deltas) * ((float)msqtr / (float)1000000));
} else
fwait = 50; // 1/50th of a second
midiprintf("\n");
for (i = 0; i < 16; i++)
if (track[i].on) {
if (track[i].pos < track[i].tend)
;//midiprintf ("<%d:%d>",(int)i,(int)track[i].iwait);
else
midiprintf("stop");
}
// FIXME: current_status is unused?
(void)current_status[0];
if (ret)
return true;
else
return false;
}
float CmidPlayer::getrefresh() {
return (fwait > 0.01f ? fwait : 0.01f);
}
void CmidPlayer::rewind(int subsong) {
long i;
pos = 0;
tins = 0;
adlib_style = MIDI_STYLE | CMF_STYLE;
adlib_mode = ADLIB_MELODIC;
/* General init */
for (i = 0; i < 9; i++) {
chp[i][0] = -1;
chp[i][2] = 0;
}
deltas = 250; // just a number, not a standard
msqtr = 500000;
fwait = 123; // gotta be a small thing.. sorta like nothing
iwait = 0;
subsongs = 1;
for (i = 0; i < 16; i++) {
track[i].tend = 0;
track[i].spos = 0;
track[i].pos = 0;
track[i].iwait = 0;
track[i].on = 0;
track[i].pv = 0;
}
curtrack = 0;
/* specific to file-type init */
pos = 0;
i = getnext(1);
switch (type) {
case FILE_MIDI:
if (type != FILE_LUCAS)
tins = 128;
getnext(9); /*skip header*/
track_count = getnext(2); //total number of tracks.
deltas = getnext(2);
midiprintf("deltas:%ld\n", deltas);
load_ultima_midi_tracks();
break;
}
/* Common::sprintf_s(info,"%s\r\nTicks/Quarter Note: %ld\r\n",info,deltas);
Common::sprintf_s(info,"%sms/Quarter Note: %ld",info,msqtr); */
for (i = 0; i < 16; i++)
if (track[i].on) {
track[i].pos = track[i].spos;
track[i].pv = 0;
track[i].iwait = 0;
}
doing = 1;
origin_fx_driver->init();
}
void CmidPlayer::load_ultima_midi_tracks() {
for (curtrack = 0; curtrack < track_count; curtrack++) {
getnext(4); //skip MTrk
track[curtrack].on = 1;
track[curtrack].tend = getnext(4);
track[curtrack].tend += pos;
track[curtrack].spos = pos;
pos = track[curtrack].tend;
midiprintf("tracklen:%ld\n", track[curtrack].tend - track[curtrack].spos);
}
}
Std::string CmidPlayer::gettype() {
switch (type) {
case FILE_LUCAS:
return Std::string("LucasArts AdLib MIDI");
case FILE_MIDI:
return Std::string("General MIDI");
case FILE_CMF:
return Std::string("Creative Music Format (CMF MIDI)");
case FILE_OLDLUCAS:
return Std::string("Lucasfilm Adlib MIDI");
case FILE_ADVSIERRA:
return Std::string("Sierra On-Line VGA MIDI");
case FILE_SIERRA:
return Std::string("Sierra On-Line EGA MIDI");
default:
return Std::string("MIDI unknown");
}
}
} // End of namespace Nuvie
} // End of namespace Ultima

View File

@@ -0,0 +1,115 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/nuvie/sound/adplug/adplug_player.h"
namespace Ultima {
namespace Nuvie {
class OriginFXAdLibDriver;
class CmidPlayer: public CPlayer {
public:
static CPlayer *factory(Copl *newopl);
CmidPlayer(Copl *newopl);
~CmidPlayer() override;
bool load(const Common::Path &filename) override;
bool load(const Common::Path &filename, int song_index);
//bool load(const Std::string &filename, const CFileProvider &fp);
bool update() override;
void rewind(int subsong) override;
float getrefresh() override;
Std::string gettype() override;
Std::string gettitle() override {
return Std::string(title);
}
Std::string getauthor() override {
return Std::string(author);
}
Std::string getdesc() override {
return Std::string(remarks);
}
unsigned int getinstruments() override {
return tins;
}
unsigned int getsubsongs() override {
return subsongs;
}
protected:
static const unsigned char adlib_opadd[];
static const int ops[], map_chan[], fnums[], percussion_map[];
struct midi_track {
unsigned long tend;
unsigned long spos;
unsigned long pos;
unsigned long iwait;
int on;
unsigned char pv;
};
char *author, *title, *remarks, emptystr;
long flen;
unsigned long pos;
int subsongs;
unsigned char *data;
int adlib_style;
int adlib_mode;
int chp[18][3];
long deltas;
long msqtr;
midi_track track[16];
unsigned int curtrack;
unsigned int track_count;
float fwait;
unsigned long iwait;
int doing;
int type, tins, stins;
OriginFXAdLibDriver *origin_fx_driver;
private:
void load_ultima_midi_tracks();
void midiprintf(const char *format, ...);
unsigned char datalook(long pos);
unsigned long getnexti(unsigned long num);
unsigned long getnext(unsigned long num);
unsigned long getval();
public:
void interrupt_vector();
};
} // End of namespace Nuvie
} // End of namespace Ultima

View File

@@ -0,0 +1,42 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef NUVIE_SOUND_ADPLUG_OPL
#define NUVIE_SOUND_ADPLUG_OPL
#include "common/scummsys.h"
namespace Ultima {
namespace Nuvie {
class Copl {
public:
virtual ~Copl() {
return;
}
virtual void write(int reg, int val) = 0; // combined register select + data write
virtual void init(void) = 0; // reinitialize OPL chip
};
} // End of namespace Nuvie
} // End of namespace Ultima
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,253 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef NUVIE_SOUND_ADPLUG_OPL_CLASS_H
#define NUVIE_SOUND_ADPLUG_OPL_CLASS_H
#include "ultima/nuvie/sound/adplug/opl.h"
namespace Ultima {
namespace Nuvie {
#define HAS_YM3812 1
#define HAS_YM3526 0
#define HAS_Y8950 0
/* --- select emulation chips --- */
#define BUILD_YM3812 (HAS_YM3812)
#define BUILD_YM3526 (HAS_YM3526)
#define BUILD_Y8950 (HAS_Y8950)
/* select output bits size of output : 8 or 16 */
#define OPL_SAMPLE_BITS 16
#if (OPL_SAMPLE_BITS==16)
typedef int16 OPLSAMPLE;
#endif
#if (OPL_SAMPLE_BITS==8)
typedef int8 OPLSAMPLE;
#endif
typedef void (*OPL_TIMERHANDLER)(int channel, double interval_Sec);
typedef void (*OPL_IRQHANDLER)(int param, int irq);
typedef void (*OPL_UPDATEHANDLER)(int param, int min_interval_us);
typedef void (*OPL_PORTHANDLER_W)(int param, unsigned char data);
typedef unsigned char (*OPL_PORTHANDLER_R)(int param);
/* Saving is necessary for member of the 'R' mark for suspend/resume */
typedef struct {
uint32 ar; /* attack rate: AR<<2 */
uint32 dr; /* decay rate: DR<<2 */
uint32 rr; /* release rate:RR<<2 */
uint8 KSR; /* key scale rate */
uint8 ksl; /* keyscale level */
uint8 ksr; /* key scale rate: kcode>>KSR */
uint8 mul; /* multiple: mul_tab[ML] */
/* Phase Generator */
uint32 Cnt; /* frequency counter */
uint32 Incr; /* frequency counter step */
uint8 FB; /* feedback shift value */
signed int *connect1; /* slot1 output pointer */
int32 op1_out[2]; /* slot1 output for feedback */
uint8 CON; /* connection (algorithm) type */
/* Envelope Generator */
uint8 eg_type; /* percussive/non-percussive mode */
uint8 state; /* phase type */
uint32 TL; /* total level: TL << 2 */
int32 TLL; /* adjusted now TL */
int32 volume; /* envelope counter */
uint32 sl; /* sustain level: sl_tab[SL] */
uint8 eg_sh_ar; /* (attack state) */
uint8 eg_sel_ar; /* (attack state) */
uint8 eg_sh_dr; /* (decay state) */
uint8 eg_sel_dr; /* (decay state) */
uint8 eg_sh_rr; /* (release state) */
uint8 eg_sel_rr; /* (release state) */
uint32 key; /* 0 = KEY OFF, >0 = KEY ON */
/* LFO */
uint32 AMmask; /* LFO Amplitude Modulation enable mask */
uint8 vib; /* LFO Phase Modulation enable flag (active high)*/
/* waveform select */
unsigned int wavetable;
} OPL_SLOT;
typedef struct {
OPL_SLOT SLOT[2];
/* phase generator state */
uint32 block_fnum; /* block+fnum */
uint32 fc; /* Freq. Increment base */
uint32 ksl_base; /* KeyScaleLevel Base step */
uint8 kcode; /* key code (for key scaling) */
} OPL_CH;
/* OPL state */
typedef struct fm_opl_f {
/* FM channel slots */
OPL_CH P_CH[9]; /* OPL/OPL2 chips have 9 channels*/
uint32 eg_cnt; /* global envelope generator counter */
uint32 eg_timer; /* global envelope generator counter works at frequency = chipclock/72 */
uint32 eg_timer_add; /* step of eg_timer */
uint32 eg_timer_overflow; /* envelope generator timer overlfows every 1 sample (on real chip) */
uint8 rhythm; /* Rhythm mode */
uint32 fn_tab[1024]; /* fnumber->increment counter */
/* LFO */
uint8 lfo_am_depth;
uint8 lfo_pm_depth_range;
uint32 lfo_am_cnt;
uint32 lfo_am_inc;
uint32 lfo_pm_cnt;
uint32 lfo_pm_inc;
uint32 noise_rng; /* 23 bit noise shift register */
uint32 noise_p; /* current noise 'phase' */
uint32 noise_f; /* current noise period */
uint8 wavesel; /* waveform select enable flag */
int T[2]; /* timer counters */
uint8 st[2]; /* timer enable */
/* external event callback handlers */
OPL_TIMERHANDLER TimerHandler; /* TIMER handler */
int TimerParam; /* TIMER parameter */
OPL_IRQHANDLER IRQHandler; /* IRQ handler */
int IRQParam; /* IRQ parameter */
OPL_UPDATEHANDLER UpdateHandler;/* stream update handler */
int UpdateParam; /* stream update parameter */
uint8 type; /* chip type */
uint8 address; /* address register */
uint8 status; /* status flag */
uint8 statusmask; /* status mask */
uint8 mode; /* Reg.08 : CSM,notesel,etc. */
int clock; /* master clock (Hz) */
int rate; /* sampling rate (Hz) */
double freqbase; /* frequency base */
double TimerBase; /* Timer base time (==sampling time)*/
} FM_OPL;
#define MAX_OPL_CHIPS 2
/* sinwave entries */
#define SIN_BITS 10
#define SIN_LEN (1<<SIN_BITS)
#define SIN_MASK (SIN_LEN-1)
#define TL_RES_LEN (256) /* 8 bits addressing (real chip) */
/* TL_TAB_LEN is calculated as:
* 12 - sinus amplitude bits (Y axis)
* 2 - sinus sign bit (Y axis)
* TL_RES_LEN - sinus resolution (X axis)
*/
#define TL_TAB_LEN (12*2*TL_RES_LEN)
class OplClass: public Copl {
private:
FM_OPL *OPL_YM3812[MAX_OPL_CHIPS]; /* array of pointers to the YM3812's */
int YM3812NumChips; /* number of chips */
signed int tl_tab[TL_TAB_LEN];
/* sin waveform table in 'decibel' scale */
/* four waveforms on OPL2 type chips */
unsigned int sin_tab[SIN_LEN * 4];
/* lock level of common table */
int num_lock;
/* work table */
void *cur_chip; /* current chip point */
OPL_SLOT *SLOT7_1, *SLOT7_2, *SLOT8_1, *SLOT8_2;
signed int phase_modulation; /* phase modulation input (SLOT 2) */
signed int output[1];
uint32 LFO_AM;
int32 LFO_PM;
bool use16bit, stereo;
int oplRate;
public:
OplClass(int rate, bool bit16, bool usestereo); // rate = sample rate
~OplClass() override {
YM3812Shutdown();
}
int getRate() {
return oplRate;
}
void update(short *buf, int samples); // fill buffer
// template methods
void write(int reg, int val) override;
void init() override;
private:
int YM3812Init(int num, int clock, int rate);
void YM3812Shutdown(void);
void YM3812ResetChip(int which);
int YM3812Write(int which, int a, int v);
unsigned char YM3812Read(int which, int a);
int YM3812TimerOver(int which, int c);
void YM3812UpdateOne(int which, int16 *buffer, int length);
void YM3812SetTimerHandler(int which, OPL_TIMERHANDLER TimerHandler, int channelOffset);
void YM3812SetIRQHandler(int which, OPL_IRQHANDLER IRQHandler, int param);
void YM3812SetUpdateHandler(int which, OPL_UPDATEHANDLER UpdateHandler, int param);
int init_tables(void);
void OPLWriteReg(FM_OPL *OPL, int r, int v);
void OPLResetChip(FM_OPL *OPL);
int OPL_LockTable(void);
FM_OPL *OPLCreate(int type, int clock, int rate);
void OPL_UnLockTable(void);
void OPLDestroy(FM_OPL *OPL);
int OPLWrite(FM_OPL *OPL, int a, int v);
inline void advance_lfo(FM_OPL *OPL);
inline void advancex(FM_OPL *OPL);
inline signed int op_calc(uint32 phase, unsigned int env, signed int pm, unsigned int wave_tab);
inline signed int op_calc1(uint32 phase, unsigned int env, signed int pm, unsigned int wave_tab);
inline void OPL_CALC_CH(OPL_CH *CH);
inline void OPL_CALC_RH(OPL_CH *CH, unsigned int noise);
};
} // End of namespace Nuvie
} // End of namespace Ultima
#endif

View File

@@ -0,0 +1,34 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/nuvie/sound/adplug/opl.h"
namespace Ultima {
namespace Nuvie {
class CSilentopl : public Copl {
public:
void write(int reg, int val) override { };
void init() override { };
};
} // End of namespace Nuvie
} // End of namespace Ultima

View File

@@ -0,0 +1,629 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "ultima/nuvie/core/nuvie_defs.h"
#include "ultima/nuvie/files/u6_lzw.h"
#include "ultima/nuvie/sound/adplug/u6m.h"
namespace Ultima {
namespace Nuvie {
Cu6mPlayer::~Cu6mPlayer() {
if (song_data)
free(song_data);
}
CPlayer *Cu6mPlayer::factory(Copl *newopl) {
return new Cu6mPlayer(newopl);
}
bool Cu6mPlayer::load(const Common::Path &filename) {
uint32 decompressed_filesize;
U6Lzw lzw;
song_data = lzw.decompress_file(filename, decompressed_filesize);
rewind(0);
return true;
}
bool Cu6mPlayer::update() {
if (!driver_active) {
driver_active = true;
dec_clip(read_delay);
if (read_delay == 0) {
command_loop();
}
// on all Adlib channels: freq slide/vibrato, mute factor slide
for (int i = 0; i < 9; i++) {
if (channel_freq_signed_delta[i] != 0)
// frequency slide + mute factor slide
{
// freq slide
freq_slide(i);
// mute factor slide
if (carrier_mf_signed_delta[i] != 0) {
mf_slide(i);
}
} else
// vibrato + mute factor slide
{
// vibrato
if ((vb_multiplier[i] != 0) && ((channel_freq[i].hi & 0x20) == 0x20)) {
vibrato(i);
}
// mute factor slide
if (carrier_mf_signed_delta[i] != 0) {
mf_slide(i);
}
}
}
driver_active = false;
}
return !songend;
}
void Cu6mPlayer::rewind(int subsong) {
played_ticks = 0;
songend = false;
// set the driver's internal variables
byte_pair freq_word = {0, 0};
driver_active = false;
song_pos = 0;
loop_position = 0; // position of the loop point
read_delay = 0; // delay (in timer ticks) before further song data is read
for (int i = 0; i < 9; i++) {
// frequency
channel_freq_signed_delta[i] = 0;
channel_freq[i] = freq_word; // Adlib freq settings for each channel
// vibrato ("vb")
vb_current_value[i] = 0;
vb_double_amplitude[i] = 0;
vb_multiplier[i] = 0;
vb_direction_flag[i] = 0;
// mute factor ("mf") == ~(volume)
carrier_mf[i] = 0;
carrier_mf_signed_delta[i] = 0;
carrier_mf_mod_delay_backup[i] = 0;
carrier_mf_mod_delay[i] = 0;
}
while (!subsong_stack.empty()) // empty subsong stack
subsong_stack.pop();
opl->init();
out_adlib(1, 32); // go to OPL2 mode
}
float Cu6mPlayer::getrefresh() {
return ((float)60); // the Ultima 6 music driver expects to be called at 60 Hz
}
// ============================================================================================
//
//
// Functions called by update()
//
//
// ============================================================================================
// This function reads the song data and executes the embedded commands.
void Cu6mPlayer::command_loop() {
unsigned char command_byte; // current command byte
int command_nibble_hi; // command byte, bits 4-7
int command_nibble_lo; // command byte, bite 0-3
bool repeat_loop = true; //
do {
// extract low and high command nibbles
command_byte = read_song_byte(); // implicitly increments song_pos
command_nibble_hi = command_byte >> 4;
command_nibble_lo = command_byte & 0xf;
switch (command_nibble_hi) {
case 0x0:
command_0(command_nibble_lo);
break;
case 0x1:
command_1(command_nibble_lo);
break;
case 0x2:
command_2(command_nibble_lo);
break;
case 0x3:
command_3(command_nibble_lo);
break;
case 0x4:
command_4(command_nibble_lo);
break;
case 0x5:
command_5(command_nibble_lo);
break;
case 0x6:
command_6(command_nibble_lo);
break;
case 0x7:
command_7(command_nibble_lo);
break;
case 0x8:
switch (command_nibble_lo) {
case 1:
command_81();
break;
case 2:
command_82();
repeat_loop = false;
break;
case 3:
command_83();
break;
case 5:
command_85();
break;
case 6:
command_86();
break;
default:
break; // maybe generate an error?
}
break;
case 0xE:
command_E();
break;
case 0xF:
command_F();
break;
default:
break; // maybe generate an error?
}
} while (repeat_loop);
}
// --------------------------------------------------------
// The commands supported by the U6 music file format
// --------------------------------------------------------
// ----------------------------------------
// Set octave and frequency, note off
// Format: 0c nn
// c = channel, nn = packed Adlib frequency
// ----------------------------------------
void Cu6mPlayer::command_0(int channel) {
unsigned char freq_byte;
byte_pair freq_word;
freq_byte = read_song_byte();
freq_word = expand_freq_byte(freq_byte);
set_adlib_freq(channel, freq_word);
}
// ---------------------------------------------------
// Set octave and frequency, old note off, new note on
// Format: 1c nn
// c = channel, nn = packed Adlib frequency
// ---------------------------------------------------
void Cu6mPlayer::command_1(int channel) {
unsigned char freq_byte;
byte_pair freq_word;
vb_direction_flag[channel] = 0;
vb_current_value[channel] = 0;
freq_byte = read_song_byte();
freq_word = expand_freq_byte(freq_byte);
set_adlib_freq(channel, freq_word);
freq_word.hi = freq_word.hi | 0x20; // note on
set_adlib_freq(channel, freq_word);
}
// ----------------------------------------
// Set octave and frequency, note on
// Format: 2c nn
// c = channel, nn = packed Adlib frequency
// ----------------------------------------
void Cu6mPlayer::command_2(int channel) {
unsigned char freq_byte;
byte_pair freq_word;
freq_byte = read_song_byte();
freq_word = expand_freq_byte(freq_byte);
freq_word.hi = freq_word.hi | 0x20; // note on
set_adlib_freq(channel, freq_word);
}
// --------------------------------------
// Set "carrier mute factor"==not(volume)
// Format: 3c nn
// c = channel, nn = mute factor
// --------------------------------------
void Cu6mPlayer::command_3(int channel) {
unsigned char mf_byte;
carrier_mf_signed_delta[channel] = 0;
mf_byte = read_song_byte();
set_carrier_mf(channel, mf_byte);
}
// ----------------------------------------
// set "modulator mute factor"==not(volume)
// Format: 4c nn
// c = channel, nn = mute factor
// ----------------------------------------
void Cu6mPlayer::command_4(int channel) {
unsigned char mf_byte;
mf_byte = read_song_byte();
set_modulator_mf(channel, mf_byte);
}
// --------------------------------------------
// Set portamento (pitch slide)
// Format: 5c nn
// c = channel, nn = signed channel pitch delta
// --------------------------------------------
void Cu6mPlayer::command_5(int channel) {
channel_freq_signed_delta[channel] = read_signed_song_byte();
}
// --------------------------------------------
// Set vibrato parameters
// Format: 6c mn
// c = channel
// m = vibrato double amplitude
// n = vibrato multiplier
// --------------------------------------------
void Cu6mPlayer::command_6(int channel) {
unsigned char vb_parameters;
vb_parameters = read_song_byte();
vb_double_amplitude[channel] = vb_parameters >> 4; // high nibble
vb_multiplier[channel] = vb_parameters & 0xF; // low nibble
}
// ----------------------------------------
// Assign Adlib instrument to Adlib channel
// Format: 7c nn
// c = channel, nn = instrument number
// ----------------------------------------
void Cu6mPlayer::command_7(int channel) {
int instrument_offset = instrument_offsets[read_song_byte()];
out_adlib_opcell(channel, false, 0x20, *(song_data + instrument_offset + 0));
out_adlib_opcell(channel, false, 0x40, *(song_data + instrument_offset + 1));
out_adlib_opcell(channel, false, 0x60, *(song_data + instrument_offset + 2));
out_adlib_opcell(channel, false, 0x80, *(song_data + instrument_offset + 3));
out_adlib_opcell(channel, false, 0xE0, *(song_data + instrument_offset + 4));
out_adlib_opcell(channel, true, 0x20, *(song_data + instrument_offset + 5));
out_adlib_opcell(channel, true, 0x40, *(song_data + instrument_offset + 6));
out_adlib_opcell(channel, true, 0x60, *(song_data + instrument_offset + 7));
out_adlib_opcell(channel, true, 0x80, *(song_data + instrument_offset + 8));
out_adlib_opcell(channel, true, 0xE0, *(song_data + instrument_offset + 9));
out_adlib(0xC0 + channel, *(song_data + instrument_offset + 10));
}
// -------------------------------------------
// Branch to a new subsong
// Format: 81 nn aa bb
// nn == number of times to repeat the subsong
// aa == subsong offset (low byte)
// bb == subsong offset (high byte)
// -------------------------------------------
void Cu6mPlayer::command_81() {
subsong_info new_ss_info;
new_ss_info.subsong_repetitions = read_song_byte();
new_ss_info.subsong_start = read_song_byte();
new_ss_info.subsong_start += read_song_byte() << 8;
new_ss_info.continue_pos = song_pos;
subsong_stack.push(new_ss_info);
song_pos = new_ss_info.subsong_start;
}
// ------------------------------------------------------------
// Stop interpreting commands for this timer tick
// Format: 82 nn
// nn == delay (in timer ticks) until further data will be read
// ------------------------------------------------------------
void Cu6mPlayer::command_82() {
read_delay = read_song_byte();
}
// -----------------------------
// Adlib instrument data follows
// Format: 83 nn <11 bytes>
// nn == instrument number
// -----------------------------
void Cu6mPlayer::command_83() {
unsigned char instrument_number = read_song_byte();
instrument_offsets[instrument_number] = song_pos;
song_pos += 11;
}
// ----------------------------------------------
// Set -1 mute factor slide (upward volume slide)
// Format: 85 cn
// c == channel
// n == slide delay
// ----------------------------------------------
void Cu6mPlayer::command_85() {
unsigned char data_byte = read_song_byte();
int channel = data_byte >> 4; // high nibble
unsigned char slide_delay = data_byte & 0xF; // low nibble
carrier_mf_signed_delta[channel] = +1;
carrier_mf_mod_delay[channel] = slide_delay + 1;
carrier_mf_mod_delay_backup[channel] = slide_delay + 1;
}
// ------------------------------------------------
// Set +1 mute factor slide (downward volume slide)
// Format: 86 cn
// c == channel
// n == slide speed
// ------------------------------------------------
void Cu6mPlayer::command_86() {
unsigned char data_byte = read_song_byte();
int channel = data_byte >> 4; // high nibble
unsigned char slide_delay = data_byte & 0xF; // low nibble
carrier_mf_signed_delta[channel] = -1;
carrier_mf_mod_delay[channel] = slide_delay + 1;
carrier_mf_mod_delay_backup[channel] = slide_delay + 1;
}
// --------------
// Set loop point
// Format: E?
// --------------
void Cu6mPlayer::command_E() {
loop_position = song_pos;
}
// ---------------------------
// Return from current subsong
// Format: F?
// ---------------------------
void Cu6mPlayer::command_F() {
if (!subsong_stack.empty()) {
subsong_info temp = subsong_stack.top();
subsong_stack.pop();
temp.subsong_repetitions--;
if (temp.subsong_repetitions == 0) {
song_pos = temp.continue_pos;
} else {
song_pos = temp.subsong_start;
subsong_stack.push(temp);
}
} else {
song_pos = loop_position;
songend = true;
}
}
// --------------------
// Additional functions
// --------------------
// This function decrements its argument, without allowing it to become negative.
void Cu6mPlayer::dec_clip(int &param) {
param--;
if (param < 0) {
param = 0;
}
}
// Returns the byte at the current song position.
// Side effect: increments song_pos.
unsigned char Cu6mPlayer::read_song_byte() {
unsigned char song_byte;
song_byte = song_data[song_pos];
song_pos++;
return song_byte;
}
// Same as read_song_byte(), except that it returns a signed byte
signed char Cu6mPlayer::read_signed_song_byte() {
unsigned char song_byte;
int signed_value;
song_byte = *(song_data + song_pos);
song_pos++;
if (song_byte <= 127) {
signed_value = song_byte;
} else {
signed_value = (int)song_byte - 0x100;
}
return ((signed char)signed_value);
}
Cu6mPlayer::byte_pair Cu6mPlayer::expand_freq_byte(unsigned char freq_byte) {
const byte_pair freq_table[24] = {
{0x00, 0x00}, {0x58, 0x01}, {0x82, 0x01}, {0xB0, 0x01},
{0xCC, 0x01}, {0x03, 0x02}, {0x41, 0x02}, {0x86, 0x02},
{0x00, 0x00}, {0x6A, 0x01}, {0x96, 0x01}, {0xC7, 0x01},
{0xE4, 0x01}, {0x1E, 0x02}, {0x5F, 0x02}, {0xA8, 0x02},
{0x00, 0x00}, {0x47, 0x01}, {0x6E, 0x01}, {0x9A, 0x01},
{0xB5, 0x01}, {0xE9, 0x01}, {0x24, 0x02}, {0x66, 0x02}
};
int packed_freq;
int octave;
byte_pair freq_word;
packed_freq = freq_byte & 0x1F;
octave = freq_byte >> 5;
// range check (not present in the original U6 music driver)
if (packed_freq >= 24) {
packed_freq = 0;
}
freq_word.hi = freq_table[packed_freq].hi + (octave << 2);
freq_word.lo = freq_table[packed_freq].lo;
return freq_word;
}
void Cu6mPlayer::set_adlib_freq(int channel, Cu6mPlayer::byte_pair freq_word) {
out_adlib(0xA0 + channel, freq_word.lo);
out_adlib(0xB0 + channel, freq_word.hi);
// update the Adlib register backups
channel_freq[channel] = freq_word;
}
// this function sets the Adlib frequency, but does not update the register backups
void Cu6mPlayer::set_adlib_freq_no_update(int channel, Cu6mPlayer::byte_pair freq_word) {
out_adlib(0xA0 + channel, freq_word.lo);
out_adlib(0xB0 + channel, freq_word.hi);
}
void Cu6mPlayer::set_carrier_mf(int channel, unsigned char mute_factor) {
out_adlib_opcell(channel, true, 0x40, mute_factor);
carrier_mf[channel] = mute_factor;
}
void Cu6mPlayer::set_modulator_mf(int channel, unsigned char mute_factor) {
out_adlib_opcell(channel, false, 0x40, mute_factor);
}
void Cu6mPlayer::freq_slide(int channel) {
byte_pair freq = channel_freq[channel];
long freq_word = freq.lo + (freq.hi << 8) + channel_freq_signed_delta[channel];
if (freq_word < 0) {
freq_word += 0x10000;
}
if (freq_word > 0xFFFF) {
freq_word -= 0x10000;
}
freq.lo = freq_word & 0xFF;
freq.hi = (freq_word >> 8) & 0xFF;
set_adlib_freq(channel, freq);
}
void Cu6mPlayer::vibrato(int channel) {
byte_pair freq;
if (vb_current_value[channel] >= vb_double_amplitude[channel]) {
vb_direction_flag[channel] = 1;
} else if (vb_current_value[channel] <= 0) {
vb_direction_flag[channel] = 0;
}
if (vb_direction_flag[channel] == 0) {
vb_current_value[channel]++;
} else {
vb_current_value[channel]--;
}
long freq_word = channel_freq[channel].lo + (channel_freq[channel].hi << 8);
freq_word += (vb_current_value[channel] - (vb_double_amplitude[channel] >> 1))
* vb_multiplier[channel];
if (freq_word < 0) {
freq_word += 0x10000;
}
if (freq_word > 0xFFFF) {
freq_word -= 0x10000;
}
freq.lo = freq_word & 0xFF;
freq.hi = (freq_word >> 8) & 0xFF;
set_adlib_freq_no_update(channel, freq);
}
void Cu6mPlayer::mf_slide(int channel) {
carrier_mf_mod_delay[channel]--;
if (carrier_mf_mod_delay[channel] == 0) {
carrier_mf_mod_delay[channel] = carrier_mf_mod_delay_backup[channel];
int current_mf = carrier_mf[channel] + carrier_mf_signed_delta[channel];
if (current_mf > 0x3F) {
current_mf = 0x3F;
carrier_mf_signed_delta[channel] = 0;
} else if (current_mf < 0) {
current_mf = 0;
carrier_mf_signed_delta[channel] = 0;
}
set_carrier_mf(channel, (unsigned char)current_mf);
}
}
void Cu6mPlayer::out_adlib(unsigned char adlib_register, unsigned char adlib_data) {
opl->write(adlib_register, adlib_data);
}
void Cu6mPlayer::out_adlib_opcell(int channel, bool carrier, unsigned char adlib_register, unsigned char out_byte) {
const unsigned char adlib_channel_to_carrier_offset[9] =
{0x03, 0x04, 0x05, 0x0B, 0x0C, 0x0D, 0x13, 0x14, 0x15};
const unsigned char adlib_channel_to_modulator_offset[9] =
{0x00, 0x01, 0x02, 0x08, 0x09, 0x0A, 0x10, 0x11, 0x12};
if (carrier) {
out_adlib(adlib_register + adlib_channel_to_carrier_offset[channel], out_byte);
} else {
out_adlib(adlib_register + adlib_channel_to_modulator_offset[channel], out_byte);
}
}
} // End of namespace Nuvie
} // End of namespace Ultima

View File

@@ -0,0 +1,137 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef NUVIE_SOUND_ADPLUG_U6M
#define NUVIE_SOUND_ADPLUG_U6M
#include "ultima/nuvie/sound/adplug/adplug_player.h"
#include "common/stack.h"
namespace Ultima {
namespace Nuvie {
class Cu6mPlayer: public CPlayer {
public:
static CPlayer *factory(Copl *newopl);
Cu6mPlayer(Copl *newopl) : CPlayer(newopl), song_data(0), driver_active(false),
songend(false), song_pos(0), loop_position(0), read_delay(0), played_ticks(0) {
ARRAYCLEAR(channel_freq);
}
~Cu6mPlayer() override;
bool load(const Common::Path &filename) override;
bool update() override;
void rewind(int subsong) override;
float getrefresh() override;
Std::string gettype() override {
return Std::string("Ultima 6 Music");
};
protected:
struct byte_pair {
unsigned char lo;
unsigned char hi;
};
struct subsong_info { // information about a subsong
int continue_pos;
int subsong_repetitions;
int subsong_start;
};
struct data_block { //
long size;
unsigned char *data;
};
// class variables
long played_ticks;
unsigned char *song_data; // the uncompressed .m file (the "song")
bool driver_active; // flag to prevent reentrancy
bool songend; // indicates song end
int song_pos; // current offset within the song
int loop_position; // position of the loop point
int read_delay; // delay (in timer ticks) before further song data is read
Common::Stack<subsong_info> subsong_stack;
int instrument_offsets[9]; // offsets of the adlib instrument data
// vibrato ("vb")
unsigned char vb_current_value[9];
unsigned char vb_double_amplitude[9];
unsigned char vb_multiplier[9];
unsigned char vb_direction_flag[9];
// mute factor ("mf") = not(volume)
unsigned char carrier_mf[9];
signed char carrier_mf_signed_delta[9];
unsigned char carrier_mf_mod_delay_backup[9];
unsigned char carrier_mf_mod_delay[9];
// frequency
byte_pair channel_freq[9]; // adlib freq settings for each channel
signed char channel_freq_signed_delta[9];
// protected functions used by update()
void command_loop();
unsigned char read_song_byte();
signed char read_signed_song_byte();
void dec_clip(int &);
byte_pair expand_freq_byte(unsigned char);
void set_adlib_freq(int channel, byte_pair freq_word);
void set_adlib_freq_no_update(int channel, byte_pair freq_word);
void set_carrier_mf(int channel, unsigned char mute_factor);
void set_modulator_mf(int channel, unsigned char mute_factor);
void freq_slide(int channel);
void vibrato(int channel);
void mf_slide(int channel);
void command_0(int channel);
void command_1(int channel);
void command_2(int channel);
void command_3(int channel);
void command_4(int channel);
void command_5(int channel);
void command_6(int channel);
void command_7(int channel);
void command_81();
void command_82();
void command_83();
void command_85();
void command_86();
void command_E();
void command_F();
void out_adlib(unsigned char adlib_register, unsigned char adlib_data);
void out_adlib_opcell(int channel, bool carrier, unsigned char adlib_register, unsigned char out_byte);
};
} // End of namespace Nuvie
} // End of namespace Ultima
#endif