src/ds/audio.c (view raw)
1/* Copyright (c) 2013-2017 Jeffrey Pfau
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6#include <mgba/internal/ds/audio.h>
7
8#include <mgba/core/blip_buf.h>
9#include <mgba/core/sync.h>
10#include <mgba/internal/ds/ds.h>
11
12mLOG_DEFINE_CATEGORY(DS_AUDIO, "DS Audio", "ds.audio");
13
14static const unsigned BLIP_BUFFER_SIZE = 0x4000;
15static const int CLOCKS_PER_FRAME = 0x4000;
16const int DS_AUDIO_VOLUME_MAX = 0x100;
17
18static void _updateChannel(struct mTiming* timing, void* user, uint32_t cyclesLate);
19static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate);
20static void _updateMixer(struct DSAudio*);
21
22void DSAudioInit(struct DSAudio* audio, size_t samples) {
23 audio->samples = samples;
24 audio->left = blip_new(BLIP_BUFFER_SIZE);
25 audio->right = blip_new(BLIP_BUFFER_SIZE);
26
27 audio->ch[0].updateEvent.name = "DS Audio Channel 0";
28 audio->ch[1].updateEvent.name = "DS Audio Channel 1";
29 audio->ch[2].updateEvent.name = "DS Audio Channel 2";
30 audio->ch[3].updateEvent.name = "DS Audio Channel 3";
31 audio->ch[4].updateEvent.name = "DS Audio Channel 4";
32 audio->ch[5].updateEvent.name = "DS Audio Channel 5";
33 audio->ch[6].updateEvent.name = "DS Audio Channel 6";
34 audio->ch[7].updateEvent.name = "DS Audio Channel 7";
35 audio->ch[8].updateEvent.name = "DS Audio Channel 8";
36 audio->ch[9].updateEvent.name = "DS Audio Channel 9";
37 audio->ch[10].updateEvent.name = "DS Audio Channel 10";
38 audio->ch[11].updateEvent.name = "DS Audio Channel 11";
39 audio->ch[12].updateEvent.name = "DS Audio Channel 12";
40 audio->ch[13].updateEvent.name = "DS Audio Channel 13";
41 audio->ch[14].updateEvent.name = "DS Audio Channel 14";
42 audio->ch[15].updateEvent.name = "DS Audio Channel 15";
43
44 int ch;
45 for (ch = 0; ch < 16; ++ch) {
46 audio->ch[ch].index = ch;
47 audio->ch[ch].updateEvent.priority = 0x10 | ch;
48 audio->ch[ch].updateEvent.context = &audio->ch[ch];
49 audio->ch[ch].updateEvent.callback = _updateChannel;
50 audio->ch[ch].p = audio;
51 audio->forceDisableCh[ch] = false;
52 }
53 audio->masterVolume = DS_AUDIO_VOLUME_MAX;
54
55 audio->sampleEvent.name = "DS Audio Sample";
56 audio->sampleEvent.context = audio;
57 audio->sampleEvent.callback = _sample;
58 audio->sampleEvent.priority = 0x110;
59
60 blip_set_rates(audio->left, DS_ARM7TDMI_FREQUENCY, 96000);
61 blip_set_rates(audio->right, DS_ARM7TDMI_FREQUENCY, 96000);
62}
63
64void DSAudioDeinit(struct DSAudio* audio) {
65 blip_delete(audio->left);
66 blip_delete(audio->right);
67}
68
69void DSAudioReset(struct DSAudio* audio) {
70 mTimingDeschedule(&audio->p->ds7.timing, &audio->sampleEvent);
71 mTimingSchedule(&audio->p->ds7.timing, &audio->sampleEvent, 0);
72 audio->sampleRate = 0x8000;
73 audio->sampleInterval = DS_ARM7TDMI_FREQUENCY / audio->sampleRate;
74
75 int ch;
76 for (ch = 0; ch < 16; ++ch) {
77 audio->ch[ch].source = 0;
78 audio->ch[ch].loopPoint = 0;
79 audio->ch[ch].length = 0;
80 audio->ch[ch].offset = 0;
81 audio->ch[ch].sample = 0;
82 }
83
84 blip_clear(audio->left);
85 blip_clear(audio->right);
86 audio->clock = 0;
87 audio->bias = 0x200;
88}
89
90void DSAudioResizeBuffer(struct DSAudio* audio, size_t samples) {
91 // TODO: Share between other cores
92 mCoreSyncLockAudio(audio->p->sync);
93 audio->samples = samples;
94 blip_clear(audio->left);
95 blip_clear(audio->right);
96 audio->clock = 0;
97 mCoreSyncConsumeAudio(audio->p->sync);
98}
99
100void DSAudioWriteSOUNDCNT_LO(struct DSAudio* audio, int chan, uint16_t value) {
101 audio->ch[chan].volume = DSRegisterSOUNDxCNTGetVolumeMul(value);
102 audio->ch[chan].divider = DSRegisterSOUNDxCNTGetVolumeDiv(value);
103 if (audio->ch[chan].divider == 3) {
104 ++audio->ch[chan].divider;
105 }
106}
107
108void DSAudioWriteSOUNDCNT_HI(struct DSAudio* audio, int chan, uint16_t value) {
109 DSRegisterSOUNDxCNT reg = value << 16;
110 struct DSAudioChannel* ch = &audio->ch[chan];
111
112 ch->panning = DSRegisterSOUNDxCNTGetPanning(reg);
113 ch->repeat = DSRegisterSOUNDxCNTGetRepeat(reg);
114 ch->format = DSRegisterSOUNDxCNTGetFormat(reg);
115
116 if (ch->format >= 2) {
117 mLOG(DS_AUDIO, STUB, "Unimplemented audio format %i", ch->format);
118 }
119
120 if (ch->enable && !DSRegisterSOUNDxCNTIsBusy(reg)) {
121 mTimingDeschedule(&audio->p->ds7.timing, &ch->updateEvent);
122 } else if (!ch->enable && DSRegisterSOUNDxCNTIsBusy(reg)) {
123 ch->offset = 0;
124 mTimingDeschedule(&audio->p->ds7.timing, &ch->updateEvent);
125 mTimingSchedule(&audio->p->ds7.timing, &ch->updateEvent, 0);
126 }
127 ch->enable = DSRegisterSOUNDxCNTIsBusy(reg);
128}
129
130void DSAudioWriteSOUNDTMR(struct DSAudio* audio, int chan, uint16_t value) {
131 audio->ch[chan].period = (0x10000 - value) << 1;
132}
133
134void DSAudioWriteSOUNDPNT(struct DSAudio* audio, int chan, uint16_t value) {
135 audio->ch[chan].loopPoint = value << 2;
136}
137
138void DSAudioWriteSOUNDSAD(struct DSAudio* audio, int chan, uint32_t value) {
139 audio->ch[chan].source = value;
140}
141
142void DSAudioWriteSOUNDLEN(struct DSAudio* audio, int chan, uint32_t value) {
143 audio->ch[chan].length = value << 2;
144}
145
146static void _updateMixer(struct DSAudio* audio) {
147 int32_t sampleLeft = 0;
148 int32_t sampleRight = 0;
149 int ch;
150 for (ch = 0; ch < 16; ++ch) {
151 if (!audio->ch[ch].enable) {
152 continue;
153 }
154 int32_t sample = audio->ch[ch].sample << 4;
155 sample >>= audio->ch[ch].divider;
156 sample *= audio->ch[ch].volume;
157 sample >>= 2;
158
159 int32_t left = sample * (0x7F - audio->ch[ch].panning);
160 int32_t right = sample * audio->ch[ch].panning;
161 sampleLeft += left >>= 16;
162 sampleRight += right >>= 16;
163 }
164 audio->sampleLeft = sampleLeft >> 6;
165 audio->sampleRight = sampleRight >> 6;
166}
167
168static void _updateChannel(struct mTiming* timing, void* user, uint32_t cyclesLate) {
169 struct DSAudioChannel* ch = user;
170 struct ARMCore* cpu = ch->p->p->ds7.cpu;
171 switch (ch->format) {
172 case 0:
173 ch->sample = cpu->memory.load8(cpu, ch->offset + ch->source, NULL) << 8;
174 ++ch->offset;
175 break;
176 case 1:
177 ch->sample = cpu->memory.load16(cpu, ch->offset + ch->source, NULL);
178 ch->offset += 2;
179 break;
180 case 2:
181 // TODO
182 ch->enable = false;
183 break;
184 }
185 _updateMixer(ch->p);
186 switch (ch->repeat) {
187 case 1:
188 if (ch->offset >= ch->length) {
189 ch->offset = ch->loopPoint;
190 }
191 break;
192 case 2:
193 if (ch->offset >= ch->length) {
194 ch->enable = false;
195 ch->p->p->memory.io7[(DS7_REG_SOUND0CNT_HI + (ch->index << 4)) >> 1] &= 0x7FFF;
196 }
197 break;
198 }
199 if (ch->enable) {
200 mTimingSchedule(timing, &ch->updateEvent, ch->period - cyclesLate);
201 }
202}
203
204static int _applyBias(struct DSAudio* audio, int sample) {
205 sample += audio->bias;
206 if (sample >= 0x400) {
207 sample = 0x3FF;
208 } else if (sample < 0) {
209 sample = 0;
210 }
211 return ((sample - audio->bias) * audio->masterVolume) >> 3;
212}
213
214static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate) {
215 struct DSAudio* audio = user;
216
217 int16_t sampleLeft = _applyBias(audio, audio->sampleLeft);
218 int16_t sampleRight = _applyBias(audio, audio->sampleRight);
219
220 mCoreSyncLockAudio(audio->p->sync);
221 unsigned produced;
222 if ((size_t) blip_samples_avail(audio->left) < audio->samples) {
223 blip_add_delta(audio->left, audio->clock, sampleLeft - audio->lastLeft);
224 blip_add_delta(audio->right, audio->clock, sampleRight - audio->lastRight);
225 audio->lastLeft = sampleLeft;
226 audio->lastRight = sampleRight;
227 audio->clock += audio->sampleInterval;
228 if (audio->clock >= CLOCKS_PER_FRAME) {
229 blip_end_frame(audio->left, audio->clock);
230 blip_end_frame(audio->right, audio->clock);
231 audio->clock -= CLOCKS_PER_FRAME;
232 }
233 }
234 produced = blip_samples_avail(audio->left);
235 if (audio->p->stream && audio->p->stream->postAudioFrame) {
236 audio->p->stream->postAudioFrame(audio->p->stream, sampleLeft, sampleRight);
237 }
238 bool wait = produced >= audio->samples;
239 mCoreSyncProduceAudio(audio->p->sync, wait);
240
241 if (wait && audio->p->stream && audio->p->stream->postAudioBuffer) {
242 audio->p->stream->postAudioBuffer(audio->p->stream, audio->left, audio->right);
243 }
244 mTimingSchedule(timing, &audio->sampleEvent, audio->sampleInterval - cyclesLate);
245}