1
0
mirror of https://github.com/RPCS3/rpcs3 synced 2024-07-08 19:56:08 +00:00

cellMusicDecode: initial implementation

Implements the basic functionality of cellMusicDecode.
Works with Space Invaders (if you add the list selection from the other PR).
Probably fixes SSX custom music.
This commit is contained in:
Megamouse 2022-03-05 14:20:07 +01:00
parent f61ee85f80
commit aafd74f9ea
6 changed files with 569 additions and 53 deletions

View File

@ -242,6 +242,7 @@ else()
set(FFMPEG_LIB_AVCODEC "${CMAKE_CURRENT_BINARY_DIR}/ffmpeg/windows/x86_64/lib/libavcodec.a")
set(FFMPEG_LIB_AVUTIL "${CMAKE_CURRENT_BINARY_DIR}/ffmpeg/windows/x86_64/lib/libavutil.a")
set(FFMPEG_LIB_SWSCALE "${CMAKE_CURRENT_BINARY_DIR}/ffmpeg/windows/x86_64/lib/libswscale.a")
set(FFMPEG_LIB_SWRESAMPLE "${CMAKE_CURRENT_BINARY_DIR}/ffmpeg/windows/x86_64/lib/libswresample.a")
else()
message("-- RPCS3: using builtin ffmpeg")
@ -260,6 +261,7 @@ else()
find_library(FFMPEG_LIB_AVCODEC avcodec PATHS ${FFMPEG_LIB_DIR} NO_DEFAULT_PATH)
find_library(FFMPEG_LIB_AVUTIL avutil PATHS ${FFMPEG_LIB_DIR} NO_DEFAULT_PATH)
find_library(FFMPEG_LIB_SWSCALE swscale PATHS ${FFMPEG_LIB_DIR} NO_DEFAULT_PATH)
find_library(FFMPEG_LIB_SWRESAMPLE swresample PATHS ${FFMPEG_LIB_DIR} NO_DEFAULT_PATH)
endif()
target_include_directories(3rdparty_ffmpeg INTERFACE "ffmpeg/include")
@ -269,7 +271,9 @@ else()
${FFMPEG_LIB_AVFORMAT}
${FFMPEG_LIB_AVCODEC}
${FFMPEG_LIB_AVUTIL}
${FFMPEG_LIB_SWSCALE})
${FFMPEG_LIB_SWSCALE}
${FFMPEG_LIB_SWRESAMPLE}
)
endif()

View File

@ -1,4 +1,4 @@
# - Try to find ffmpeg libraries (libavcodec, libavformat, libavutil and libswscale)
# - Try to find ffmpeg libraries (libavcodec, libavformat, libavutil, libswresample and libswscale)
# Once done this will define
#
# FFMPEG_FOUND - system has ffmpeg or libav
@ -8,6 +8,7 @@
# FFMPEG_LIBAVFORMAT
# FFMPEG_LIBAVUTIL
# FFMPEG_LIBSWSCALE
# FFMPEG_LIBSWRESAMPLE
#
# Copyright (c) 2008 Andreas Schneider <mail@cynapses.org>
# Modified for other libraries by Lasse Kärkkäinen <tronic>
@ -29,6 +30,7 @@ else (FFMPEG_LIBRARIES AND FFMPEG_INCLUDE_DIR)
pkg_check_modules(_FFMPEG_AVFORMAT libavformat)
pkg_check_modules(_FFMPEG_AVUTIL libavutil)
pkg_check_modules(_FFMPEG_SWSCALE libswscale)
pkg_check_modules(_FFMPEG_SWRESAMPLE libswresample)
endif (PKG_CONFIG_FOUND)
find_path(FFMPEG_AVCODEC_INCLUDE_DIR
@ -57,7 +59,12 @@ else (FFMPEG_LIBRARIES AND FFMPEG_INCLUDE_DIR)
PATHS ${_FFMPEG_SWSCALE_LIBRARY_DIRS} /usr/lib /usr/local/lib /opt/local/lib /sw/lib
)
if (FFMPEG_LIBAVCODEC AND FFMPEG_LIBAVFORMAT AND FFMPEG_LIBSWSCALE)
find_library(FFMPEG_LIBSWRESAMPLE
NAMES swresample
PATHS ${_FFMPEG_SWRESAMPLE_LIBRARY_DIRS} /usr/lib /usr/local/lib /opt/local/lib /sw/lib
)
if (FFMPEG_LIBAVCODEC AND FFMPEG_LIBAVFORMAT AND FFMPEG_LIBSWSCALE AND FFMPEG_LIBSWRESAMPLE)
set(FFMPEG_FOUND TRUE)
endif()
@ -69,6 +76,7 @@ else (FFMPEG_LIBRARIES AND FFMPEG_INCLUDE_DIR)
${FFMPEG_LIBAVFORMAT}
${FFMPEG_LIBAVUTIL}
${FFMPEG_LIBSWSCALE}
${FFMPEG_LIBSWRESAMPLE}
)
endif (FFMPEG_FOUND)
@ -79,7 +87,7 @@ else (FFMPEG_LIBRARIES AND FFMPEG_INCLUDE_DIR)
endif (NOT FFMPEG_FIND_QUIETLY)
else (FFMPEG_FOUND)
if (FFMPEG_FIND_REQUIRED)
message(FATAL_ERROR "Could not find libavcodec or libavformat or libavutil or libswscale")
message(FATAL_ERROR "Could not find libavcodec or libavformat or libavutil or libswscale or libswresample")
endif (FFMPEG_FIND_REQUIRED)
endif (FFMPEG_FOUND)

View File

@ -3,18 +3,22 @@
#include "Emu/Cell/lv2/sys_lwmutex.h"
#include "Emu/Cell/lv2/sys_lwcond.h"
#include "Emu/Cell/lv2/sys_spu.h"
#include "Emu/VFS.h"
#include "cellMusic.h"
#include "cellSearch.h"
#include "cellSpurs.h"
#include "cellSysutil.h"
#include "util/media_utils.h"
#include <deque>
LOG_CHANNEL(cellMusicDecode);
// Return Codes
// Return Codes (CELL_MUSIC_DECODE2 codes are omitted if they are identical)
enum CellMusicDecodeError : u32
{
CELL_MUSIC_DECODE_CANCELED = 1,
CELL_MUSIC_DECODE_DECODE_FINISHED = 0x8002C101,
CELL_MUSIC_DECODE_ERROR_PARAM = 0x8002C102,
CELL_MUSIC_DECODE_ERROR_BUSY = 0x8002C103,
@ -37,6 +41,7 @@ void fmt_class_string<CellMusicDecodeError>::format(std::string& out, u64 arg)
{
switch (error)
{
STR_CASE(CELL_MUSIC_DECODE_CANCELED);
STR_CASE(CELL_MUSIC_DECODE_DECODE_FINISHED);
STR_CASE(CELL_MUSIC_DECODE_ERROR_PARAM);
STR_CASE(CELL_MUSIC_DECODE_ERROR_BUSY);
@ -56,6 +61,7 @@ void fmt_class_string<CellMusicDecodeError>::format(std::string& out, u64 arg)
});
}
// Constants (CELL_MUSIC_DECODE2 codes are omitted if they are identical)
enum
{
CELL_MUSIC_DECODE_EVENT_STATUS_NOTIFICATION = 0,
@ -66,18 +72,37 @@ enum
CELL_MUSIC_DECODE_EVENT_SET_SELECTION_CONTEXT_RESULT = 5,
CELL_MUSIC_DECODE_EVENT_UI_NOTIFICATION = 6,
CELL_MUSIC_DECODE_EVENT_NEXT_CONTENTS_READY_RESULT = 7,
};
enum
{
CELL_MUSIC_DECODE2_EVENT_STATUS_NOTIFICATION = 0,
CELL_MUSIC_DECODE2_EVENT_INITIALIZE_RESULT = 1,
CELL_MUSIC_DECODE2_EVENT_FINALIZE_RESULT = 2,
CELL_MUSIC_DECODE2_EVENT_SELECT_CONTENTS_RESULT = 3,
CELL_MUSIC_DECODE2_EVENT_SET_DECODE_COMMAND_RESULT = 4,
CELL_MUSIC_DECODE2_EVENT_SET_SELECTION_CONTEXT_RESULT = 5,
CELL_MUSIC_DECODE2_EVENT_UI_NOTIFICATION = 6,
CELL_MUSIC_DECODE2_EVENT_NEXT_CONTENTS_READY_RESULT = 7,
CELL_MUSIC_DECODE_MODE_NORMAL = 0,
CELL_MUSIC_DECODE_CMD_STOP = 0,
CELL_MUSIC_DECODE_CMD_START = 1,
CELL_MUSIC_DECODE_CMD_NEXT = 2,
CELL_MUSIC_DECODE_CMD_PREV = 3,
CELL_MUSIC_DECODE_STATUS_DORMANT = 0,
CELL_MUSIC_DECODE_STATUS_DECODING = 1,
CELL_MUSIC_DECODE_POSITION_NONE = 0,
CELL_MUSIC_DECODE_POSITION_START = 1,
CELL_MUSIC_DECODE_POSITION_MID = 2,
CELL_MUSIC_DECODE_POSITION_END = 3,
CELL_MUSIC_DECODE_POSITION_END_LIST_END = 4,
CELL_MUSIC_DECODE2_SPEED_MAX = 0,
CELL_MUSIC_DECODE2_SPEED_2 = 2,
CELL_SYSUTIL_MUSIC_DECODE2_INITIALIZING_FINISHED = 1,
CELL_SYSUTIL_MUSIC_DECODE2_SHUTDOWN_FINISHED = 4, // 3(SDK103) -> 4(SDK110)
CELL_SYSUTIL_MUSIC_DECODE2_LOADING_FINISHED = 5,
CELL_SYSUTIL_MUSIC_DECODE2_UNLOADING_FINISHED = 7,
CELL_SYSUTIL_MUSIC_DECODE2_RELEASED = 9,
CELL_SYSUTIL_MUSIC_DECODE2_GRABBED = 11,
CELL_MUSIC_DECODE2_MIN_BUFFER_SIZE = 448 * 1024,
CELL_MUSIC_DECODE2_MANAGEMENT_SIZE = 64 * 1024,
CELL_MUSIC_DECODE2_PAGESIZE_64K = 64 * 1024,
CELL_MUSIC_DECODE2_PAGESIZE_1M = 1 * 1024 * 1024,
};
using CellMusicDecodeCallback = void(u32, vm::ptr<void> param, vm::ptr<void> userData);
@ -87,29 +112,131 @@ struct music_decode
{
vm::ptr<CellMusicDecodeCallback> func{};
vm::ptr<void> userData{};
music_selection_context current_selection_context{};
s32 decode_status = CELL_MUSIC_DECODE_STATUS_DORMANT;
s32 decode_command = CELL_MUSIC_DECODE_CMD_STOP;
u64 readPos = 0;
utils::audio_decoder decoder{};
shared_mutex mutex;
error_code set_decode_command(s32 command)
{
decode_command = command;
switch (command)
{
case CELL_MUSIC_DECODE_CMD_STOP:
{
decoder.stop();
decode_status = CELL_MUSIC_DECODE_STATUS_DORMANT;
break;
}
case CELL_MUSIC_DECODE_CMD_START:
{
decode_status = CELL_MUSIC_DECODE_STATUS_DECODING;
readPos = 0;
// Decode data. The format of the decoded data is 48kHz, float 32bit, 2ch LPCM data interleaved in order from left to right.
decoder.set_path(vfs::get(current_selection_context.path));
decoder.set_swap_endianness(true);
decoder.decode();
break;
}
case CELL_MUSIC_DECODE_CMD_NEXT: // TODO: set path of next file if possible
case CELL_MUSIC_DECODE_CMD_PREV: // TODO: set path of prev file if possible
{
return CELL_MUSIC_DECODE_ERROR_NO_MORE_CONTENT;
}
}
return CELL_OK;
}
error_code finalize()
{
decoder.stop();
decode_status = CELL_MUSIC_DECODE_STATUS_DORMANT;
decode_command = CELL_MUSIC_DECODE_CMD_STOP;
readPos = 0;
return CELL_OK;
}
};
struct music_decode2
struct music_decode2 : music_decode
{
vm::ptr<CellMusicDecode2Callback> func{};
vm::ptr<void> userData{};
shared_mutex mutex;
s32 speed = CELL_MUSIC_DECODE2_SPEED_MAX;
};
template <typename Music_Decode>
error_code cell_music_decode_read(vm::ptr<void> buf, vm::ptr<u32> startTime, u64 reqSize, vm::ptr<u64> readSize, vm::ptr<s32> position)
{
if (!buf || !startTime || !position || !readSize)
return CELL_MUSIC_DECODE_ERROR_PARAM;
auto& dec = g_fxo->get<Music_Decode>();
std::lock_guard lock(dec.mutex);
std::scoped_lock slock(dec.decoder.m_mtx);
if (dec.decoder.has_error)
return CELL_MUSIC_DECODE_ERROR_DECODE_FAILURE;
if (dec.decoder.m_size == 0)
{
*position = CELL_MUSIC_DECODE_POSITION_NONE;
*readSize = 0;
return CELL_MUSIC_DECODE_ERROR_NO_LPCM_DATA;
}
if (dec.readPos == 0)
{
*position = CELL_MUSIC_DECODE_POSITION_START;
}
else if ((dec.readPos + reqSize) >= dec.decoder.m_size)
{
*position = CELL_MUSIC_DECODE_POSITION_END_LIST_END;
}
else
{
*position = CELL_MUSIC_DECODE_POSITION_MID;
}
const u64 size_to_read = (dec.readPos + reqSize) <= dec.decoder.m_size ? reqSize : dec.decoder.m_size - dec.readPos;
std::memcpy(buf.get_ptr(), &dec.decoder.data[dec.readPos], size_to_read);
dec.readPos += size_to_read;
*readSize = size_to_read;
s64 start_time_ms = 0;
if (!dec.decoder.timestamps_ms.empty())
{
start_time_ms = dec.decoder.timestamps_ms.front().second;
while (dec.decoder.timestamps_ms.size() > 1 && dec.readPos >= dec.decoder.timestamps_ms.at(1).first)
{
dec.decoder.timestamps_ms.pop_front();
}
}
*startTime = static_cast<u32>(start_time_ms); // startTime is milliseconds
cellMusicDecode.trace("cell_music_decode_read(size_to_read=%d, samples=%d, start_time_ms=%d)", size_to_read, size_to_read / sizeof(u64), start_time_ms);
return CELL_OK;
}
error_code cellMusicDecodeInitialize(s32 mode, u32 container, s32 spuPriority, vm::ptr<CellMusicDecodeCallback> func, vm::ptr<void> userData)
{
cellMusicDecode.todo("cellMusicDecodeInitialize(mode=0x%x, container=0x%x, spuPriority=0x%x, func=*0x%x, userData=*0x%x)", mode, container, spuPriority, func, userData);
auto& dec = g_fxo->get<music_decode>();
std::lock_guard lock(dec.mutex);
dec.func = func;
dec.userData = userData;
sysutil_register_cb([=, &dec](ppu_thread& ppu) -> s32
sysutil_register_cb([&dec](ppu_thread& ppu) -> s32
{
dec.func(ppu, CELL_MUSIC_DECODE_EVENT_INITIALIZE_RESULT, vm::addr_t(CELL_OK), userData);
dec.func(ppu, CELL_MUSIC_DECODE_EVENT_INITIALIZE_RESULT, vm::addr_t(CELL_OK), dec.userData);
return CELL_OK;
});
@ -121,12 +248,13 @@ error_code cellMusicDecodeInitializeSystemWorkload(s32 mode, u32 container, vm::
cellMusicDecode.todo("cellMusicDecodeInitializeSystemWorkload(mode=0x%x, container=0x%x, func=*0x%x, userData=*0x%x, spuUsageRate=0x%x, spurs=*0x%x, priority=*0x%x, attr=*0x%x)", mode, container, func, userData, spuUsageRate, spurs, priority, attr);
auto& dec = g_fxo->get<music_decode>();
std::lock_guard lock(dec.mutex);
dec.func = func;
dec.userData = userData;
sysutil_register_cb([=, &dec](ppu_thread& ppu) -> s32
sysutil_register_cb([&dec](ppu_thread& ppu) -> s32
{
dec.func(ppu, CELL_MUSIC_DECODE_EVENT_INITIALIZE_RESULT, vm::addr_t(CELL_OK), userData);
dec.func(ppu, CELL_MUSIC_DECODE_EVENT_INITIALIZE_RESULT, vm::addr_t(CELL_OK), dec.userData);
return CELL_OK;
});
@ -138,10 +266,12 @@ error_code cellMusicDecodeFinalize()
cellMusicDecode.todo("cellMusicDecodeFinalize()");
auto& dec = g_fxo->get<music_decode>();
std::lock_guard lock(dec.mutex);
dec.finalize();
if (dec.func)
{
sysutil_register_cb([=, &dec](ppu_thread& ppu) -> s32
sysutil_register_cb([&dec](ppu_thread& ppu) -> s32
{
dec.func(ppu, CELL_MUSIC_DECODE_EVENT_FINALIZE_RESULT, vm::addr_t(CELL_OK), dec.userData);
return CELL_OK;
@ -156,6 +286,7 @@ error_code cellMusicDecodeSelectContents()
cellMusicDecode.todo("cellMusicDecodeSelectContents()");
auto& dec = g_fxo->get<music_decode>();
std::lock_guard lock(dec.mutex);
if (!dec.func)
return CELL_MUSIC_DECODE_ERROR_GENERIC;
@ -174,13 +305,16 @@ error_code cellMusicDecodeSetDecodeCommand(s32 command)
cellMusicDecode.todo("cellMusicDecodeSetDecodeCommand(command=0x%x)", command);
auto& dec = g_fxo->get<music_decode>();
std::lock_guard lock(dec.mutex);
if (!dec.func)
return CELL_MUSIC_DECODE_ERROR_GENERIC;
sysutil_register_cb([=, &dec](ppu_thread& ppu) -> s32
const error_code result = dec.set_decode_command(command);
sysutil_register_cb([&dec, result](ppu_thread& ppu) -> s32
{
dec.func(ppu, CELL_MUSIC_DECODE_EVENT_SET_DECODE_COMMAND_RESULT, vm::addr_t(CELL_OK), dec.userData);
dec.func(ppu, CELL_MUSIC_DECODE_EVENT_SET_DECODE_COMMAND_RESULT, vm::addr_t(s32{result}), dec.userData);
return CELL_OK;
});
@ -190,18 +324,36 @@ error_code cellMusicDecodeSetDecodeCommand(s32 command)
error_code cellMusicDecodeGetDecodeStatus(vm::ptr<s32> status)
{
cellMusicDecode.todo("cellMusicDecodeGetDecodeStatus(status=*0x%x)", status);
if (!status)
return CELL_MUSIC_DECODE_ERROR_PARAM;
auto& dec = g_fxo->get<music_decode>();
std::lock_guard lock(dec.mutex);
*status = dec.decode_status;
return CELL_OK;
}
error_code cellMusicDecodeRead(vm::ptr<void> buf, vm::ptr<u32> startTime, u64 reqSize, vm::ptr<u64> readSize, vm::ptr<s32> position)
{
cellMusicDecode.todo("cellMusicDecodeRead(buf=*0x%x, startTime=*0x%x, reqSize=0x%llx, readSize=*0x%x, position=*0x%x)", buf, startTime, reqSize, readSize, position);
return CELL_OK;
cellMusicDecode.notice("cellMusicDecodeRead(buf=*0x%x, startTime=*0x%x, reqSize=0x%llx, readSize=*0x%x, position=*0x%x)", buf, startTime, reqSize, readSize, position);
return cell_music_decode_read<music_decode>(buf, startTime, reqSize, readSize, position);
}
error_code cellMusicDecodeGetSelectionContext(vm::ptr<CellMusicSelectionContext> context)
{
cellMusicDecode.todo("cellMusicDecodeGetSelectionContext(context=*0x%x)", context);
if (!context)
return CELL_MUSIC_DECODE_ERROR_PARAM;
auto& dec = g_fxo->get<music_decode>();
std::lock_guard lock(dec.mutex);
*context = dec.current_selection_context.get();
cellMusicDecode.warning("cellMusicDecodeGetSelectionContext: selection_context = %s", dec.current_selection_context.to_string());
return CELL_OK;
}
@ -209,14 +361,23 @@ error_code cellMusicDecodeSetSelectionContext(vm::ptr<CellMusicSelectionContext>
{
cellMusicDecode.todo("cellMusicDecodeSetSelectionContext(context=*0x%x)", context);
if (!context)
return CELL_MUSIC_DECODE_ERROR_PARAM;
auto& dec = g_fxo->get<music_decode>();
std::lock_guard lock(dec.mutex);
if (!dec.func)
return CELL_MUSIC_DECODE_ERROR_GENERIC;
sysutil_register_cb([=, &dec](ppu_thread& ppu) -> s32
const bool result = dec.current_selection_context.set(*context);
if (result) cellMusicDecode.warning("cellMusicDecodeSetSelectionContext: new selection_context = %s", dec.current_selection_context.to_string());
else cellMusicDecode.error("cellMusicDecodeSetSelectionContext: failed. context = '%s'", context->data);
sysutil_register_cb([&dec, result](ppu_thread& ppu) -> s32
{
dec.func(ppu, CELL_MUSIC_DECODE_EVENT_SET_SELECTION_CONTEXT_RESULT, vm::addr_t(CELL_OK), dec.userData);
const u32 status = result ? u32{CELL_OK} : u32{CELL_MUSIC_DECODE_ERROR_INVALID_CONTEXT};
dec.func(ppu, CELL_MUSIC_DECODE_EVENT_SET_SELECTION_CONTEXT_RESULT, vm::addr_t(status), dec.userData);
return CELL_OK;
});
@ -226,37 +387,53 @@ error_code cellMusicDecodeSetSelectionContext(vm::ptr<CellMusicSelectionContext>
error_code cellMusicDecodeGetContentsId(vm::ptr<CellSearchContentId> contents_id)
{
cellMusicDecode.todo("cellMusicDecodeGetContentsId(contents_id=*0x%x)", contents_id);
return CELL_OK;
if (!contents_id)
return CELL_MUSIC_ERROR_PARAM;
// HACKY
auto& dec = g_fxo->get<music_decode>();
std::lock_guard lock(dec.mutex);
return dec.current_selection_context.find_content_id(contents_id);
}
error_code cellMusicDecodeInitialize2(s32 mode, u32 container, s32 spuPriority, vm::ptr<CellMusicDecode2Callback> func, vm::ptr<void> userData, s32 speed, s32 bufsize)
error_code cellMusicDecodeInitialize2(s32 mode, u32 container, s32 spuPriority, vm::ptr<CellMusicDecode2Callback> func, vm::ptr<void> userData, s32 speed, s32 bufSize)
{
cellMusicDecode.todo("cellMusicDecodeInitialize2(mode=0x%x, container=0x%x, spuPriority=0x%x, func=*0x%x, userData=*0x%x, speed=0x%x, bufsize=0x%x)", mode, container, spuPriority, func, userData, speed, bufsize);
cellMusicDecode.todo("cellMusicDecodeInitialize2(mode=0x%x, container=0x%x, spuPriority=0x%x, func=*0x%x, userData=*0x%x, speed=0x%x, bufSize=0x%x)", mode, container, spuPriority, func, userData, speed, bufSize);
if (bufSize < CELL_MUSIC_DECODE2_MIN_BUFFER_SIZE)
return CELL_MUSIC_DECODE_ERROR_PARAM;
auto& dec = g_fxo->get<music_decode2>();
std::lock_guard lock(dec.mutex);
dec.func = func;
dec.userData = userData;
dec.speed = speed;
sysutil_register_cb([=, &dec](ppu_thread& ppu) -> s32
sysutil_register_cb([userData, &dec](ppu_thread& ppu) -> s32
{
dec.func(ppu, CELL_MUSIC_DECODE2_EVENT_INITIALIZE_RESULT, vm::addr_t(CELL_OK), userData);
dec.func(ppu, CELL_MUSIC_DECODE_EVENT_INITIALIZE_RESULT, vm::addr_t(CELL_OK), userData);
return CELL_OK;
});
return CELL_OK;
}
error_code cellMusicDecodeInitialize2SystemWorkload(s32 mode, u32 container, vm::ptr<CellMusicDecode2Callback> func, vm::ptr<void> userData, s32 spuUsageRate, s32 bufsize, vm::ptr<CellSpurs> spurs, vm::cptr<u8> priority, vm::cptr<CellSpursSystemWorkloadAttribute> attr)
error_code cellMusicDecodeInitialize2SystemWorkload(s32 mode, u32 container, vm::ptr<CellMusicDecode2Callback> func, vm::ptr<void> userData, s32 spuUsageRate, s32 bufSize, vm::ptr<CellSpurs> spurs, vm::cptr<u8> priority, vm::cptr<CellSpursSystemWorkloadAttribute> attr)
{
cellMusicDecode.todo("cellMusicDecodeInitialize2SystemWorkload(mode=0x%x, container=0x%x, func=*0x%x, userData=*0x%x, spuUsageRate=0x%x, bufsize=0x%x, spurs=*0x%x, priority=*0x%x, attr=*0x%x)", mode, container, func, userData, spuUsageRate, bufsize, spurs, priority, attr);
cellMusicDecode.todo("cellMusicDecodeInitialize2SystemWorkload(mode=0x%x, container=0x%x, func=*0x%x, userData=*0x%x, spuUsageRate=0x%x, bufSize=0x%x, spurs=*0x%x, priority=*0x%x, attr=*0x%x)", mode, container, func, userData, spuUsageRate, bufSize, spurs, priority, attr);
if (bufSize < CELL_MUSIC_DECODE2_MIN_BUFFER_SIZE)
return CELL_MUSIC_DECODE_ERROR_PARAM;
auto& dec = g_fxo->get<music_decode2>();
std::lock_guard lock(dec.mutex);
dec.func = func;
dec.userData = userData;
sysutil_register_cb([=, &dec](ppu_thread& ppu) -> s32
sysutil_register_cb([&dec](ppu_thread& ppu) -> s32
{
dec.func(ppu, CELL_MUSIC_DECODE2_EVENT_INITIALIZE_RESULT, vm::addr_t(CELL_OK), userData);
dec.func(ppu, CELL_MUSIC_DECODE_EVENT_INITIALIZE_RESULT, vm::addr_t(CELL_OK), dec.userData);
return CELL_OK;
});
@ -268,12 +445,14 @@ error_code cellMusicDecodeFinalize2()
cellMusicDecode.todo("cellMusicDecodeFinalize2()");
auto& dec = g_fxo->get<music_decode2>();
std::lock_guard lock(dec.mutex);
dec.finalize();
if (dec.func)
{
sysutil_register_cb([=, &dec](ppu_thread& ppu) -> s32
sysutil_register_cb([&dec](ppu_thread& ppu) -> s32
{
dec.func(ppu, CELL_MUSIC_DECODE2_EVENT_FINALIZE_RESULT, vm::addr_t(CELL_OK), dec.userData);
dec.func(ppu, CELL_MUSIC_DECODE_EVENT_FINALIZE_RESULT, vm::addr_t(CELL_OK), dec.userData);
return CELL_OK;
});
}
@ -286,13 +465,14 @@ error_code cellMusicDecodeSelectContents2()
cellMusicDecode.todo("cellMusicDecodeSelectContents2()");
auto& dec = g_fxo->get<music_decode2>();
std::lock_guard lock(dec.mutex);
if (!dec.func)
return CELL_MUSIC_DECODE_ERROR_GENERIC;
sysutil_register_cb([=, &dec](ppu_thread& ppu) -> s32
{
dec.func(ppu, CELL_MUSIC_DECODE2_EVENT_SELECT_CONTENTS_RESULT, vm::addr_t(CELL_OK), dec.userData);
dec.func(ppu, CELL_MUSIC_DECODE_EVENT_SELECT_CONTENTS_RESULT, vm::addr_t(CELL_OK), dec.userData);
return CELL_OK;
});
@ -304,13 +484,16 @@ error_code cellMusicDecodeSetDecodeCommand2(s32 command)
cellMusicDecode.todo("cellMusicDecodeSetDecodeCommand2(command=0x%x)", command);
auto& dec = g_fxo->get<music_decode2>();
std::lock_guard lock(dec.mutex);
if (!dec.func)
return CELL_MUSIC_DECODE_ERROR_GENERIC;
sysutil_register_cb([=, &dec](ppu_thread& ppu) -> s32
const error_code result = dec.set_decode_command(command);
sysutil_register_cb([&dec, result](ppu_thread& ppu) -> s32
{
dec.func(ppu, CELL_MUSIC_DECODE2_EVENT_SET_DECODE_COMMAND_RESULT, vm::addr_t(CELL_OK), dec.userData);
dec.func(ppu, CELL_MUSIC_DECODE_EVENT_SET_DECODE_COMMAND_RESULT, vm::addr_t(s32{result}), dec.userData);
return CELL_OK;
});
@ -320,18 +503,36 @@ error_code cellMusicDecodeSetDecodeCommand2(s32 command)
error_code cellMusicDecodeGetDecodeStatus2(vm::ptr<s32> status)
{
cellMusicDecode.todo("cellMusicDecodeGetDecodeStatus2(status=*0x%x)", status);
if (!status)
return CELL_MUSIC_DECODE_ERROR_PARAM;
auto& dec = g_fxo->get<music_decode2>();
std::lock_guard lock(dec.mutex);
*status = dec.decode_status;
return CELL_OK;
}
error_code cellMusicDecodeRead2(vm::ptr<void> buf, vm::ptr<u32> startTime, u64 reqSize, vm::ptr<u64> readSize, vm::ptr<s32> position)
{
cellMusicDecode.todo("cellMusicDecodeRead2(buf=*0x%x, startTime=*0x%x, reqSize=0x%llx, readSize=*0x%x, position=*0x%x)", buf, startTime, reqSize, readSize, position);
return CELL_OK;
cellMusicDecode.notice("cellMusicDecodeRead2(buf=*0x%x, startTime=*0x%x, reqSize=0x%llx, readSize=*0x%x, position=*0x%x)", buf, startTime, reqSize, readSize, position);
return cell_music_decode_read<music_decode2>(buf, startTime, reqSize, readSize, position);
}
error_code cellMusicDecodeGetSelectionContext2(vm::ptr<CellMusicSelectionContext> context)
{
cellMusicDecode.todo("cellMusicDecodeGetSelectionContext2(context=*0x%x)", context);
if (!context)
return CELL_MUSIC_DECODE_ERROR_PARAM;
auto& dec = g_fxo->get<music_decode2>();
std::lock_guard lock(dec.mutex);
*context = dec.current_selection_context.get();
cellMusicDecode.warning("cellMusicDecodeGetSelectionContext2: selection context = %s)", dec.current_selection_context.to_string());
return CELL_OK;
}
@ -340,23 +541,36 @@ error_code cellMusicDecodeSetSelectionContext2(vm::ptr<CellMusicSelectionContext
cellMusicDecode.todo("cellMusicDecodeSetSelectionContext2(context=*0x%x)", context);
auto& dec = g_fxo->get<music_decode2>();
std::lock_guard lock(dec.mutex);
if (!dec.func)
return CELL_MUSIC_DECODE_ERROR_GENERIC;
sysutil_register_cb([=, &dec](ppu_thread& ppu) -> s32
const bool result = dec.current_selection_context.set(*context);
if (result) cellMusicDecode.warning("cellMusicDecodeSetSelectionContext2: new selection_context = %s", dec.current_selection_context.to_string());
else cellMusicDecode.error("cellMusicDecodeSetSelectionContext2: failed. context = '%s'", context->data);
sysutil_register_cb([&dec, result](ppu_thread& ppu) -> s32
{
dec.func(ppu, CELL_MUSIC_DECODE2_EVENT_SET_SELECTION_CONTEXT_RESULT, vm::addr_t(CELL_OK), dec.userData);
const u32 status = result ? u32{CELL_OK} : u32{CELL_MUSIC_DECODE_ERROR_INVALID_CONTEXT};
dec.func(ppu, CELL_MUSIC_DECODE_EVENT_SET_SELECTION_CONTEXT_RESULT, vm::addr_t(status), dec.userData);
return CELL_OK;
});
return CELL_OK;
}
error_code cellMusicDecodeGetContentsId2(vm::ptr<CellSearchContentId> contents_id )
error_code cellMusicDecodeGetContentsId2(vm::ptr<CellSearchContentId> contents_id)
{
cellMusicDecode.todo("cellMusicDecodeGetContentsId2(contents_id=*0x%x)", contents_id);
return CELL_OK;
if (!contents_id)
return CELL_MUSIC2_ERROR_PARAM;
// HACKY
auto& dec = g_fxo->get<music_decode2>();
std::lock_guard lock(dec.mutex);
return dec.current_selection_context.find_content_id(contents_id);
}

View File

@ -25,6 +25,7 @@ namespace rsx
else
{
// Fallback
// TODO: use proper icon
static_cast<image_view*>(image.get())->set_image_resource(resource_config::standard_image_resource::square);
}
@ -192,7 +193,6 @@ namespace rsx
m_dim_background->back_color.a = 0.5f;
}
std::vector<u8> icon;
std::vector<std::unique_ptr<overlay_element>> entries;
const std::string home_dir = rpcs3::utils::get_hdd0_dir() + "home/";

View File

@ -7,12 +7,18 @@
#pragma warning(push, 0)
#else
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wall"
#pragma GCC diagnostic ignored "-Wextra"
#pragma GCC diagnostic ignored "-Wold-style-cast"
#endif
extern "C" {
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavutil/dict.h"
#include "libavutil/opt.h"
#include "libswresample/swresample.h"
}
constexpr int averror_eof = AVERROR_EOF; // workaround for old-style-cast error
#ifdef _MSC_VER
#pragma warning(pop)
#else
@ -156,4 +162,257 @@ namespace utils
return { true, std::move(info) };
}
struct scoped_av
{
AVFormatContext* format = nullptr;
AVCodec* codec = nullptr;
AVCodecContext* context = nullptr;
AVFrame* frame = nullptr;
SwrContext* swr = nullptr;
~scoped_av()
{
// Clean up
if (frame)
av_frame_free(&frame);
if (swr)
swr_free(&swr);
if (context)
avcodec_close(context);
if (codec)
av_free(codec);
if (format)
avformat_free_context(format);
}
};
audio_decoder::audio_decoder()
{
}
audio_decoder::~audio_decoder()
{
stop();
}
void audio_decoder::set_path(const std::string& path)
{
m_path = path;
}
void audio_decoder::set_swap_endianness(bool swapped)
{
m_swap_endianness = swapped;
}
void audio_decoder::stop()
{
if (m_thread)
{
auto& thread = *m_thread;
thread = thread_state::aborting;
thread();
}
has_error = false;
m_size = 0;
timestamps_ms.clear();
data.clear();
}
void audio_decoder::decode()
{
stop();
m_thread = std::make_unique<named_thread<std::function<void()>>>("Music Decode Thread", [this, path = m_path]()
{
scoped_av av;
// Get format from audio file
av.format = avformat_alloc_context();
if (int err = avformat_open_input(&av.format, path.c_str(), nullptr, nullptr); err < 0)
{
media_log.error("audio_decoder: Could not open file '%s'. Error: %d='%s'", path, err, error_to_string(err));
has_error = true;
return;
}
if (int err = avformat_find_stream_info(av.format, nullptr); err < 0)
{
media_log.error("audio_decoder: Could not retrieve stream info from file '%s'. Error: %d='%s'", path, err, error_to_string(err));
has_error = true;
return;
}
// Find the first audio stream
AVStream* stream = nullptr;
unsigned int stream_index;
for (stream_index = 0; stream_index < av.format->nb_streams; stream_index++)
{
if (av.format->streams[stream_index]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
{
stream = av.format->streams[stream_index];
break;
}
}
if (!stream)
{
media_log.error("audio_decoder: Could not retrieve audio stream from file '%s'", path);
has_error = true;
return;
}
// Find decoder
av.codec = avcodec_find_decoder(stream->codecpar->codec_id);
if (!av.codec)
{
media_log.error("audio_decoder: Failed to find decoder for stream #%u in file '%s'", stream_index, path);
has_error = true;
return;
}
// Allocate context
av.context = avcodec_alloc_context3(av.codec);
if (!av.context)
{
media_log.error("audio_decoder: Failed to allocate context for stream #%u in file '%s'", stream_index, path);
has_error = true;
return;
}
// Open decoder
if (int err = avcodec_open2(av.context, av.codec, nullptr); err < 0)
{
media_log.error("audio_decoder: Failed to open decoder for stream #%u in file '%s'. Error: %d='%s'", stream_index, path, err, error_to_string(err));
has_error = true;
return;
}
// Prepare resampler
av.swr = swr_alloc();
if (!av.swr)
{
media_log.error("audio_decoder: Failed to allocate resampler for stream #%u in file '%s'", stream_index, path);
has_error = true;
return;
}
const int dst_channels = 2;
const u64 dst_channel_layout = AV_CH_LAYOUT_STEREO;
const AVSampleFormat dst_format = AV_SAMPLE_FMT_FLT;
int set_err = 0;
if ((set_err = av_opt_set_int(av.swr, "in_channel_count", stream->codecpar->channels, 0)) ||
(set_err = av_opt_set_int(av.swr, "out_channel_count", dst_channels, 0)) ||
(set_err = av_opt_set_channel_layout(av.swr, "in_channel_layout", stream->codecpar->channel_layout, 0)) ||
(set_err = av_opt_set_channel_layout(av.swr, "out_channel_layout", dst_channel_layout, 0)) ||
(set_err = av_opt_set_int(av.swr, "in_sample_rate", stream->codecpar->sample_rate, 0)) ||
(set_err = av_opt_set_int(av.swr, "out_sample_rate", sample_rate, 0)) ||
(set_err = av_opt_set_sample_fmt(av.swr, "in_sample_fmt", static_cast<AVSampleFormat>(stream->codecpar->format), 0)) ||
(set_err = av_opt_set_sample_fmt(av.swr, "out_sample_fmt", dst_format, 0)))
{
media_log.error("audio_decoder: Failed to set resampler options: Error: %d='%s'", set_err, error_to_string(set_err));
has_error = true;
return;
}
if (int err = swr_init(av.swr); err < 0 || !swr_is_initialized(av.swr))
{
media_log.error("audio_decoder: Resampler has not been properly initialized: %d='%s'", err, error_to_string(err));
has_error = true;
return;
}
// Prepare to read data
AVPacket packet;
av_init_packet(&packet);
av.frame = av_frame_alloc();
if (!av.frame)
{
media_log.error("audio_decoder: Error allocating the frame");
has_error = true;
return;
}
duration_ms = stream->duration / 1000;
// Iterate through frames
while (thread_ctrl::state() != thread_state::aborting && av_read_frame(av.format, &packet) >= 0)
{
if (int err = avcodec_send_packet(av.context, &packet); err < 0)
{
media_log.error("audio_decoder: Queuing error: %d='%s'", err, error_to_string(err));
has_error = true;
return;
}
while (thread_ctrl::state() != thread_state::aborting)
{
if (int err = avcodec_receive_frame(av.context, av.frame); err < 0)
{
if (err == AVERROR(EAGAIN) || err == averror_eof)
break;
media_log.error("audio_decoder: Decoding error: %d='%s'", err, error_to_string(err));
has_error = true;
return;
}
// Resample frames
u8* buffer;
const int align = 1;
const int buffer_size = av_samples_alloc(&buffer, nullptr, dst_channels, av.frame->nb_samples, dst_format, align);
if (buffer_size < 0)
{
media_log.error("audio_decoder: Error allocating buffer: %d='%s'", buffer_size, error_to_string(buffer_size));
has_error = true;
return;
}
const int frame_count = swr_convert(av.swr, &buffer, av.frame->nb_samples, const_cast<const uint8_t**>(av.frame->data), av.frame->nb_samples);
if (frame_count < 0)
{
media_log.error("audio_decoder: Error converting frame: %d='%s'", frame_count, error_to_string(frame_count));
has_error = true;
if (buffer)
av_free(buffer);
return;
}
// Append resampled frames to data
{
std::scoped_lock lock(m_mtx);
data.resize(m_size + buffer_size);
if (m_swap_endianness)
{
// The format is float 32bit per channel.
const auto write_byteswapped = [](const void* src, void* dst) -> void
{
*static_cast<f32*>(dst) = *static_cast<const be_t<f32>*>(src);
};
for (size_t i = 0; i < (buffer_size - sizeof(f32)); i += sizeof(f32))
{
write_byteswapped(buffer + i, data.data() + m_size + i);
}
}
else
{
memcpy(&data[m_size], buffer, buffer_size);
}
const u32 timestamp_ms = stream->time_base.den ? (1000 * av.frame->best_effort_timestamp * stream->time_base.num) / stream->time_base.den : 0;
timestamps_ms.push_back({m_size, timestamp_ms});
m_size += buffer_size;
}
if (buffer)
av_free(buffer);
media_log.trace("audio_decoder: decoded frame_count=%d buffer_size=%d timestamp_us=%d", frame_count, buffer_size, av.frame->best_effort_timestamp);
}
}
});
}
}

View File

@ -1,6 +1,12 @@
#pragma once
#include <unordered_map>
#include <atomic>
#include <deque>
#include <mutex>
#include <thread>
#include "Utilities/StrUtil.h"
#include "Utilities/Thread.h"
namespace utils
{
@ -34,4 +40,29 @@ namespace utils
}
strcpy_trunc(dst, value);
};
class audio_decoder
{
public:
audio_decoder();
~audio_decoder();
void set_path(const std::string& path);
void set_swap_endianness(bool swapped);
void stop();
void decode();
std::mutex m_mtx;
const s32 sample_rate = 48000;
std::vector<u8> data;
atomic_t<u64> m_size = 0;
atomic_t<u64> duration_ms = 0;
atomic_t<bool> has_error{false};
std::deque<std::pair<u64, u64>> timestamps_ms;
private:
bool m_swap_endianness = false;
std::string m_path;
std::unique_ptr<named_thread<std::function<void()>>> m_thread;
};
}