3 * (C) 2018 by Andreas Eversberg <jolly@eversberg.eu>
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include "../libsdl/print.h"
26 #include "../libcpu/m68kcpu.h"
30 #define CLOCK 3546895.0
31 #define DMACON 0xdff096
32 #define INTENA 0xdff09a
33 #define IOBASE 0xdff000
34 #define AUDIOBASE 0xdff0a0
41 static struct audio_channel {
42 int dma_on; /* set if dma is running */
43 int irq_on; /* set if irq is enabled */
44 uint32_t start; /* sample address to start from */
45 int length; /* length of samples (not words) */
46 uint32_t pointer; /* pointer of renderer */
47 uint32_t end; /* end of current sample */
48 double offset; /* offset between two (input) samples */
49 double step; /* number of input samples per output sample */
50 double volume; /* volume of channel in the result sample */
51 float sample; /* current (last read) sample */
53 { 0, 0, 0xffffffff, 0, 0, 0, 0.0, 0.0, 0.0, 0.0 },
54 { 0, 0, 0xffffffff, 0, 0, 0, 0.0, 0.0, 0.0, 0.0 },
55 { 0, 0, 0xffffffff, 0, 0, 0, 0.0, 0.0, 0.0, 0.0 },
56 { 0, 0, 0xffffffff, 0, 0, 0, 0.0, 0.0, 0.0, 0.0 },
59 double filter_alpha = 0.0, filter_last = 0.0;
61 static void (*sound_irq)(void) = NULL;
63 static int start_dma(int c, struct audio_channel *chan)
65 /* fetch sample start and length and use it for rendering */
66 chan->pointer = chan->start;
67 chan->end = chan->start + chan->length;
69 if (chan->length == 0) {
70 print_error("Sample length of channel %d is 0, please fix!\n", c);
74 if (chan->pointer >= 0x80000) {
75 print_error("Sample pointer 0x%08x of channel %d is outside memory, please fix!\n", chan->pointer, c);
79 if (chan->end > 0x80000) {
80 print_error("Sample end (pointer 0x%08x + length 0x%x) of channel %d is outside memory, please fix!\n", chan->pointer, chan->length, c);
88 static void do_irq(int __attribute__((unused)) c, struct audio_channel *chan)
92 printf("Audio channel %d: IRQ is triggered\n", c);
94 print_info("Audio channel %d: IRQ is triggered, please report to author!\n", c);
99 void emulate_sound_write(uint32_t address, uint16_t value, int samplerate)
105 /* handle DMA flag and start DMA, if required */
106 for (c = 0; c < 4; c++) {
107 /* if nothing should be changed */
108 if (!(value & (1 << c)))
110 if (!(value & 0x8000)) {
111 channel[c].dma_on = 0;
113 printf("Audio channel %d: Turning DMA off\n", c);
117 if (channel[c].dma_on == 1)
119 if (start_dma(c, &channel[c]) < 0) {
121 printf("Audio channel %d: Turning DMA on failed\n", c);
126 printf("Audio channel %d: Turning DMA on\n", c);
128 channel[c].dma_on = 1;
132 /* handle IRQ flag */
133 for (c = 0; c < 4; c++) {
134 /* if nothing should be changed */
135 if (!(value & (0x0080 << c)))
137 if (!(value & 0x8000)) {
138 channel[c].irq_on = 0;
140 printf("Audio channel %d: Turning IRQ off\n", c);
144 channel[c].irq_on = 1;
146 printf("Audio channel %d: Turning IRQ on\n", c);
154 /* return if not register OR get channel from address */
155 if (address < AUDIOBASE || address >= AUDIOBASE + 0x03f)
157 c = (address - AUDIOBASE) >> 4;
159 switch (address & 0xf) {
162 printf("Audio channel %d: Setting high word of sample start to 0x%04x\n", c, value);
164 channel[c].start = (channel[c].start & 0x0000ffff) | (value << 16);
168 printf("Audio channel %d: Setting low word of sample start to 0x%04x\n", c, value);
170 channel[c].start = (channel[c].start & 0xffff0000) | (value & 0xfffe);
174 printf("Audio channel %d: Setting sample length to %d words\n", c, value);
176 channel[c].length = value << 1;
182 printf("Audio channel %d: Setting sample frequency to %.2f Hz\n", c, CLOCK / (double)value);
184 channel[c].step = CLOCK / (double)value / (double)samplerate;
185 /* sample rate never at or above output rate */
186 if (channel[c].step > 0.999)
187 channel[c].step = 0.999;
193 printf("Audio channel %d: Setting volume to %d (0..64)\n", c, value);
195 channel[c].volume = (double)value / 64.0 / 4.0; /* 1/4th volume per channel */
200 /* render sound from memory and add each channel to ring buffer array */
201 void render_sound(uint8_t *memory, stereo_t *sample, int buffer_size, int buffer_pos, int length, int filter)
204 struct audio_channel *chan;
208 /* clear buffer first */
210 for (s = 0; s < length; s++) {
211 sample[pos].left = 0.0;
212 pos = (pos + 1) % buffer_size;
215 /* Note: We must render sound, even if volume is 0.0,
216 * because the sample pointers must continue, as it would happen in a real Amiga.
217 * But this may have no practical use in this game.
220 for (s = 0; s < length; s++) {
221 /* render each channel and sum the result */
222 for (c = 0; c < 4; c++) {
224 /* skip if DMA is disabled */
227 new_offset = chan->offset + chan->step;
228 if (new_offset < 1.0)
229 /* no interpolation: use last input sample, because there is no change */
230 sample[pos].left += chan->sample * chan->volume;
232 /* there is a change of input sample, so read new sample */
234 new_sample = (double)(((int8_t *)memory)[chan->pointer++]) / 128.0;
235 if (chan->pointer == chan->end) {
239 /* linear interpolation: add portion of last sample to portion of new sample */
240 sample[pos].left += (chan->sample * (1.0 - chan->offset) + new_sample * new_offset) / chan->step * chan->volume;
241 chan->sample = new_sample;
243 chan->offset = new_offset;
245 pos = (pos + 1) % buffer_size;
249 /* do low-pass filtering */
251 for (s = 0; s < length; s++) {
252 /* y[i] := y[i-1] + alpha * (x[i] - y[i-1]) */
253 filter_last = sample[pos].left = filter_last + filter_alpha * (sample[pos].left - filter_last);
254 pos = (pos + 1) % buffer_size;
258 /* copy left to right */
260 for (s = 0; s < length; s++) {
261 sample[pos].right = sample[pos].left;
262 pos = (pos + 1) % buffer_size;
266 void sound_init(int samplerate, void (*_sound_irq)(void))
268 double dt = 1.0 / (double)samplerate;
269 double C = 0.0000001;
271 double alpha = dt / (R*C + dt);
273 double cutoff = 1.0 / (2 * M_PI * R * C);
277 printf("filter-cutoff: %.0f Hz (alpha = %.4f)\n", cutoff, alpha);
279 filter_alpha = alpha;
281 sound_irq = _sound_irq;