furnace/src/engine/platform/sound/ted-sound.c

232 lines
8.5 KiB
C

/*
* ted-sound.c
*
* Written by
* Andreas Boose <viceteam@t-online.de>
* Tibor Biczo <crown @ axelero . hu>
* Marco van den Heuvel <blackystardust68@yahoo.com>
*
* This file is part of VICE, the Versatile Commodore Emulator.
* See README for copyright notice.
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "ted-sound.h"
/* ------------------------------------------------------------------------- */
/* FIXME: Find proper volume multiplier. */
const int16_t volume_tab[16] = {
0x0000, 0x0800, 0x1000, 0x1800, 0x2000, 0x2800, 0x3000, 0x3800,
0x3fff, 0x3fff, 0x3fff, 0x3fff, 0x3fff, 0x3fff, 0x3fff, 0x3fff
};
int ted_sound_machine_calculate_samples(struct plus4_sound_s* snd, int16_t *pbuf, int nr, int scc)
{
int i;
int j;
int16_t volume;
if (snd->digital) {
for (i = 0; i < nr; i++) {
pbuf[i] = (snd->volume * (snd->voice0_output_enabled + snd->voice1_output_enabled));
}
} else {
for (i = 0; i < nr; i++) {
snd->sample_position_remainder += snd->sample_length_remainder;
if (snd->sample_position_remainder >= snd->speed) {
snd->sample_position_remainder -= snd->speed;
snd->sample_position_integer++;
}
snd->sample_position_integer += snd->sample_length_integer;
if (snd->sample_position_integer >= 8) {
/* Advance state engine */
uint32_t ticks = snd->sample_position_integer >> 3;
if (snd->voice0_accu <= ticks) {
uint32_t delay = ticks - snd->voice0_accu;
snd->voice0_sign ^= 1;
snd->voice0_accu = 1023 - snd->voice0_reload;
if (snd->voice0_accu == 0) {
snd->voice0_accu = 1024;
}
if (delay >= snd->voice0_accu) {
snd->voice0_sign = ((delay / snd->voice0_accu)
& 1) ? snd->voice0_sign ^ 1
: snd->voice0_sign;
snd->voice0_accu = snd->voice0_accu - (delay % snd->voice0_accu);
} else {
snd->voice0_accu -= delay;
}
} else {
snd->voice0_accu -= ticks;
}
if (snd->voice1_accu <= ticks) {
uint32_t delay = ticks - snd->voice1_accu;
snd->voice1_sign ^= 1;
snd->noise_shift_register
= (snd->noise_shift_register << 1) +
( 1 ^ ((snd->noise_shift_register >> 7) & 1) ^
((snd->noise_shift_register >> 5) & 1) ^
((snd->noise_shift_register >> 4) & 1) ^
((snd->noise_shift_register >> 1) & 1));
snd->voice1_accu = 1023 - snd->voice1_reload;
if (snd->voice1_accu == 0) {
snd->voice1_accu = 1024;
}
if (delay >= snd->voice1_accu) {
snd->voice1_sign = ((delay / snd->voice1_accu)
& 1) ? snd->voice1_sign ^ 1
: snd->voice1_sign;
for (j = 0; j < (int)(delay / snd->voice1_accu);
j++) {
snd->noise_shift_register
= (snd->noise_shift_register << 1) +
( 1 ^ ((snd->noise_shift_register >> 7) & 1) ^
((snd->noise_shift_register >> 5) & 1) ^
((snd->noise_shift_register >> 4) & 1) ^
((snd->noise_shift_register >> 1) & 1));
}
snd->voice1_accu = snd->voice1_accu - (delay % snd->voice1_accu);
} else {
snd->voice1_accu -= delay;
}
} else {
snd->voice1_accu -= ticks;
}
}
snd->sample_position_integer = snd->sample_position_integer & 7;
volume = 0;
if (snd->voice0_output_enabled && snd->voice0_sign) {
volume += snd->volume;
}
if (snd->voice1_output_enabled && !snd->noise && snd->voice1_sign) {
volume += snd->volume;
}
if (snd->voice1_output_enabled && snd->noise && (!(snd->noise_shift_register & 1))) {
volume += snd->volume;
}
pbuf[i] = volume;
}
}
return nr;
}
int ted_sound_machine_init(struct plus4_sound_s* snd, int speed, int cycles_per_sec)
{
uint8_t val;
memset(snd,0,sizeof(struct plus4_sound_s));
snd->speed = speed;
snd->sample_length_integer = cycles_per_sec / speed;
snd->sample_length_remainder = cycles_per_sec % speed;
snd->sample_position_integer = 0;
snd->sample_position_remainder = 0;
snd->noise_shift_register = 0;
snd->voice0_reload = (snd->plus4_sound_data[0] | (snd->plus4_sound_data[4] << 8));
snd->voice1_reload = (snd->plus4_sound_data[1] | (snd->plus4_sound_data[2] << 8));
val = snd->plus4_sound_data[3];
snd->volume = volume_tab[val & 0x0f];
snd->voice0_output_enabled = (val & 0x10) ? 1 : 0;
snd->voice1_output_enabled = (val & 0x60) ? 1 : 0;
snd->noise = ((val & 0x60) == 0x40) ? 1 : 0;
snd->digital = val & 0x80;
if (snd->digital) {
snd->voice0_sign = 1;
snd->voice0_accu = 0;
snd->voice1_sign = 1;
snd->voice1_accu = 0;
snd->noise_shift_register = 0;
}
return 1;
}
void ted_sound_machine_store(struct plus4_sound_s* snd, uint16_t addr, uint8_t val)
{
switch (addr) {
case 0x0e:
snd->plus4_sound_data[0] = val;
snd->voice0_reload = (snd->plus4_sound_data[0] | (snd->plus4_sound_data[4] << 8));
break;
case 0x0f:
snd->plus4_sound_data[1] = val;
snd->voice1_reload = (snd->plus4_sound_data[1] | (snd->plus4_sound_data[2] << 8));
break;
case 0x10:
snd->plus4_sound_data[2] = val & 3;
snd->voice1_reload = (snd->plus4_sound_data[1] | (snd->plus4_sound_data[2] << 8));
break;
case 0x11:
snd->volume = volume_tab[val & 0x0f];
snd->voice0_output_enabled = (val & 0x10) ? 1 : 0;
snd->voice1_output_enabled = (val & 0x60) ? 1 : 0;
snd->noise = ((val & 0x60) == 0x40) ? 1 : 0;
snd->digital = val & 0x80;
if (snd->digital) {
snd->voice0_sign = 1;
snd->voice0_accu = 0;
snd->voice1_sign = 1;
snd->voice1_accu = 0;
snd->noise_shift_register = 0;
}
snd->plus4_sound_data[3] = val;
break;
case 0x12:
snd->plus4_sound_data[4] = val & 3;
snd->voice0_reload = (snd->plus4_sound_data[0] | (snd->plus4_sound_data[4] << 8));
break;
}
}
uint8_t ted_sound_machine_read(struct plus4_sound_s* snd, uint16_t addr)
{
switch (addr) {
case 0x0e:
return snd->plus4_sound_data[0];
case 0x0f:
return snd->plus4_sound_data[1];
case 0x10:
return snd->plus4_sound_data[2] | 0xc0;
case 0x11:
return snd->plus4_sound_data[3];
case 0x12:
return snd->plus4_sound_data[4];
}
return 0;
}
void ted_sound_reset(struct plus4_sound_s* snd)
{
uint16_t i;
snd->noise_shift_register = 0;
for (i = 0x0e; i <= 0x12; i++) {
ted_sound_machine_store(snd,i,0);
}
}