ChonkyStation/spu.cpp
2023-07-15 16:19:55 +02:00

317 lines
No EOL
9.4 KiB
C++

#include "spu.h"
std::ofstream file;
spu::spu() {
for (int i = 0; i < 24; i++) {
voices[i].ram = spu_ram;
}
file.open("sample", std::ios::binary);
}
void voice::on() {
current_addr = adpcm_start * 8;
adpcm_repeat = current_addr;
enabled = true;
}
void voice::off() {
// release
enabled = false;
}
auto voice::step() {
if (!enabled) return std::make_pair<std::optional<int16_t>, bool>(std::nullopt, false);
bool endx = false;
if (samples.empty()) {
pitch_counter = 0;
printf("Decoding samples at 0x%08x\n", current_addr);
decode_samples(&ram[current_addr]);
uint8_t flags = ram[current_addr + 1];
const bool end = flags & 1;
const bool repeat = (flags >> 1) & 1;
const bool start = (flags >> 2) & 1;
/* if (!start && !end && !repeat) {
current_addr += 16;
}
else if (!end && !repeat) {
current_addr += 16;
adpcm_repeat = current_addr;
}
else if (end) {
current_addr = adpcm_repeat;
if (!repeat) {
// release here
adsr_vol = 0;
}
endx = true;
}*/
if (start) adpcm_repeat = current_addr;
current_addr += 16;
if (end) {
endx = true;
enabled = false;
if (repeat) {
current_addr = adpcm_repeat;
}
else {
// release here
adsr_vol = 0;
}
}
}
std::optional<int16_t> sample = samples.front();
// Pitch counter
auto old = pitch_counter >> 12;
if (pitch > 0x3fff) pitch = 0x4000;
pitch_counter += pitch;
if ((pitch_counter >> 12) > old)
samples.pop();
return std::make_pair(sample, endx);
}
void voice::decode_samples(uint8_t* sample_start) {
const auto shift = 12 - ((sample_start[0] & 0xf) > 12 ? 9 : (sample_start[0] & 0xf));
const auto filter = (((sample_start[0]) >> 4) & 3);
const auto f0 = pos_xa_adpcm_table[filter];
const auto f1 = neg_xa_adpcm_table[filter];
int8_t t = 0;
int16_t s = 0;
int sample_count = 0;
for (int i = 2; i < 16; i++) {
uint8_t byte = sample_start[i];
t = byte & 0xf;
t = (t << 4) >> 4;
s = minmax((t << shift) + ((old * f0 + older * f1 + 32) / 64), -0x8000, 0x7fff);
samples.push(s);
older = old;
old = s;
t = ((byte >> 4) & 0xf);
t = (t << 4) >> 4;
s = minmax((t << shift) + ((old * f0 + older * f1 + 32) / 64), -0x8000, 0x7fff);
samples.push(s);
older = old;
old = s;
}
}
void spu::write(uint32_t addr, uint32_t data) {
if (addr >= 0x1f801c00 && (addr <= 0x1f801d7f)) { // Voice regs
int voice = (addr - 0x1f801c00) >> 4;
switch (addr & 0xf) {
case 0x0: voices[voice].volume_left = data; break;
case 0x2: voices[voice].volume_right = data; break;
case 0x4: voices[voice].pitch = data; break;
case 0x6: voices[voice].adpcm_start = data; break;
case 0x8: WRITE_LOWER(voices[voice].adsr, data); break;
case 0xa: WRITE_UPPER(voices[voice].adsr, data); break;
default:
printf("Unhandled voice register write 0x%08x\n", addr);
//exit(0);
}
}
else if (addr == 0x1f801d80) mainvolume_left = data;
else if (addr == 0x1f801d82) mainvolume_right = data;
else if (addr == 0x1f801d84) reverb_regs.vLOUT = data;
else if (addr == 0x1f801d86) reverb_regs.rLOUT = data;
else if (addr == 0x1f801d88) {
WRITE_LOWER(key_on, data);
for (int i = 0; i < 16; i++) {
if ((key_on >> i) & 1) voices[i].on();
}
}
else if (addr == 0x1f801d8a) {
WRITE_UPPER(key_on, data);
for (int i = 16; i < 24; i++) {
if ((key_on >> i) & 1) voices[i].on();
}
}
else if (addr == 0x1f801d8c) {
WRITE_LOWER(key_off, data);
for (int i = 0; i < 16; i++) {
if ((key_off >> i) & 1) voices[i].off();
}
}
else if (addr == 0x1f801d8e) {
WRITE_UPPER(key_off, data);
for (int i = 16; i < 24; i++) {
if ((key_off >> i) & 1) voices[i].off();
}
}
else if (addr == 0x1f801d90) WRITE_LOWER(pmod, data)
else if (addr == 0x1f801d92) WRITE_UPPER(pmod, data)
else if (addr == 0x1f801d94) {
WRITE_LOWER(noise_mode, data);
for (int i = 0; i < 16; i++) {
if ((noise_mode >> i) & 1) printf("Voice %d noise mode\n", i);
else printf("Voice %d ADPCM mode\n", i);
}
}
else if (addr == 0x1f801d96) {
WRITE_UPPER(noise_mode, data);
for (int i = 16; i < 24; i++) {
if ((noise_mode >> i) & 1) printf("Voice %d noise mode\n", i);
else printf("Voice %d ADPCM mode\n", i);
}
}
else if (addr == 0x1f801d98) {
WRITE_LOWER(echo_on, data);
for (int i = 0; i < 16; i++) {
if ((echo_on >> i) & 1) printf("Voice %d reverb on\n", i);
else printf("Voice %d reverb off\n", i);
}
}
else if (addr == 0x1f801d9a) {
WRITE_UPPER(echo_on, data);
for (int i = 16; i < 24; i++) {
if ((echo_on >> i) & 1) printf("Voice %d reverb on\n", i);
else printf("Voice %d reverb off\n", i);
}
}
else if (addr == 0x1f801da2) {
reverb_regs.mBASE = data;
}
else if (addr == 0x1f801da6) {
data_transfer_addr = data;
current_transfer_addr = data_transfer_addr * 8;
}
else if (addr == 0x1f801da8) {
//printf("SPU FIFO: 0x%04x\n", data);
transfer_fifo.push_back(data);
}
else if (addr == 0x1f801daa) {
spucnt = data;
spustat &= ~0x3f;
spustat |= spucnt & 0x3f;
spustat &= ~0x80;
spustat |= (spucnt & 0x20) << 2;
if (spucnt >> 15) {
printf("SPU enable\n");
//exit(1);
}
// Handle manual writes
if (((spucnt >> 4) & 3) == 1) {
printf("Writing samples to SPU ram\n");
for (int i = 0; i < transfer_fifo.size(); i++) {
//spu_ram[current_transfer_addr] = transfer_fifo[i] & 0xff;
//spu_ram[current_transfer_addr + 1] = (transfer_fifo[i] >> 8) & 0xff;
*((uint16_t*)&spu_ram[current_transfer_addr]) = transfer_fifo[i];
current_transfer_addr += 2;
}
transfer_fifo.clear();
}
}
else if (addr == 0x1f801db0) printf("Unhandled SPU CD Audio Input Volume write lower\n");
else if (addr == 0x1f801db2) printf("Unhandled SPU CD Audio Input Volume write upper\n");
else if (addr == 0x1f801db4) printf("Unhandled SPU External Audio Input Volume write lower\n");
else if (addr == 0x1f801db6) printf("Unhandled SPU External Audio Input Volume write upper\n");
else if (addr == 0x1f801dac) {
data_transfer_control = data;
if (data != 4) {
printf("Tried to write a value different from 4 to Sound RAM Data Transfer Control!\n");
//exit(0);
}
}
else if (addr == 0x1f801dc0) reverb_regs.dAPF1 = data;
else if (addr == 0x1f801dc2) reverb_regs.dAPF2 = data;
else if (addr == 0x1f801dc4) reverb_regs.vIIR = data;
else if (addr == 0x1f801dc6) reverb_regs.vCOMB1 = data;
else if (addr == 0x1f801dc8) reverb_regs.vCOMB2 = data;
else if (addr == 0x1f801dca) reverb_regs.vCOMB3 = data;
else if (addr == 0x1f801dcc) reverb_regs.vCOMB4 = data;
else if (addr == 0x1f801dce) reverb_regs.vWALL = data;
else if (addr == 0x1f801dd0) reverb_regs.vAPF1 = data;
else if (addr == 0x1f801dd2) reverb_regs.vAPF2 = data;
else if (addr == 0x1f801dd4) reverb_regs.mLSAME = data;
else if (addr == 0x1f801dd6) reverb_regs.mRSAME = data;
else if (addr == 0x1f801dd8) reverb_regs.mLCOMB1 = data;
else if (addr == 0x1f801dda) reverb_regs.mRCOMB1 = data;
else if (addr == 0x1f801ddc) reverb_regs.mLCOMB2 = data;
else if (addr == 0x1f801dde) reverb_regs.mRCOMB2 = data;
else if (addr == 0x1f801de0) reverb_regs.dLSAME = data;
else if (addr == 0x1f801de2) reverb_regs.dRSAME = data;
else if (addr == 0x1f801de4) reverb_regs.mLDIFF = data;
else if (addr == 0x1f801de6) reverb_regs.mRDIFF = data;
else if (addr == 0x1f801de8) reverb_regs.mLCOMB3 = data;
else if (addr == 0x1f801dea) reverb_regs.mRCOMB3 = data;
else if (addr == 0x1f801dec) reverb_regs.mLCOMB4 = data;
else if (addr == 0x1f801dee) reverb_regs.mRCOMB4 = data;
else if (addr == 0x1f801df0) reverb_regs.dLDIFF = data;
else if (addr == 0x1f801df2) reverb_regs.dRDIFF = data;
else if (addr == 0x1f801df4) reverb_regs.mLAPF1 = data;
else if (addr == 0x1f801df6) reverb_regs.mRAPF1 = data;
else if (addr == 0x1f801df8) reverb_regs.mLAPF2 = data;
else if (addr == 0x1f801dfa) reverb_regs.mRAPF2 = data;
else if (addr == 0x1f801dfc) reverb_regs.vLIN = data;
else if (addr == 0x1f801dfe) reverb_regs.vRIN = data;
else {
printf("Unhandled SPU write 0x%08x\n", addr);
//exit(1);
}
}
uint16_t spu::read(uint32_t addr) {
if (addr >= 0x1f801c00 && (addr <= 0x1f801d7f)) { // Voice regs
int voice = (addr - 0x1f801c00) >> 4;
switch (addr & 0xf) {
case 0x0: return voices[voice].volume_left;
case 0x2: return voices[voice].volume_right;
case 0x4: return voices[voice].pitch;
case 0x6: return voices[voice].adpcm_start;
case 0x8: return voices[voice].adsr;
case 0xa: return voices[voice].adsr >> 16;
case 0xc: return voices[voice].adsr_vol;
default:
printf("Unhandled voice register read 0x%08x\n", addr);
//exit(0);
}
}
else if (addr == 0x1f801d88) return key_on;
else if (addr == 0x1f801d8a) return key_on >> 16;
else if (addr == 0x1f801d8c) return key_off;
else if (addr == 0x1f801d8e) return key_off >> 16;
else if (addr == 0x1f801da6) return data_transfer_addr;
else if (addr == 0x1f801daa) return spucnt;
else if (addr == 0x1f801dac) return data_transfer_control;
else if (addr == 0x1f801dae) return spustat;
else {
printf("Unhandled SPU read 0x%08x\n", addr);
//exit(1);
}
}
void spu::step(int cycles) {
this->cycles += cycles;
if (this->cycles > SAMPLE_RATE) {
this->cycles = 0;
// Mixer
int16_t sample = 0;
/*for (int i = 0; i < 24; i++) {
if (voices[i].enabled) {
auto [temp, endx] = voices[i].step();
if (temp.has_value()) {
sample += (temp.value() * 0.5);
//mixed_samples++;
}
}
}*/
if (voices[2].enabled) sample = voices[2].step().first.value();
//sample = minmax(sample, INT16_MIN, INT16_MAX);
file.write((const char*)&sample, sizeof(int16_t));
}
}