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