pavone@467
|
1 /*
|
pavone@467
|
2 Copyright 2013 Michael Pavone
|
pavone@483
|
3 This file is part of BlastEm.
|
pavone@467
|
4 BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text.
|
pavone@467
|
5 */
|
pavone@354
|
6 #include "psg.h"
|
pavone@354
|
7 #include "render.h"
|
pavone@505
|
8 #include "blastem.h"
|
pavone@354
|
9 #include <string.h>
|
pavone@354
|
10 #include <stdlib.h>
|
pavone@838
|
11 #include <stdio.h>
|
pavone@964
|
12 #include <math.h>
|
pavone@964
|
13 #define LOWPASS_CUTOFF 3390
|
pavone@838
|
14
|
pavone@380
|
15 void psg_init(psg_context * context, uint32_t sample_rate, uint32_t master_clock, uint32_t clock_div, uint32_t samples_frame)
|
pavone@354
|
16 {
|
pavone@354
|
17 memset(context, 0, sizeof(*context));
|
pavone@354
|
18 context->audio_buffer = malloc(sizeof(*context->audio_buffer) * samples_frame);
|
pavone@354
|
19 context->back_buffer = malloc(sizeof(*context->audio_buffer) * samples_frame);
|
pavone@380
|
20 context->clock_inc = clock_div;
|
pavone@483
|
21 context->sample_rate = sample_rate;
|
pavone@354
|
22 context->samples_frame = samples_frame;
|
pavone@964
|
23 double rc = (1.0 / (double)LOWPASS_CUTOFF) / (2.0 * M_PI);
|
pavone@964
|
24 double dt = 1.0 / ((double)master_clock / (double)clock_div);
|
pavone@964
|
25 double alpha = dt / (dt + rc);
|
pavone@964
|
26 context->lowpass_alpha = (int32_t)(((double)0x10000) * alpha);
|
pavone@483
|
27 psg_adjust_master_clock(context, master_clock);
|
pavone@354
|
28 for (int i = 0; i < 4; i++) {
|
pavone@354
|
29 context->volume[i] = 0xF;
|
pavone@354
|
30 }
|
pavone@354
|
31 }
|
pavone@354
|
32
|
pavone@884
|
33 void psg_free(psg_context *context)
|
pavone@884
|
34 {
|
pavone@884
|
35 free(context->audio_buffer);
|
pavone@884
|
36 //TODO: Figure out how to make this 100% safe
|
pavone@884
|
37 //audio thread could still be using this
|
pavone@884
|
38 free(context->back_buffer);
|
pavone@884
|
39 free(context);
|
pavone@884
|
40 }
|
pavone@884
|
41
|
pavone@963
|
42 #define BUFFER_INC_RES 0x40000000UL
|
pavone@483
|
43
|
pavone@483
|
44 void psg_adjust_master_clock(psg_context * context, uint32_t master_clock)
|
pavone@483
|
45 {
|
pavone@483
|
46 uint64_t old_inc = context->buffer_inc;
|
pavone@483
|
47 context->buffer_inc = ((BUFFER_INC_RES * (uint64_t)context->sample_rate) / (uint64_t)master_clock) * (uint64_t)context->clock_inc;
|
pavone@483
|
48 }
|
pavone@483
|
49
|
pavone@354
|
50 void psg_write(psg_context * context, uint8_t value)
|
pavone@354
|
51 {
|
pavone@354
|
52 if (value & 0x80) {
|
pavone@354
|
53 context->latch = value & 0x70;
|
pavone@354
|
54 uint8_t channel = value >> 5 & 0x3;
|
pavone@354
|
55 if (value & 0x10) {
|
pavone@354
|
56 context->volume[channel] = value & 0xF;
|
pavone@354
|
57 } else {
|
pavone@354
|
58 if (channel == 3) {
|
pavone@354
|
59 switch(value & 0x3)
|
pavone@354
|
60 {
|
pavone@354
|
61 case 0:
|
pavone@354
|
62 case 1:
|
pavone@354
|
63 case 2:
|
pavone@354
|
64 context->counter_load[3] = 0x10 << (value & 0x3);
|
pavone@354
|
65 context->noise_use_tone = 0;
|
pavone@354
|
66 break;
|
pavone@354
|
67 default:
|
pavone@354
|
68 context->counter_load[3] = context->counter_load[2];
|
pavone@354
|
69 context->noise_use_tone = 1;
|
pavone@354
|
70 }
|
pavone@354
|
71 context->noise_type = value & 0x4;
|
pavone@354
|
72 context->lsfr = 0x8000;
|
pavone@354
|
73 } else {
|
pavone@354
|
74 context->counter_load[channel] = (context->counter_load[channel] & 0x3F0) | (value & 0xF);
|
pavone@354
|
75 if (channel == 2 && context->noise_use_tone) {
|
pavone@354
|
76 context->counter_load[3] = context->counter_load[2];
|
pavone@354
|
77 }
|
pavone@354
|
78 }
|
pavone@354
|
79 }
|
pavone@354
|
80 } else {
|
pavone@354
|
81 if (!(context->latch & 0x10)) {
|
pavone@354
|
82 uint8_t channel = context->latch >> 5 & 0x3;
|
pavone@354
|
83 if (channel != 3) {
|
pavone@354
|
84 context->counter_load[channel] = (value << 4 & 0x3F0) | (context->counter_load[channel] & 0xF);
|
pavone@354
|
85 if (channel == 2 && context->noise_use_tone) {
|
pavone@354
|
86 context->counter_load[3] = context->counter_load[2];
|
pavone@354
|
87 }
|
pavone@354
|
88 }
|
pavone@354
|
89 }
|
pavone@354
|
90 }
|
pavone@354
|
91 }
|
pavone@354
|
92
|
pavone@522
|
93 #define PSG_VOL_DIV 14
|
pavone@354
|
94
|
pavone@354
|
95 //table shamelessly swiped from PSG doc from smspower.org
|
pavone@354
|
96 int16_t volume_table[16] = {
|
pavone@354
|
97 32767/PSG_VOL_DIV, 26028/PSG_VOL_DIV, 20675/PSG_VOL_DIV, 16422/PSG_VOL_DIV, 13045/PSG_VOL_DIV, 10362/PSG_VOL_DIV,
|
pavone@483
|
98 8231/PSG_VOL_DIV, 6568/PSG_VOL_DIV, 5193/PSG_VOL_DIV, 4125/PSG_VOL_DIV, 3277/PSG_VOL_DIV, 2603/PSG_VOL_DIV,
|
pavone@354
|
99 2067/PSG_VOL_DIV, 1642/PSG_VOL_DIV, 1304/PSG_VOL_DIV, 0
|
pavone@354
|
100 };
|
pavone@354
|
101
|
pavone@354
|
102 void psg_run(psg_context * context, uint32_t cycles)
|
pavone@354
|
103 {
|
pavone@354
|
104 while (context->cycles < cycles) {
|
pavone@354
|
105 for (int i = 0; i < 4; i++) {
|
pavone@354
|
106 if (context->counters[i]) {
|
pavone@354
|
107 context->counters[i] -= 1;
|
pavone@354
|
108 }
|
pavone@354
|
109 if (!context->counters[i]) {
|
pavone@354
|
110 context->counters[i] = context->counter_load[i];
|
pavone@354
|
111 context->output_state[i] = !context->output_state[i];
|
pavone@354
|
112 if (i == 3 && context->output_state[i]) {
|
pavone@354
|
113 context->noise_out = context->lsfr & 1;
|
pavone@354
|
114 context->lsfr = (context->lsfr >> 1) | (context->lsfr << 15);
|
pavone@354
|
115 if (context->noise_type) {
|
pavone@354
|
116 //white noise
|
pavone@354
|
117 if (context->lsfr & 0x40) {
|
pavone@354
|
118 context->lsfr ^= 0x8000;
|
pavone@354
|
119 }
|
pavone@354
|
120 }
|
pavone@354
|
121 }
|
pavone@354
|
122 }
|
pavone@354
|
123 }
|
pavone@838
|
124
|
pavone@963
|
125 context->last_sample = context->accum;
|
pavone@963
|
126 context->accum = 0;
|
pavone@963
|
127
|
pavone@838
|
128 for (int i = 0; i < 3; i++) {
|
pavone@838
|
129 if (context->output_state[i]) {
|
pavone@838
|
130 context->accum += volume_table[context->volume[i]];
|
pavone@838
|
131 }
|
pavone@838
|
132 }
|
pavone@838
|
133 if (context->noise_out) {
|
pavone@838
|
134 context->accum += volume_table[context->volume[3]];
|
pavone@838
|
135 }
|
pavone@964
|
136 int32_t tmp = context->accum * context->lowpass_alpha + context->last_sample * (0x10000 - context->lowpass_alpha);
|
pavone@964
|
137 context->accum = tmp >> 16;
|
pavone@838
|
138
|
pavone@354
|
139 context->buffer_fraction += context->buffer_inc;
|
pavone@483
|
140 if (context->buffer_fraction >= BUFFER_INC_RES) {
|
pavone@483
|
141 context->buffer_fraction -= BUFFER_INC_RES;
|
pavone@964
|
142 int32_t tmp = context->last_sample * ((context->buffer_fraction << 16) / context->buffer_inc);
|
pavone@964
|
143 tmp += context->accum * (0x10000 - ((context->buffer_fraction << 16) / context->buffer_inc));
|
pavone@963
|
144 context->audio_buffer[context->buffer_pos++] = tmp >> 16;
|
pavone@964
|
145
|
pavone@354
|
146 if (context->buffer_pos == context->samples_frame) {
|
pavone@505
|
147 if (!headless) {
|
pavone@505
|
148 render_wait_psg(context);
|
pavone@505
|
149 }
|
pavone@354
|
150 }
|
pavone@354
|
151 }
|
pavone@380
|
152 context->cycles += context->clock_inc;
|
pavone@354
|
153 }
|
pavone@354
|
154 }
|
pavone@354
|
155
|