mirror of
https://github.com/TwilitRealm/dusklight
synced 2026-06-02 17:48:21 -04:00
Works a lot better now
This commit is contained in:
@@ -1334,6 +1334,7 @@ set(DUSK_FILES
|
||||
include/dusk/endian_gx.hpp
|
||||
src/dusk/asserts.cpp
|
||||
src/dusk/logging.cpp
|
||||
src/dusk/layout.cpp
|
||||
src/dusk/stubs.cpp
|
||||
src/dusk/endian.cpp
|
||||
src/dusk/extras.c
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
#ifndef D_A_MOVIE_PLAYER_H
|
||||
#define D_A_MOVIE_PLAYER_H
|
||||
|
||||
#if !TARGET_PC
|
||||
#include <thp.h>
|
||||
#endif
|
||||
#include "f_op/f_op_actor.h"
|
||||
#include "d/d_drawlist.h"
|
||||
|
||||
@@ -11,6 +13,85 @@ struct daMP_THPReadBuffer {
|
||||
BOOL isValid;
|
||||
};
|
||||
|
||||
#if TARGET_PC
|
||||
// Copying here because thp.h is probably erroneous in the dolphin lib,
|
||||
// and it's kind of a problem being there (Aurora owns the headers).
|
||||
// TODO: Move this stuff in decomp?
|
||||
typedef struct THPAudioRecordHeader {
|
||||
BE(u32) offsetNextChannel;
|
||||
BE(u32) sampleSize;
|
||||
BE(s16) lCoef[8][2];
|
||||
BE(s16) rCoef[8][2];
|
||||
BE(s16) lYn1;
|
||||
BE(s16) lYn2;
|
||||
BE(s16) rYn1;
|
||||
BE(s16) rYn2;
|
||||
} THPAudioRecordHeader;
|
||||
|
||||
typedef struct THPAudioDecodeInfo {
|
||||
u8* encodeData;
|
||||
u32 offsetNibbles;
|
||||
u8 predictor;
|
||||
u8 scale;
|
||||
s16 yn1;
|
||||
s16 yn2;
|
||||
} THPAudioDecodeInfo;
|
||||
|
||||
typedef struct THPTextureSet {
|
||||
u8* ytexture;
|
||||
u8* utexture;
|
||||
u8* vtexture;
|
||||
s32 frameNumber;
|
||||
} THPTextureSet;
|
||||
|
||||
typedef struct THPAudioBuffer {
|
||||
s16* buffer;
|
||||
s16* curPtr;
|
||||
u32 validSample;
|
||||
} THPAudioBuffer;
|
||||
|
||||
typedef struct THPVideoInfo {
|
||||
BE(u32) xSize;
|
||||
BE(u32) ySize;
|
||||
BE(u32) videoType;
|
||||
} THPVideoInfo;
|
||||
|
||||
typedef struct THPAudioInfo {
|
||||
BE(u32) sndChannels;
|
||||
BE(u32) sndFrequency;
|
||||
BE(u32) sndNumSamples;
|
||||
BE(u32) sndNumTracks;
|
||||
} THPAudioInfo;
|
||||
|
||||
typedef struct THPFrameCompInfo {
|
||||
BE(u32) numComponents;
|
||||
u8 frameComp[16];
|
||||
} THPFrameCompInfo;
|
||||
|
||||
typedef struct THPHeader {
|
||||
/* 0x00 */ char magic[4];
|
||||
/* 0x04 */ BE(u32) version;
|
||||
/* 0x08 */ BE(u32) bufsize;
|
||||
/* 0x0C */ BE(u32) audioMaxSamples;
|
||||
/* 0x10 */ BE(f32) frameRate;
|
||||
/* 0x14 */ BE(u32) numFrames;
|
||||
/* 0x18 */ BE(u32) firstFrameSize;
|
||||
/* 0x1C */ BE(u32) movieDataSize;
|
||||
/* 0x20 */ BE(u32) compInfoDataOffsets;
|
||||
/* 0x24 */ BE(u32) offsetDataOffsets;
|
||||
/* 0x28 */ BE(u32) movieDataOffsets;
|
||||
/* 0x2C */ BE(u32) finalFrameDataOffsets;
|
||||
} THPHeader;
|
||||
|
||||
u32 THPAudioDecode(s16* audioBuffer, u8* audioFrame, s32 flag);
|
||||
s32 __THPAudioGetNewSample(THPAudioDecodeInfo* info);
|
||||
void __THPAudioInitialize(THPAudioDecodeInfo* info, u8* ptr);
|
||||
|
||||
#define THP_AUDIO_BUFFER_COUNT 3
|
||||
#define THP_READ_BUFFER_COUNT 10
|
||||
#define THP_TEXTURE_SET_COUNT 3
|
||||
#endif
|
||||
|
||||
struct daMP_THPPlayer {
|
||||
/* 0x000 */ DVDFileInfo fileInfo;
|
||||
/* 0x03C */ THPHeader header;
|
||||
@@ -34,7 +115,11 @@ struct daMP_THPPlayer {
|
||||
/* 0x0C8 */ s64 retaceCount;
|
||||
/* 0x0D0 */ s32 prevCount;
|
||||
/* 0x0D4 */ s32 curCount;
|
||||
#if TARGET_PC
|
||||
/* 0x0D8 */ std::atomic<s32> videoDecodeCount;
|
||||
#else
|
||||
/* 0x0D8 */ s32 videoDecodeCount;
|
||||
#endif
|
||||
/* 0x0DC */ f32 curVolume;
|
||||
/* 0x0E0 */ f32 targetVolume;
|
||||
/* 0x0E4 */ f32 deltaVolume;
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
#ifndef DUSK_LAYOUT_H
|
||||
#define DUSK_LAYOUT_H
|
||||
|
||||
#include "dolphin/types.h"
|
||||
|
||||
namespace dusk {
|
||||
|
||||
/**
|
||||
* Helper struct for laying things out on the screen. Represents a rectangle via two corner
|
||||
* positions.
|
||||
*/
|
||||
struct LayoutRect {
|
||||
f32 PosX;
|
||||
f32 PosY;
|
||||
f32 PosX2;
|
||||
f32 PosY2;
|
||||
|
||||
[[nodiscard]] constexpr f32 Width() const {
|
||||
return PosX2 - PosX;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr f32 Height() const {
|
||||
return PosY2 - PosY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the position to render one rectangle inside another, centered and maintaining aspect ratio.
|
||||
*/
|
||||
[[nodiscard]] static LayoutRect FitRectInRect(
|
||||
f32 widthOuter,
|
||||
f32 heightOuter,
|
||||
f32 widthInner,
|
||||
f32 heightInner);
|
||||
};
|
||||
}
|
||||
|
||||
#endif // DUSK_LAYOUT_H
|
||||
@@ -27,9 +27,10 @@
|
||||
#include "f_op/f_op_overlap_mng.h"
|
||||
|
||||
#include "dusk/gx_helper.h"
|
||||
#include "dusk/memory.h"
|
||||
#include "dusk/os.h"
|
||||
#include "dusk/layout.hpp"
|
||||
|
||||
#include "JSystem/JAudio2/JASCriticalSection.h"
|
||||
#include "turbojpeg.h"
|
||||
|
||||
inline s32 daMP_NEXT_READ_SIZE(daMP_THPReadBuffer* readBuf) {
|
||||
@@ -2573,9 +2574,12 @@ static void __THPHuffDecodeDCTCompV(__REGISTER THPFileInfo* info, THPCoeff* bloc
|
||||
}
|
||||
#else // !TARGET_PC
|
||||
|
||||
static std::vector<u8> FixJpeg(const std::span<u8> data) {
|
||||
std::vector<u8> fixedData;
|
||||
fixedData.reserve(data.size());
|
||||
static std::vector<u8> FixedJpegData;
|
||||
static tjhandle JpegDecompressHandle;
|
||||
|
||||
static const std::vector<u8>& FixJpeg(const std::span<u8> data) {
|
||||
FixedJpegData.resize(0);
|
||||
FixedJpegData.reserve(data.size());
|
||||
|
||||
size_t startOfScanLocation = 0;
|
||||
for (; startOfScanLocation < data.size() - 1; startOfScanLocation++) {
|
||||
@@ -2602,48 +2606,32 @@ static std::vector<u8> FixJpeg(const std::span<u8> data) {
|
||||
|
||||
// Copy data before SOS
|
||||
for (size_t i = 0; i < startOfScanLocation; i++) {
|
||||
fixedData.push_back(data[i]);
|
||||
FixedJpegData.push_back(data[i]);
|
||||
}
|
||||
|
||||
// Copy data inside SOS, fixing up lacking of "byte shuffling"
|
||||
for (size_t i = startOfScanLocation; i < endOfImage; i++) {
|
||||
u8 value = data[i];
|
||||
fixedData.push_back(value);
|
||||
FixedJpegData.push_back(value);
|
||||
if (value == 0xFF) {
|
||||
fixedData.push_back(0x00);
|
||||
FixedJpegData.push_back(0x00);
|
||||
}
|
||||
}
|
||||
|
||||
// Copy data after SOS.
|
||||
for (size_t i = endOfImage; i < data.size(); i++) {
|
||||
fixedData.push_back(data[i]);
|
||||
FixedJpegData.push_back(data[i]);
|
||||
}
|
||||
|
||||
return fixedData;
|
||||
return FixedJpegData;
|
||||
}
|
||||
|
||||
struct TjHandle {
|
||||
tjhandle mHandle;
|
||||
|
||||
explicit TjHandle(const tjhandle handle) : mHandle(handle) {}
|
||||
~TjHandle() {
|
||||
tj3Destroy(mHandle);
|
||||
}
|
||||
|
||||
operator tjhandle() const {
|
||||
return mHandle;
|
||||
}
|
||||
};
|
||||
|
||||
extern daMP_THPPlayer daMP_ActivePlayer;
|
||||
|
||||
static s32 THPVideoDecode(void* file, size_t fileSize, void* tileY, void* tileU, void* tileV, void* work) {
|
||||
const TjHandle handle(tj3Init(TJINIT_DECOMPRESS));
|
||||
if (handle == nullptr) {
|
||||
OSReport_Error("Failed to create turbojpeg handle: %s", tj3GetErrorStr(nullptr));
|
||||
return 1;
|
||||
}
|
||||
static s32 THPVideoDecode(void* file, size_t fileSize, void* tileY, void* tileU, void* tileV, void*) {
|
||||
assert(JpegDecompressHandle);
|
||||
|
||||
const auto handle = JpegDecompressHandle;
|
||||
const auto fixedData = FixJpeg(std::span(static_cast<u8*>(file), fileSize));
|
||||
|
||||
auto ret = tj3DecompressHeader(handle, fixedData.data(), fixedData.size());
|
||||
@@ -3220,8 +3208,13 @@ static void daMP_THPGXYuv2RgbSetup(const GXRenderModeObj* rmode) {
|
||||
Mtx44 m;
|
||||
Mtx e_m;
|
||||
|
||||
#if TARGET_PC
|
||||
w = JUTVideo::getManager()->getFbWidth();
|
||||
h = JUTVideo::getManager()->getEfbHeight();
|
||||
#else
|
||||
w = rmode->fbWidth;
|
||||
h = rmode->efbHeight;
|
||||
#endif
|
||||
var_f31 = 0.0f;
|
||||
|
||||
#if WIDESCREEN_SUPPORT
|
||||
@@ -3651,6 +3644,13 @@ static BOOL daMP_THPPlayerOpen(char const* filename, BOOL onMemory) {
|
||||
}
|
||||
|
||||
static BOOL daMP_THPPlayerClose() {
|
||||
#if TARGET_PC
|
||||
tj3Destroy(JpegDecompressHandle);
|
||||
JpegDecompressHandle = nullptr;
|
||||
|
||||
FixedJpegData.clear();
|
||||
#endif
|
||||
|
||||
if (daMP_ActivePlayer.open && daMP_ActivePlayer.state == 0) {
|
||||
daMP_ActivePlayer.open = 0;
|
||||
DVDClose(&daMP_ActivePlayer.fileInfo);
|
||||
@@ -3784,6 +3784,12 @@ static BOOL daMP_ProperTimingForGettingNextFrame() {
|
||||
}
|
||||
} else {
|
||||
s32 frameRate = daMP_ActivePlayer.header.frameRate * 100.0f;
|
||||
#if TARGET_PC
|
||||
// DUSK HACK: We only fire retrace callbacks *half* as often as the game expects,
|
||||
// because we only run them once per frame, and normally there should be two scans
|
||||
// per game frame.
|
||||
frameRate *= 2;
|
||||
#endif
|
||||
if (VIGetTvFormat() == VI_PAL) {
|
||||
daMP_ActivePlayer.curCount = daMP_ActivePlayer.retaceCount * frameRate / 5000;
|
||||
} else {
|
||||
@@ -4112,6 +4118,9 @@ static BOOL daMP_THPPlayerSetVolume(s32 vol, s32 duration) {
|
||||
if (duration < 0)
|
||||
duration = 0;
|
||||
|
||||
#if TARGET_PC
|
||||
JASCriticalSection section;
|
||||
#endif
|
||||
interrupt = OSDisableInterrupts();
|
||||
|
||||
daMP_ActivePlayer.targetVolume = vol;
|
||||
@@ -4155,11 +4164,14 @@ static BOOL daMP_ActivePlayer_Init(char const* moviePath) {
|
||||
daMP_THPPlayerGetVideoInfo(&daMP_videoInfo);
|
||||
daMP_THPPlayerGetAudioInfo(&daMP_audioInfo);
|
||||
|
||||
#if !TARGET_PC
|
||||
// Window can be resized during playback, update this during draw.
|
||||
u16 width = JUTVideo::getManager()->getRenderMode()->fbWidth;
|
||||
u16 height = JUTVideo::getManager()->getRenderMode()->efbHeight;
|
||||
|
||||
daMP_DrawPosX = (width - daMP_videoInfo.xSize) >> 1;
|
||||
daMP_DrawPosY = (height - daMP_videoInfo.ySize) >> 1;
|
||||
#endif
|
||||
|
||||
// "The memory needed for this THP movie is %d bytes\n"
|
||||
OS_REPORT("このTHPムービーが必要なメモリは%dバイトです\n", daMP_THPPlayerCalcNeedMemory());
|
||||
@@ -4176,6 +4188,15 @@ static BOOL daMP_ActivePlayer_Init(char const* moviePath) {
|
||||
|
||||
daMP_THPPlayerSetBuffer((u8*)daMP_buffer);
|
||||
|
||||
#if TARGET_PC
|
||||
assert(JpegDecompressHandle == nullptr);
|
||||
JpegDecompressHandle = tj3Init(TJINIT_DECOMPRESS);
|
||||
if (JpegDecompressHandle == nullptr) {
|
||||
OSReport_Error("Failed to create turbojpeg handle: %s", tj3GetErrorStr(nullptr));
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!daMP_THPPlayerPrepare(0, 0, daMP_audioInfo.sndNumTracks != 1 ? OSGetTick() % daMP_audioInfo.sndNumTracks : 0)) {
|
||||
OSReport("Fail to prepare\n");
|
||||
#if DEBUG
|
||||
@@ -4211,14 +4232,55 @@ static void daMP_ActivePlayer_Main() {
|
||||
}
|
||||
}
|
||||
|
||||
#if TARGET_PC && 0
|
||||
#include "imgui.h"
|
||||
#endif
|
||||
|
||||
static void daMP_ActivePlayer_Draw() {
|
||||
int frame = daMP_THPPlayerDrawCurrentFrame(JUTVideo::getManager()->getRenderMode(), daMP_DrawPosX, daMP_DrawPosY, daMP_videoInfo.xSize, daMP_videoInfo.ySize);
|
||||
#if TARGET_PC
|
||||
u16 width = JUTVideo::getManager()->getFbWidth();
|
||||
u16 height = JUTVideo::getManager()->getEfbHeight();
|
||||
|
||||
const auto rect = dusk::LayoutRect::FitRectInRect(
|
||||
width,
|
||||
height,
|
||||
static_cast<f32>(daMP_videoInfo.xSize),
|
||||
static_cast<f32>(daMP_videoInfo.ySize));
|
||||
|
||||
daMP_DrawPosX = static_cast<u32>(rect.PosX);
|
||||
daMP_DrawPosY = static_cast<u32>(rect.PosY);
|
||||
#endif
|
||||
|
||||
int frame = daMP_THPPlayerDrawCurrentFrame(
|
||||
JUTVideo::getManager()->getRenderMode(),
|
||||
daMP_DrawPosX, daMP_DrawPosY,
|
||||
#if TARGET_PC
|
||||
static_cast<u32>(rect.Width()),
|
||||
static_cast<u32>(rect.Height()));
|
||||
#else
|
||||
daMP_videoInfo.xSize,
|
||||
daMP_videoInfo.ySize);
|
||||
#endif
|
||||
daMP_THPPlayerDrawDone();
|
||||
|
||||
if (!fopOvlpM_IsPeek() && frame > 0 && (cAPICPad_ANY_BUTTON(0) || !daMP_c::daMP_c_Get_MovieRestFrame())) {
|
||||
dComIfGp_event_reset();
|
||||
daMP_c::daMP_c_Set_PercentMovieVolume(0.0f);
|
||||
}
|
||||
|
||||
#if TARGET_PC && 0
|
||||
if (ImGui::Begin("Movie player")) {
|
||||
ImGui::Text("daMP_ReadedBufferQueue: %d", daMP_ReadedBufferQueue.usedCount);
|
||||
ImGui::Text("daMP_ReadedBufferQueue2: %d", daMP_ReadedBufferQueue2.usedCount);
|
||||
ImGui::Text("daMP_FreeReadBufferQueue: %d", daMP_FreeReadBufferQueue.usedCount);
|
||||
ImGui::Text("daMP_DecodedTextureSetQueue: %d", daMP_DecodedTextureSetQueue.usedCount);
|
||||
ImGui::Text("daMP_FreeTextureSetQueue: %d", daMP_FreeTextureSetQueue.usedCount);
|
||||
ImGui::Text("daMP_DecodedAudioBufferQueue: %d", daMP_DecodedAudioBufferQueue.usedCount);
|
||||
ImGui::Text("daMP_FreeAudioBufferQueue: %d", daMP_FreeAudioBufferQueue.usedCount);
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
#endif
|
||||
}
|
||||
|
||||
static BOOL daMP_Fail_alloc;
|
||||
|
||||
@@ -111,11 +111,6 @@ int RenderNewAudioFrame() {
|
||||
outRaw.flush();
|
||||
#endif
|
||||
|
||||
if (JASDriver::extMixCallback != nullptr) {
|
||||
// TODO: actually mix this data in.
|
||||
JASDriver::extMixCallback(countSubframes * DSP_SUBFRAME_SIZE);
|
||||
}
|
||||
|
||||
return static_cast<u16>(countSubframes) * DSP_SUBFRAME_SIZE;
|
||||
}
|
||||
|
||||
@@ -138,6 +133,19 @@ void RenderAudioSubframe() {
|
||||
|
||||
InterleaveOutputData(OutBuffer, OutInterleaveBuffer);
|
||||
|
||||
if (JASDriver::extMixCallback != nullptr && JASDriver::sMixMode == MIX_MODE_INTERLEAVE) {
|
||||
static_assert(OutputSubframe::NUM_CHANNELS == 2); // This code only works with Stereo so far.
|
||||
// NOTE: In the real game, this gets called on the entire audio frame, rather than the subframe.
|
||||
// That's probably more efficient, but I didn't wanna change the code to calculate the
|
||||
// entire audio buffers at once.
|
||||
const auto mixData = JASDriver::extMixCallback(DSP_SUBFRAME_SIZE);
|
||||
if (mixData) {
|
||||
for (int i = 0; i < OutInterleaveBuffer.size(); i++) {
|
||||
OutInterleaveBuffer[i] += static_cast<f32>(mixData[i]) / static_cast<f32>(0x7FFF);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(DUSK_DUMP_AUDIO)
|
||||
outRaw.write((const char*)OutInterleaveBuffer.data(), sizeof(OutInterleaveBuffer));
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
#include "dusk/layout.hpp"
|
||||
|
||||
using namespace dusk;
|
||||
|
||||
LayoutRect LayoutRect::FitRectInRect(
|
||||
const f32 widthOuter,
|
||||
const f32 heightOuter,
|
||||
const f32 widthInner,
|
||||
const f32 heightInner) {
|
||||
|
||||
// Try as if constrained vertically first.
|
||||
auto width = widthInner * (heightOuter / heightInner);
|
||||
auto height = heightOuter;
|
||||
if (width > widthOuter) {
|
||||
// Otherwise, constrained horizontally.
|
||||
width = widthOuter;
|
||||
height = heightOuter * (widthOuter / widthInner);
|
||||
}
|
||||
|
||||
// Center it
|
||||
const auto posX = (widthOuter - width) / 2;
|
||||
const auto posY = (heightOuter - height) / 2;
|
||||
|
||||
return {posX, posY, posX + width, posY + height};
|
||||
}
|
||||
Reference in New Issue
Block a user