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/>.
25 #include "../libcpu/m68kcpu.h"
29 #define CLOCK 3546895.0
30 #define DMACON 0xdff096
31 #define IOBASE 0xdff000
32 #define AUDIOBASE 0xdff0a0
39 static struct audio_channel {
40 int dma_on; /* set if dma is running */
41 uint32_t start; /* sample address to start from */
42 int length; /* length of samples (not words) */
43 uint32_t pointer; /* pointer of renderer */
44 uint32_t end; /* end of current sample */
45 double offset; /* offset between two (input) samples */
46 double step; /* number of input samples per output sample */
47 double volume; /* volume of channel in the result sample */
48 float sample; /* current (last read) sample */
50 { 0, 0xffffffff, 0, 0, 0, 0.0, 0.0, 0.0, 0.0 },
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 },
56 double filter_alpha = 0.0, filter_last = 0.0;
58 static int start_dma(int c, struct audio_channel *chan)
60 /* fetch sample start and length and use it for rendering */
61 chan->pointer = chan->start;
62 chan->end = chan->start + chan->length;
64 if (chan->length == 0) {
65 fprintf(stderr, "Sample length of channel %d is 0, please fix!\n", c);
69 if (chan->pointer >= 0x80000) {
70 fprintf(stderr, "Sample pointer 0x%08x of channel %d is outside memory, please fix!\n", chan->pointer, c);
74 if (chan->end > 0x80000) {
75 fprintf(stderr, "Sample end (pointer 0x%08x + length 0x%x) of channel %d is outside memory, please fix!\n", chan->pointer, chan->length, c);
83 void emulate_sound_write(uint32_t address, uint16_t value, int samplerate)
87 if (address == DMACON) {
88 /* handle DMA flag and start DMA, if required */
89 for (c = 0; c < 4; c++) {
90 /* if nothing should be changed */
91 if (!(value & (1 << c)))
93 if (!(value & 0x8000)) {
94 channel[c].dma_on = 0;
96 printf("Audio channel %d: Turning DMA off\n", c);
100 if (channel[c].dma_on == 1)
102 if (start_dma(c, &channel[c]) < 0) {
104 printf("Audio channel %d: Turning DMA on failed\n", c);
109 printf("Audio channel %d: Turning DMA on\n", c);
111 channel[c].dma_on = 1;
116 /* return if not register OR get channel from address */
117 if (address < AUDIOBASE || address >= AUDIOBASE + 0x03f)
119 c = (address - AUDIOBASE) >> 4;
121 switch (address & 0xf) {
124 printf("Audio channel %d: Setting high word of sample start to 0x%04x\n", c, value);
126 channel[c].start = (channel[c].start & 0x0000ffff) | (value << 16);
130 printf("Audio channel %d: Setting low word of sample start to 0x%04x\n", c, value);
132 channel[c].start = (channel[c].start & 0xffff0000) | (value & 0xfffe);
136 printf("Audio channel %d: Setting sample length to %d words\n", c, value);
138 channel[c].length = value << 1;
144 printf("Audio channel %d: Setting sample frequency to %.2f Hz\n", c, CLOCK / (double)value);
146 channel[c].step = CLOCK / (double)value / (double)samplerate;
147 /* sample rate never at or above output rate */
148 if (channel[c].step > 0.999)
149 channel[c].step = 0.999;
155 printf("Audio channel %d: Setting volume to %d (0..64)\n", c, value);
157 channel[c].volume = (double)value / 64.0 / 4.0; /* 1/4th volume per channel */
162 /* render sound from memory and add each channel to ring buffer array */
163 void render_sound(uint8_t *memory, float *sample, int buffer_size, int buffer_pos, int length, int filter)
166 struct audio_channel *chan;
170 /* clear buffer first */
172 for (s = 0; s < length; s++) {
174 pos = (pos + 1) % buffer_size;
177 /* render each channel and sum the result */
178 for (c = 0; c < 4; c++) {
180 /* skip if DMA is disabled */
183 /* Note: We must render sound, even if volume is 0.0,
184 * because the sample pointers must continue, as it would happen in a real Amiga.
185 * But this may have no practical use in this game.
188 for (s = 0; s < length; s++) {
189 new_offset = chan->offset + chan->step;
190 if (new_offset < 1.0)
191 /* no interpolation: use last input sample, because there is no change */
192 sample[pos] += chan->sample * chan->volume;
194 /* there is a change of input sample, so read new sample */
196 new_sample = (double)(((int8_t *)memory)[chan->pointer++]) / 128.0;
197 if (chan->pointer == chan->end) {
198 if (start_dma(c, chan) < 0)
201 /* linear interpolation: add portion of last sample to portion of new sample */
202 sample[pos] += (chan->sample * (1.0 - chan->offset) + new_sample * new_offset) / chan->step * chan->volume;
203 chan->sample = new_sample;
205 chan->offset = new_offset;
206 pos = (pos + 1) % buffer_size;
211 /* do low-pass filtering */
213 for (s = 0; s < length; s++) {
214 /* y[i] := y[i-1] + alpha * (x[i] - y[i-1]) */
215 filter_last = sample[pos] = filter_last + filter_alpha * (sample[pos] - filter_last);
216 pos = (pos + 1) % buffer_size;
221 void sound_init_filter(int samplerate)
223 double dt = 1.0 / (double)samplerate;
224 double C = 0.0000001;
226 double alpha = dt / (R*C + dt);
228 double cutoff = 1.0 / (2 * M_PI * R * C);
232 printf("filter-cutoff: %.0f Hz (alpha = %.4f)\n", cutoff, alpha);
234 filter_alpha = alpha;