Merge branch 'ToolBox_TODOs_SupportSFX' into ToolBox_TODOs
This commit is contained in:
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
44
src/Library/internal-include/SPU/spu_internal.hpp
Normal file
44
src/Library/internal-include/SPU/spu_internal.hpp
Normal 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());
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
10
src/Library/internal-include/SPU/spu_mmu.hpp
Normal file
10
src/Library/internal-include/SPU/spu_mmu.hpp
Normal 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);
|
||||
}
|
||||
}
|
@@ -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();
|
||||
}
|
||||
}
|
||||
|
@@ -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();
|
||||
}
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@@ -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);
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
state.words_left -= words_used;
|
||||
config.processed(words_used*sizeof(uint32_t));
|
||||
return Progress::InProgress;
|
||||
}
|
||||
state.words_left -= words_used;
|
||||
config.processed(words_used*sizeof(uint32_t));
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
81
src/Library/src/File/Processor/vag_processor.cpp
Normal file
81
src/Library/src/File/Processor/vag_processor.cpp
Normal 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__
|
@@ -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;
|
||||
#endif // __SUPPORT_PS3__
|
||||
}
|
||||
#ifdef __SUPPORT_PS3__
|
||||
uintptr_t DMA :: MADR = 0;
|
||||
#endif // __SUPPORT_PS3__
|
||||
|
||||
static SysCall::InterruptVerifierResult interrupt_verifier();
|
||||
static void interrupt_handler(uint32_t);
|
||||
|
31
src/Library/src/SPU/spu.cpp
Normal file
31
src/Library/src/SPU/spu.cpp
Normal 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());
|
||||
}
|
||||
}
|
||||
}
|
125
src/Library/src/SPU/spu_mmu.cpp
Normal file
125
src/Library/src/SPU/spu_mmu.cpp
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "jaby_engine_fconv"
|
||||
version = "0.1.6"
|
||||
version = "0.2.0"
|
||||
edition = "2021"
|
||||
|
||||
[profile.release]
|
||||
|
@@ -1 +1,43 @@
|
||||
pub mod xa;
|
||||
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)
|
||||
}
|
22
src/Tools/jaby_engine_fconv/src/audio/vag/mod.rs
Normal file
22
src/Tools/jaby_engine_fconv/src/audio/vag/mod.rs
Normal 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)
|
||||
}
|
@@ -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(())
|
||||
])
|
||||
}
|
@@ -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::XA(args) => xa::convert(args, input_file, output_file),
|
||||
_ => Err(Error::not_implemented("Internal functions can not be called for external conversion"))
|
||||
SubCommands::VAG(args) => vag::convert(args, input_file, output_file),
|
||||
SubCommands::XA(args) => xa::convert(args, input_file, output_file),
|
||||
_ => 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)
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user