mirror of https://github.com/mgba-emu/mgba.git
DS Audio: Initial implementation, only PCM
This commit is contained in:
parent
ebac3c65ea
commit
498aa541fc
|
@ -0,0 +1,102 @@
|
|||
/* Copyright (c) 2013-2017 Jeffrey Pfau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#ifndef DS_AUDIO_H
|
||||
#define DS_AUDIO_H
|
||||
|
||||
#include <mgba-util/common.h>
|
||||
|
||||
CXX_GUARD_START
|
||||
|
||||
#include <mgba/core/log.h>
|
||||
#include <mgba/core/timing.h>
|
||||
|
||||
mLOG_DECLARE_CATEGORY(DS_AUDIO);
|
||||
|
||||
DECL_BITFIELD(DSRegisterSOUNDxCNT, uint32_t);
|
||||
DECL_BITS(DSRegisterSOUNDxCNT, VolumeMul, 0, 7);
|
||||
DECL_BITS(DSRegisterSOUNDxCNT, VolumeDiv, 8, 2);
|
||||
DECL_BIT(DSRegisterSOUNDxCNT, Hold, 15);
|
||||
DECL_BITS(DSRegisterSOUNDxCNT, Panning, 16, 7);
|
||||
DECL_BITS(DSRegisterSOUNDxCNT, Duty, 24, 3);
|
||||
DECL_BITS(DSRegisterSOUNDxCNT, Repeat, 27, 2);
|
||||
DECL_BITS(DSRegisterSOUNDxCNT, Format, 29, 2);
|
||||
DECL_BIT(DSRegisterSOUNDxCNT, Busy, 31);
|
||||
|
||||
struct DSAudio;
|
||||
struct DSAudioChannel {
|
||||
struct DSAudio* p;
|
||||
int index;
|
||||
|
||||
struct mTimingEvent updateEvent;
|
||||
|
||||
uint32_t source;
|
||||
uint32_t loopPoint;
|
||||
uint32_t length;
|
||||
uint32_t offset;
|
||||
|
||||
unsigned volume;
|
||||
unsigned divider;
|
||||
int panning;
|
||||
int format;
|
||||
int repeat;
|
||||
|
||||
uint32_t period;
|
||||
int16_t sample;
|
||||
|
||||
int adpcmOffset;
|
||||
|
||||
int16_t adpcmStartSample;
|
||||
unsigned adpcmStartIndex;
|
||||
|
||||
int16_t adpcmSample;
|
||||
unsigned adpcmIndex;
|
||||
|
||||
bool enable;
|
||||
};
|
||||
|
||||
struct DS;
|
||||
struct DSAudio {
|
||||
struct DS* p;
|
||||
struct blip_t* left;
|
||||
struct blip_t* right;
|
||||
|
||||
struct DSAudioChannel ch[16];
|
||||
|
||||
int16_t lastLeft;
|
||||
int16_t lastRight;
|
||||
int clock;
|
||||
|
||||
int16_t sampleLeft;
|
||||
int16_t sampleRight;
|
||||
|
||||
size_t samples;
|
||||
unsigned sampleRate;
|
||||
|
||||
int32_t sampleInterval;
|
||||
|
||||
bool forceDisableCh[16];
|
||||
int bias;
|
||||
int masterVolume;
|
||||
|
||||
struct mTimingEvent sampleEvent;
|
||||
};
|
||||
|
||||
void DSAudioInit(struct DSAudio*, size_t samples);
|
||||
void DSAudioDeinit(struct DSAudio*);
|
||||
void DSAudioReset(struct DSAudio*);
|
||||
|
||||
void DSAudioResizeBuffer(struct DSAudio* audio, size_t samples);
|
||||
|
||||
void DSAudioWriteSOUNDCNT_LO(struct DSAudio*, int chan, uint16_t value);
|
||||
void DSAudioWriteSOUNDCNT_HI(struct DSAudio*, int chan, uint16_t value);
|
||||
void DSAudioWriteSOUNDTMR(struct DSAudio*, int chan, uint16_t value);
|
||||
void DSAudioWriteSOUNDPNT(struct DSAudio*, int chan, uint16_t value);
|
||||
void DSAudioWriteSOUNDSAD(struct DSAudio*, int chan, uint32_t value);
|
||||
void DSAudioWriteSOUNDLEN(struct DSAudio*, int chan, uint32_t value);
|
||||
|
||||
CXX_GUARD_END
|
||||
|
||||
#endif
|
|
@ -14,6 +14,7 @@ CXX_GUARD_START
|
|||
#include <mgba/core/timing.h>
|
||||
#include <mgba-util/circle-buffer.h>
|
||||
|
||||
#include <mgba/internal/ds/audio.h>
|
||||
#include <mgba/internal/ds/gx.h>
|
||||
#include <mgba/internal/ds/memory.h>
|
||||
#include <mgba/internal/ds/timer.h>
|
||||
|
@ -85,6 +86,7 @@ struct DS {
|
|||
struct DSCommon ds9;
|
||||
struct DSMemory memory;
|
||||
struct DSVideo video;
|
||||
struct DSAudio audio;
|
||||
struct DSGX gx;
|
||||
struct DSWifi wifi;
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (c) 2013-2016 Jeffrey Pfau
|
||||
/* Copyright (c) 2013-2017 Jeffrey Pfau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
|
@ -121,6 +121,138 @@ enum DS7IORegisters {
|
|||
DS7_REG_BIOSPROT_LO = 0x308,
|
||||
DS7_REG_BIOSPROT_HI = 0x30A,
|
||||
|
||||
// Audio
|
||||
DS7_REG_SOUND0CNT_LO = 0x400,
|
||||
DS7_REG_SOUND0CNT_HI = 0x402,
|
||||
DS7_REG_SOUND0SAD_LO = 0x404,
|
||||
DS7_REG_SOUND0SAD_HI = 0x406,
|
||||
DS7_REG_SOUND0TMR = 0x408,
|
||||
DS7_REG_SOUND0PNT = 0x40A,
|
||||
DS7_REG_SOUND0LEN_LO = 0x40C,
|
||||
DS7_REG_SOUND0LEN_HI = 0x40E,
|
||||
DS7_REG_SOUND1CNT_LO = 0x410,
|
||||
DS7_REG_SOUND1CNT_HI = 0x412,
|
||||
DS7_REG_SOUND1SAD_LO = 0x414,
|
||||
DS7_REG_SOUND1SAD_HI = 0x416,
|
||||
DS7_REG_SOUND1TMR = 0x418,
|
||||
DS7_REG_SOUND1PNT = 0x41A,
|
||||
DS7_REG_SOUND1LEN_LO = 0x41C,
|
||||
DS7_REG_SOUND1LEN_HI = 0x41E,
|
||||
DS7_REG_SOUND2CNT_LO = 0x420,
|
||||
DS7_REG_SOUND2CNT_HI = 0x422,
|
||||
DS7_REG_SOUND2SAD_LO = 0x424,
|
||||
DS7_REG_SOUND2SAD_HI = 0x426,
|
||||
DS7_REG_SOUND2TMR = 0x428,
|
||||
DS7_REG_SOUND2PNT = 0x42A,
|
||||
DS7_REG_SOUND2LEN_LO = 0x42C,
|
||||
DS7_REG_SOUND2LEN_HI = 0x42E,
|
||||
DS7_REG_SOUND3CNT_LO = 0x430,
|
||||
DS7_REG_SOUND3CNT_HI = 0x432,
|
||||
DS7_REG_SOUND3SAD_LO = 0x434,
|
||||
DS7_REG_SOUND3SAD_HI = 0x436,
|
||||
DS7_REG_SOUND3TMR = 0x438,
|
||||
DS7_REG_SOUND3PNT = 0x43A,
|
||||
DS7_REG_SOUND3LEN_LO = 0x43C,
|
||||
DS7_REG_SOUND3LEN_HI = 0x43E,
|
||||
DS7_REG_SOUND4CNT_LO = 0x440,
|
||||
DS7_REG_SOUND4CNT_HI = 0x442,
|
||||
DS7_REG_SOUND4SAD_LO = 0x444,
|
||||
DS7_REG_SOUND4SAD_HI = 0x446,
|
||||
DS7_REG_SOUND4TMR = 0x448,
|
||||
DS7_REG_SOUND4PNT = 0x44A,
|
||||
DS7_REG_SOUND4LEN_LO = 0x44C,
|
||||
DS7_REG_SOUND4LEN_HI = 0x44E,
|
||||
DS7_REG_SOUND5CNT_LO = 0x450,
|
||||
DS7_REG_SOUND5CNT_HI = 0x452,
|
||||
DS7_REG_SOUND5SAD_LO = 0x454,
|
||||
DS7_REG_SOUND5SAD_HI = 0x456,
|
||||
DS7_REG_SOUND5TMR = 0x458,
|
||||
DS7_REG_SOUND5PNT = 0x45A,
|
||||
DS7_REG_SOUND5LEN_LO = 0x45C,
|
||||
DS7_REG_SOUND5LEN_HI = 0x45E,
|
||||
DS7_REG_SOUND6CNT_LO = 0x460,
|
||||
DS7_REG_SOUND6CNT_HI = 0x462,
|
||||
DS7_REG_SOUND6SAD_LO = 0x464,
|
||||
DS7_REG_SOUND6SAD_HI = 0x466,
|
||||
DS7_REG_SOUND6TMR = 0x468,
|
||||
DS7_REG_SOUND6PNT = 0x46A,
|
||||
DS7_REG_SOUND6LEN_LO = 0x46C,
|
||||
DS7_REG_SOUND6LEN_HI = 0x46E,
|
||||
DS7_REG_SOUND7CNT_LO = 0x470,
|
||||
DS7_REG_SOUND7CNT_HI = 0x472,
|
||||
DS7_REG_SOUND7SAD_LO = 0x474,
|
||||
DS7_REG_SOUND7SAD_HI = 0x476,
|
||||
DS7_REG_SOUND7TMR = 0x478,
|
||||
DS7_REG_SOUND7PNT = 0x47A,
|
||||
DS7_REG_SOUND7LEN_LO = 0x47C,
|
||||
DS7_REG_SOUND7LEN_HI = 0x47E,
|
||||
DS7_REG_SOUND8CNT_LO = 0x480,
|
||||
DS7_REG_SOUND8CNT_HI = 0x482,
|
||||
DS7_REG_SOUND8SAD_LO = 0x484,
|
||||
DS7_REG_SOUND8SAD_HI = 0x486,
|
||||
DS7_REG_SOUND8TMR = 0x488,
|
||||
DS7_REG_SOUND8PNT = 0x48A,
|
||||
DS7_REG_SOUND8LEN_LO = 0x48C,
|
||||
DS7_REG_SOUND8LEN_HI = 0x48E,
|
||||
DS7_REG_SOUND9CNT_LO = 0x490,
|
||||
DS7_REG_SOUND9CNT_HI = 0x492,
|
||||
DS7_REG_SOUND9SAD_LO = 0x494,
|
||||
DS7_REG_SOUND9SAD_HI = 0x496,
|
||||
DS7_REG_SOUND9TMR = 0x498,
|
||||
DS7_REG_SOUND9PNT = 0x49A,
|
||||
DS7_REG_SOUND9LEN_LO = 0x49C,
|
||||
DS7_REG_SOUND9LEN_HI = 0x49E,
|
||||
DS7_REG_SOUNDACNT_LO = 0x4A0,
|
||||
DS7_REG_SOUNDACNT_HI = 0x4A2,
|
||||
DS7_REG_SOUNDASAD_LO = 0x4A4,
|
||||
DS7_REG_SOUNDASAD_HI = 0x4A6,
|
||||
DS7_REG_SOUNDATMR = 0x4A8,
|
||||
DS7_REG_SOUNDAPNT = 0x4AA,
|
||||
DS7_REG_SOUNDALEN_LO = 0x4AC,
|
||||
DS7_REG_SOUNDALEN_HI = 0x4AE,
|
||||
DS7_REG_SOUNDBCNT_LO = 0x4B0,
|
||||
DS7_REG_SOUNDBCNT_HI = 0x4B2,
|
||||
DS7_REG_SOUNDBSAD_LO = 0x4B4,
|
||||
DS7_REG_SOUNDBSAD_HI = 0x4B6,
|
||||
DS7_REG_SOUNDBTMR = 0x4B8,
|
||||
DS7_REG_SOUNDBPNT = 0x4BA,
|
||||
DS7_REG_SOUNDBLEN_LO = 0x4BC,
|
||||
DS7_REG_SOUNDBLEN_HI = 0x4BE,
|
||||
DS7_REG_SOUNDCCNT_LO = 0x4C0,
|
||||
DS7_REG_SOUNDCCNT_HI = 0x4C2,
|
||||
DS7_REG_SOUNDCSAD_LO = 0x4C4,
|
||||
DS7_REG_SOUNDCSAD_HI = 0x4C6,
|
||||
DS7_REG_SOUNDCTMR = 0x4C8,
|
||||
DS7_REG_SOUNDCPNT = 0x4CA,
|
||||
DS7_REG_SOUNDCLEN_LO = 0x4CC,
|
||||
DS7_REG_SOUNDCLEN_HI = 0x4CE,
|
||||
DS7_REG_SOUNDDCNT_LO = 0x4D0,
|
||||
DS7_REG_SOUNDDCNT_HI = 0x4D2,
|
||||
DS7_REG_SOUNDDSAD_LO = 0x4D4,
|
||||
DS7_REG_SOUNDDSAD_HI = 0x4D6,
|
||||
DS7_REG_SOUNDDTMR = 0x4D8,
|
||||
DS7_REG_SOUNDDPNT = 0x4DA,
|
||||
DS7_REG_SOUNDDLEN_LO = 0x4DC,
|
||||
DS7_REG_SOUNDDLEN_HI = 0x4DE,
|
||||
DS7_REG_SOUNDECNT_LO = 0x4E0,
|
||||
DS7_REG_SOUNDECNT_HI = 0x4E2,
|
||||
DS7_REG_SOUNDESAD_LO = 0x4E4,
|
||||
DS7_REG_SOUNDESAD_HI = 0x4E6,
|
||||
DS7_REG_SOUNDETMR = 0x4E8,
|
||||
DS7_REG_SOUNDEPNT = 0x4EA,
|
||||
DS7_REG_SOUNDELEN_LO = 0x4EC,
|
||||
DS7_REG_SOUNDELEN_HI = 0x4EE,
|
||||
DS7_REG_SOUNDFCNT_LO = 0x4F0,
|
||||
DS7_REG_SOUNDFCNT_HI = 0x4F2,
|
||||
DS7_REG_SOUNDFSAD_LO = 0x4F4,
|
||||
DS7_REG_SOUNDFSAD_HI = 0x4F6,
|
||||
DS7_REG_SOUNDFTMR = 0x4F8,
|
||||
DS7_REG_SOUNDFPNT = 0x4FA,
|
||||
DS7_REG_SOUNDFLEN_LO = 0x4FC,
|
||||
DS7_REG_SOUNDFLEN_HI = 0x4FE,
|
||||
DS7_REG_SOUNDCNT = 0x500,
|
||||
DS7_REG_SOUNDBIAS = 0x504,
|
||||
|
||||
DS7_REG_MAX = 0x51E,
|
||||
|
||||
DS7_IO_BASE_WIFI = 0x800000,
|
||||
|
|
|
@ -0,0 +1,245 @@
|
|||
/* Copyright (c) 2013-2017 Jeffrey Pfau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#include <mgba/internal/ds/audio.h>
|
||||
|
||||
#include <mgba/core/blip_buf.h>
|
||||
#include <mgba/core/sync.h>
|
||||
#include <mgba/internal/ds/ds.h>
|
||||
|
||||
mLOG_DEFINE_CATEGORY(DS_AUDIO, "DS Audio", "ds.audio");
|
||||
|
||||
static const unsigned BLIP_BUFFER_SIZE = 0x4000;
|
||||
static const int CLOCKS_PER_FRAME = 0x4000;
|
||||
const int DS_AUDIO_VOLUME_MAX = 0x100;
|
||||
|
||||
static void _updateChannel(struct mTiming* timing, void* user, uint32_t cyclesLate);
|
||||
static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate);
|
||||
static void _updateMixer(struct DSAudio*);
|
||||
|
||||
void DSAudioInit(struct DSAudio* audio, size_t samples) {
|
||||
audio->samples = samples;
|
||||
audio->left = blip_new(BLIP_BUFFER_SIZE);
|
||||
audio->right = blip_new(BLIP_BUFFER_SIZE);
|
||||
|
||||
audio->ch[0].updateEvent.name = "DS Audio Channel 0";
|
||||
audio->ch[1].updateEvent.name = "DS Audio Channel 1";
|
||||
audio->ch[2].updateEvent.name = "DS Audio Channel 2";
|
||||
audio->ch[3].updateEvent.name = "DS Audio Channel 3";
|
||||
audio->ch[4].updateEvent.name = "DS Audio Channel 4";
|
||||
audio->ch[5].updateEvent.name = "DS Audio Channel 5";
|
||||
audio->ch[6].updateEvent.name = "DS Audio Channel 6";
|
||||
audio->ch[7].updateEvent.name = "DS Audio Channel 7";
|
||||
audio->ch[8].updateEvent.name = "DS Audio Channel 8";
|
||||
audio->ch[9].updateEvent.name = "DS Audio Channel 9";
|
||||
audio->ch[10].updateEvent.name = "DS Audio Channel 10";
|
||||
audio->ch[11].updateEvent.name = "DS Audio Channel 11";
|
||||
audio->ch[12].updateEvent.name = "DS Audio Channel 12";
|
||||
audio->ch[13].updateEvent.name = "DS Audio Channel 13";
|
||||
audio->ch[14].updateEvent.name = "DS Audio Channel 14";
|
||||
audio->ch[15].updateEvent.name = "DS Audio Channel 15";
|
||||
|
||||
int ch;
|
||||
for (ch = 0; ch < 16; ++ch) {
|
||||
audio->ch[ch].index = ch;
|
||||
audio->ch[ch].updateEvent.priority = 0x10 | ch;
|
||||
audio->ch[ch].updateEvent.context = &audio->ch[ch];
|
||||
audio->ch[ch].updateEvent.callback = _updateChannel;
|
||||
audio->ch[ch].p = audio;
|
||||
audio->forceDisableCh[ch] = false;
|
||||
}
|
||||
audio->masterVolume = DS_AUDIO_VOLUME_MAX;
|
||||
|
||||
audio->sampleEvent.name = "DS Audio Sample";
|
||||
audio->sampleEvent.context = audio;
|
||||
audio->sampleEvent.callback = _sample;
|
||||
audio->sampleEvent.priority = 0x110;
|
||||
|
||||
blip_set_rates(audio->left, DS_ARM7TDMI_FREQUENCY, 96000);
|
||||
blip_set_rates(audio->right, DS_ARM7TDMI_FREQUENCY, 96000);
|
||||
}
|
||||
|
||||
void DSAudioDeinit(struct DSAudio* audio) {
|
||||
blip_delete(audio->left);
|
||||
blip_delete(audio->right);
|
||||
}
|
||||
|
||||
void DSAudioReset(struct DSAudio* audio) {
|
||||
mTimingDeschedule(&audio->p->ds7.timing, &audio->sampleEvent);
|
||||
mTimingSchedule(&audio->p->ds7.timing, &audio->sampleEvent, 0);
|
||||
audio->sampleRate = 0x8000;
|
||||
audio->sampleInterval = DS_ARM7TDMI_FREQUENCY / audio->sampleRate;
|
||||
|
||||
int ch;
|
||||
for (ch = 0; ch < 16; ++ch) {
|
||||
audio->ch[ch].source = 0;
|
||||
audio->ch[ch].loopPoint = 0;
|
||||
audio->ch[ch].length = 0;
|
||||
audio->ch[ch].offset = 0;
|
||||
audio->ch[ch].sample = 0;
|
||||
}
|
||||
|
||||
blip_clear(audio->left);
|
||||
blip_clear(audio->right);
|
||||
audio->clock = 0;
|
||||
audio->bias = 0x200;
|
||||
}
|
||||
|
||||
void DSAudioResizeBuffer(struct DSAudio* audio, size_t samples) {
|
||||
// TODO: Share between other cores
|
||||
mCoreSyncLockAudio(audio->p->sync);
|
||||
audio->samples = samples;
|
||||
blip_clear(audio->left);
|
||||
blip_clear(audio->right);
|
||||
audio->clock = 0;
|
||||
mCoreSyncConsumeAudio(audio->p->sync);
|
||||
}
|
||||
|
||||
void DSAudioWriteSOUNDCNT_LO(struct DSAudio* audio, int chan, uint16_t value) {
|
||||
audio->ch[chan].volume = DSRegisterSOUNDxCNTGetVolumeMul(value);
|
||||
audio->ch[chan].divider = DSRegisterSOUNDxCNTGetVolumeDiv(value);
|
||||
if (audio->ch[chan].divider == 3) {
|
||||
++audio->ch[chan].divider;
|
||||
}
|
||||
}
|
||||
|
||||
void DSAudioWriteSOUNDCNT_HI(struct DSAudio* audio, int chan, uint16_t value) {
|
||||
DSRegisterSOUNDxCNT reg = value << 16;
|
||||
struct DSAudioChannel* ch = &audio->ch[chan];
|
||||
|
||||
ch->panning = DSRegisterSOUNDxCNTGetPanning(reg);
|
||||
ch->repeat = DSRegisterSOUNDxCNTGetRepeat(reg);
|
||||
ch->format = DSRegisterSOUNDxCNTGetFormat(reg);
|
||||
|
||||
if (ch->format >= 2) {
|
||||
mLOG(DS_AUDIO, STUB, "Unimplemented audio format %i", ch->format);
|
||||
}
|
||||
|
||||
if (ch->enable && !DSRegisterSOUNDxCNTIsBusy(reg)) {
|
||||
mTimingDeschedule(&audio->p->ds7.timing, &ch->updateEvent);
|
||||
} else if (!ch->enable && DSRegisterSOUNDxCNTIsBusy(reg)) {
|
||||
ch->offset = 0;
|
||||
mTimingDeschedule(&audio->p->ds7.timing, &ch->updateEvent);
|
||||
mTimingSchedule(&audio->p->ds7.timing, &ch->updateEvent, 0);
|
||||
}
|
||||
ch->enable = DSRegisterSOUNDxCNTIsBusy(reg);
|
||||
}
|
||||
|
||||
void DSAudioWriteSOUNDTMR(struct DSAudio* audio, int chan, uint16_t value) {
|
||||
audio->ch[chan].period = (0x10000 - value) << 1;
|
||||
}
|
||||
|
||||
void DSAudioWriteSOUNDPNT(struct DSAudio* audio, int chan, uint16_t value) {
|
||||
audio->ch[chan].loopPoint = value << 2;
|
||||
}
|
||||
|
||||
void DSAudioWriteSOUNDSAD(struct DSAudio* audio, int chan, uint32_t value) {
|
||||
audio->ch[chan].source = value;
|
||||
}
|
||||
|
||||
void DSAudioWriteSOUNDLEN(struct DSAudio* audio, int chan, uint32_t value) {
|
||||
audio->ch[chan].length = value << 2;
|
||||
}
|
||||
|
||||
static void _updateMixer(struct DSAudio* audio) {
|
||||
int32_t sampleLeft = 0;
|
||||
int32_t sampleRight = 0;
|
||||
int ch;
|
||||
for (ch = 0; ch < 16; ++ch) {
|
||||
if (!audio->ch[ch].enable) {
|
||||
continue;
|
||||
}
|
||||
int32_t sample = audio->ch[ch].sample << 4;
|
||||
sample >>= audio->ch[ch].divider;
|
||||
sample *= audio->ch[ch].volume;
|
||||
sample >>= 2;
|
||||
|
||||
int32_t left = sample * (0x7F - audio->ch[ch].panning);
|
||||
int32_t right = sample * audio->ch[ch].panning;
|
||||
sampleLeft += left >>= 16;
|
||||
sampleRight += right >>= 16;
|
||||
}
|
||||
audio->sampleLeft = sampleLeft >> 6;
|
||||
audio->sampleRight = sampleRight >> 6;
|
||||
}
|
||||
|
||||
static void _updateChannel(struct mTiming* timing, void* user, uint32_t cyclesLate) {
|
||||
struct DSAudioChannel* ch = user;
|
||||
struct ARMCore* cpu = ch->p->p->ds7.cpu;
|
||||
switch (ch->format) {
|
||||
case 0:
|
||||
ch->sample = cpu->memory.load8(cpu, ch->offset + ch->source, NULL) << 8;
|
||||
++ch->offset;
|
||||
break;
|
||||
case 1:
|
||||
ch->sample = cpu->memory.load16(cpu, ch->offset + ch->source, NULL);
|
||||
ch->offset += 2;
|
||||
break;
|
||||
case 2:
|
||||
// TODO
|
||||
ch->enable = false;
|
||||
break;
|
||||
}
|
||||
_updateMixer(ch->p);
|
||||
switch (ch->repeat) {
|
||||
case 1:
|
||||
if (ch->offset >= ch->length) {
|
||||
ch->offset = ch->loopPoint;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if (ch->offset >= ch->length) {
|
||||
ch->enable = false;
|
||||
ch->p->p->memory.io7[(DS7_REG_SOUND0CNT_HI + (ch->index << 4)) >> 1] &= 0x7FFF;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (ch->enable) {
|
||||
mTimingSchedule(timing, &ch->updateEvent, ch->period - cyclesLate);
|
||||
}
|
||||
}
|
||||
|
||||
static int _applyBias(struct DSAudio* audio, int sample) {
|
||||
sample += audio->bias;
|
||||
if (sample >= 0x400) {
|
||||
sample = 0x3FF;
|
||||
} else if (sample < 0) {
|
||||
sample = 0;
|
||||
}
|
||||
return ((sample - audio->bias) * audio->masterVolume) >> 3;
|
||||
}
|
||||
|
||||
static void _sample(struct mTiming* timing, void* user, uint32_t cyclesLate) {
|
||||
struct DSAudio* audio = user;
|
||||
|
||||
int16_t sampleLeft = _applyBias(audio, audio->sampleLeft);
|
||||
int16_t sampleRight = _applyBias(audio, audio->sampleRight);
|
||||
|
||||
mCoreSyncLockAudio(audio->p->sync);
|
||||
unsigned produced;
|
||||
if ((size_t) blip_samples_avail(audio->left) < audio->samples) {
|
||||
blip_add_delta(audio->left, audio->clock, sampleLeft - audio->lastLeft);
|
||||
blip_add_delta(audio->right, audio->clock, sampleRight - audio->lastRight);
|
||||
audio->lastLeft = sampleLeft;
|
||||
audio->lastRight = sampleRight;
|
||||
audio->clock += audio->sampleInterval;
|
||||
if (audio->clock >= CLOCKS_PER_FRAME) {
|
||||
blip_end_frame(audio->left, audio->clock);
|
||||
blip_end_frame(audio->right, audio->clock);
|
||||
audio->clock -= CLOCKS_PER_FRAME;
|
||||
}
|
||||
}
|
||||
produced = blip_samples_avail(audio->left);
|
||||
if (audio->p->stream && audio->p->stream->postAudioFrame) {
|
||||
audio->p->stream->postAudioFrame(audio->p->stream, sampleLeft, sampleRight);
|
||||
}
|
||||
bool wait = produced >= audio->samples;
|
||||
mCoreSyncProduceAudio(audio->p->sync, wait);
|
||||
|
||||
if (wait && audio->p->stream && audio->p->stream->postAudioBuffer) {
|
||||
audio->p->stream->postAudioBuffer(audio->p->stream, audio->left, audio->right);
|
||||
}
|
||||
mTimingSchedule(timing, &audio->sampleEvent, audio->sampleInterval - cyclesLate);
|
||||
}
|
|
@ -146,14 +146,25 @@ static void _DSCorePutPixels(struct mCore* core, const void* buffer, size_t stri
|
|||
}
|
||||
|
||||
static struct blip_t* _DSCoreGetAudioChannel(struct mCore* core, int ch) {
|
||||
return NULL;
|
||||
struct DS* ds = core->board;
|
||||
switch (ch) {
|
||||
case 0:
|
||||
return ds->audio.left;
|
||||
case 1:
|
||||
return ds->audio.right;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void _DSCoreSetAudioBufferSize(struct mCore* core, size_t samples) {
|
||||
struct DS* ds = core->board;
|
||||
DSAudioResizeBuffer(&ds->audio, samples);
|
||||
}
|
||||
|
||||
static size_t _DSCoreGetAudioBufferSize(struct mCore* core) {
|
||||
return 2048;
|
||||
struct DS* ds = core->board;
|
||||
return ds->audio.samples;
|
||||
}
|
||||
|
||||
static void _DSCoreAddCoreCallbacks(struct mCore* core, struct mCoreCallbacks* coreCallbacks) {
|
||||
|
|
|
@ -199,6 +199,9 @@ static void DSInit(void* cpu, struct mCPUComponent* component) {
|
|||
DSGXInit(&ds->gx);
|
||||
ds->gx.p = ds;
|
||||
|
||||
DSAudioInit(&ds->audio, 2048);
|
||||
ds->audio.p = ds;
|
||||
|
||||
ds->ds7.springIRQ = 0;
|
||||
ds->ds9.springIRQ = 0;
|
||||
DSTimerInit(ds);
|
||||
|
@ -240,6 +243,7 @@ void DSDestroy(struct DS* ds) {
|
|||
DSUnloadROM(ds);
|
||||
DSMemoryDeinit(ds);
|
||||
DSGXDeinit(&ds->gx);
|
||||
DSAudioDeinit(&ds->audio);
|
||||
mTimingDeinit(&ds->ds7.timing);
|
||||
mTimingDeinit(&ds->ds9.timing);
|
||||
mCoreCallbacksListDeinit(&ds->coreCallbacks);
|
||||
|
@ -286,6 +290,7 @@ void DS7Reset(struct ARMCore* cpu) {
|
|||
CircleBufferInit(&ds->ds7.fifo, 64);
|
||||
DSMemoryReset(ds);
|
||||
DSDMAReset(&ds->ds7);
|
||||
DSAudioReset(&ds->audio);
|
||||
DS7IOInit(ds);
|
||||
|
||||
DSConfigureWRAM(&ds->memory, 3);
|
||||
|
|
117
src/ds/io.c
117
src/ds/io.c
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (c) 2013-2016 Jeffrey Pfau
|
||||
/* Copyright (c) 2013-2017 Jeffrey Pfau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
|
@ -6,6 +6,7 @@
|
|||
#include <mgba/internal/ds/io.h>
|
||||
|
||||
#include <mgba/core/interface.h>
|
||||
#include <mgba/internal/ds/audio.h>
|
||||
#include <mgba/internal/ds/ds.h>
|
||||
#include <mgba/internal/ds/gx.h>
|
||||
#include <mgba/internal/ds/ipc.h>
|
||||
|
@ -272,6 +273,80 @@ void DS7IOWrite(struct DS* ds, uint32_t address, uint16_t value) {
|
|||
case DS7_REG_SPIDATA:
|
||||
DSSPIWrite(ds, value);
|
||||
break;
|
||||
case DS7_REG_SOUND0CNT_LO:
|
||||
case DS7_REG_SOUND1CNT_LO:
|
||||
case DS7_REG_SOUND2CNT_LO:
|
||||
case DS7_REG_SOUND3CNT_LO:
|
||||
case DS7_REG_SOUND4CNT_LO:
|
||||
case DS7_REG_SOUND5CNT_LO:
|
||||
case DS7_REG_SOUND6CNT_LO:
|
||||
case DS7_REG_SOUND7CNT_LO:
|
||||
case DS7_REG_SOUND8CNT_LO:
|
||||
case DS7_REG_SOUND9CNT_LO:
|
||||
case DS7_REG_SOUNDACNT_LO:
|
||||
case DS7_REG_SOUNDBCNT_LO:
|
||||
case DS7_REG_SOUNDCCNT_LO:
|
||||
case DS7_REG_SOUNDDCNT_LO:
|
||||
case DS7_REG_SOUNDECNT_LO:
|
||||
case DS7_REG_SOUNDFCNT_LO:
|
||||
value &= 0x837F;
|
||||
DSAudioWriteSOUNDCNT_LO(&ds->audio, (address - DS7_REG_SOUND0CNT_LO) >> 4, value);
|
||||
break;
|
||||
case DS7_REG_SOUND0CNT_HI:
|
||||
case DS7_REG_SOUND1CNT_HI:
|
||||
case DS7_REG_SOUND2CNT_HI:
|
||||
case DS7_REG_SOUND3CNT_HI:
|
||||
case DS7_REG_SOUND4CNT_HI:
|
||||
case DS7_REG_SOUND5CNT_HI:
|
||||
case DS7_REG_SOUND6CNT_HI:
|
||||
case DS7_REG_SOUND7CNT_HI:
|
||||
case DS7_REG_SOUND8CNT_HI:
|
||||
case DS7_REG_SOUND9CNT_HI:
|
||||
case DS7_REG_SOUNDACNT_HI:
|
||||
case DS7_REG_SOUNDBCNT_HI:
|
||||
case DS7_REG_SOUNDCCNT_HI:
|
||||
case DS7_REG_SOUNDDCNT_HI:
|
||||
case DS7_REG_SOUNDECNT_HI:
|
||||
case DS7_REG_SOUNDFCNT_HI:
|
||||
value &= 0xFF7F;
|
||||
DSAudioWriteSOUNDCNT_HI(&ds->audio, (address - DS7_REG_SOUND0CNT_HI) >> 4, value);
|
||||
break;
|
||||
case DS7_REG_SOUND0TMR:
|
||||
case DS7_REG_SOUND1TMR:
|
||||
case DS7_REG_SOUND2TMR:
|
||||
case DS7_REG_SOUND3TMR:
|
||||
case DS7_REG_SOUND4TMR:
|
||||
case DS7_REG_SOUND5TMR:
|
||||
case DS7_REG_SOUND6TMR:
|
||||
case DS7_REG_SOUND7TMR:
|
||||
case DS7_REG_SOUND8TMR:
|
||||
case DS7_REG_SOUND9TMR:
|
||||
case DS7_REG_SOUNDATMR:
|
||||
case DS7_REG_SOUNDBTMR:
|
||||
case DS7_REG_SOUNDCTMR:
|
||||
case DS7_REG_SOUNDDTMR:
|
||||
case DS7_REG_SOUNDETMR:
|
||||
case DS7_REG_SOUNDFTMR:
|
||||
DSAudioWriteSOUNDTMR(&ds->audio, (address - DS7_REG_SOUND0TMR) >> 4, value);
|
||||
break;
|
||||
case DS7_REG_SOUND0PNT:
|
||||
case DS7_REG_SOUND1PNT:
|
||||
case DS7_REG_SOUND2PNT:
|
||||
case DS7_REG_SOUND3PNT:
|
||||
case DS7_REG_SOUND4PNT:
|
||||
case DS7_REG_SOUND5PNT:
|
||||
case DS7_REG_SOUND6PNT:
|
||||
case DS7_REG_SOUND7PNT:
|
||||
case DS7_REG_SOUND8PNT:
|
||||
case DS7_REG_SOUND9PNT:
|
||||
case DS7_REG_SOUNDAPNT:
|
||||
case DS7_REG_SOUNDBPNT:
|
||||
case DS7_REG_SOUNDCPNT:
|
||||
case DS7_REG_SOUNDDPNT:
|
||||
case DS7_REG_SOUNDEPNT:
|
||||
case DS7_REG_SOUNDFPNT:
|
||||
DSAudioWriteSOUNDPNT(&ds->audio, (address - DS7_REG_SOUND0PNT) >> 4, value);
|
||||
break;
|
||||
default:
|
||||
{
|
||||
uint32_t v2 = DSIOWrite(&ds->ds7, address, value);
|
||||
|
@ -337,6 +412,46 @@ void DS7IOWrite32(struct DS* ds, uint32_t address, uint32_t value) {
|
|||
case DS_REG_DMA3CNT_LO:
|
||||
DS7DMAWriteCNT(&ds->ds7, 3, value);
|
||||
break;
|
||||
|
||||
case DS7_REG_SOUND0SAD_LO:
|
||||
case DS7_REG_SOUND1SAD_LO:
|
||||
case DS7_REG_SOUND2SAD_LO:
|
||||
case DS7_REG_SOUND3SAD_LO:
|
||||
case DS7_REG_SOUND4SAD_LO:
|
||||
case DS7_REG_SOUND5SAD_LO:
|
||||
case DS7_REG_SOUND6SAD_LO:
|
||||
case DS7_REG_SOUND7SAD_LO:
|
||||
case DS7_REG_SOUND8SAD_LO:
|
||||
case DS7_REG_SOUND9SAD_LO:
|
||||
case DS7_REG_SOUNDASAD_LO:
|
||||
case DS7_REG_SOUNDBSAD_LO:
|
||||
case DS7_REG_SOUNDCSAD_LO:
|
||||
case DS7_REG_SOUNDDSAD_LO:
|
||||
case DS7_REG_SOUNDESAD_LO:
|
||||
case DS7_REG_SOUNDFSAD_LO:
|
||||
DSAudioWriteSOUNDSAD(&ds->audio, (address - DS7_REG_SOUND0SAD_LO) >> 4, value);
|
||||
break;
|
||||
|
||||
case DS7_REG_SOUND0LEN_LO:
|
||||
case DS7_REG_SOUND1LEN_LO:
|
||||
case DS7_REG_SOUND2LEN_LO:
|
||||
case DS7_REG_SOUND3LEN_LO:
|
||||
case DS7_REG_SOUND4LEN_LO:
|
||||
case DS7_REG_SOUND5LEN_LO:
|
||||
case DS7_REG_SOUND6LEN_LO:
|
||||
case DS7_REG_SOUND7LEN_LO:
|
||||
case DS7_REG_SOUND8LEN_LO:
|
||||
case DS7_REG_SOUND9LEN_LO:
|
||||
case DS7_REG_SOUNDALEN_LO:
|
||||
case DS7_REG_SOUNDBLEN_LO:
|
||||
case DS7_REG_SOUNDCLEN_LO:
|
||||
case DS7_REG_SOUNDDLEN_LO:
|
||||
case DS7_REG_SOUNDELEN_LO:
|
||||
case DS7_REG_SOUNDFLEN_LO:
|
||||
value &= 0x3FFFFF;
|
||||
DSAudioWriteSOUNDLEN(&ds->audio, (address - DS7_REG_SOUND0LEN_LO) >> 4, value);
|
||||
break;
|
||||
|
||||
default:
|
||||
DS7IOWrite(ds, address, value & 0xFFFF);
|
||||
DS7IOWrite(ds, address | 2, value >> 16);
|
||||
|
|
Loading…
Reference in New Issue