Works a lot better now

This commit is contained in:
PJB3005
2026-03-29 00:19:56 +01:00
parent bb92f955c8
commit b17b5fe405
6 changed files with 252 additions and 34 deletions
+1
View File
@@ -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
+85
View File
@@ -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;
+37
View File
@@ -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
+91 -29
View File
@@ -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;
+13 -5
View File
@@ -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
+25
View File
@@ -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};
}