Initial commit
This commit is contained in:
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
|
||||
Reference in New Issue
Block a user