furnace/src/engine/platform/sound/vera_pcm.c

136 lines
2.6 KiB
C

// Commander X16 Emulator
// Copyright (c) 2020 Frank van den Hoef
// All rights reserved. License: 2-clause BSD
#include "vera_pcm.h"
#include <stdio.h>
#include <string.h>
static uint8_t volume_lut[16] = {0, 1, 2, 3, 4, 5, 6, 8, 11, 14, 18, 23, 30, 38, 49, 64};
static void
fifo_reset(struct VERA_PCM* pcm)
{
pcm->fifo_wridx = 0;
pcm->fifo_rdidx = 0;
pcm->fifo_cnt = 0;
memset(pcm->fifo,0,sizeof(pcm->fifo));
}
void
pcm_reset(struct VERA_PCM* pcm)
{
fifo_reset(pcm);
pcm->ctrl = 0;
pcm->rate = 0;
pcm->cur_l = 0;
pcm->cur_r = 0;
pcm->phase = 0;
}
void
pcm_write_ctrl(struct VERA_PCM* pcm, uint8_t val)
{
if (val & 0x80) {
fifo_reset(pcm);
}
pcm->ctrl = val & 0x3F;
}
uint8_t
pcm_read_ctrl(struct VERA_PCM* pcm)
{
uint8_t result = pcm->ctrl;
if (pcm->fifo_cnt == sizeof(pcm->fifo)) {
result |= 0x80;
}
return result;
}
void
pcm_write_rate(struct VERA_PCM* pcm, uint8_t val)
{
pcm->rate = val;
}
uint8_t
pcm_read_rate(struct VERA_PCM* pcm)
{
return pcm->rate;
}
void
pcm_write_fifo(struct VERA_PCM* pcm, uint8_t val)
{
if (pcm->fifo_cnt < sizeof(pcm->fifo)) {
pcm->fifo[pcm->fifo_wridx++] = val;
if (pcm->fifo_wridx == sizeof(pcm->fifo)) {
pcm->fifo_wridx = 0;
}
pcm->fifo_cnt++;
}
}
static uint8_t
read_fifo(struct VERA_PCM* pcm)
{
if (pcm->fifo_cnt == 0) {
return 0;
}
uint8_t result = pcm->fifo[pcm->fifo_rdidx++];
if (pcm->fifo_rdidx == sizeof(pcm->fifo)) {
pcm->fifo_rdidx = 0;
}
pcm->fifo_cnt--;
return result;
}
bool
pcm_is_fifo_almost_empty(struct VERA_PCM* pcm)
{
return pcm->fifo_cnt < 1024;
}
void
pcm_render(struct VERA_PCM* pcm, int16_t* buf_l, int16_t* buf_r, unsigned num_samples)
{
while (num_samples--) {
uint8_t old_phase = pcm->phase;
pcm->phase += pcm->rate;
if ((old_phase & 0x80) != (pcm->phase & 0x80)) {
switch ((pcm->ctrl >> 4) & 3) {
case 0: { // mono 8-bit
pcm->cur_l = (int16_t)read_fifo(pcm) << 8;
pcm->cur_r = pcm->cur_l;
break;
}
case 1: { // stereo 8-bit
pcm->cur_l = read_fifo(pcm) << 8;
pcm->cur_r = read_fifo(pcm) << 8;
break;
}
case 2: { // mono 16-bit
pcm->cur_l = read_fifo(pcm);
pcm->cur_l |= read_fifo(pcm) << 8;
pcm->cur_r = pcm->cur_l;
break;
}
case 3: { // stereo 16-bit
pcm->cur_l = read_fifo(pcm);
pcm->cur_l |= read_fifo(pcm) << 8;
pcm->cur_r = read_fifo(pcm);
pcm->cur_r |= read_fifo(pcm) << 8;
break;
}
}
}
*(buf_l) += ((int)pcm->cur_l * (int)volume_lut[pcm->ctrl & 0xF]) >> 6;
*(buf_r) += ((int)pcm->cur_r * (int)volume_lut[pcm->ctrl & 0xF]) >> 6;
buf_l++;
buf_r++;
}
}