Initial commit
This commit is contained in:
71
engines/ultima/nuvie/sound/adplug/adplug_player.cpp
Normal file
71
engines/ultima/nuvie/sound/adplug/adplug_player.cpp
Normal 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
|
||||
99
engines/ultima/nuvie/sound/adplug/adplug_player.h
Normal file
99
engines/ultima/nuvie/sound/adplug/adplug_player.h
Normal 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
|
||||
76
engines/ultima/nuvie/sound/adplug/emu_opl.cpp
Normal file
76
engines/ultima/nuvie/sound/adplug/emu_opl.cpp
Normal 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
|
||||
54
engines/ultima/nuvie/sound/adplug/emu_opl.h
Normal file
54
engines/ultima/nuvie/sound/adplug/emu_opl.h
Normal 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
|
||||
2401
engines/ultima/nuvie/sound/adplug/fm_opl.cpp
Normal file
2401
engines/ultima/nuvie/sound/adplug/fm_opl.cpp
Normal file
File diff suppressed because it is too large
Load Diff
137
engines/ultima/nuvie/sound/adplug/fm_opl.h
Normal file
137
engines/ultima/nuvie/sound/adplug/fm_opl.h
Normal 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
|
||||
572
engines/ultima/nuvie/sound/adplug/mid.cpp
Normal file
572
engines/ultima/nuvie/sound/adplug/mid.cpp
Normal 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
|
||||
115
engines/ultima/nuvie/sound/adplug/mid.h
Normal file
115
engines/ultima/nuvie/sound/adplug/mid.h
Normal 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
|
||||
42
engines/ultima/nuvie/sound/adplug/opl.h
Normal file
42
engines/ultima/nuvie/sound/adplug/opl.h
Normal 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
|
||||
1630
engines/ultima/nuvie/sound/adplug/opl_class.cpp
Normal file
1630
engines/ultima/nuvie/sound/adplug/opl_class.cpp
Normal file
File diff suppressed because it is too large
Load Diff
253
engines/ultima/nuvie/sound/adplug/opl_class.h
Normal file
253
engines/ultima/nuvie/sound/adplug/opl_class.h
Normal 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
|
||||
34
engines/ultima/nuvie/sound/adplug/silent_opl.h
Normal file
34
engines/ultima/nuvie/sound/adplug/silent_opl.h
Normal 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
|
||||
629
engines/ultima/nuvie/sound/adplug/u6m.cpp
Normal file
629
engines/ultima/nuvie/sound/adplug/u6m.cpp
Normal 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 ¶m) {
|
||||
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
|
||||
137
engines/ultima/nuvie/sound/adplug/u6m.h
Normal file
137
engines/ultima/nuvie/sound/adplug/u6m.h
Normal 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
|
||||
Reference in New Issue
Block a user