mirror of
https://github.com/scummvm/scummvm.git
synced 2026-06-20 05:45:29 +00:00
295 lines
7.2 KiB
C++
295 lines
7.2 KiB
C++
/* 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 "audio/softsynth/ay8912.h"
|
|
#include "common/util.h"
|
|
|
|
namespace Audio {
|
|
|
|
bool AY8912Stream::_envGenInit = false;
|
|
int AY8912Stream::_envelope[16][128];
|
|
|
|
/* AY volume table (c) by V_Soft and Lion 17 */
|
|
static int Lion17_AY_table[32] = {
|
|
0, 513, 828, 1239, 1923, 3238, 4926, 9110,
|
|
10344, 17876, 24682, 30442, 38844, 47270, 56402, 65535,
|
|
// Duplicate for safety as the C code used 32 size array but init with 16 elements and logic uses /2
|
|
0, 513, 828, 1239, 1923, 3238, 4926, 9110,
|
|
10344, 17876, 24682, 30442, 38844, 47270, 56402, 65535
|
|
};
|
|
|
|
/* default equlaizer (layout) settings for AY, ABC stereo */
|
|
static const int default_layout_ay_abc[6] = {
|
|
100, 33, 70, 70, 33, 100
|
|
};
|
|
|
|
AY8912Stream::AY8912Stream(int rate, int chipFreq) : _rate(rate), _chipFreq(chipFreq) {
|
|
if (!_envGenInit)
|
|
genEnv();
|
|
|
|
// Reset state
|
|
_bit_a = _bit_b = _bit_c = _bit_n = 0;
|
|
_cnt_a = _cnt_b = _cnt_c = _cnt_n = _cnt_e = 0;
|
|
_envPos = 0;
|
|
_curSeed = 0xffff;
|
|
|
|
// Reset registers
|
|
_regs.tone_a = _regs.tone_b = _regs.tone_c = 0;
|
|
_regs.noise = 0;
|
|
_regs.R7_tone_a = _regs.R7_tone_b = _regs.R7_tone_c = 0;
|
|
_regs.R7_noise_a = _regs.R7_noise_b = _regs.R7_noise_c = 0;
|
|
_regs.vol_a = _regs.vol_b = _regs.vol_c = 0;
|
|
_regs.env_a = _regs.env_b = _regs.env_c = 0;
|
|
_regs.env_freq = _regs.env_style = 0;
|
|
|
|
// Initialize table and eq
|
|
for (int i = 0; i < 32; i++)
|
|
_table[i] = Lion17_AY_table[i/2]; // AYEMU_AY style
|
|
|
|
for (int i = 0; i < 6; i++)
|
|
_eq[i] = default_layout_ay_abc[i]; // AYEMU_ABC style
|
|
|
|
prepareGeneration();
|
|
}
|
|
|
|
AY8912Stream::~AY8912Stream() {
|
|
}
|
|
|
|
void AY8912Stream::genEnv() {
|
|
int env;
|
|
int pos;
|
|
int hold;
|
|
int dir;
|
|
int vol;
|
|
|
|
for (env = 0; env < 16; env++) {
|
|
hold = 0;
|
|
dir = (env & 4) ? 1 : -1;
|
|
vol = (env & 4) ? -1 : 32;
|
|
for (pos = 0; pos < 128; pos++) {
|
|
if (!hold) {
|
|
vol += dir;
|
|
if (vol < 0 || vol >= 32) {
|
|
if (env & 8) {
|
|
if (env & 2)
|
|
dir = -dir;
|
|
vol = (dir > 0) ? 0 : 31;
|
|
if (env & 1) {
|
|
hold = 1;
|
|
vol = (dir > 0) ? 31 : 0;
|
|
}
|
|
} else {
|
|
vol = 0;
|
|
hold = 1;
|
|
}
|
|
}
|
|
}
|
|
_envelope[env][pos] = vol;
|
|
}
|
|
}
|
|
_envGenInit = true;
|
|
}
|
|
|
|
void AY8912Stream::prepareGeneration() {
|
|
int vol, max_l, max_r;
|
|
|
|
_chipTactsPerOutcount = _chipFreq / _rate / 8;
|
|
|
|
// GenVols
|
|
for (int n = 0; n < 32; n++) {
|
|
vol = _table[n];
|
|
for (int m = 0; m < 6; m++)
|
|
_vols[m][n] = (int)(((double)vol * _eq[m]) / 100);
|
|
}
|
|
|
|
max_l = _vols[0][31] + _vols[2][31] + _vols[3][31];
|
|
max_r = _vols[1][31] + _vols[3][31] + _vols[5][31];
|
|
vol = (max_l > max_r) ? max_l : max_r;
|
|
_ampGlobal = _chipTactsPerOutcount * vol / 24575; // AYEMU_MAX_AMP
|
|
}
|
|
|
|
void AY8912Stream::setRegs(const unsigned char *regs) {
|
|
Common::StackLock lock(_mutex);
|
|
|
|
_regs.tone_a = regs[0] + ((regs[1] & 0x0f) << 8);
|
|
_regs.tone_b = regs[2] + ((regs[3] & 0x0f) << 8);
|
|
_regs.tone_c = regs[4] + ((regs[5] & 0x0f) << 8);
|
|
|
|
_regs.noise = regs[6] & 0x1f;
|
|
|
|
_regs.R7_tone_a = !(regs[7] & 0x01);
|
|
_regs.R7_tone_b = !(regs[7] & 0x02);
|
|
_regs.R7_tone_c = !(regs[7] & 0x04);
|
|
|
|
_regs.R7_noise_a = !(regs[7] & 0x08);
|
|
_regs.R7_noise_b = !(regs[7] & 0x10);
|
|
_regs.R7_noise_c = !(regs[7] & 0x20);
|
|
|
|
_regs.vol_a = regs[8] & 0x0f;
|
|
_regs.vol_b = regs[9] & 0x0f;
|
|
_regs.vol_c = regs[10] & 0x0f;
|
|
_regs.env_a = regs[8] & 0x10;
|
|
_regs.env_b = regs[9] & 0x10;
|
|
_regs.env_c = regs[10] & 0x10;
|
|
_regs.env_freq = regs[11] + (regs[12] << 8);
|
|
|
|
if (regs[13] != 0xff) {
|
|
_regs.env_style = regs[13] & 0x0f;
|
|
_envPos = _cnt_e = 0;
|
|
}
|
|
}
|
|
|
|
void AY8912Stream::setReg(int reg, unsigned char value) {
|
|
Common::StackLock lock(_mutex);
|
|
|
|
switch (reg) {
|
|
case 0:
|
|
_regs.tone_a = (_regs.tone_a & 0x0f00) | value;
|
|
break;
|
|
case 1:
|
|
_regs.tone_a = (_regs.tone_a & 0x00ff) | ((value & 0x0f) << 8);
|
|
break;
|
|
case 2:
|
|
_regs.tone_b = (_regs.tone_b & 0x0f00) | value;
|
|
break;
|
|
case 3:
|
|
_regs.tone_b = (_regs.tone_b & 0x00ff) | ((value & 0x0f) << 8);
|
|
break;
|
|
case 4:
|
|
_regs.tone_c = (_regs.tone_c & 0x0f00) | value;
|
|
break;
|
|
case 5:
|
|
_regs.tone_c = (_regs.tone_c & 0x00ff) | ((value & 0x0f) << 8);
|
|
break;
|
|
case 6:
|
|
_regs.noise = value & 0x1f;
|
|
break;
|
|
case 7:
|
|
_regs.R7_tone_a = !(value & 0x01);
|
|
_regs.R7_tone_b = !(value & 0x02);
|
|
_regs.R7_tone_c = !(value & 0x04);
|
|
|
|
_regs.R7_noise_a = !(value & 0x08);
|
|
_regs.R7_noise_b = !(value & 0x10);
|
|
_regs.R7_noise_c = !(value & 0x20);
|
|
break;
|
|
case 8:
|
|
_regs.vol_a = value & 0x0f;
|
|
_regs.env_a = value & 0x10;
|
|
break;
|
|
case 9:
|
|
_regs.vol_b = value & 0x0f;
|
|
_regs.env_b = value & 0x10;
|
|
break;
|
|
case 10:
|
|
_regs.vol_c = value & 0x0f;
|
|
_regs.env_c = value & 0x10;
|
|
break;
|
|
case 11:
|
|
_regs.env_freq = (_regs.env_freq & 0xff00) | value;
|
|
break;
|
|
case 12:
|
|
_regs.env_freq = (_regs.env_freq & 0x00ff) | (value << 8);
|
|
break;
|
|
case 13:
|
|
_regs.env_style = value & 0x0f;
|
|
_envPos = _cnt_e = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
int AY8912Stream::readBuffer(int16 *buffer, const int numSamples) {
|
|
generateSamples(buffer, numSamples);
|
|
return numSamples;
|
|
}
|
|
|
|
void AY8912Stream::generateSamples(int16 *buffer, int numSamples) {
|
|
Common::StackLock lock(_mutex);
|
|
|
|
int mix_l, mix_r;
|
|
int tmpvol;
|
|
int m;
|
|
int frame_count = numSamples / 2; // Stereo samples
|
|
|
|
int16 *bufPtr = buffer;
|
|
|
|
while (frame_count-- > 0) {
|
|
mix_l = mix_r = 0;
|
|
|
|
for (m = 0; m < _chipTactsPerOutcount; m++) {
|
|
if (++_cnt_a >= _regs.tone_a) {
|
|
_cnt_a = 0;
|
|
_bit_a = !_bit_a;
|
|
}
|
|
if (++_cnt_b >= _regs.tone_b) {
|
|
_cnt_b = 0;
|
|
_bit_b = !_bit_b;
|
|
}
|
|
if (++_cnt_c >= _regs.tone_c) {
|
|
_cnt_c = 0;
|
|
_bit_c = !_bit_c;
|
|
}
|
|
|
|
if (++_cnt_n >= (_regs.noise * 2)) {
|
|
_cnt_n = 0;
|
|
_curSeed = (_curSeed * 2 + 1) ^ (((_curSeed >> 16) ^ (_curSeed >> 13)) & 1);
|
|
_bit_n = ((_curSeed >> 16) & 1);
|
|
}
|
|
|
|
if (++_cnt_e >= _regs.env_freq) {
|
|
_cnt_e = 0;
|
|
if (++_envPos > 127)
|
|
_envPos = 64;
|
|
}
|
|
|
|
int envVol = _envelope[_regs.env_style][_envPos];
|
|
|
|
if ((_bit_a | !_regs.R7_tone_a) & (_bit_n | !_regs.R7_noise_a)) {
|
|
tmpvol = (_regs.env_a) ? envVol : _regs.vol_a * 2 + 1;
|
|
mix_l += _vols[0][tmpvol];
|
|
mix_r += _vols[1][tmpvol];
|
|
}
|
|
|
|
if ((_bit_b | !_regs.R7_tone_b) & (_bit_n | !_regs.R7_noise_b)) {
|
|
tmpvol = (_regs.env_b) ? envVol : _regs.vol_b * 2 + 1;
|
|
mix_l += _vols[2][tmpvol];
|
|
mix_r += _vols[3][tmpvol];
|
|
}
|
|
|
|
if ((_bit_c | !_regs.R7_tone_c) & (_bit_n | !_regs.R7_noise_c)) {
|
|
tmpvol = (_regs.env_c) ? envVol : _regs.vol_c * 2 + 1;
|
|
mix_l += _vols[4][tmpvol];
|
|
mix_r += _vols[5][tmpvol];
|
|
}
|
|
}
|
|
|
|
if (_ampGlobal > 0) {
|
|
mix_l /= _ampGlobal;
|
|
mix_r /= _ampGlobal;
|
|
}
|
|
|
|
*bufPtr++ = mix_l;
|
|
*bufPtr++ = mix_r;
|
|
}
|
|
}
|
|
|
|
} // End of namespace Audio
|