Merge branch 'ToolBox_TODOs_SupportSFX' into ToolBox_TODOs

This commit is contained in:
Jaby 2024-09-28 13:02:26 +02:00
commit d258d79f90
38 changed files with 977 additions and 299 deletions

View File

@ -39,6 +39,8 @@ struct DefaultConfiguration {
```
### `CustomConfiguration` macros
```c++
// Turns on support of Sonys VAG files for the CDFileProcessor (off by default)
#define __SUPPORT_VAG__
// Turns on colored rectangles during boot (off by default)
#define __USE_DEBUG_COLOR__
// Turns on PS3 support (on by default)

View File

@ -16,7 +16,7 @@ namespace JabyEngine {
State create_custom(const uint32_t* data_adr, const CDFileType_t& file_type, const CDFile::Payload& payload) {
switch(static_cast<FileType>(file_type)) {
case FileType::Jingle:
return State::from(JingleState{.sfx_id = payload.raw}, reinterpret_cast<const uint8_t*>(data_adr), parse_jingle);
return State::from(JingleState{.sfx_id = payload.raw}, data_adr, parse_jingle);
default:
return FileProcessor::create(data_adr, Nothing());

View File

@ -1,5 +1,6 @@
#include "include/controller_state.hpp"
#include <PSX/GPU/gpu.hpp>
#include <PSX/SPU/spu.hpp>
namespace ControllerTest {
using DigitalButton = Periphery::AnalogeController::Button;
@ -7,6 +8,9 @@ namespace ControllerTest {
static void set_active(GPU::SPRT_16::Linked& sprt, bool is_active) {
sprt->tex_offset.y = is_active ? 16 : 0;
if(is_active) {
SPU::voice[1].play_if_end();
}
}
static void set_active(GPU::POLY_FT4::Linked& poly, bool is_active) {
@ -14,6 +18,10 @@ namespace ControllerTest {
poly->tex_offset1.y = is_active ? 16 : 0;
poly->tex_offset2.y = is_active ? 32 : 16;
poly->tex_offset3.y = is_active ? 32 : 16;
if(is_active) {
SPU::voice[1].play_if_end();
}
}
static const char* get_type_name(Periphery::ControllerType type) {

View File

@ -17,6 +17,8 @@ namespace Assets {
__jabyengine_start_lba_request
__jabyengine_request_lba_for(PACO, "ASSETS/MAIN/PACO.IMG"),
__jabyengine_request_lba_for(DFISH, "ASSETS/MAIN/DFISH.IMG"),
__jabyengine_request_lba_for(APPLE_SFX, "SFX/APPLE.VAG"),
__jabyengine_request_lba_for(BLUBB_SFX, "SFX/BLUBB.VAG"),
__jabyengine_request_lba_for(MIX_XA, "XAAUDIO/MIX.XA"),
__jabyengine_request_lba_for(BIOS_INFO_OVL, "BIO.BIN"),
__jabyengine_request_lba_for(GPU_TEST_OVL, "GTO.BIN"),
@ -58,9 +60,13 @@ namespace Assets {
}
namespace Main {
using SPU::operator""_vol;
static const CDFile Files[] = {
CDFileBuilder::simple_tim(LBA::PACO, PacoTIM),
CDFileBuilder::simple_tim(LBA::DFISH, DoenerFishInfo.tim),
CDFileBuilder::sony_vag(LBA::APPLE_SFX, VAG::create(0, 1.0_vol)),
CDFileBuilder::sony_vag(LBA::BLUBB_SFX, VAG::create(1, 1.0_vol)),
CustomCDFileBuilder::jingle(32),
};

View File

@ -1,6 +1,7 @@
#include "../include/shared.hpp"
#include "include/menu.hpp"
#include <PSX/Periphery/periphery.hpp>
#include <PSX/SPU/spu.hpp>
namespace Menu {
using DigitalButton = Periphery::GenericController::Button;
@ -24,6 +25,7 @@ namespace Menu {
}
if(controller.button.went_down(DigitalButton::Cross)) {
SPU::voice[0].play();
this->selection_callback(this->cur_selection);
}

View File

@ -12,6 +12,8 @@ CLUT_4_COLOR_TRANS_FLAGS = simple-tim clut4 --color-trans
## Music tracks
INPUT += $(OUTPUT_DIR)/Evacuation_cdda.xa
INPUT += $(OUTPUT_DIR)/fox.xa
INPUT += $(OUTPUT_DIR)/apple.vag
INPUT += $(OUTPUT_DIR)/blubb.vag
## Images
INPUT += $(OUTPUT_DIR)/TexturePage.img
@ -36,6 +38,10 @@ $(OUTPUT_DIR)/fox.xa: audio/temp/fox.wav
@mkdir -p $(OUTPUT_DIR)
jaby_engine_fconv $< -o $@ xa
$(OUTPUT_DIR)/%.vag: audio/temp/%.wav
@mkdir -p $(OUTPUT_DIR)
jaby_engine_fconv $< -o $@ vag
$(OUTPUT_DIR)/%.xa: audio/%.wav
@mkdir -p $(OUTPUT_DIR)
jaby_engine_fconv $< -o $@ xa

View File

@ -47,6 +47,11 @@
<Channel>../assets/bin/fox.xa</Channel>
</InterleavedFile>
</Directory>
<Directory name = "SFX" hidden = "true">
<!--TODO: test lz4 version-->
<File name = "APPLE.VAG" lz4 = "none">../assets/bin/apple.vag</File>
<File name = "BLUBB.VAG" lz4 = "none">../assets/bin/blubb.vag</File>
</Directory>
</Filesystem>
<AudioTrack align = "true">../assets/audio/temp/breaking.wav</AudioTrack>
</PSXCD>

View File

@ -0,0 +1,19 @@
#pragma once
#include "../jabyengine_defines.hpp"
namespace JabyEngine {
// Taken from boost endian
static constexpr uint8_t read_be(uint8_t x) {
return x;
}
static constexpr uint16_t read_be(uint16_t x) {
return (x << 8) | (x >> 8);
}
static constexpr uint32_t read_be(uint32_t x) {
const uint32_t step16 = x << 16 | x >> 16;
return ((step16 << 8) & 0xff00ff00) | ((step16 >> 8) & 0x00ff00ff);
}
}

View File

@ -34,4 +34,14 @@ namespace JabyEngine {
}
return result;
}
template<typename T>
static constexpr T min_of(T a, T b) {
return (a < b) ? a : b;
}
template<typename T>
static constexpr T max_of(T a, T b) {
return (a > b) ? a : b;
}
}

View File

@ -0,0 +1,15 @@
#pragma once
#include <stddef.hpp>
namespace JabyEngine {
using word_t = uint32_t;
static constexpr size_t bytes_to_words(size_t bytes) {
return bytes/sizeof(word_t);
}
static constexpr size_t words_to_bytes(size_t words) {
return words*sizeof(word_t);
}
}

View File

@ -37,8 +37,8 @@ namespace JabyEngine {
Reserved reserved;
template<typename T>
static __always_inline State from(const T& state, const uint8_t* data_adr, GenericProcessRoutine<T> process_routine) {
return {Configuration::from(process_routine, data_adr), *reinterpret_cast<const Reserved*>(&state)};
static __always_inline State from(const T& state, const uint32_t* data_adr, GenericProcessRoutine<T> process_routine) {
return {Configuration::from(process_routine, reinterpret_cast<const uint8_t*>(data_adr)), *reinterpret_cast<const Reserved*>(&state)};
static_assert(sizeof(T) <= sizeof(Reserved));
}
@ -52,6 +52,7 @@ namespace JabyEngine {
// The nothing state
State create(const uint32_t* data_adr, const Nothing& nothing);
State create(const uint32_t* data_adr, const SimpleTIM& file);
State create(const uint32_t* data_adr, const VAG& file);
State create_custom(const uint32_t* data_adr, const CDFileType_t& file_type, const CDFile::Payload& payload);
}

View File

@ -1,4 +1,5 @@
#pragma once
#include <PSX/jabyengine_config.hpp>
#include "../Overlay/overlay.hpp"
#include "file_types.hpp"
@ -7,8 +8,11 @@ namespace JabyEngine {
using RawPayload_t = uint32_t;
enum struct CDFileType : CDFileType_t {
SimpleTIM = 0,
CopyTo,
CopyTo = 0,
SimpleTIM,
#ifdef __SUPPORT_VAG__
SonyVAG,
#endif // __SUPPORT_VAG__
Custom,
};
@ -16,8 +20,9 @@ namespace JabyEngine {
struct CDFile {
union Payload {
RawPayload_t raw;
SimpleTIM simple_tim;
CopyTo copy_to;
SimpleTIM simple_tim;
VAG vag;
Overlay overlay;
};
@ -38,13 +43,19 @@ namespace JabyEngine {
#pragma pack(pop)
struct CDFileBuilder {
static constexpr CDFile copy_to(uint8_t rel_lba_idx, uint32_t* dst) {
return CDFile{.rel_lba_idx = rel_lba_idx, .type = CDFileType::CopyTo, .payload = {.copy_to = CopyTo{dst}}};
}
static constexpr CDFile simple_tim(uint8_t rel_lba_idx, SimpleTIM simple_tim) {
return CDFile{.rel_lba_idx = rel_lba_idx, .type = CDFileType::SimpleTIM, .payload = {.simple_tim = simple_tim}};
}
static constexpr CDFile copy_to(uint8_t rel_lba_idx, uint32_t* dst) {
return CDFile{.rel_lba_idx = rel_lba_idx, .type = CDFileType::CopyTo, .payload = {.copy_to = CopyTo{dst}}};
#ifdef __SUPPORT_VAG__
static constexpr CDFile sony_vag(uint8_t lba_idx, VAG vag) {
return CDFile{.rel_lba_idx = lba_idx, .type = CDFileType::SonyVAG, .payload = {.vag = vag}};
}
#endif //__SUPPORT_VAG__
static constexpr CDFile overlay(uint8_t rel_lba_idx, uint32_t* overlay_dst) {
return CDFile{.rel_lba_idx = rel_lba_idx, .type = CDFileType::CopyTo, .payload = {.overlay = Overlay{overlay_dst}}};

View File

@ -1,6 +1,7 @@
#pragma once
#include "Processor/file_processor.hpp"
#include "cd_file_types.hpp"
#include <limits.hpp>
namespace JabyEngine {
namespace FileProcessor {
@ -18,6 +19,54 @@ namespace JabyEngine {
config.process_routine = reinterpret_cast<State::ProcessRoutine>(process_routine);
return process_routine(config, state);
}
namespace DMA {
using words_t = size_t;
struct WordsReady {
uint32_t words_to_use;
bool is_last;
static constexpr WordsReady calculate(const State::Configuration& config, words_t words_left) {
const auto config_data_words = (config.data_bytes/sizeof(uint32_t));
const auto words_to_use = (config_data_words > words_left) ? words_left : config_data_words;
return {
.words_to_use = words_to_use,
.is_last = words_to_use == words_left
};
}
};
template<typename T>
static words_t send_words(words_t words_to_send, bool send_all) {
auto blocks_to_send = words_to_send/16;
while(blocks_to_send > 0) {
const auto block_send = (blocks_to_send > UI16_MAX) ? UI16_MAX : blocks_to_send;
// Send data!
T::wait();
T::Receive::start(blocks_to_send);
blocks_to_send -= block_send;
}
if(send_all) {
const auto last_words_to_send = (words_to_send & 0b1111);
if(last_words_to_send > 0) {
T::wait();
T::Receive::start(1, last_words_to_send);
}
T::wait();
T::end();
return words_to_send;
}
else {
return (words_to_send & ~0b1111);
}
}
}
}
}
}

View File

@ -2,12 +2,19 @@
#include "../Auxiliary/bits.hpp"
#include "../GPU/gpu_types.hpp"
#include "../jabyengine_defines.hpp"
#include "../SPU/spu.hpp"
namespace JabyEngine {
#pragma pack(push, 1)
struct Nothing {
};
struct CopyTo {
uint32_t* dst;
};
// TODO: Call TIM?
// TODO: Add create function?
struct SimpleTIM {
static constexpr auto TextureX = BitRange::from_to(0, 8);
static constexpr auto TextureY = BitRange::from_to(9, 16);
@ -61,9 +68,15 @@ namespace JabyEngine {
}
};
struct CopyTo {
uint32_t* dst;
struct VAG {
uint8_t voice_number;
SPU::SimpleVolume inital_stereo_vol;
static constexpr VAG create(uint8_t voice_num, SPU::SimpleVolume volume) {
return VAG{.voice_number = voice_num, .inital_stereo_vol = volume};
}
};
#pragma pack(pop)
typedef CopyTo Overlay;

53
include/PSX/SPU/spu.hpp Normal file
View File

@ -0,0 +1,53 @@
#pragma once
#include "../System/IOPorts/spu_io.hpp"
namespace JabyEngine {
namespace SPU {
using SPU_IO::operator""_vol;
using SRAMAdr = SPU_IO::SRAMAdr;
using SimpleVolume = SPU_IO::SimpleVolume;
using SweepVolume = SPU_IO::SweepVolume;
// TODO: Rename to sample...?
struct Voice {
size_t get_id() const {
return reinterpret_cast<size_t>(this);
}
SRAMAdr allocate(size_t size);
SRAMAdr allocate(SPU_IO::SampleRate frequency, size_t size);
void deallocate();
void set_sample_rate(SPU_IO::SampleRate frequency) {
SPU_IO::Voice[Voice::get_id()].sampleRate.write(frequency);
}
void set_volume(SimpleVolume left, SimpleVolume right) {
// TODO: Verify that assembly code is super simple
SPU_IO::Voice[Voice::get_id()].volumeLeft.write(SweepVolume::create(left));
SPU_IO::Voice[Voice::get_id()].volumeRight.write(SweepVolume::create(right));
}
void play() {
SPU_IO::Key::On.write(SPU_IO::KeyOn::for_specific(Voice::get_id()));
}
void play_if_end() {
if(Voice::is_end()) {
Voice::play();
}
}
void stop() {
SPU_IO::Key::Off.write(SPU_IO::KeyOff::for_specific(Voice::get_id()));
}
bool is_end() const {
return SPU_IO::Voice[Voice::get_id()].adsr_volume.read() == SimpleVolume::mute();
}
};
static auto& voice = __new_declare_io_port_array(Voice, SPU_IO::VoiceCount, 0x0);
}
}

View File

@ -0,0 +1,214 @@
#pragma once
#include "../ioport.hpp"
#include <limits.hpp>
namespace JabyEngine {
namespace SPU_IO_Values {
namespace MemoryMap {
static constexpr uintptr_t ADPCM = 0x01000;
}
__declare_io_value(AD, uint16_t) {
static constexpr auto AttackMode = Bit(15);
static constexpr auto AttackShift = BitRange::from_to(10, 14);
static constexpr auto AttackStep = BitRange::from_to(8, 9);
static constexpr auto DecayShift = BitRange::from_to(4, 7);
static constexpr auto SustainLevel = BitRange::from_to(0, 3);
static constexpr AD none() {
return AD{0};
}
};
__declare_io_value(ControlRegister, uint16_t) {
enum RAMTransferMode {
Stop = 0,
ManualWrite = 1,
DMAWrite = 2,
DMARead = 3
};
static constexpr auto Enable = Bit(15);
static constexpr auto Unmute = Bit(14);
static constexpr auto NoiseFrequcenyShift = BitRange::from_to(10, 13);
static constexpr auto NoiseFrequcenyStep = BitRange::from_to(8, 9);
static constexpr auto ReverbMasterEnable = Bit(7);
static constexpr auto IRQ9Enable = Bit(6);
static constexpr auto TransferMode = BitRange::from_to(4, 5);
static constexpr auto ExternalAudioReverb = Bit(3);
static constexpr auto CDAudioReverb = Bit(2);
static constexpr auto ExternalAudioEnable = Bit(1);
static constexpr auto CDAudioEnable = Bit(0);
};
__declare_io_value(DataTransferControl, uint16_t) {
static constexpr DataTransferControl NormalTransferMode() {
return DataTransferControl{0x0004};
}
};
__declare_io_value(Echo, uint32_t) {
static constexpr auto EchoBits = BitRange::from_to(0, 23);
static constexpr Echo AllOff() {
return Echo{0};
}
};
__declare_io_value(KeyOff, uint32_t) {
static constexpr KeyOff for_specific(uint32_t id) {
return KeyOff{1u << id};
}
static constexpr KeyOff all() {
return KeyOff{UI32_MAX};
}
};
__declare_io_value(KeyOn, uint32_t) {
static constexpr KeyOn for_specific(uint32_t id) {
return KeyOn{1u << id};
}
static constexpr KeyOn all() {
return KeyOn{UI32_MAX};
}
};
__declare_io_value(KeyStatus, uint32_t) {
};
__declare_io_value(Noise, uint16_t) {
static constexpr auto NoiseBits = BitRange::from_to(0, 23);
static constexpr Noise AllOff() {
return Noise{0};
}
};
__declare_io_value(PitchModulation, uint32_t) {
static constexpr auto EnableBits = BitRange::from_to(1, 23);
static constexpr PitchModulation AllOff() {
return PitchModulation{0};
}
};
__declare_io_value(SampleRate, uint16_t) {
static constexpr SampleRate stop() {
return SampleRate{0};
}
static constexpr SampleRate from_HZ(uint32_t freq) {
constexpr uint32_t Base1024Hz = static_cast<uint32_t>((4096.0/44100.0)*1024.0);
return SampleRate{static_cast<uint16_t>((freq >> 10)*Base1024Hz)};
}
static constexpr SampleRate from_HZ(double freq) {
//4096 == 44100Hz
constexpr double Base = (4096.0 / 44100.0);
return SampleRate{static_cast<uint16_t>((freq*Base))};
}
};
__declare_io_value(SimpleVolume, int16_t) {
static constexpr auto MaxVolume = I16_MAX;
static constexpr SimpleVolume mute() {
return SimpleVolume{0};
}
constexpr operator int16_t() const {
return this->raw;
}
};
static constexpr SimpleVolume operator""_vol(long double fraction) {
return {static_cast<int16_t>(static_cast<long double>(SimpleVolume::MaxVolume)*fraction)};
}
__declare_io_value(SR, uint16_t) {
static constexpr auto SustainMode = Bit(31 - 16);
static constexpr auto SustainDirection = Bit(30 - 16);
static constexpr auto SustainShift = BitRange::from_to((24 - 16), (28 - 16));
static constexpr auto SustainStep = BitRange::from_to((22 - 16), (23 - 16));
static constexpr auto ReleaseMode = Bit(21 - 16);
static constexpr auto ReleaseShift = BitRange::from_to((16 - 16), (20 - 16));
static constexpr SR none() {
return SR{0};
}
};
__declare_io_value(SRAMAdr, uint16_t) {
static constexpr SRAMAdr null() {
return SRAMAdr{0x0};
}
static constexpr SRAMAdr adpcm_start() {
return SRAMAdr{MemoryMap::ADPCM};
}
};
__declare_io_value(StatusRegister, uint16_t) {
enum CapureBufferHalf {
First = 0,
Second = 1
};
static constexpr auto Unused = BitRange::from_to(12, 15);
static constexpr auto CaputreBufferHalf = Bit(11);
static constexpr auto TransferBusy = Bit(10);
static constexpr auto IsDMARead = Bit(9);
static constexpr auto isDMAWrite = Bit(8);
static constexpr auto isDMA = Bit(7);
static constexpr auto isIRQ = Bit(6);
// Copies of ControlRegister
static constexpr auto TransferMode = BitRange::from_to(4, 5);
static constexpr auto ExternalAudioReverb = Bit(3);
static constexpr auto CDAudioReverb = Bit(2);
static constexpr auto ExternalAudioEnable = Bit(1);
static constexpr auto CDAudioEnable = Bit(0);
};
__declare_io_value(SweepVolume, int16_t) {
struct VolumeMode {
static constexpr auto MaxVolume = (I16_MAX >> 1);
static constexpr auto EnableSweep = Bit(15);
static constexpr auto Enable = !EnableSweep;
static constexpr auto Volume = BitRange::from_to(0, 14);
};
struct SweepMode {
enum Mode {
Linear = 0,
Exponential = 1,
};
enum Direction {
Increase = 0,
Decrease = 1,
};
enum Phase {
Posititve = 0,
Negative = 1,
};
static constexpr auto Mode = Bit(14);
static constexpr auto Direction = Bit(13);
static constexpr auto Phase = Bit(12);
static constexpr auto Shift = BitRange::from_to(2, 6);
static constexpr auto Step = BitRange::from_to(0, 1);
};
static constexpr SweepVolume create(SimpleVolume volume) {
return from(VolumeMode::Enable, VolumeMode::Volume.with(volume.raw >> 1));
}
static constexpr SweepVolume mute() {
return SweepVolume{0};
}
};
}
}

View File

@ -65,17 +65,9 @@ namespace JabyEngine {
};
}
struct ubus32_t {
uint16_t low;
uint16_t high;
static ubus32_t from(uint32_t value) {
return {.low = static_cast<uint16_t>(value & 0xFFFF), .high = static_cast<uint16_t>(value >> 16)};
}
};
template<typename T>
struct IOPort {
using Value = T;
T value;
T read() const {
@ -88,24 +80,34 @@ namespace JabyEngine {
};
template<>
struct IOPort<ubus32_t> {
ubus32_t value;
struct IOPort<uint32_t>;
ubus32_t read() const {
auto*const cv_this = const_cast<const volatile IOPort<ubus32_t>*>(this);
template<typename T>
struct IOPort32 {
union ValueHelper {
struct {
uint16_t low;
uint16_t high;
};
T value;
};
using Value = T;
T value;
return {.low = cv_this->value.low, .high = cv_this->value.high};
T read() const {
const auto* cast_this = reinterpret_cast<const IOPort32<ValueHelper>*>(this);
const volatile auto* cv_this = const_cast<volatile decltype(cast_this)>(cast_this);
return ValueHelper{.low = cv_this->value.low, .high = cv_this->value.high}.value;
}
void write(ubus32_t value) {
auto*const cv_this = const_cast<volatile IOPort<ubus32_t>*>(this);
void write(T value) {
const auto new_value = ValueHelper{.value = value};
auto* cast_this = reinterpret_cast<IOPort32<ValueHelper>*>(this);
volatile auto* v_this = const_cast<volatile decltype(cast_this)>(cast_this);
cv_this->value.low = value.low;
cv_this->value.high = value.high;
}
void write(uint32_t value) {
IOPort<ubus32_t>::write(ubus32_t::from(value));
v_this->value.low = new_value.low;
v_this->value.high = new_value.high;
}
};
@ -115,4 +117,7 @@ namespace JabyEngine {
#define __declare_io_port_w_type(cv, type, name, adr) __declare_value_at(cv, ::JabyEngine::IOPort<type>, name, adr)
#define __declare_io_port(cv, name, adr) __declare_io_port_w_type(cv, struct name, name, adr)
#define __declare_io_port_array(cv, name, size, adr) __declare_array_at(cv, struct name, name, size, adr)
#define __new_declare_io_port(type, adr) *reinterpret_cast<type*>(adr)
#define __new_declare_io_port_array(type, size, adr) reinterpret_cast<type(&)[size]>(*reinterpret_cast<type*>(adr))
}

View File

@ -1,172 +1,88 @@
#pragma once
#include "ioport.hpp"
#include "IOValues/spu_io_values.hpp"
#include <limits.hpp>
namespace JabyEngine {
namespace SPU_IO {
enum struct Mode {
Linear = 0,
Exponential = 1,
};
using namespace SPU_IO_Values;
enum struct Direction {
Increase = 0,
Decrease = 1,
};
static constexpr size_t VoiceCount = 24;
static constexpr size_t ReverbCount = 1;
enum struct Phase {
Posititve = 0,
Negative = 1,
};
struct ControlRegisterIO : public IOPort<SPU_IO_Values::ControlRegister> {
using TransferMode = Value::RAMTransferMode;
//0..0x1F = Fast..Slow
typedef uint8_t Shift;
//0..3 = +7, +6, +5, +4 or -6, -7, -6, -5
typedef uint8_t Step;
__declare_io_value(DataTransferControl, uint16_t) {
};
__declare_io_value(SimpleVolume, int16_t) {
constexpr operator int16_t() const {
return this->raw;
}
};
__declare_io_value(Adr, uint16_t) {
};
__declare_io_value(SampleRate, uint16_t) {
static constexpr SampleRate from_HZ(double freq) {
//4096 == 44100Hz
constexpr double Base = (4096.0 / 44100.0);
return {static_cast<uint16_t>((freq*Base))};
void set_transfer_mode(TransferMode mode) {
this->write(this->read().set(ControlRegister::TransferMode.with(mode)));
while(this->read().get(ControlRegister::TransferMode) != mode);
}
};
__declare_io_value(SweepVolume, int16_t) {
// For Volume Mode
static constexpr auto SweepEnable = Bit(15);
static constexpr auto VolumeEnable = !SweepEnable;
static constexpr auto Volume = BitRange::from_to(0, 14);
// For Sweep Mode
static constexpr auto SweepMode = Bit(14);
static constexpr auto SweepDirection = Bit(13);
static constexpr auto SweepPhase = Bit(12);
static constexpr auto SweepShift = BitRange::from_to(2, 6);
static constexpr auto SweepStep = BitRange::from_to(0, 1);
};
__declare_io_value(SR, uint16_t) {
static constexpr auto SustainMode = Bit(31 - 16);
static constexpr auto SustainDirection = Bit(30 - 16);
static constexpr auto SustainShift = BitRange::from_to((24 - 16), (28 - 16));
static constexpr auto SustainStep = BitRange::from_to((22 - 16), (23 - 16));
static constexpr auto ReleaseMode = Bit(21 - 16);
static constexpr auto ReleaseShift = BitRange::from_to((16 - 16), (20 - 16));
};
__declare_io_value(AD, uint16_t) {
static constexpr auto AttackMode = Bit(15);
static constexpr auto AttackShift = BitRange::from_to(10, 14);
static constexpr auto AttackStep = BitRange::from_to(8, 9);
static constexpr auto DecayShift = BitRange::from_to(4, 7);
static constexpr auto SustainLevel = BitRange::from_to(0, 3);
};
using ADIO = IOPort<SPU_IO_Values::AD>;
using DataTransferControlIO = IOPort<SPU_IO_Values::DataTransferControl>;
using EchoIO = IOPort32<SPU_IO_Values::Echo>;
using KeyOffIO = IOPort32<SPU_IO_Values::KeyOff>;
using KeyOnIO = IOPort32<SPU_IO_Values::KeyOn>;
using KeyStatusIO = IOPort32<SPU_IO_Values::KeyStatus>;
using NoiseIO = IOPort<SPU_IO_Values::Noise>;
using PitchModulationIO = IOPort32<SPU_IO_Values::PitchModulation>;
using SampleRateIO = IOPort<SPU_IO_Values::SampleRate>;
using SimpleVolumeIO = IOPort<SPU_IO_Values::SimpleVolume>;
using StatusRegisterIO = IOPort<SPU_IO_Values::StatusRegister>;
using SRIO = IOPort<SPU_IO_Values::SR>;
using SRAMAdrIO = IOPort<SPU_IO_Values::SRAMAdr>;
using SweepVolumeIO = IOPort<SweepVolume>;
#pragma pack(push, 1)
struct Voice {
IOPort<SweepVolume> volumeLeft; //Offset: 0x0
IOPort<SweepVolume> volumeRight; //Offset: 0x2
IOPort<SampleRate> sampleRate; //Offset: 0x4;
IOPort<Adr> adr; //Offset: 0x6
IOPort<AD> ad; //Offset: 0x8
IOPort<SR> sr; //Offset: 0xA
IOPort<SimpleVolume> currentVolume; //Offset: 0xC
IOPort<Adr> repeatAdr; //Offset: 0xE
static constexpr Adr start_adr() {
return {0x200};
}
SweepVolumeIO volumeLeft; //Offset: 0x0
SweepVolumeIO volumeRight; //Offset: 0x2
SampleRateIO sampleRate; //Offset: 0x4;
SRAMAdrIO adr; //Offset: 0x6
ADIO ad; //Offset: 0x8
SRIO sr; //Offset: 0xA
SimpleVolumeIO adsr_volume; //Offset: 0xC
SRAMAdrIO repeatAdr; //Offset: 0xE
};
#pragma pack(pop)
__declare_io_value(ControlRegister, uint16_t) {
enum RAMTransferMode {
Stop = 0,
ManualWrite = 1,
DMAWrite = 2,
DMARead = 3
};
static constexpr auto Enable = Bit(15);
static constexpr auto Unmute = Bit(14);
static constexpr auto NoiseFrequcenyShift = BitRange::from_to(10, 13);
static constexpr auto NoiseFrequcenyStep = BitRange::from_to(8, 9);
static constexpr auto ReverbMasterEnable = Bit(7);
static constexpr auto IRQ9Enable = Bit(6);
static constexpr auto TransferMode = BitRange::from_to(4, 5);
static constexpr auto ExternalAudioReverb = Bit(3);
static constexpr auto CDAudioReverb = Bit(2);
static constexpr auto ExternalAudioEnable = Bit(1);
static constexpr auto CDAudioEnable = Bit(0);
};
__declare_io_value(PMON, uint16_t) {
static constexpr auto EnableBits = BitRange::from_to(1, 23);
};
__declare_io_value(NON, uint16_t) {
static constexpr auto NoiseBits = BitRange::from_to(0, 23);
};
__declare_io_value(EON, uint16_t) {
static constexpr auto EchoBits = BitRange::from_to(0, 23);
};
static constexpr size_t VoiceCount = 24;
struct Key {
__declare_io_port_w_type(inline, ubus32_t, On, 0x1F801D88);
__declare_io_port_w_type(inline, ubus32_t, Off, 0x1F801D8C);
__declare_io_port_w_type(inline, ubus32_t, Status, 0x1F801D9C);
};
struct MainVolume {
__declare_io_port_w_type(inline, SweepVolume, Left, 0x1F801D80);
__declare_io_port_w_type(inline, SweepVolume, Right, 0x1F801D82);
};
static auto& Voice = __new_declare_io_port_array(struct Voice, VoiceCount, 0x1F801C00);
static auto& PMON = __new_declare_io_port(PitchModulationIO, 0x1F801D90);
static auto& NON = __new_declare_io_port(NoiseIO, 0x1F801D94);
static auto& EON = __new_declare_io_port(EchoIO, 0x1F801D98);
static auto& SRAMTransferAdr = __new_declare_io_port(SRAMAdrIO, 0x1F801DA6);
static auto& ControlRegister = __new_declare_io_port(ControlRegisterIO, 0x1F801DAA);
static auto& DataTransferControl = __new_declare_io_port(DataTransferControlIO, 0x1F801DAC);
static auto& StatusRegister = __new_declare_io_port(StatusRegisterIO, 0x1F801DAE);
struct CDVolume {
__declare_io_port_w_type(inline, SimpleVolume, Left, 0x1F801DB0);
__declare_io_port_w_type(inline, SimpleVolume, Right, 0x1F801DB2);
static inline auto& Left = __new_declare_io_port(SimpleVolumeIO, 0x1F801DB0);
static inline auto& Right = __new_declare_io_port(SimpleVolumeIO, 0x1F801DB2);
};
struct ExternalAudioInputVolume {
__declare_io_port_w_type(inline, SimpleVolume, Left, 0x1F801DB4);
__declare_io_port_w_type(inline, SimpleVolume, Right, 0x1F801DB6);
static inline auto& Left = __new_declare_io_port(SimpleVolumeIO, 0x1F801DB4);
static inline auto& Right = __new_declare_io_port(SimpleVolumeIO, 0x1F801DB6);
};
struct Key {
static inline auto& On = __new_declare_io_port(KeyOnIO, 0x1F801D88);
static inline auto& Off = __new_declare_io_port(KeyOffIO, 0x1F801D8C);
static inline auto& Status = __new_declare_io_port(KeyStatusIO, 0x1F801D9C);
};
struct MainVolume {
static inline auto& Left = __new_declare_io_port(SweepVolumeIO, 0x1F801D80);
static inline auto& Right = __new_declare_io_port(SweepVolumeIO, 0x1F801D82);
};
struct Reverb {
struct Volume {
__declare_io_port_w_type(inline, SimpleVolume, Left, 0x1F801D84);
__declare_io_port_w_type(inline, SimpleVolume, Right, 0x1F801D86);
static inline auto& Left = __new_declare_io_port(SimpleVolumeIO, 0x1F801D84);
static inline auto& Right = __new_declare_io_port(SimpleVolumeIO, 0x1F801D86);
};
__declare_io_port_w_type(inline, Adr, WorkAreaAdr, 0x1F801DA2);
static inline auto& On = EON;
static inline auto& WorkAreaAdr = __new_declare_io_port(SRAMAdrIO, 0x1F801DA2);
};
static constexpr SimpleVolume operator""_vol(long double fraction) {
return {static_cast<int16_t>(static_cast<long double>(I16_MAX)*fraction)};
}
__declare_io_port(, ControlRegister, 0x1F801DAA);
__declare_io_port(, DataTransferControl, 0x1F801DAC);
__declare_io_port(, PMON, 0x1F801D90);
__declare_io_port(, NON, 0x1F801D94);
__declare_io_port(, EON, 0x1F801D98);
__declare_io_port_array(, Voice, VoiceCount, 0x1F801C00);
}
}

View File

@ -31,6 +31,8 @@ namespace JabyEngine {
using Configuration = CustomConfiguration;
#else
using Configuration = DefaultConfiguration;
// TODO: v Turn off when this is supported
#define __SUPPORT_VAG__
#define __SUPPORT_PS3__
#endif // has jabyengine_custom_config
}

View File

@ -55,7 +55,7 @@ __stack_start = ORIGIN(ram) + LENGTH(ram);
SECTIONS {
.zero (NOLOAD) : {
_ZN10JabyEngine2CD4zeroE = .;
_ZN10JabyEngine3SPU5voiceE = .;
} > zero
.bios (NOLOAD) : {

View File

@ -9,7 +9,7 @@
- [ ] Emulator
- [ ] Real Hardware
# Todo
# TODO
- [ ] Ko-fi supporter list
- [ ] Support more GTE
- [X] Easy serial code swap
@ -17,3 +17,10 @@
- [X] Support pop-fe
- [ ] PS3 PKG generation tool?
- [ ] PS3 runtime detection?
- [ ] Move DMA code to public include for custom loading of files?
- [ ] Maybe make it an interface with SPU/GPU as a specification...?
- [ ] Could be empty classes that the linker maps to 0 or somewhere
- [ ] Could be a all static struct (I like that better; We are not Nicolas Noble)
- [ ] Redo the IO ports again...?
- [ ] Support better file loading with threads
- [ ] Loading Screen with GPU IO? (Does DMA and IO work together?)

View File

@ -65,21 +65,21 @@ namespace JabyEngine {
GPU_IO::GP1.write(GPU_IO::Command::ResetCMDBufer());
}
namespace DMA {
struct DMA {
#ifdef __SUPPORT_PS3__
// The PS3 doesn't autoincrement the GPU MADR register so we have to do it
extern uintptr_t MADR;
static uintptr_t MADR;
#endif // __SUPPORT_PS3__
static void wait() {
::JabyEngine::DMA_IO::GPU.wait();
DMA_IO::GPU.wait();
}
static void end() {
reset_cmd_buffer();
}
namespace Receive {
struct Receive {
static void prepare() {
GPU_IO::GP1.write(GPU_IO::Command::DMADirection(GPU_IO::DMADirection::CPU2GPU));
reset_cmd_buffer();
@ -87,7 +87,7 @@ namespace JabyEngine {
static void set_src(uintptr_t adr) {
#ifdef __SUPPORT_PS3__
MADR = adr;
DMA::MADR = adr;
#else
DMA_IO::GPU.set_adr(adr);
#endif // __SUPPORT_PS3__
@ -101,17 +101,17 @@ namespace JabyEngine {
}
static void start(uint16_t blockCount, uint16_t wordsPerBlock = 0x10) {
typedef DMA_IO::BCR::SyncMode1 SyncMode1;
using SyncMode1 = DMA_IO::BCR::SyncMode1;
#ifdef __SUPPORT_PS3__
DMA_IO::GPU.set_adr(MADR);
MADR += (blockCount * wordsPerBlock) << 2;
DMA::MADR += (blockCount * wordsPerBlock) << 2;
#endif // __SUPPORT_PS3__
DMA_IO::GPU.block_ctrl.write(DMA_IO::BCR::from(SyncMode1::BlockSize.with(wordsPerBlock), SyncMode1::BlockAmount.with(blockCount)));
DMA_IO::GPU.channel_ctrl.write(DMA_IO::CHCHR::StartGPUReceive());
}
}
}
};
};
}
}
}

View File

@ -0,0 +1,44 @@
#pragma once
#include <PSX/System/IOPorts/dma_io.hpp>
#include <PSX/SPU/spu.hpp>
namespace JabyEngine {
namespace SPU {
namespace internal {
struct DMA {
static void wait() {
DMA_IO::SPU.wait();
while(SPU_IO::StatusRegister.read().is_set(SPU_IO_Values::StatusRegister::TransferBusy));
}
static void end() {
SPU_IO::ControlRegister.set_transfer_mode(SPU_IO::ControlRegister::Stop);
}
struct Receive {
static void prepare() {
end();
SPU_IO::DataTransferControl.write(SPU_IO::DataTransferControl::NormalTransferMode());
SPU_IO::ControlRegister.set_transfer_mode(SPU_IO::ControlRegister::Stop);
}
static void set_src(uintptr_t adr) {
DMA_IO::SPU.set_adr(adr);
}
static void set_dst(SPU::SRAMAdr adr) {
SPU_IO::SRAMTransferAdr.write(adr);
SPU_IO::ControlRegister.set_transfer_mode(SPU_IO::ControlRegister::DMAWrite);
}
static void start(uint16_t blockCount, uint16_t wordsPerBlock = 0x10) {
using SyncMode1 = DMA_IO::BCR::SyncMode1;
DMA_IO::SPU.block_ctrl.write(DMA_IO::BCR::from(SyncMode1::BlockSize.with(wordsPerBlock), SyncMode1::BlockAmount.with(blockCount)));
DMA_IO::SPU.channel_ctrl.write(DMA_IO::CHCHR::StartSPUReceive());
}
};
};
}
}
}

View File

@ -0,0 +1,10 @@
#pragma once
#include <PSX/jabyengine.hpp>
namespace JabyEngine {
namespace SPU_MMU {
// TODO: Make this work with words? Word align?
const uint8_t* allocate(uint8_t voice, size_t size);
void deallocate(uint8_t voice);
}
}

View File

@ -8,7 +8,7 @@ namespace JabyEngine {
using namespace SPU_IO;
static void clear_main_volume() {
static constexpr auto StartVol = SweepVolume::from(SweepVolume::VolumeEnable, SweepVolume::Volume.with(static_cast<int16_t>(I16_MAX >> 2)));
static constexpr auto StartVol = SweepVolume::from(SweepVolume::VolumeMode::Enable, SweepVolume::VolumeMode::Volume.with(SweepVolume::VolumeMode::MaxVolume >> 1));
MainVolume::Left.write(StartVol);
MainVolume::Right.write(StartVol);
@ -18,37 +18,37 @@ namespace JabyEngine {
CDVolume::Left.write(0.75_vol);
CDVolume::Right.write(0.75_vol);
ExternalAudioInputVolume::Left.write({0});
ExternalAudioInputVolume::Right.write({0});
ExternalAudioInputVolume::Left.write(SimpleVolume::mute());
ExternalAudioInputVolume::Right.write(SimpleVolume::mute());
}
static void clear_voice() {
for(auto& voice : SPU_IO::Voice) {
voice.volumeLeft.write({0});
voice.volumeRight.write({0});
voice.sampleRate.write({0});
voice.ad.write({0});
voice.sr.write({0});
voice.currentVolume.write({0});
voice.volumeLeft.write(SweepVolume::mute());
voice.volumeRight.write(SweepVolume::mute());
voice.sampleRate.write(SampleRate::stop());
voice.ad.write(AD::none());
voice.sr.write(SR::none());
voice.adsr_volume.write(SimpleVolume::mute());
voice.adr.write(Voice::start_adr());
voice.repeatAdr.write(Voice::start_adr());
voice.adr.write(SRAMAdr::adpcm_start());
voice.repeatAdr.write(SRAMAdr::adpcm_start());
}
}
static void clear_pmon() {
SPU_IO::PMON.write({0});
SPU_IO::PMON.write(PitchModulation::AllOff());
}
static void clear_noise_and_echo() {
SPU_IO::NON.write({0});
SPU_IO::EON.write({0});
SPU_IO::NON.write(Noise::AllOff());
SPU_IO::EON.write(Echo::AllOff());
}
static void clear_reverb() {
Reverb::Volume::Left.write({0});
Reverb::Volume::Right.write({0});
Reverb::WorkAreaAdr.write({0});
Reverb::Volume::Left.write(SimpleVolume::mute());
Reverb::Volume::Right.write(SimpleVolume::mute());
Reverb::WorkAreaAdr.write(SRAMAdr::null());
}
static void setup_control_register() {
@ -57,25 +57,19 @@ namespace JabyEngine {
SPU_IO::ControlRegister.write(SetupValue);
}
static void setup_data_transfer_control() {
static constexpr struct DataTransferControl RequiredValue{(2 << 1)};
DataTransferControl.write(RequiredValue);
}
static void wait_voices() {
static constexpr SimpleVolume Treshhold{static_cast<int16_t>(I16_MAX*0.03)};
static constexpr auto Treshhold = SimpleVolume::mute();
try_again:
for(const auto& voice : SPU_IO::Voice) {
if(voice.currentVolume.read() > Treshhold) {
if(voice.adsr_volume.read() > Treshhold) {
goto try_again;
}
}
}
void stop_voices() {
SPU_IO::Key::Off.write({UI32_MAX});
SPU_IO::Key::Off.write(KeyOff::all());
}
void setup() {
@ -88,7 +82,6 @@ namespace JabyEngine {
clear_noise_and_echo();
clear_reverb();
setup_data_transfer_control();
setup_control_register();
}
}

View File

@ -2,7 +2,10 @@
#include "../../internal-include/GPU/gpu_internal.hpp"
#include <stdio.hpp>
#include "../../internal-include/SPU/spu_mmu.hpp"
#include <PSX/GTE/gte.hpp>
#include <PSX/SPU/spu.hpp>
#include <PSX/System/IOPorts/spu_io.hpp>
#include <PSX/System/syscalls.hpp>
extern "C" uint32_t __heap_start;
@ -39,6 +42,26 @@ namespace JabyEngine {
}
}
static void test_spu_alloc() {
static const auto calculate_spu_adr = [](size_t size) -> const uint8_t* {
return reinterpret_cast<const uint8_t*>(SPU_IO::MemoryMap::ADPCM + size);
};
static const auto simple_assert = [](uint32_t test_id, const uint8_t* adr, const uint8_t* expected) {
static const char* ok_text = "Test %i: 0x%p == 0x%p; OK\n";
static const char* failed_text = "Test %i: 0x%p != 0x%p; Failed\n";
printf(adr == expected ? ok_text : failed_text, test_id, adr, expected);
};
printf("=== SPU test ===\n");
simple_assert(0, SPU_MMU::allocate(0, 0x600), calculate_spu_adr(0x0));
simple_assert(1, SPU_MMU::allocate(1, 0x800), calculate_spu_adr(0x600));
simple_assert(2, SPU_MMU::allocate(0, 0x300), calculate_spu_adr(0x0));
simple_assert(3, SPU_MMU::allocate(2, 0x300), calculate_spu_adr(0x300));
// TODO: More tests
}
namespace boot {
namespace Start {
// Thanks to Nicolas Noble!
@ -77,6 +100,7 @@ namespace JabyEngine {
GTE::setup();
test_bios_font();
test_gte_scale();
test_spu_alloc();
SPU::setup();
}

View File

@ -37,11 +37,17 @@ namespace JabyEngine {
}(file, buf_cfg, is_lz4);
switch(file.type) {
case CDFileType::SimpleTIM:
return FileProcessor::create(data_adr, file.payload.simple_tim);
case CDFileType::CopyTo:
return FileProcessor::create(data_adr, Nothing());
case CDFileType::SimpleTIM:
return FileProcessor::create(data_adr, file.payload.simple_tim);
#ifdef __SUPPORT_VAG__
case CDFileType::SonyVAG:
return FileProcessor::create(data_adr, file.payload.vag);
#endif //__SUPPORT_VAG__
default:
return FileProcessor::create_custom(data_adr, static_cast<CDFileType_t>(file.type) - static_cast<CDFileType_t>(CDFileType::Custom), file.payload);
}

View File

@ -10,7 +10,7 @@ namespace JabyEngine {
}
State create(const uint32_t* data_adr, const Nothing& nothing) {
return State::from(NothingState(), reinterpret_cast<const uint8_t*>(data_adr), parse_nothing);
return State::from(NothingState(), data_adr, parse_nothing);
}
}
}

View File

@ -1,7 +1,6 @@
#include "../../../internal-include/GPU/gpu_internal.hpp"
#include <PSX/File/file_processor_helper.hpp>
#include <PSX/GPU/gpu_types.hpp>
#include <limits.hpp>
#include <stdio.hpp>
namespace JabyEngine {
@ -63,44 +62,12 @@ namespace JabyEngine {
}
static Progress parse_data(State::Configuration& config, SimpleTIMState& state) {
const auto config_data_words = (config.data_bytes/sizeof(uint32_t));
const auto words_to_use = (config_data_words > state.words_left) ? state.words_left : config_data_words;
bool is_last = (words_to_use == state.words_left);
auto block_count = (words_to_use >> 4);
while(block_count > 0) {
const auto block_send = (block_count > UI16_MAX) ? UI16_MAX : block_count;
// Send data!
GPU::internal::DMA::wait();
GPU::internal::DMA::Receive::start(block_send);
block_count -= block_send;
}
if(is_last) {
// Send words
const auto last_words = (words_to_use & 0b1111);
if(last_words > 0) {
GPU::internal::DMA::wait();
GPU::internal::DMA::Receive::start(1, last_words);
}
GPU::internal::DMA::wait();
GPU::internal::DMA::end();
state.words_left = 0;
config.processed(words_to_use*sizeof(uint32_t));
return Progress::Done;
}
else {
const auto words_used = (words_to_use & ~0b1111);
const auto [words_to_use, is_last] = Helper::DMA::WordsReady::calculate(config, state.words_left);
const auto words_used = Helper::DMA::send_words<GPU::internal::DMA>(words_to_use, is_last);
state.words_left -= words_used;
config.processed(words_used*sizeof(uint32_t));
return Progress::InProgress;
}
return is_last ? Progress::Done : Progress::InProgress;
}
static Progress switch_state_parse_data(State::Configuration& config, SimpleTIMState& state) {
@ -139,7 +106,7 @@ namespace JabyEngine {
}
State create(const uint32_t* data_adr, const SimpleTIM& file) {
return State::from(SimpleTIMState(file), reinterpret_cast<const uint8_t*>(data_adr), parse_header);
return State::from(SimpleTIMState(file), data_adr, parse_header);
}
}
}

View File

@ -0,0 +1,81 @@
#include "../../../internal-include/SPU/spu_internal.hpp"
#include <PSX/Auxiliary/big_endian.hpp>
#include <PSX/Auxiliary/word_helper.hpp>
#include <PSX/File/file_processor_helper.hpp>
#include <PSX/SPU/spu.hpp>
#include <stdio.hpp>
#ifdef __SUPPORT_VAG__
namespace JabyEngine {
namespace FileProcessor {
struct VAGHeader {
char id[4];
uint32_t version;
uint32_t reserved;
uint32_t sample_size;
uint32_t sample_frequency;
uint8_t reserved_2[12];
char name[16];
constexpr uint32_t get_version() const {
return read_be(this->version);
}
constexpr uint32_t get_sample_size() const {
return read_be(this->sample_size);
}
constexpr uint32_t get_sample_frequency() const {
return read_be(this->sample_frequency);
}
};
struct VAGState {
uint32_t voice_id;
size_t words_left;
SPU::SimpleVolume inital_vol;
static constexpr VAGState create(uint32_t voice_id, SPU::SimpleVolume inital_vol) {
return VAGState{.voice_id = voice_id, .words_left = 0, .inital_vol = inital_vol};
}
};
static Progress parse_sample(State::Configuration& config, VAGState& state) {
const auto [words_to_use, is_last] = Helper::DMA::WordsReady::calculate(config, state.words_left);
const auto words_used = Helper::DMA::send_words<SPU::internal::DMA>(words_to_use, is_last);
state.words_left -= words_used;
config.processed(words_used*sizeof(uint32_t));
return is_last ? Progress::Done : Progress::InProgress;
}
static Progress parse_header(State::Configuration& config, VAGState& state) {
if(config.data_bytes >= sizeof(VAGHeader)) {
const auto& header = *reinterpret_cast<const VAGHeader*>(config.data_adr);
const auto words = bytes_to_words(header.get_sample_size());
const auto bytes = words_to_bytes(words);
state.words_left = words;
auto sram_adr = SPU::voice[state.voice_id].allocate(SPU_IO::SampleRate::from_HZ(header.get_sample_frequency()), bytes);
// TODO: Keep this as optional?
printf("SPU: Allocated %i @0x%p to 0x%p (%i bytes)\n", state.voice_id, sram_adr.raw, (sram_adr.raw + bytes), bytes);
SPU::voice[state.voice_id].set_volume(state.inital_vol, state.inital_vol);
config.processed(sizeof(VAGHeader));
SPU::internal::DMA::Receive::prepare();
SPU::internal::DMA::Receive::set_dst(sram_adr);
SPU::internal::DMA::Receive::set_src(reinterpret_cast<uintptr_t>(config.data_adr));
return Helper::exchange_and_execute_process_function(parse_sample, config, state);
}
return Progress::InProgress;
}
State create(const uint32_t* data_adr, const VAG& file) {
return State::from(VAGState::create(file.voice_number, file.inital_stereo_vol), data_adr, parse_header);
}
}
}
#endif // __SUPPORT_VAG__

View File

@ -8,11 +8,9 @@ namespace JabyEngine {
uint8_t Display :: current_id = 1; //< Setup will call exchange and set it to 0
namespace internal {
namespace DMA {
#ifdef __SUPPORT_PS3__
uintptr_t MADR = 0;
uintptr_t DMA :: MADR = 0;
#endif // __SUPPORT_PS3__
}
static SysCall::InterruptVerifierResult interrupt_verifier();
static void interrupt_handler(uint32_t);

View File

@ -0,0 +1,31 @@
#include "../../internal-include/SPU/spu_internal.hpp"
#include "../../internal-include/SPU/spu_mmu.hpp"
#include <PSX/System/IOPorts/spu_io.hpp>
#include <PSX/SPU/spu.hpp>
#include <stddef.hpp>
#include <stdio.hpp>
namespace JabyEngine {
namespace SPU {
SRAMAdr Voice :: allocate(size_t size) {
Voice::stop();
const auto voice_id = Voice::get_id();
const auto adr = SRAMAdr{static_cast<SRAMAdr::UnderlyingType>(reinterpret_cast<uintptr_t>(SPU_MMU::allocate(voice_id, size)))};
SPU_IO::Voice[voice_id].adr.write(adr);
return adr;
}
SRAMAdr Voice :: allocate(SPU_IO::SampleRate frequency, size_t size) {
const auto result = Voice::allocate(size);
Voice::set_sample_rate(frequency);
return result;
}
void Voice :: deallocate() {
Voice::stop();
SPU_MMU::deallocate(Voice::get_id());
}
}
}

View File

@ -0,0 +1,125 @@
#include "../../internal-include/SPU/spu_mmu.hpp"
#include <PSX/System/IOPorts/spu_io.hpp>
#include <PSX/Auxiliary/math_helper.hpp>
#include <PSX/SPU/spu.hpp>
#include <stddef.hpp>
namespace JabyEngine {
namespace SPU_MMU {
namespace SPU_MemoryMap = SPU_IO::MemoryMap;
struct SPUMemory {
// TODO: v change to uint16_t??
const uint8_t* adr;
size_t size;
static SPUMemory create(size_t size) {
return SPUMemory{.adr = reinterpret_cast<const uint8_t*>(SPU_MemoryMap::ADPCM), .size = size};
}
constexpr void clear() {
this->adr = nullptr;
this->size = 0;
}
constexpr const uint8_t* get_end_adr() const {
return (this->adr + this->size);
}
constexpr bool is_free() const {
return this->adr == nullptr;
}
constexpr bool intersects(const SPUMemory& other) const {
const auto* min = max_of(this->adr, other.adr);
const auto* max = min_of(this->get_end_adr(), other.get_end_adr());
return min < max;
}
};
struct AllocatedVoice {
SPUMemory memory;
AllocatedVoice* next_entry;
};
struct VoiceManager {
struct Iterator {
AllocatedVoice* *prev_voice;
AllocatedVoice* current_voice;
void next() {
this->prev_voice = &this->current_voice->next_entry;
this->current_voice = this->current_voice->next_entry;
}
bool has_next() const {
return this->current_voice;
}
operator bool() const {
return Iterator::has_next();
}
};
AllocatedVoice allocated_voice_buffer[SPU_IO::VoiceCount + SPU_IO::ReverbCount] = {0};
AllocatedVoice* first_allocated_voice = nullptr;
Iterator iterator() {
return Iterator{.prev_voice = &this->first_allocated_voice, .current_voice = this->first_allocated_voice};
}
AllocatedVoice& get_voice(uint8_t id) {
return this->allocated_voice_buffer[id];
}
};
static VoiceManager voice_mgr;
using MemoryFoundCallback = const uint8_t* (AllocatedVoice& new_entry, AllocatedVoice* &prev_entry, AllocatedVoice* next_entry);
static const uint8_t* verify_and_add(AllocatedVoice& new_entry, AllocatedVoice* &prev_entry, AllocatedVoice* next_entry) {
// TODO: Verify that we are not crashing into reverb or that we are higher then SPU
prev_entry = &new_entry;
new_entry.next_entry = next_entry;
return new_entry.memory.adr;
}
static const uint8_t* find_first_fit(AllocatedVoice &new_entry, MemoryFoundCallback memory_found) {
auto iterator = voice_mgr.iterator();
while(iterator) {
if(!iterator.current_voice->memory.intersects(new_entry.memory)) {
break;
}
new_entry.memory.adr = iterator.current_voice->memory.get_end_adr();
iterator.next();
}
return memory_found(new_entry, *iterator.prev_voice, iterator.current_voice);
}
const uint8_t* allocate(uint8_t voice, size_t size) {
auto& voice_entry = voice_mgr.get_voice(voice);
if(!voice_entry.memory.is_free()) {
deallocate(voice);
}
voice_entry.memory = SPUMemory::create(size);
return find_first_fit(voice_entry, verify_and_add);
}
void deallocate(uint8_t voice) {
auto* voice_adr = &voice_mgr.get_voice(voice);
auto iterator = voice_mgr.iterator();
voice_adr->memory.clear();
while(iterator) {
if(iterator.current_voice == voice_adr) {
*iterator.prev_voice = voice_adr->next_entry;
break;
}
iterator.next();
}
}
}
}

View File

@ -1,6 +1,6 @@
[package]
name = "jaby_engine_fconv"
version = "0.1.6"
version = "0.2.0"
edition = "2021"
[profile.release]

View File

@ -1 +1,43 @@
pub mod xa;
pub mod vag;
use std::{env, path::PathBuf, process::Command};
use tool_helper::Error;
fn run_psxavenc<I, S>(input: PathBuf, output: PathBuf, args: I) -> Result<(), Error>
where
I: IntoIterator<Item = S>,
S: AsRef<std::ffi::OsStr>, {
let psxavenc = get_psxavenc_path()?;
let result = Command::new(psxavenc).args(args).arg(input.to_string_lossy().as_ref()).arg(output.to_string_lossy().as_ref()).output()?;
let stderr = tool_helper::vec_helper::to_string(result.stderr)?;
let stdout = tool_helper::vec_helper::to_string(result.stdout)?;
if !result.status.success() {
return Err(Error::from_text(format!("psxavenc returned: {}. {}", result.status, stderr)));
}
if !stdout.is_empty() {
println!("{}", stdout);
}
Ok(())
}
fn get_psxavenc_path() -> Result<PathBuf, Error> {
let tool_path = {
let mut my_path = env::current_exe()?;
my_path.pop();
my_path.push("extern");
my_path.push("psxavenc");
my_path
};
if !tool_path.exists() {
return Err(Error::from_str("Could not locate psxavenc. Make sure it is installed under \"<Jaby Engine>/bin/extern\""));
}
Ok(tool_path)
}

View File

@ -0,0 +1,22 @@
use clap::Args;
use std::path::PathBuf;
use tool_helper::Error;
#[derive(Args)]
pub struct Arguments {
frequency: Option<u32>
}
pub fn convert(args: Arguments, input: PathBuf, output: PathBuf) -> Result<(), Error> {
let mut cmd_args = vec!["-t", "vag"];
let frequency_str;
if let Some(frequency) = args.frequency {
frequency_str = frequency.to_string().to_owned();
cmd_args.push("-f");
cmd_args.push(frequency_str.as_ref());
}
super::run_psxavenc(input, output, cmd_args)
}

View File

@ -1,5 +1,5 @@
use clap::{Args, ValueEnum};
use std::{env, path::PathBuf, process::Command, str};
use std::{path::PathBuf, str};
use tool_helper::Error;
#[derive(Args)]
@ -43,39 +43,13 @@ impl Sample {
pub fn convert(args: Arguments, input: PathBuf, output: PathBuf) -> Result<(), Error> {
let quality = args.quality;
let sample = args.sample;
let tool_path = {
let mut my_path = env::current_exe()?;
my_path.pop();
my_path.push("extern");
my_path.push("psxavenc");
my_path
};
if !tool_path.exists() {
return Err(Error::from_str("Could not locate psxavenc. Make sure it is installed under \"<Jaby Engine>/bin/extern\""));
}
let result = Command::new(tool_path).args([
super::run_psxavenc(input, output, [
"-t", "xacd",
"-f", quality.get_frequency().to_string().as_ref(),
"-b", "4",
"-c", sample.get_channel().to_string().as_ref(),
"-F", "0",
"-C", "0",
input.to_string_lossy().as_ref(), output.to_string_lossy().as_ref()
]) .output()?;
let stderr = tool_helper::vec_helper::to_string(result.stderr)?;
let stdout = tool_helper::vec_helper::to_string(result.stdout)?;
if !result.status.success() {
return Err(Error::from_text(format!("psxavenc returned: {}. {}", result.status, stderr)));
}
if !stdout.is_empty() {
println!("{}", stdout);
}
Ok(())
])
}

View File

@ -21,9 +21,25 @@ struct CommandLine {
#[derive(Subcommand)]
enum SubCommands {
// === Internal Commands ===
Nothing,
SimpleTIM(reduced_tim::Arguments),
XA(xa::Arguments)
// === External Commands ===
VAG(vag::Arguments),
XA(xa::Arguments),
}
impl SubCommands {
pub fn is_external_command(&self) -> bool {
match self {
SubCommands::Nothing => false,
SubCommands::SimpleTIM(_) => false,
SubCommands::VAG(_) => true,
SubCommands::XA(_) => true
}
}
}
fn run_internal_conversion(cmd: CommandLine) -> Result<(), Error> {
@ -44,7 +60,7 @@ fn run_internal_conversion(cmd: CommandLine) -> Result<(), Error> {
match cmd.sub_command {
SubCommands::Nothing => nothing::copy(&mut input, dst_buffer),
SubCommands::SimpleTIM(args) => reduced_tim::convert(args, input, dst_buffer),
_ => Err(Error::not_implemented("External functions can not be called for internal conversion"))
_ => Err(Error::from_str("External functions can not be called for internal conversion"))
}
};
@ -74,13 +90,14 @@ fn run_external_conversion(cmd: CommandLine) -> Result<(), Error> {
let output_file = cmd.output_file.ok_or(Error::from_str("Output has to be a file"))?;
match cmd.sub_command {
SubCommands::VAG(args) => vag::convert(args, input_file, output_file),
SubCommands::XA(args) => xa::convert(args, input_file, output_file),
_ => Err(Error::not_implemented("Internal functions can not be called for external conversion"))
_ => Err(Error::from_str("Internal functions can not be called for external conversion"))
}
}
fn run_main(cmd: CommandLine) -> Result<(), Error> {
if matches!(cmd.sub_command, SubCommands::XA(_)) {
if cmd.sub_command.is_external_command() {
run_external_conversion(cmd)
}