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 IOBASE 0xdff000
33 #define AUDIOBASE 0xdff0a0
40 static struct audio_channel {
41 int dma_on; /* set if dma is running */
42 uint32_t start; /* sample address to start from */
43 int length; /* length of samples (not words) */
44 uint32_t pointer; /* pointer of renderer */
45 uint32_t end; /* end of current sample */
46 double offset; /* offset between two (input) samples */
47 double step; /* number of input samples per output sample */
48 double volume; /* volume of channel in the result sample */
49 float sample; /* current (last read) sample */
51 { 0, 0xffffffff, 0, 0, 0, 0.0, 0.0, 0.0, 0.0 },
52 { 0, 0xffffffff, 0, 0, 0, 0.0, 0.0, 0.0, 0.0 },
53 { 0, 0xffffffff, 0, 0, 0, 0.0, 0.0, 0.0, 0.0 },
54 { 0, 0xffffffff, 0, 0, 0, 0.0, 0.0, 0.0, 0.0 },
57 double filter_alpha = 0.0, filter_last = 0.0;
59 static int start_dma(int c, struct audio_channel *chan)
61 /* fetch sample start and length and use it for rendering */
62 chan->pointer = chan->start;
63 chan->end = chan->start + chan->length;
65 if (chan->length == 0) {
66 print_error("Sample length of channel %d is 0, please fix!\n", c);
70 if (chan->pointer >= 0x80000) {
71 print_error("Sample pointer 0x%08x of channel %d is outside memory, please fix!\n", chan->pointer, c);
75 if (chan->end > 0x80000) {
76 print_error("Sample end (pointer 0x%08x + length 0x%x) of channel %d is outside memory, please fix!\n", chan->pointer, chan->length, c);
84 void emulate_sound_write(uint32_t address, uint16_t value, int samplerate)
88 if (address == DMACON) {
89 /* handle DMA flag and start DMA, if required */
90 for (c = 0; c < 4; c++) {
91 /* if nothing should be changed */
92 if (!(value & (1 << c)))
94 if (!(value & 0x8000)) {
95 channel[c].dma_on = 0;
97 printf("Audio channel %d: Turning DMA off\n", c);
101 if (channel[c].dma_on == 1)
103 if (start_dma(c, &channel[c]) < 0) {
105 printf("Audio channel %d: Turning DMA on failed\n", c);
110 printf("Audio channel %d: Turning DMA on\n", c);
112 channel[c].dma_on = 1;
117 /* return if not register OR get channel from address */
118 if (address < AUDIOBASE || address >= AUDIOBASE + 0x03f)
120 c = (address - AUDIOBASE) >> 4;
122 switch (address & 0xf) {
125 printf("Audio channel %d: Setting high word of sample start to 0x%04x\n", c, value);
127 channel[c].start = (channel[c].start & 0x0000ffff) | (value << 16);
131 printf("Audio channel %d: Setting low word of sample start to 0x%04x\n", c, value);
133 channel[c].start = (channel[c].start & 0xffff0000) | (value & 0xfffe);
137 printf("Audio channel %d: Setting sample length to %d words\n", c, value);
139 channel[c].length = value << 1;
145 printf("Audio channel %d: Setting sample frequency to %.2f Hz\n", c, CLOCK / (double)value);
147 channel[c].step = CLOCK / (double)value / (double)samplerate;
148 /* sample rate never at or above output rate */
149 if (channel[c].step > 0.999)
150 channel[c].step = 0.999;
156 printf("Audio channel %d: Setting volume to %d (0..64)\n", c, value);
158 channel[c].volume = (double)value / 64.0 / 4.0; /* 1/4th volume per channel */
163 /* render sound from memory and add each channel to ring buffer array */
164 void render_sound(uint8_t *memory, float *sample, int buffer_size, int buffer_pos, int length, int filter)
167 struct audio_channel *chan;
171 /* clear buffer first */
173 for (s = 0; s < length; s++) {
175 pos = (pos + 1) % buffer_size;
178 /* render each channel and sum the result */
179 for (c = 0; c < 4; c++) {
181 /* skip if DMA is disabled */
184 /* Note: We must render sound, even if volume is 0.0,
185 * because the sample pointers must continue, as it would happen in a real Amiga.
186 * But this may have no practical use in this game.
189 for (s = 0; s < length; s++) {
190 new_offset = chan->offset + chan->step;
191 if (new_offset < 1.0)
192 /* no interpolation: use last input sample, because there is no change */
193 sample[pos] += chan->sample * chan->volume;
195 /* there is a change of input sample, so read new sample */
197 new_sample = (double)(((int8_t *)memory)[chan->pointer++]) / 128.0;
198 if (chan->pointer == chan->end) {
199 if (start_dma(c, chan) < 0)
202 /* linear interpolation: add portion of last sample to portion of new sample */
203 sample[pos] += (chan->sample * (1.0 - chan->offset) + new_sample * new_offset) / chan->step * chan->volume;
204 chan->sample = new_sample;
206 chan->offset = new_offset;
207 pos = (pos + 1) % buffer_size;
212 /* do low-pass filtering */
214 for (s = 0; s < length; s++) {
215 /* y[i] := y[i-1] + alpha * (x[i] - y[i-1]) */
216 filter_last = sample[pos] = filter_last + filter_alpha * (sample[pos] - filter_last);
217 pos = (pos + 1) % buffer_size;
222 void sound_init_filter(int samplerate)
224 double dt = 1.0 / (double)samplerate;
225 double C = 0.0000001;
227 double alpha = dt / (R*C + dt);
229 double cutoff = 1.0 / (2 * M_PI * R * C);
233 printf("filter-cutoff: %.0f Hz (alpha = %.4f)\n", cutoff, alpha);
235 filter_alpha = alpha;