OXYGENHUB
◆ main x16-emulator / src / vera_pcm.c
3.0 KiB · C · last changed chore(): initial commit 15 days ago source blame history raw
1
// Commander X16 Emulator
2
// Copyright (c) 2020 Frank van den Hoef
3
// All rights reserved. License: 2-clause BSD
4
5
#include "vera_pcm.h"
6
#include <stdio.h>
7
8
static uint8_t  fifo[4096];
9
static unsigned fifo_wridx;
10
static unsigned fifo_rdidx;
11
static unsigned fifo_cnt;
12
13
static uint8_t ctrl;
14
static uint8_t rate;
15
static uint8_t loop;
16
17
static uint8_t volume_lut[16] = {0, 1, 2, 3, 4, 5, 6, 8, 11, 14, 18, 23, 30, 38, 49, 64};
18
19
static int16_t cur_l, cur_r;
20
static uint8_t phase;
21
22
static void
23
fifo_reset(void)
24
{
25
	fifo_wridx = 0;
26
	fifo_rdidx = 0;
27
	fifo_cnt   = 0;
28
}
29
30
static void
31
fifo_restart(void)
32
{
33
	fifo_rdidx = 0;
34
	fifo_cnt = fifo_wridx;
35
}
36
37
void
38
pcm_reset(void)
39
{
40
	fifo_reset();
41
	ctrl  = 0;
42
	rate  = 0;
43
	cur_l = 0;
44
	cur_r = 0;
45
	phase = 0;
46
}
47
48
void
49
pcm_write_ctrl(uint8_t val)
50
{
51
	if ((val & 0xc0) == 0xc0) {
52
		loop = true;
53
	} else {
54
		loop = false;
55
		if (val & 0x80) {
56
			fifo_reset();
57
		}
58
	}
59
	if (val & 0x40) {
60
		fifo_restart();
61
	}
62
	ctrl = val & 0x3F;
63
}
64
65
uint8_t
66
pcm_read_ctrl(void)
67
{
68
	uint8_t result = ctrl;
69
	if (fifo_cnt == sizeof(fifo) - 1) {
70
		result |= 0x80;
71
	}
72
	if (fifo_cnt == 0) {
73
		result |= 0x40;
74
	}
75
	return result;
76
}
77
78
void
79
pcm_write_rate(uint8_t val)
80
{
81
	rate = (val > 128) ? (256 - val) : val;
82
}
83
84
uint8_t
85
pcm_read_rate(void)
86
{
87
	return rate;
88
}
89
90
void
91
pcm_write_fifo(uint8_t val)
92
{
93
	if (fifo_cnt < sizeof(fifo) - 1) {
94
		fifo[fifo_wridx++] = val;
95
		if (fifo_wridx == sizeof(fifo)) {
96
			fifo_wridx = 0;
97
		}
98
		fifo_cnt++;
99
	}
100
}
101
102
static uint8_t
103
read_fifo()
104
{
105
	static uint8_t result = 0;
106
	if (fifo_cnt == 0) {
107
		return 0;
108
	}
109
	result = fifo[fifo_rdidx++];
110
	if (fifo_rdidx == sizeof(fifo)) {
111
		fifo_rdidx = 0;
112
	}
113
	fifo_cnt--;
114
	return result;
115
}
116
117
bool
118
pcm_is_fifo_almost_empty(void)
119
{
120
	return fifo_cnt < 1024;
121
}
122
123
void
124
pcm_render(int16_t *buf, unsigned num_samples)
125
{
126
	while (num_samples--) {
127
		uint8_t old_phase = phase;
128
		phase += rate;
129
		if ((old_phase & 0x80) != (phase & 0x80)) {
130
			if (fifo_cnt == 0) {
131
				cur_l = 0;
132
				cur_r = 0;
133
			} else {
134
				switch ((ctrl >> 4) & 3) {
135
					case 0: { // mono 8-bit
136
						cur_l = (int16_t)read_fifo() << 8;
137
						cur_r = cur_l;
138
						break;
139
					}
140
					case 1: { // stereo 8-bit
141
						if (fifo_cnt < 2) {
142
							fifo_cnt = 0;
143
							fifo_rdidx = fifo_wridx;
144
						} else {
145
							cur_l = read_fifo() << 8;
146
							cur_r = read_fifo() << 8;
147
						}
148
						break;
149
					}
150
					case 2: { // mono 16-bit
151
						if (fifo_cnt < 2) {
152
							fifo_cnt = 0;
153
							fifo_rdidx = fifo_wridx;
154
						} else {
155
							cur_l = read_fifo();
156
							cur_l |= read_fifo() << 8;
157
							cur_r = cur_l;
158
						}
159
						break;
160
					}
161
					case 3: { // stereo 16-bit
162
						if (fifo_cnt < 4) {
163
							fifo_cnt = 0;
164
							fifo_rdidx = fifo_wridx;
165
						} else {
166
							cur_l = read_fifo();
167
							cur_l |= read_fifo() << 8;
168
							cur_r = read_fifo();
169
							cur_r |= read_fifo() << 8;
170
						}
171
						break;
172
					}
173
				}
174
				if (loop && fifo_cnt == 0) {
175
					fifo_restart();
176
				}
177
			}
178
		}
179
		*(buf++) = (int16_t)((int32_t)cur_l * volume_lut[ctrl & 0xF] / 64);
180
		*(buf++) = (int16_t)((int32_t)cur_r * volume_lut[ctrl & 0xF] / 64);
181
	}
182
}