N64 Audio integration
This commit is contained in:
83
src/core/Audio.cpp
Normal file
83
src/core/Audio.cpp
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
#include <Audio.hpp>
|
||||||
|
#include <SDL2/SDL.h>
|
||||||
|
#include <util.hpp>
|
||||||
|
|
||||||
|
namespace natsukashii::core {
|
||||||
|
#define AUDIO_SAMPLE_RATE 48000
|
||||||
|
#define SYSTEM_SAMPLE_FORMAT AUDIO_F32SYS
|
||||||
|
#define SYSTEM_SAMPLE_SIZE 4
|
||||||
|
#define BYTES_PER_HALF_SECOND ((AUDIO_SAMPLE_RATE / 2) * SYSTEM_SAMPLE_SIZE)
|
||||||
|
|
||||||
|
static SDL_AudioStream* audioStream = nullptr;
|
||||||
|
SDL_mutex* audioStreamMutex;
|
||||||
|
SDL_AudioSpec audioSpec;
|
||||||
|
SDL_AudioSpec request;
|
||||||
|
SDL_AudioDeviceID audioDev;
|
||||||
|
|
||||||
|
#define LockAudioMutex() SDL_LockMutex(audioStreamMutex)
|
||||||
|
#define UnlockAudioMutex() SDL_UnlockMutex(audioStreamMutex)
|
||||||
|
|
||||||
|
void audioCallback(void* userdata, Uint8* stream, int length) {
|
||||||
|
int gotten = 0;
|
||||||
|
LockAudioMutex();
|
||||||
|
int available = SDL_AudioStreamAvailable(audioStream);
|
||||||
|
|
||||||
|
if (available > 0) {
|
||||||
|
gotten = SDL_AudioStreamGet(audioStream, stream, length);
|
||||||
|
}
|
||||||
|
UnlockAudioMutex();
|
||||||
|
|
||||||
|
int gotten_samples = (int)(gotten / sizeof(float));
|
||||||
|
auto* out = (float*)stream;
|
||||||
|
out += gotten_samples;
|
||||||
|
|
||||||
|
for (int i = gotten_samples; i < length / sizeof(float); i++) {
|
||||||
|
float sample = 0;
|
||||||
|
*out++ = sample;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void InitAudio() {
|
||||||
|
AdjustSampleRate(AUDIO_SAMPLE_RATE);
|
||||||
|
memset(&request, 0, sizeof(request));
|
||||||
|
|
||||||
|
request.freq = AUDIO_SAMPLE_RATE;
|
||||||
|
request.format = SYSTEM_SAMPLE_FORMAT;
|
||||||
|
request.channels = 2;
|
||||||
|
request.samples = 1024;
|
||||||
|
request.callback = audioCallback;
|
||||||
|
request.userdata = nullptr;
|
||||||
|
|
||||||
|
audioDev = SDL_OpenAudioDevice(nullptr, 0, &request, &audioSpec, 0);
|
||||||
|
|
||||||
|
if(!audioDev) {
|
||||||
|
util::panic("Failed to initialize SDL Audio: {}", SDL_GetError());
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_PauseAudioDevice(audioDev, false);
|
||||||
|
|
||||||
|
audioStreamMutex = SDL_CreateMutex();
|
||||||
|
|
||||||
|
if(!audioStreamMutex) {
|
||||||
|
util::panic("Unable to initialize audio mutex: {}", SDL_GetError());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PushSample(s16 left, s16 right) {
|
||||||
|
s16 samples[2]{ left, right };
|
||||||
|
|
||||||
|
int availableBytes = SDL_AudioStreamAvailable(audioStream);
|
||||||
|
if(availableBytes < BYTES_PER_HALF_SECOND) {
|
||||||
|
SDL_AudioStreamPut(audioStream, samples, 2 * sizeof(16));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AdjustSampleRate(int sampleRate) {
|
||||||
|
LockAudioMutex();
|
||||||
|
if(audioStream) SDL_FreeAudioStream(audioStream);
|
||||||
|
|
||||||
|
audioStream = SDL_NewAudioStream(AUDIO_S16SYS, 2, sampleRate, SYSTEM_SAMPLE_FORMAT, 2, AUDIO_SAMPLE_RATE);
|
||||||
|
UnlockAudioMutex();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
8
src/core/Audio.hpp
Normal file
8
src/core/Audio.hpp
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <common.hpp>
|
||||||
|
|
||||||
|
namespace natsukashii::core {
|
||||||
|
void PushSample(s16, s16);
|
||||||
|
void InitAudio();
|
||||||
|
void AdjustSampleRate(int);
|
||||||
|
}
|
||||||
@@ -10,6 +10,6 @@ add_library(cores
|
|||||||
Scheduler.cpp
|
Scheduler.cpp
|
||||||
Scheduler.hpp
|
Scheduler.hpp
|
||||||
common.hpp
|
common.hpp
|
||||||
util.hpp)
|
util.hpp Audio.hpp Audio.cpp)
|
||||||
target_include_directories(cores PUBLIC . ../../external)
|
target_include_directories(cores PUBLIC . ../../external)
|
||||||
target_link_libraries(cores PUBLIC gb n64)
|
target_link_libraries(cores PUBLIC gb n64)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <n64/core/mmio/VI.hpp>
|
#include <n64/core/mmio/VI.hpp>
|
||||||
#include <n64/core/mmio/MI.hpp>
|
#include <n64/core/mmio/MI.hpp>
|
||||||
|
#include <n64/core/mmio/AI.hpp>
|
||||||
#include <n64/core/RSP.hpp>
|
#include <n64/core/RSP.hpp>
|
||||||
#include <n64/core/RDP.hpp>
|
#include <n64/core/RDP.hpp>
|
||||||
|
|
||||||
@@ -12,6 +13,7 @@ struct MMIO {
|
|||||||
MMIO() = default;
|
MMIO() = default;
|
||||||
VI vi;
|
VI vi;
|
||||||
MI mi;
|
MI mi;
|
||||||
|
AI ai;
|
||||||
RSP rsp;
|
RSP rsp;
|
||||||
RDP rdp;
|
RDP rdp;
|
||||||
|
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ void RDP::RunCommand(MI& mi, Registers& regs, RSP& rsp) {
|
|||||||
|
|
||||||
if (cmd == 0x29) {
|
if (cmd == 0x29) {
|
||||||
OnFullSync();
|
OnFullSync();
|
||||||
InterruptRaise(mi, regs, InterruptType::DP);
|
InterruptRaise(mi, regs, Interrupt::DP);
|
||||||
}
|
}
|
||||||
|
|
||||||
buf_index += cmd_len;
|
buf_index += cmd_len;
|
||||||
|
|||||||
@@ -78,8 +78,8 @@ void RSP::Write(Mem& mem, Registers& regs, u32 addr, u32 value) {
|
|||||||
write.raw = value;
|
write.raw = value;
|
||||||
CLEAR_SET(spStatus.halt, write.clearHalt, write.setHalt);
|
CLEAR_SET(spStatus.halt, write.clearHalt, write.setHalt);
|
||||||
CLEAR_SET(spStatus.broke, write.clearBroke, false);
|
CLEAR_SET(spStatus.broke, write.clearBroke, false);
|
||||||
if(write.clearIntr) InterruptLower(mi, regs, InterruptType::SP);
|
if(write.clearIntr) InterruptLower(mi, regs, Interrupt::SP);
|
||||||
if(write.setIntr) InterruptRaise(mi, regs, InterruptType::SP);
|
if(write.setIntr) InterruptRaise(mi, regs, Interrupt::SP);
|
||||||
CLEAR_SET(spStatus.singleStep, write.clearSstep, write.setSstep);
|
CLEAR_SET(spStatus.singleStep, write.clearSstep, write.setSstep);
|
||||||
CLEAR_SET(spStatus.interruptOnBreak, write.clearIntrOnBreak, write.setIntrOnBreak);
|
CLEAR_SET(spStatus.interruptOnBreak, write.clearIntrOnBreak, write.setIntrOnBreak);
|
||||||
CLEAR_SET(spStatus.signal0Set, write.clearSignal0, write.setSignal0);
|
CLEAR_SET(spStatus.signal0Set, write.clearSignal0, write.setSignal0);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
#include <util.hpp>
|
#include <util.hpp>
|
||||||
#include <n64/core/Mem.hpp>
|
#include <n64/core/Mem.hpp>
|
||||||
#include <n64/core/cpu/Registers.hpp>
|
#include <n64/core/cpu/Registers.hpp>
|
||||||
|
#include <Audio.hpp>
|
||||||
|
|
||||||
namespace natsukashii::n64::core {
|
namespace natsukashii::n64::core {
|
||||||
auto AI::Read(u32 addr) const -> u32 {
|
auto AI::Read(u32 addr) const -> u32 {
|
||||||
@@ -23,6 +24,8 @@ auto AI::Read(u32 addr) const -> u32 {
|
|||||||
|
|
||||||
#define max(x, y) ((x) > (y) ? (x) : (y))
|
#define max(x, y) ((x) > (y) ? (x) : (y))
|
||||||
|
|
||||||
|
using namespace natsukashii::core;
|
||||||
|
|
||||||
void AI::Write(Mem& mem, Registers& regs, u32 addr, u32 val) {
|
void AI::Write(Mem& mem, Registers& regs, u32 addr, u32 val) {
|
||||||
switch(addr) {
|
switch(addr) {
|
||||||
case 0x04500000:
|
case 0x04500000:
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ auto MI::Read(u32 paddr) const -> u32 {
|
|||||||
case 0x4: return MI_VERSION_REG;
|
case 0x4: return MI_VERSION_REG;
|
||||||
case 0x8: return miIntr.raw & 0x3F;
|
case 0x8: return miIntr.raw & 0x3F;
|
||||||
case 0xC: return miIntrMask.raw & 0x3F;
|
case 0xC: return miIntrMask.raw & 0x3F;
|
||||||
default: util::panic("Unhandled MI[%08X] read\n", paddr);
|
default: util::panic("Unhandled MI[{:08X}] read\n", paddr);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -45,7 +45,7 @@ void MI::Write(Registers& regs, u32 paddr, u32 val) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (val & (1 << 11)) {
|
if (val & (1 << 11)) {
|
||||||
InterruptLower(*this, regs, InterruptType::DP);
|
InterruptLower(*this, regs, Interrupt::DP);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (val & (1 << 12)) {
|
if (val & (1 << 12)) {
|
||||||
@@ -74,7 +74,7 @@ void MI::Write(Registers& regs, u32 paddr, u32 val) {
|
|||||||
UpdateInterrupt(*this, regs);
|
UpdateInterrupt(*this, regs);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
util::panic("Unhandled MI[%08X] write (%08X)\n", val, paddr);
|
util::panic("Unhandled MI[{:08X}] write ({:08X})\n", val, paddr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -60,7 +60,7 @@ void VI::Write(MI& mi, Registers& regs, u32 paddr, u32 val) {
|
|||||||
intr = val & 0x3FF;
|
intr = val & 0x3FF;
|
||||||
} break;
|
} break;
|
||||||
case 0x04400010:
|
case 0x04400010:
|
||||||
InterruptLower(mi, regs, InterruptType::VI);
|
InterruptLower(mi, regs, Interrupt::VI);
|
||||||
break;
|
break;
|
||||||
case 0x04400014: burst.raw = val; break;
|
case 0x04400014: burst.raw = val; break;
|
||||||
case 0x04400018: {
|
case 0x04400018: {
|
||||||
|
|||||||
Reference in New Issue
Block a user