mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-02-21 18:39:57 +00:00
cellMusic/Decode: implement playlist shuffle and repeat
This commit is contained in:
parent
683fa2a392
commit
c40439ae6b
@ -264,6 +264,7 @@ target_sources(rpcs3_emu PRIVATE
|
||||
Cell/Modules/cellMsgDialog.cpp
|
||||
Cell/Modules/cellMusic.cpp
|
||||
Cell/Modules/cellMusicDecode.cpp
|
||||
Cell/Modules/cellMusicSelectionContext.cpp
|
||||
Cell/Modules/cellMusicExport.cpp
|
||||
Cell/Modules/cellNetAoi.cpp
|
||||
Cell/Modules/cellNetCtl.cpp
|
||||
|
@ -72,7 +72,7 @@ struct music_state
|
||||
vm::ptr<void> userData{};
|
||||
std::mutex mtx;
|
||||
std::shared_ptr<music_handler_base> handler;
|
||||
music_selection_context current_selection_context;
|
||||
music_selection_context current_selection_context{};
|
||||
|
||||
SAVESTATE_INIT_POS(16);
|
||||
|
||||
@ -118,17 +118,17 @@ error_code cell_music_select_contents()
|
||||
{
|
||||
sysutil_register_cb([&music, dir_path, vfs_dir_path, info, status](ppu_thread& ppu) -> s32
|
||||
{
|
||||
std::lock_guard lock(music.mtx);
|
||||
const u32 result = status >= 0 ? u32{CELL_OK} : u32{CELL_MUSIC_CANCELED};
|
||||
if (result == CELL_OK)
|
||||
{
|
||||
music_selection_context context;
|
||||
context.content_path = info.path;
|
||||
context.content_path = dir_path + info.path.substr(vfs_dir_path.length()); // We need the non-vfs path here
|
||||
context.content_type = fs::is_dir(info.path) ? CELL_SEARCH_CONTENTTYPE_MUSICLIST : CELL_SEARCH_CONTENTTYPE_MUSIC;
|
||||
music_selection_context context{};
|
||||
context.set_playlist(info.path);
|
||||
// TODO: context.repeat_mode = CELL_SEARCH_REPEATMODE_NONE;
|
||||
// TODO: context.context_option = CELL_SEARCH_CONTEXTOPTION_NONE;
|
||||
music.current_selection_context = context;
|
||||
cellMusic.success("Media list dialog: selected entry '%s'", context.content_path);
|
||||
music.current_selection_context.create_playlist(music_selection_context::get_next_hash());
|
||||
cellMusic.success("Media list dialog: selected entry '%s'", context.playlist.front());
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -178,21 +178,8 @@ error_code cellMusicSetSelectionContext2(vm::ptr<CellMusicSelectionContext> cont
|
||||
}
|
||||
const u32 status = result ? u32{CELL_OK} : u32{CELL_MUSIC2_ERROR_INVALID_CONTEXT};
|
||||
|
||||
if (result)
|
||||
{
|
||||
cellMusic.success("cellMusicSetSelectionContext2: new selection context = %s", music.current_selection_context.to_string());
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string dahex;
|
||||
|
||||
for (usz i = 0; i < CELL_MUSIC_SELECTION_CONTEXT_SIZE; i++)
|
||||
{
|
||||
fmt::append(dahex, " %.2x", context.data[i]);
|
||||
}
|
||||
|
||||
cellMusic.todo("cellMusicSetSelectionContext2: failed. context = %s", dahex);
|
||||
}
|
||||
if (result) cellMusic.success("cellMusicSetSelectionContext2: new selection context = %s", music.current_selection_context.to_string());
|
||||
else cellMusic.todo("cellMusicSetSelectionContext2: failed. context = %s", music_selection_context::context_to_hex(context));
|
||||
|
||||
music.func(ppu, CELL_MUSIC2_EVENT_SET_SELECTION_CONTEXT_RESULT, vm::addr_t(status), music.userData);
|
||||
return CELL_OK;
|
||||
@ -248,17 +235,17 @@ error_code cellMusicSetSelectionContext(vm::ptr<CellMusicSelectionContext> conte
|
||||
if (!music.func)
|
||||
return CELL_MUSIC_ERROR_GENERIC;
|
||||
|
||||
sysutil_register_cb([=, &music](ppu_thread& ppu) -> s32
|
||||
sysutil_register_cb([context = *context, &music](ppu_thread& ppu) -> s32
|
||||
{
|
||||
bool result = false;
|
||||
{
|
||||
std::lock_guard lock(music.mtx);
|
||||
result = music.current_selection_context.set(*context);
|
||||
result = music.current_selection_context.set(context);
|
||||
}
|
||||
const u32 status = result ? u32{CELL_OK} : u32{CELL_MUSIC_ERROR_INVALID_CONTEXT};
|
||||
|
||||
if (result) cellMusic.success("cellMusicSetSelectionContext: new selection context = %s)", music.current_selection_context.to_string());
|
||||
else cellMusic.todo("cellMusicSetSelectionContext: failed. context = %s)", context->data);
|
||||
else cellMusic.todo("cellMusicSetSelectionContext: failed. context = %s)", music_selection_context::context_to_hex(context));
|
||||
|
||||
music.func(ppu, CELL_MUSIC_EVENT_SET_SELECTION_CONTEXT_RESULT, vm::addr_t(status), music.userData);
|
||||
return CELL_OK;
|
||||
@ -404,6 +391,7 @@ error_code cellMusicGetSelectionContext2(vm::ptr<CellMusicSelectionContext> cont
|
||||
|
||||
auto& music = g_fxo->get<music_state>();
|
||||
std::lock_guard lock(music.mtx);
|
||||
|
||||
*context = music.current_selection_context.get();
|
||||
cellMusic.success("cellMusicGetSelectionContext2: selection context = %s", music.current_selection_context.to_string());
|
||||
|
||||
@ -450,31 +438,53 @@ error_code cellMusicSetPlaybackCommand2(s32 command, vm::ptr<void> param)
|
||||
|
||||
sysutil_register_cb([=, &music](ppu_thread& ppu) -> s32
|
||||
{
|
||||
// TODO: play proper song when the context is a playlist
|
||||
std::string path;
|
||||
{
|
||||
std::lock_guard lock(music.mtx);
|
||||
path = vfs::get(music.current_selection_context.content_path);
|
||||
cellMusic.notice("cellMusicSetPlaybackCommand2: current vfs path: '%s' (unresolved='%s')", path, music.current_selection_context.content_path);
|
||||
}
|
||||
|
||||
switch (command)
|
||||
{
|
||||
case CELL_MUSIC2_PB_CMD_STOP:
|
||||
music.handler->stop();
|
||||
break;
|
||||
case CELL_MUSIC2_PB_CMD_PLAY:
|
||||
music.handler->play(path);
|
||||
break;
|
||||
case CELL_MUSIC2_PB_CMD_PAUSE:
|
||||
music.handler->pause();
|
||||
break;
|
||||
case CELL_MUSIC2_PB_CMD_PLAY:
|
||||
case CELL_MUSIC2_PB_CMD_NEXT:
|
||||
music.handler->play(path);
|
||||
break;
|
||||
case CELL_MUSIC2_PB_CMD_PREV:
|
||||
{
|
||||
std::string path;
|
||||
bool playback_finished = false;
|
||||
{
|
||||
std::lock_guard lock(music.mtx);
|
||||
const std::vector<std::string>& playlist = music.current_selection_context.playlist;
|
||||
u32 next_track = music.current_selection_context.current_track;
|
||||
|
||||
if (command != CELL_MUSIC2_PB_CMD_PLAY)
|
||||
{
|
||||
next_track = music.current_selection_context.step_track(command == CELL_MUSIC2_PB_CMD_NEXT);
|
||||
}
|
||||
|
||||
if (next_track < playlist.size())
|
||||
{
|
||||
path = vfs::get(playlist.at(next_track));
|
||||
cellMusic.notice("cellMusicSetPlaybackCommand2: current vfs path: '%s' (unresolved='%s')", path, playlist.at(next_track));
|
||||
}
|
||||
else
|
||||
{
|
||||
playback_finished = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (playback_finished)
|
||||
{
|
||||
// TODO: is CELL_MUSIC2_PLAYBACK_FINISHED correct here ?
|
||||
cellMusic.notice("cellMusicSetPlaybackCommand2: no more tracks to play");
|
||||
music.handler->stop();
|
||||
music.func(ppu, CELL_MUSIC2_EVENT_SET_PLAYBACK_COMMAND_RESULT, vm::addr_t(CELL_MUSIC2_PLAYBACK_FINISHED), music.userData);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
music.handler->play(path);
|
||||
break;
|
||||
}
|
||||
case CELL_MUSIC2_PB_CMD_FASTFORWARD:
|
||||
music.handler->fast_forward();
|
||||
break;
|
||||
@ -506,31 +516,53 @@ error_code cellMusicSetPlaybackCommand(s32 command, vm::ptr<void> param)
|
||||
|
||||
sysutil_register_cb([=, &music](ppu_thread& ppu) -> s32
|
||||
{
|
||||
// TODO: play proper song when the context is a playlist
|
||||
std::string path;
|
||||
{
|
||||
std::lock_guard lock(music.mtx);
|
||||
path = vfs::get(music.current_selection_context.content_path);
|
||||
cellMusic.notice("cellMusicSetPlaybackCommand: current vfs path: '%s' (unresolved='%s')", path, music.current_selection_context.content_path);
|
||||
}
|
||||
|
||||
switch (command)
|
||||
{
|
||||
case CELL_MUSIC_PB_CMD_STOP:
|
||||
music.handler->stop();
|
||||
break;
|
||||
case CELL_MUSIC_PB_CMD_PLAY:
|
||||
music.handler->play(path);
|
||||
break;
|
||||
case CELL_MUSIC_PB_CMD_PAUSE:
|
||||
music.handler->pause();
|
||||
break;
|
||||
case CELL_MUSIC_PB_CMD_PLAY:
|
||||
case CELL_MUSIC_PB_CMD_NEXT:
|
||||
music.handler->play(path);
|
||||
break;
|
||||
case CELL_MUSIC_PB_CMD_PREV:
|
||||
{
|
||||
std::string path;
|
||||
bool playback_finished = false;
|
||||
{
|
||||
std::lock_guard lock(music.mtx);
|
||||
const std::vector<std::string>& playlist = music.current_selection_context.playlist;
|
||||
u32 next_track = music.current_selection_context.current_track;
|
||||
|
||||
if (command != CELL_MUSIC_PB_CMD_PLAY)
|
||||
{
|
||||
next_track = music.current_selection_context.step_track(command == CELL_MUSIC_PB_CMD_NEXT);
|
||||
}
|
||||
|
||||
if (next_track < playlist.size())
|
||||
{
|
||||
path = vfs::get(playlist.at(next_track));
|
||||
cellMusic.notice("cellMusicSetPlaybackCommand: current vfs path: '%s' (unresolved='%s')", path, playlist.at(next_track));
|
||||
}
|
||||
else
|
||||
{
|
||||
playback_finished = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (playback_finished)
|
||||
{
|
||||
// TODO: is CELL_MUSIC_PLAYBACK_FINISHED correct here ?
|
||||
cellMusic.notice("cellMusicSetPlaybackCommand: no more tracks to play");
|
||||
music.handler->stop();
|
||||
music.func(ppu, CELL_MUSIC_EVENT_SET_PLAYBACK_COMMAND_RESULT, vm::addr_t(CELL_MUSIC_PLAYBACK_FINISHED), music.userData);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
music.handler->play(path);
|
||||
break;
|
||||
}
|
||||
case CELL_MUSIC_PB_CMD_FASTFORWARD:
|
||||
music.handler->fast_forward();
|
||||
break;
|
||||
|
@ -142,56 +142,30 @@ struct CellMusicSelectionContext
|
||||
struct music_selection_context
|
||||
{
|
||||
char magic[4] = "SUS";
|
||||
u32 content_type{0};
|
||||
u32 repeat_mode{0};
|
||||
u32 context_option{0};
|
||||
std::string content_path;
|
||||
std::string hash;
|
||||
CellSearchContentType content_type = CELL_SEARCH_CONTENTTYPE_MUSIC;
|
||||
CellSearchRepeatMode repeat_mode = CELL_SEARCH_REPEATMODE_NONE;
|
||||
CellSearchContextOption context_option = CELL_SEARCH_CONTEXTOPTION_NONE;
|
||||
u32 first_track{0};
|
||||
u32 current_track{0};
|
||||
std::vector<std::string> playlist;
|
||||
|
||||
static constexpr u32 max_depth = 2; // root + 1 folder + file
|
||||
static constexpr const char* target_file_type = "Music Playlist";
|
||||
static constexpr const char* target_version = "1.0";
|
||||
static std::string get_next_hash();
|
||||
static std::string context_to_hex(const CellMusicSelectionContext& context);
|
||||
|
||||
bool set(const CellMusicSelectionContext& in)
|
||||
{
|
||||
if (memcmp(in.data, magic, sizeof(magic)) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
bool set(const CellMusicSelectionContext& in);
|
||||
CellMusicSelectionContext get() const;
|
||||
|
||||
u32 pos = sizeof(magic);
|
||||
memcpy(&content_type, &in.data[pos], sizeof(content_type));
|
||||
pos += sizeof(content_type);
|
||||
repeat_mode = in.data[pos++];
|
||||
context_option = in.data[pos++];
|
||||
content_path = &in.data[pos];
|
||||
std::string to_string() const;
|
||||
std::string get_yaml_path() const;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
CellMusicSelectionContext get() const
|
||||
{
|
||||
if (content_path.size() + 2 + sizeof(content_type) + sizeof(magic) > CELL_MUSIC_SELECTION_CONTEXT_SIZE)
|
||||
{
|
||||
fmt::throw_exception("Contents of music_selection_context are too large");
|
||||
}
|
||||
|
||||
CellMusicSelectionContext out{};
|
||||
u32 pos = 0;
|
||||
|
||||
std::memset(out.data, 0, CELL_MUSIC_SELECTION_CONTEXT_SIZE);
|
||||
std::memcpy(out.data, magic, sizeof(magic));
|
||||
pos += sizeof(magic);
|
||||
std::memcpy(&out.data[pos], &content_type, sizeof(content_type));
|
||||
pos += sizeof(content_type);
|
||||
out.data[pos++] = repeat_mode;
|
||||
out.data[pos++] = context_option;
|
||||
std::memcpy(&out.data[pos], content_path.c_str(), content_path.size());
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
std::string to_string() const
|
||||
{
|
||||
return fmt::format("{ .magic='%s', .content_type=%d, .repeat_mode=%d, .context_option=%d, .path='%s' }", magic, content_type, repeat_mode, context_option, content_path);
|
||||
}
|
||||
void set_playlist(const std::string& path);
|
||||
void create_playlist(const std::string& new_hash);
|
||||
bool load_playlist();
|
||||
u32 step_track(bool next);
|
||||
|
||||
// Helper
|
||||
error_code find_content_id(vm::ptr<CellSearchContentId> contents_id);
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "Emu/Cell/lv2/sys_spu.h"
|
||||
#include "Emu/RSX/Overlays/overlay_media_list_dialog.h"
|
||||
#include "Emu/VFS.h"
|
||||
#include "cellMusicDecode.h"
|
||||
#include "cellMusic.h"
|
||||
#include "cellSearch.h"
|
||||
#include "cellSpurs.h"
|
||||
@ -16,25 +17,6 @@
|
||||
|
||||
LOG_CHANNEL(cellMusicDecode);
|
||||
|
||||
// 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,
|
||||
CELL_MUSIC_DECODE_ERROR_NO_ACTIVE_CONTENT = 0x8002C104,
|
||||
CELL_MUSIC_DECODE_ERROR_NO_MATCH_FOUND = 0x8002C105,
|
||||
CELL_MUSIC_DECODE_ERROR_INVALID_CONTEXT = 0x8002C106,
|
||||
CELL_MUSIC_DECODE_ERROR_DECODE_FAILURE = 0x8002C107,
|
||||
CELL_MUSIC_DECODE_ERROR_NO_MORE_CONTENT = 0x8002C108,
|
||||
CELL_MUSIC_DECODE_DIALOG_OPEN = 0x8002C109,
|
||||
CELL_MUSIC_DECODE_DIALOG_CLOSE = 0x8002C10A,
|
||||
CELL_MUSIC_DECODE_ERROR_NO_LPCM_DATA = 0x8002C10B,
|
||||
CELL_MUSIC_DECODE_NEXT_CONTENTS_READY = 0x8002C10C,
|
||||
CELL_MUSIC_DECODE_ERROR_GENERIC = 0x8002C1FF,
|
||||
};
|
||||
|
||||
template<>
|
||||
void fmt_class_string<CellMusicDecodeError>::format(std::string& out, u64 arg)
|
||||
{
|
||||
@ -62,55 +44,6 @@ 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,
|
||||
CELL_MUSIC_DECODE_EVENT_INITIALIZE_RESULT = 1,
|
||||
CELL_MUSIC_DECODE_EVENT_FINALIZE_RESULT = 2,
|
||||
CELL_MUSIC_DECODE_EVENT_SELECT_CONTENTS_RESULT = 3,
|
||||
CELL_MUSIC_DECODE_EVENT_SET_DECODE_COMMAND_RESULT = 4,
|
||||
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,
|
||||
|
||||
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_MODE_NORMAL = 0,
|
||||
|
||||
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);
|
||||
using CellMusicDecode2Callback = void(u32, vm::ptr<void> param, vm::ptr<void> userData);
|
||||
|
||||
struct music_decode
|
||||
{
|
||||
vm::ptr<CellMusicDecodeCallback> func{};
|
||||
@ -118,7 +51,7 @@ struct music_decode
|
||||
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;
|
||||
u64 read_pos = 0;
|
||||
utils::audio_decoder decoder{};
|
||||
|
||||
shared_mutex mutex;
|
||||
@ -138,21 +71,41 @@ struct music_decode
|
||||
case CELL_MUSIC_DECODE_CMD_START:
|
||||
{
|
||||
decode_status = CELL_MUSIC_DECODE_STATUS_DECODING;
|
||||
readPos = 0;
|
||||
read_pos = 0;
|
||||
|
||||
// Decode data. The format of the decoded data is 48kHz, float 32bit, 2ch LPCM data interleaved in order from left to right.
|
||||
const std::string path = vfs::get(current_selection_context.content_path);
|
||||
cellMusicDecode.notice("set_decode_command(START): Setting vfs path: '%s' (unresolved='%s')", path, current_selection_context.content_path);
|
||||
cellMusicDecode.notice("set_decode_command(START): context: %s", current_selection_context.to_string());
|
||||
|
||||
decoder.set_path(path);
|
||||
music_selection_context context = current_selection_context;
|
||||
|
||||
for (usz i = 0; i < context.playlist.size(); i++)
|
||||
{
|
||||
context.playlist[i] = vfs::get(context.playlist[i]);
|
||||
}
|
||||
|
||||
// TODO: set speed if small-memory decoding is used (music_decode2)
|
||||
decoder.set_context(std::move(context));
|
||||
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
|
||||
case CELL_MUSIC_DECODE_CMD_NEXT:
|
||||
case CELL_MUSIC_DECODE_CMD_PREV:
|
||||
{
|
||||
return CELL_MUSIC_DECODE_ERROR_NO_MORE_CONTENT;
|
||||
decoder.stop();
|
||||
|
||||
if (decoder.set_next_index(command == CELL_MUSIC_DECODE_CMD_NEXT) == umax)
|
||||
{
|
||||
decode_status = CELL_MUSIC_DECODE_STATUS_DORMANT;
|
||||
return CELL_MUSIC_DECODE_ERROR_NO_MORE_CONTENT;
|
||||
}
|
||||
|
||||
decoder.decode();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
fmt::throw_exception("Unknown decode command %d", command);
|
||||
}
|
||||
}
|
||||
return CELL_OK;
|
||||
@ -163,7 +116,7 @@ struct music_decode
|
||||
decoder.stop();
|
||||
decode_status = CELL_MUSIC_DECODE_STATUS_DORMANT;
|
||||
decode_command = CELL_MUSIC_DECODE_CMD_STOP;
|
||||
readPos = 0;
|
||||
read_pos = 0;
|
||||
return CELL_OK;
|
||||
}
|
||||
};
|
||||
@ -194,13 +147,13 @@ error_code cell_music_decode_select_contents()
|
||||
const u32 result = status >= 0 ? u32{CELL_OK} : u32{CELL_MUSIC_DECODE_CANCELED};
|
||||
if (result == CELL_OK)
|
||||
{
|
||||
music_selection_context context;
|
||||
context.content_path = dir_path + info.path.substr(vfs_dir_path.length()); // We need the non-vfs path here
|
||||
context.content_type = fs::is_dir(info.path) ? CELL_SEARCH_CONTENTTYPE_MUSICLIST : CELL_SEARCH_CONTENTTYPE_MUSIC;
|
||||
music_selection_context context{};
|
||||
context.set_playlist(info.path);
|
||||
// TODO: context.repeat_mode = CELL_SEARCH_REPEATMODE_NONE;
|
||||
// TODO: context.context_option = CELL_SEARCH_CONTEXTOPTION_NONE;
|
||||
dec.current_selection_context = context;
|
||||
cellMusicDecode.success("Media list dialog: selected entry '%s'", context.content_path);
|
||||
dec.current_selection_context.create_playlist(music_selection_context::get_next_hash());
|
||||
cellMusicDecode.success("Media list dialog: selected entry '%s'", context.playlist.front());
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -217,49 +170,73 @@ 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 || !reqSize || !readSize)
|
||||
{
|
||||
return CELL_MUSIC_DECODE_ERROR_PARAM;
|
||||
}
|
||||
|
||||
*position = CELL_MUSIC_DECODE_POSITION_NONE;
|
||||
*readSize = 0;
|
||||
*startTime = 0;
|
||||
|
||||
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)
|
||||
const u64 size_left = dec.decoder.m_size - dec.read_pos;
|
||||
|
||||
if (dec.read_pos == 0)
|
||||
{
|
||||
cellMusicDecode.trace("cell_music_decode_read: position=CELL_MUSIC_DECODE_POSITION_START, read_pos=%d, reqSize=%d, m_size=%d", dec.read_pos, reqSize, dec.decoder.m_size.load());
|
||||
*position = CELL_MUSIC_DECODE_POSITION_START;
|
||||
}
|
||||
else if ((dec.readPos + reqSize) >= dec.decoder.m_size)
|
||||
else if (!dec.decoder.track_fully_decoded || size_left > reqSize) // track_fully_decoded is not guarded by a mutex, but since it is set to true after the decode, it should be fine.
|
||||
{
|
||||
// TODO: CELL_MUSIC_DECODE_POSITION_END
|
||||
*position = CELL_MUSIC_DECODE_POSITION_END_LIST_END;
|
||||
cellMusicDecode.trace("cell_music_decode_read: position=CELL_MUSIC_DECODE_POSITION_MID, read_pos=%d, reqSize=%d, m_size=%d", dec.read_pos, reqSize, dec.decoder.m_size.load());
|
||||
*position = CELL_MUSIC_DECODE_POSITION_MID;
|
||||
}
|
||||
else
|
||||
{
|
||||
*position = CELL_MUSIC_DECODE_POSITION_MID;
|
||||
if (dec.decoder.set_next_index(true) == umax)
|
||||
{
|
||||
cellMusicDecode.trace("cell_music_decode_read: position=CELL_MUSIC_DECODE_POSITION_END_LIST_END, read_pos=%d, reqSize=%d, m_size=%d", dec.read_pos, reqSize, dec.decoder.m_size.load());
|
||||
*position = CELL_MUSIC_DECODE_POSITION_END_LIST_END;
|
||||
}
|
||||
else
|
||||
{
|
||||
cellMusicDecode.trace("cell_music_decode_read: position=CELL_MUSIC_DECODE_POSITION_END, read_pos=%d, reqSize=%d, m_size=%d", dec.read_pos, reqSize, dec.decoder.m_size.load());
|
||||
*position = CELL_MUSIC_DECODE_POSITION_END;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
const u64 size_to_read = std::min(reqSize, size_left);
|
||||
*readSize = size_to_read;
|
||||
|
||||
if (size_to_read == 0)
|
||||
{
|
||||
return CELL_MUSIC_DECODE_ERROR_NO_LPCM_DATA; // TODO: speculative
|
||||
}
|
||||
|
||||
std::memcpy(buf.get_ptr(), &dec.decoder.data[dec.read_pos], size_to_read);
|
||||
|
||||
dec.read_pos += 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)
|
||||
while (dec.decoder.timestamps_ms.size() > 1 && dec.read_pos >= dec.decoder.timestamps_ms.at(1).first)
|
||||
{
|
||||
dec.decoder.timestamps_ms.pop_front();
|
||||
}
|
||||
@ -267,6 +244,29 @@ error_code cell_music_decode_read(vm::ptr<void> buf, vm::ptr<u32> startTime, u64
|
||||
|
||||
*startTime = static_cast<u32>(start_time_ms); // startTime is milliseconds
|
||||
|
||||
switch (*position)
|
||||
{
|
||||
case CELL_MUSIC_DECODE_POSITION_END_LIST_END:
|
||||
{
|
||||
// Reset the decoder and the decode status
|
||||
ensure(dec.set_decode_command(CELL_MUSIC_DECODE_CMD_STOP) == CELL_OK);
|
||||
dec.read_pos = 0;
|
||||
break;
|
||||
}
|
||||
case CELL_MUSIC_DECODE_POSITION_END:
|
||||
{
|
||||
dec.read_pos = 0;
|
||||
dec.decoder.clear();
|
||||
dec.decoder.track_fully_consumed = true;
|
||||
dec.decoder.track_fully_consumed.notify_one();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
@ -360,7 +360,11 @@ error_code cellMusicDecodeSetDecodeCommand(s32 command)
|
||||
if (!dec.func)
|
||||
return CELL_MUSIC_DECODE_ERROR_GENERIC;
|
||||
|
||||
const error_code result = dec.set_decode_command(command);
|
||||
error_code result = CELL_OK;
|
||||
{
|
||||
std::scoped_lock slock(dec.decoder.m_mtx);
|
||||
result = dec.set_decode_command(command);
|
||||
}
|
||||
|
||||
sysutil_register_cb([&dec, result](ppu_thread& ppu) -> s32
|
||||
{
|
||||
@ -402,6 +406,7 @@ error_code cellMusicDecodeGetSelectionContext(vm::ptr<CellMusicSelectionContext>
|
||||
|
||||
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());
|
||||
|
||||
@ -423,7 +428,7 @@ error_code cellMusicDecodeSetSelectionContext(vm::ptr<CellMusicSelectionContext>
|
||||
|
||||
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);
|
||||
else cellMusicDecode.error("cellMusicDecodeSetSelectionContext: failed. context = '%s'", music_selection_context::context_to_hex(*context));
|
||||
|
||||
sysutil_register_cb([&dec, result](ppu_thread& ppu) -> s32
|
||||
{
|
||||
@ -540,7 +545,11 @@ error_code cellMusicDecodeSetDecodeCommand2(s32 command)
|
||||
if (!dec.func)
|
||||
return CELL_MUSIC_DECODE_ERROR_GENERIC;
|
||||
|
||||
const error_code result = dec.set_decode_command(command);
|
||||
error_code result = CELL_OK;
|
||||
{
|
||||
std::scoped_lock slock(dec.decoder.m_mtx);
|
||||
result = dec.set_decode_command(command);
|
||||
}
|
||||
|
||||
sysutil_register_cb([&dec, result](ppu_thread& ppu) -> s32
|
||||
{
|
||||
@ -582,6 +591,7 @@ error_code cellMusicDecodeGetSelectionContext2(vm::ptr<CellMusicSelectionContext
|
||||
|
||||
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());
|
||||
|
||||
@ -603,7 +613,7 @@ error_code cellMusicDecodeSetSelectionContext2(vm::ptr<CellMusicSelectionContext
|
||||
|
||||
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);
|
||||
else cellMusicDecode.error("cellMusicDecodeSetSelectionContext2: failed. context = '%s'", music_selection_context::context_to_hex(*context));
|
||||
|
||||
sysutil_register_cb([&dec, result](ppu_thread& ppu) -> s32
|
||||
{
|
||||
|
69
rpcs3/Emu/Cell/Modules/cellMusicDecode.h
Normal file
69
rpcs3/Emu/Cell/Modules/cellMusicDecode.h
Normal file
@ -0,0 +1,69 @@
|
||||
#pragma once
|
||||
|
||||
// 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,
|
||||
CELL_MUSIC_DECODE_ERROR_NO_ACTIVE_CONTENT = 0x8002C104,
|
||||
CELL_MUSIC_DECODE_ERROR_NO_MATCH_FOUND = 0x8002C105,
|
||||
CELL_MUSIC_DECODE_ERROR_INVALID_CONTEXT = 0x8002C106,
|
||||
CELL_MUSIC_DECODE_ERROR_DECODE_FAILURE = 0x8002C107,
|
||||
CELL_MUSIC_DECODE_ERROR_NO_MORE_CONTENT = 0x8002C108,
|
||||
CELL_MUSIC_DECODE_DIALOG_OPEN = 0x8002C109,
|
||||
CELL_MUSIC_DECODE_DIALOG_CLOSE = 0x8002C10A,
|
||||
CELL_MUSIC_DECODE_ERROR_NO_LPCM_DATA = 0x8002C10B,
|
||||
CELL_MUSIC_DECODE_NEXT_CONTENTS_READY = 0x8002C10C,
|
||||
CELL_MUSIC_DECODE_ERROR_GENERIC = 0x8002C1FF,
|
||||
};
|
||||
|
||||
// Constants (CELL_MUSIC_DECODE2 codes are omitted if they are identical)
|
||||
enum
|
||||
{
|
||||
CELL_MUSIC_DECODE_EVENT_STATUS_NOTIFICATION = 0,
|
||||
CELL_MUSIC_DECODE_EVENT_INITIALIZE_RESULT = 1,
|
||||
CELL_MUSIC_DECODE_EVENT_FINALIZE_RESULT = 2,
|
||||
CELL_MUSIC_DECODE_EVENT_SELECT_CONTENTS_RESULT = 3,
|
||||
CELL_MUSIC_DECODE_EVENT_SET_DECODE_COMMAND_RESULT = 4,
|
||||
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,
|
||||
|
||||
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_MODE_NORMAL = 0,
|
||||
|
||||
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);
|
||||
using CellMusicDecode2Callback = void(u32, vm::ptr<void> param, vm::ptr<void> userData);
|
333
rpcs3/Emu/Cell/Modules/cellMusicSelectionContext.cpp
Normal file
333
rpcs3/Emu/Cell/Modules/cellMusicSelectionContext.cpp
Normal file
@ -0,0 +1,333 @@
|
||||
#include "stdafx.h"
|
||||
#include "cellMusic.h"
|
||||
#include "util/yaml.hpp"
|
||||
#include "Emu/VFS.h"
|
||||
|
||||
#include <random>
|
||||
|
||||
// This is just a helper and not a real cell entity
|
||||
|
||||
LOG_CHANNEL(cellMusicSelectionContext);
|
||||
|
||||
bool music_selection_context::set(const CellMusicSelectionContext& in)
|
||||
{
|
||||
if (memcmp(in.data, magic, sizeof(magic)) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
u32 pos = sizeof(magic);
|
||||
hash = &in.data[pos];
|
||||
|
||||
return load_playlist();
|
||||
}
|
||||
|
||||
CellMusicSelectionContext music_selection_context::get() const
|
||||
{
|
||||
if (hash.size() + sizeof(magic) > CELL_MUSIC_SELECTION_CONTEXT_SIZE)
|
||||
{
|
||||
fmt::throw_exception("Contents of music_selection_context are too large");
|
||||
}
|
||||
|
||||
CellMusicSelectionContext out{};
|
||||
u32 pos = 0;
|
||||
|
||||
std::memset(out.data, 0, CELL_MUSIC_SELECTION_CONTEXT_SIZE);
|
||||
std::memcpy(out.data, magic, sizeof(magic));
|
||||
pos += sizeof(magic);
|
||||
std::memcpy(&out.data[pos], hash.c_str(), hash.size());
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
std::string music_selection_context::to_string() const
|
||||
{
|
||||
return fmt::format("{ .magic='%s', .content_type=%d, .repeat_mode=%d, .context_option=%d, .first_track=%d, .tracks=%d, .hash='%s' }",
|
||||
magic, static_cast<u32>(content_type), static_cast<u32>(repeat_mode), static_cast<u32>(context_option), first_track, playlist.size(), hash);
|
||||
}
|
||||
|
||||
std::string music_selection_context::get_next_hash()
|
||||
{
|
||||
static u64 hash_counter = 0;
|
||||
return fmt::format("music_selection_context_%d", hash_counter++);
|
||||
}
|
||||
|
||||
std::string music_selection_context::context_to_hex(const CellMusicSelectionContext& context)
|
||||
{
|
||||
std::string dahex;
|
||||
|
||||
for (usz i = 0; i < CELL_MUSIC_SELECTION_CONTEXT_SIZE; i++)
|
||||
{
|
||||
fmt::append(dahex, " %.2x", context.data[i]);
|
||||
}
|
||||
|
||||
return dahex;
|
||||
}
|
||||
|
||||
std::string music_selection_context::get_yaml_path() const
|
||||
{
|
||||
std::string path = fs::get_cache_dir() + "cache/playlists/";
|
||||
|
||||
if (!fs::create_path(path))
|
||||
{
|
||||
cellMusicSelectionContext.fatal("Failed to create path: %s (%s)", path, fs::g_tls_error);
|
||||
}
|
||||
|
||||
return path + hash + ".yml";
|
||||
}
|
||||
|
||||
void music_selection_context::set_playlist(const std::string& path)
|
||||
{
|
||||
playlist.clear();
|
||||
|
||||
const std::string dir_path = "/dev_hdd0/music";
|
||||
const std::string vfs_dir_path = vfs::get("/dev_hdd0/music");
|
||||
|
||||
if (fs::is_dir(path))
|
||||
{
|
||||
content_type = CELL_SEARCH_CONTENTTYPE_MUSICLIST;
|
||||
|
||||
for (auto&& dir_entry : fs::dir{path})
|
||||
{
|
||||
dir_entry.name = vfs::unescape(dir_entry.name);
|
||||
|
||||
if (dir_entry.name == "." || dir_entry.name == "..")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
playlist.push_back(dir_path + std::string(path + "/" + dir_entry.name).substr(vfs_dir_path.length()));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
content_type = CELL_SEARCH_CONTENTTYPE_MUSIC;
|
||||
playlist.push_back(dir_path + path.substr(vfs_dir_path.length()));
|
||||
}
|
||||
}
|
||||
|
||||
void music_selection_context::create_playlist(const std::string& new_hash)
|
||||
{
|
||||
hash = new_hash;
|
||||
|
||||
const std::string yaml_path = get_yaml_path();
|
||||
cellMusicSelectionContext.notice("Saving music playlist file %s", yaml_path);
|
||||
|
||||
YAML::Emitter out;
|
||||
out << YAML::BeginMap;
|
||||
out << "Version" << target_version;
|
||||
out << "FileType" << target_file_type;
|
||||
out << "ContentType" << content_type;
|
||||
out << "ContextOption" << context_option;
|
||||
out << "RepeatMode" << repeat_mode;
|
||||
out << "FirstTrack" << first_track;
|
||||
out << "Tracks" << YAML::BeginSeq;
|
||||
|
||||
for (const std::string& track : playlist)
|
||||
{
|
||||
out << track;
|
||||
}
|
||||
|
||||
out << YAML::EndSeq;
|
||||
out << YAML::EndMap;
|
||||
|
||||
fs::pending_file file(yaml_path);
|
||||
|
||||
if (!file.file || (file.file.write(out.c_str(), out.size()), !file.commit()))
|
||||
{
|
||||
cellMusicSelectionContext.error("Failed to create music playlist file %s (%s)", yaml_path, fs::g_tls_error);
|
||||
}
|
||||
}
|
||||
|
||||
bool music_selection_context::load_playlist()
|
||||
{
|
||||
playlist.clear();
|
||||
|
||||
const std::string path = get_yaml_path();
|
||||
cellMusicSelectionContext.notice("Loading music playlist file %s", path);
|
||||
|
||||
std::string content;
|
||||
{
|
||||
// Load patch file
|
||||
fs::file file{path};
|
||||
|
||||
if (!file)
|
||||
{
|
||||
cellMusicSelectionContext.error("Failed to load music playlist file %s: %s", path, fs::g_tls_error);
|
||||
return false;
|
||||
}
|
||||
|
||||
content = file.to_string();
|
||||
}
|
||||
|
||||
auto [root, error] = yaml_load(content);
|
||||
|
||||
if (!error.empty() || !root)
|
||||
{
|
||||
cellMusicSelectionContext.error("Failed to load music playlist file %s:\n%s", path, error);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string err;
|
||||
|
||||
const std::string version = get_yaml_node_value<std::string>(root["Version"], err);
|
||||
if (!err.empty())
|
||||
{
|
||||
cellMusicSelectionContext.error("No Version entry found. Error: '%s' (file: %s)", err, path);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (version != target_version)
|
||||
{
|
||||
cellMusicSelectionContext.error("Version '%s' does not match music playlist target '%s' (file: %s)", version, target_version, path);
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::string file_type = get_yaml_node_value<std::string>(root["FileType"], err);
|
||||
if (!err.empty())
|
||||
{
|
||||
cellMusicSelectionContext.error("No FileType entry found. Error: '%s' (file: %s)", err, path);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (file_type != target_file_type)
|
||||
{
|
||||
cellMusicSelectionContext.error("FileType '%s' does not match music playlist target '%s' (file: %s)", file_type, target_file_type, path);
|
||||
return false;
|
||||
}
|
||||
|
||||
content_type = static_cast<CellSearchContentType>(get_yaml_node_value<u32>(root["ContentType"], err));
|
||||
if (!err.empty())
|
||||
{
|
||||
cellMusicSelectionContext.error("No ContentType entry found. Error: '%s' (file: %s)", err, path);
|
||||
return false;
|
||||
}
|
||||
|
||||
context_option = static_cast<CellSearchContextOption>(get_yaml_node_value<u32>(root["ContextOption"], err));
|
||||
if (!err.empty())
|
||||
{
|
||||
cellMusicSelectionContext.error("No ContextOption entry found. Error: '%s' (file: %s)", err, path);
|
||||
return false;
|
||||
}
|
||||
|
||||
repeat_mode = static_cast<CellSearchRepeatMode>(get_yaml_node_value<u32>(root["RepeatMode"], err));
|
||||
if (!err.empty())
|
||||
{
|
||||
cellMusicSelectionContext.error("No RepeatMode entry found. Error: '%s' (file: %s)", err, path);
|
||||
return false;
|
||||
}
|
||||
|
||||
first_track = get_yaml_node_value<u32>(root["FirstTrack"], err);
|
||||
if (!err.empty())
|
||||
{
|
||||
cellMusicSelectionContext.error("No FirstTrack entry found. Error: '%s' (file: %s)", err, path);
|
||||
return false;
|
||||
}
|
||||
|
||||
const YAML::Node& track_node = root["Tracks"];
|
||||
|
||||
if (!track_node || track_node.Type() != YAML::NodeType::Sequence)
|
||||
{
|
||||
cellMusicSelectionContext.error("No Tracks entry found or Tracks is not a Sequence. (file: %s)", path);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (usz i = 0; i < track_node.size(); i++)
|
||||
{
|
||||
playlist.push_back(track_node[i].Scalar());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
u32 music_selection_context::step_track(bool next)
|
||||
{
|
||||
if (playlist.empty())
|
||||
{
|
||||
cellMusicSelectionContext.error("No tracks to play...");
|
||||
current_track = umax;
|
||||
return umax;
|
||||
}
|
||||
|
||||
switch (repeat_mode)
|
||||
{
|
||||
case CELL_SEARCH_REPEATMODE_NONE:
|
||||
{
|
||||
if (next)
|
||||
{
|
||||
// Try to play the next track.
|
||||
if (++current_track >= playlist.size())
|
||||
{
|
||||
// We are at the end of the playlist.
|
||||
cellMusicSelectionContext.notice("No more tracks to play in playlist...");
|
||||
current_track = umax;
|
||||
return umax;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Try to play the previous track.
|
||||
if (current_track == 0)
|
||||
{
|
||||
// We are at the start of the playlist.
|
||||
cellMusicSelectionContext.notice("No more tracks to play in playlist...");
|
||||
current_track = umax;
|
||||
return umax;
|
||||
}
|
||||
|
||||
current_track--;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CELL_SEARCH_REPEATMODE_REPEAT1:
|
||||
{
|
||||
// Keep decoding the same track.
|
||||
break;
|
||||
}
|
||||
case CELL_SEARCH_REPEATMODE_ALL:
|
||||
{
|
||||
if (next)
|
||||
{
|
||||
// Play the next track. Start with the first track if we reached the end of the playlist.
|
||||
current_track = (current_track + 1) % playlist.size();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Play the previous track. Start with the last track if we reached the start of the playlist.
|
||||
if (current_track == 0)
|
||||
{
|
||||
current_track = playlist.size() - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
current_track--;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CELL_SEARCH_REPEATMODE_NOREPEAT1:
|
||||
{
|
||||
// We are done. We only wanted to decode a single track.
|
||||
cellMusicSelectionContext.notice("No more tracks to play...");
|
||||
current_track = umax;
|
||||
return umax;
|
||||
}
|
||||
default:
|
||||
{
|
||||
fmt::throw_exception("Unknown repeat mode %d", static_cast<u32>(repeat_mode));
|
||||
}
|
||||
}
|
||||
|
||||
if (context_option == CELL_SEARCH_CONTEXTOPTION_SHUFFLE && repeat_mode == CELL_SEARCH_REPEATMODE_ALL && playlist.size() > 1)
|
||||
{
|
||||
if (next ? current_track == 0 : current_track == (playlist.size() - 1))
|
||||
{
|
||||
// We reached the first or last track again. Let's shuffle!
|
||||
cellMusicSelectionContext.notice("Shuffling playlist...");
|
||||
auto engine = std::default_random_engine{};
|
||||
std::shuffle(std::begin(playlist), std::end(playlist), engine);
|
||||
}
|
||||
}
|
||||
|
||||
return current_track;
|
||||
}
|
@ -71,7 +71,9 @@ struct search_info
|
||||
struct search_content_t
|
||||
{
|
||||
CellSearchContentType type = CELL_SEARCH_CONTENTTYPE_NONE;
|
||||
CellSearchTimeInfo timeInfo;
|
||||
CellSearchRepeatMode repeat_mode = CELL_SEARCH_REPEATMODE_NONE;
|
||||
CellSearchContextOption context_option = CELL_SEARCH_CONTEXTOPTION_NONE;
|
||||
CellSearchTimeInfo time_info;
|
||||
CellSearchContentInfoPath infoPath;
|
||||
union
|
||||
{
|
||||
@ -1555,7 +1557,7 @@ error_code cellSearchGetContentIdByOffset(CellSearchId searchId, s32 offset, vm:
|
||||
|
||||
if (outTimeInfo)
|
||||
{
|
||||
std::memcpy(outTimeInfo.get_ptr(), &content_id.second->timeInfo, sizeof(content_id.second->timeInfo));
|
||||
std::memcpy(outTimeInfo.get_ptr(), &content_id.second->time_info, sizeof(content_id.second->time_info));
|
||||
}
|
||||
|
||||
}
|
||||
@ -1644,7 +1646,7 @@ error_code cellSearchGetMusicSelectionContext(CellSearchId searchId, vm::cptr<Ce
|
||||
return CELL_SEARCH_ERROR_CONTENT_NOT_FOUND;
|
||||
}
|
||||
|
||||
music_selection_context context;
|
||||
music_selection_context context{};
|
||||
|
||||
// Use the first track in order to get info about this search
|
||||
const auto& first_content_id = searchObject->content_ids[0];
|
||||
@ -1685,8 +1687,8 @@ error_code cellSearchGetMusicSelectionContext(CellSearchId searchId, vm::cptr<Ce
|
||||
}
|
||||
|
||||
// Use the found content
|
||||
context.content_path = content->second->infoPath.contentPath;
|
||||
cellSearch.notice("cellSearchGetMusicSelectionContext(): Hash=%08X, Assigning found track: Type=0x%x, Path=%s", content_hash, +content->second->type, context.content_path);
|
||||
context.playlist.push_back(content->second->infoPath.contentPath);
|
||||
cellSearch.notice("cellSearchGetMusicSelectionContext(): Hash=%08X, Assigning found track: Type=0x%x, Path=%s", content_hash, +content->second->type, context.playlist.back());
|
||||
}
|
||||
else if (first_content->type == CELL_SEARCH_CONTENTTYPE_MUSICLIST)
|
||||
{
|
||||
@ -1696,15 +1698,17 @@ error_code cellSearchGetMusicSelectionContext(CellSearchId searchId, vm::cptr<Ce
|
||||
else if (option == CELL_SEARCH_CONTEXTOPTION_SHUFFLE)
|
||||
{
|
||||
// Select random track
|
||||
// TODO: whole playlist
|
||||
std::shared_ptr<search_content_t> content = get_random_content();
|
||||
context.content_path = content->infoPath.contentPath;
|
||||
cellSearch.notice("cellSearchGetMusicSelectionContext(): Hash=%08X, Assigning random track: Type=0x%x, Path=%s", content_hash, +content->type, context.content_path);
|
||||
context.playlist.push_back(content->infoPath.contentPath);
|
||||
cellSearch.notice("cellSearchGetMusicSelectionContext(): Hash=%08X, Assigning random track: Type=0x%x, Path=%s", content_hash, +content->type, context.playlist.back());
|
||||
}
|
||||
else
|
||||
{
|
||||
// Select the first track by default
|
||||
context.content_path = first_content->infoPath.contentPath;
|
||||
cellSearch.notice("cellSearchGetMusicSelectionContext(): Hash=%08X, Assigning first track: Type=0x%x, Path=%s", content_hash, +first_content->type, context.content_path);
|
||||
// TODO: whole playlist
|
||||
context.playlist.push_back(first_content->infoPath.contentPath);
|
||||
cellSearch.notice("cellSearchGetMusicSelectionContext(): Hash=%08X, Assigning first track: Type=0x%x, Path=%s", content_hash, +first_content->type, context.playlist.back());
|
||||
}
|
||||
}
|
||||
else if (first_content->type == CELL_SEARCH_CONTENTTYPE_MUSICLIST)
|
||||
@ -1715,27 +1719,34 @@ error_code cellSearchGetMusicSelectionContext(CellSearchId searchId, vm::cptr<Ce
|
||||
else if (option == CELL_SEARCH_CONTEXTOPTION_SHUFFLE)
|
||||
{
|
||||
// Select random track
|
||||
// TODO: whole playlist
|
||||
std::shared_ptr<search_content_t> content = get_random_content();
|
||||
context.content_path = content->infoPath.contentPath;
|
||||
cellSearch.notice("cellSearchGetMusicSelectionContext(): Assigning random track: Type=0x%x, Path=%s", +content->type, context.content_path);
|
||||
context.playlist.push_back(content->infoPath.contentPath);
|
||||
cellSearch.notice("cellSearchGetMusicSelectionContext(): Assigning random track: Type=0x%x, Path=%s", +content->type, context.playlist.back());
|
||||
}
|
||||
else
|
||||
{
|
||||
// Select the first track by default
|
||||
context.content_path = first_content->infoPath.contentPath;
|
||||
cellSearch.notice("cellSearchGetMusicSelectionContext(): Assigning first track: Type=0x%x, Path=%s", +first_content->type, context.content_path);
|
||||
// TODO: whole playlist
|
||||
context.playlist.push_back(first_content->infoPath.contentPath);
|
||||
cellSearch.notice("cellSearchGetMusicSelectionContext(): Assigning first track: Type=0x%x, Path=%s", +first_content->type, context.playlist.back());
|
||||
}
|
||||
|
||||
context.content_type = first_content->type;
|
||||
context.repeat_mode = repeatMode;
|
||||
context.context_option = option;
|
||||
// TODO: context.first_track = ?;
|
||||
|
||||
// Resolve hashed paths
|
||||
if (auto found = search.content_links.find(context.content_path); found != search.content_links.end())
|
||||
for (std::string& track : context.playlist)
|
||||
{
|
||||
context.content_path = found->second;
|
||||
if (auto found = search.content_links.find(track); found != search.content_links.end())
|
||||
{
|
||||
track = found->second;
|
||||
}
|
||||
}
|
||||
|
||||
context.create_playlist(music_selection_context::get_next_hash());
|
||||
*outContext = context.get();
|
||||
|
||||
cellSearch.success("cellSearchGetMusicSelectionContext: found selection context: %d", context.to_string());
|
||||
@ -1780,15 +1791,21 @@ error_code cellSearchGetMusicSelectionContextOfSingleTrack(vm::cptr<CellSearchCo
|
||||
return CELL_SEARCH_ERROR_INVALID_CONTENTTYPE;
|
||||
}
|
||||
|
||||
music_selection_context context;
|
||||
context.content_path = content_info->infoPath.contentPath;
|
||||
music_selection_context context{};
|
||||
context.playlist.push_back(content_info->infoPath.contentPath);
|
||||
context.repeat_mode = content_info->repeat_mode;
|
||||
context.context_option = content_info->context_option;
|
||||
|
||||
// Resolve hashed paths
|
||||
if (auto found = search.content_links.find(context.content_path); found != search.content_links.end())
|
||||
for (std::string& track : context.playlist)
|
||||
{
|
||||
context.content_path = found->second;
|
||||
if (auto found = search.content_links.find(track); found != search.content_links.end())
|
||||
{
|
||||
track = found->second;
|
||||
}
|
||||
}
|
||||
|
||||
context.create_playlist(music_selection_context::get_next_hash());
|
||||
*outContext = context.get();
|
||||
|
||||
cellSearch.success("cellSearchGetMusicSelectionContextOfSingleTrack: found selection context: %s", context.to_string());
|
||||
@ -2066,14 +2083,33 @@ error_code music_selection_context::find_content_id(vm::ptr<CellSearchContentId>
|
||||
|
||||
// Search for the content that matches our current selection
|
||||
auto& content_map = g_fxo->get<content_id_map>();
|
||||
const u64 hash = std::hash<std::string>()(content_path);
|
||||
auto found = content_map.map.find(hash);
|
||||
if (found != content_map.map.end())
|
||||
std::shared_ptr<search_content_t> found_content;
|
||||
u64 hash = 0;
|
||||
|
||||
for (const std::string& track : playlist)
|
||||
{
|
||||
if (content_type == CELL_SEARCH_CONTENTTYPE_MUSICLIST)
|
||||
{
|
||||
hash = std::hash<std::string>()(fs::get_parent_dir(track));
|
||||
}
|
||||
else
|
||||
{
|
||||
hash = std::hash<std::string>()(track);
|
||||
}
|
||||
|
||||
if (auto found = content_map.map.find(hash); found != content_map.map.end())
|
||||
{
|
||||
found_content = found->second;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (found_content)
|
||||
{
|
||||
// TODO: check if the content type is correct
|
||||
const u128 content_id_128 = found->first;
|
||||
const u128 content_id_128 = hash;
|
||||
std::memcpy(contents_id->data, &content_id_128, CELL_SEARCH_CONTENT_ID_SIZE);
|
||||
cellSearch.warning("find_content_id: found existing content for %s (path control: '%s')", to_string(), found->second->infoPath.contentPath);
|
||||
cellSearch.warning("find_content_id: found existing content for %s (path control: '%s')", to_string(), found_content->infoPath.contentPath);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@ -2116,6 +2152,8 @@ error_code music_selection_context::find_content_id(vm::ptr<CellSearchContentId>
|
||||
// TODO: check for actual content inside the directory
|
||||
std::shared_ptr<search_content_t> curr_find = std::make_shared<search_content_t>();
|
||||
curr_find->type = CELL_SEARCH_CONTENTTYPE_MUSICLIST;
|
||||
curr_find->repeat_mode = repeat_mode;
|
||||
curr_find->context_option = context_option;
|
||||
|
||||
if (dir_path.length() > CELL_SEARCH_PATH_LEN_MAX)
|
||||
{
|
||||
@ -2170,6 +2208,8 @@ error_code music_selection_context::find_content_id(vm::ptr<CellSearchContentId>
|
||||
|
||||
std::shared_ptr<search_content_t> curr_find = std::make_shared<search_content_t>();
|
||||
curr_find->type = CELL_SEARCH_CONTENTTYPE_MUSIC;
|
||||
curr_find->repeat_mode = repeat_mode;
|
||||
curr_find->context_option = context_option;
|
||||
|
||||
if (file_path.length() > CELL_SEARCH_PATH_LEN_MAX)
|
||||
{
|
||||
|
@ -282,6 +282,7 @@ namespace rsx
|
||||
{
|
||||
rsx_log.notice("parse_media_recursive: Found %d matches in directory '%s'", current_entry.children.size(), media_path);
|
||||
current_entry.type = media_list_dialog::media_type::directory;
|
||||
current_entry.info.path = media_path;
|
||||
}
|
||||
}
|
||||
else if (type == media_list_dialog::media_type::photo)
|
||||
@ -352,32 +353,32 @@ namespace rsx
|
||||
ensure(static_cast<size_t>(result) < media->children.size());
|
||||
media = &media->children[result];
|
||||
rsx_log.notice("Left media list dialog with entry: '%s' ('%s')", media->name, media->path);
|
||||
continue;
|
||||
}
|
||||
else if (result == user_interface::selection_code::canceled)
|
||||
|
||||
if (result == user_interface::selection_code::canceled)
|
||||
{
|
||||
if (media == &root_media_entry)
|
||||
{
|
||||
rsx_log.notice("Media list dialog canceled");
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
focused = media->index;
|
||||
media = media->parent;
|
||||
result = 0;
|
||||
rsx_log.notice("Media list dialog moving to parent directory (focused=%d)", focused);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
rsx_log.error("Left media list dialog with error: '%d'", result);
|
||||
|
||||
focused = media->index;
|
||||
media = media->parent;
|
||||
result = 0;
|
||||
rsx_log.notice("Media list dialog moving to parent directory (focused=%d)", focused);
|
||||
continue;
|
||||
}
|
||||
|
||||
rsx_log.error("Left media list dialog with error: '%d'", result);
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
media = nullptr;
|
||||
result = user_interface::selection_code::canceled;
|
||||
rsx_log.error("Media selection is only possible when the native user interface is enabled in the settings. The action will be canceled.");
|
||||
}
|
||||
|
||||
media = nullptr;
|
||||
result = user_interface::selection_code::canceled;
|
||||
rsx_log.error("Media selection is only possible when the native user interface is enabled in the settings. The action will be canceled.");
|
||||
break;
|
||||
}
|
||||
|
||||
if (result >= 0 && media && media->type == type)
|
||||
|
@ -61,6 +61,7 @@
|
||||
<ExcludedFromBuild>true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Emu\cache_utils.cpp" />
|
||||
<ClCompile Include="Emu\Cell\Modules\cellMusicSelectionContext.cpp" />
|
||||
<ClCompile Include="Emu\Cell\Modules\libfs_utility_init.cpp" />
|
||||
<ClCompile Include="Emu\Cell\Modules\sys_crashdump.cpp" />
|
||||
<ClCompile Include="Emu\Io\camera_config.cpp" />
|
||||
@ -470,6 +471,7 @@
|
||||
<ClInclude Include="Emu\cache_utils.hpp" />
|
||||
<ClInclude Include="Emu\Cell\lv2\sys_crypto_engine.h" />
|
||||
<ClInclude Include="Emu\Cell\Modules\cellCrossController.h" />
|
||||
<ClInclude Include="Emu\Cell\Modules\cellMusicDecode.h" />
|
||||
<ClInclude Include="Emu\Cell\Modules\cellRec.h" />
|
||||
<ClInclude Include="Emu\Cell\Modules\cellRemotePlay.h" />
|
||||
<ClInclude Include="Emu\Cell\Modules\cellSsl.h" />
|
||||
@ -843,4 +845,4 @@
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
</Project>
|
@ -1087,6 +1087,9 @@
|
||||
<ClCompile Include="Emu\savestate_utils.cpp">
|
||||
<Filter>Emu</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Emu\Cell\Modules\cellMusicSelectionContext.cpp">
|
||||
<Filter>Emu\Cell\Modules</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Crypto\aes.h">
|
||||
@ -2164,6 +2167,9 @@
|
||||
<ClInclude Include="Emu\RSX\Overlays\overlay_cursor.h">
|
||||
<Filter>Emu\GPU\RSX\Overlays</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Emu\Cell\Modules\cellMusicDecode.h">
|
||||
<Filter>Emu\Cell\Modules</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="Emu\RSX\Program\GLSLSnippets\GPUDeswizzle.glsl">
|
||||
@ -2194,4 +2200,4 @@
|
||||
<Filter>Emu\GPU\RSX\Program\Snippets</Filter>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
@ -2,6 +2,9 @@
|
||||
#include "media_utils.h"
|
||||
#include "logs.hpp"
|
||||
#include "Utilities/StrUtil.h"
|
||||
#include "Emu/Cell/Modules/cellSearch.h"
|
||||
|
||||
#include <random>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push, 0)
|
||||
@ -196,9 +199,9 @@ namespace utils
|
||||
stop();
|
||||
}
|
||||
|
||||
void audio_decoder::set_path(const std::string& path)
|
||||
void audio_decoder::set_context(music_selection_context context)
|
||||
{
|
||||
m_path = path;
|
||||
m_context = std::move(context);
|
||||
}
|
||||
|
||||
void audio_decoder::set_swap_endianness(bool swapped)
|
||||
@ -206,27 +209,41 @@ namespace utils
|
||||
m_swap_endianness = swapped;
|
||||
}
|
||||
|
||||
void audio_decoder::clear()
|
||||
{
|
||||
track_fully_decoded = false;
|
||||
track_fully_consumed = false;
|
||||
has_error = false;
|
||||
m_size = 0;
|
||||
duration_ms = 0;
|
||||
timestamps_ms.clear();
|
||||
data.clear();
|
||||
}
|
||||
|
||||
void audio_decoder::stop()
|
||||
{
|
||||
if (m_thread)
|
||||
{
|
||||
auto& thread = *m_thread;
|
||||
thread = thread_state::aborting;
|
||||
track_fully_consumed = true;
|
||||
track_fully_consumed.notify_one();
|
||||
thread();
|
||||
m_thread.reset();
|
||||
}
|
||||
|
||||
has_error = false;
|
||||
m_size = 0;
|
||||
timestamps_ms.clear();
|
||||
data.clear();
|
||||
clear();
|
||||
}
|
||||
|
||||
void audio_decoder::decode()
|
||||
{
|
||||
stop();
|
||||
|
||||
m_thread = std::make_unique<named_thread<std::function<void()>>>("Music Decode Thread", [this, path = m_path]()
|
||||
media_log.notice("audio_decoder: %d entries in playlist. Start decoding...", m_context.playlist.size());
|
||||
|
||||
const auto decode_track = [this](const std::string& path)
|
||||
{
|
||||
media_log.notice("audio_decoder: decoding %s", path);
|
||||
scoped_av av;
|
||||
|
||||
// Get format from audio file
|
||||
@ -411,9 +428,61 @@ namespace utils
|
||||
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);
|
||||
media_log.notice("audio_decoder: decoded frame_count=%d buffer_size=%d timestamp_us=%d", frame_count, buffer_size, av.frame->best_effort_timestamp);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
m_thread = std::make_unique<named_thread<std::function<void()>>>("Music Decode Thread", [this, decode_track]()
|
||||
{
|
||||
for (const std::string& track : m_context.playlist)
|
||||
{
|
||||
media_log.notice("audio_decoder: playlist entry: %s", track);
|
||||
}
|
||||
|
||||
if (m_context.playlist.empty())
|
||||
{
|
||||
media_log.error("audio_decoder: Can not play empty playlist");
|
||||
has_error = true;
|
||||
return;
|
||||
}
|
||||
|
||||
m_context.current_track = m_context.first_track;
|
||||
|
||||
if (m_context.context_option == CELL_SEARCH_CONTEXTOPTION_SHUFFLE && m_context.playlist.size() > 1)
|
||||
{
|
||||
// Shuffle once if necessary
|
||||
media_log.notice("audio_decoder: shuffling initial playlist...");
|
||||
auto engine = std::default_random_engine{};
|
||||
std::shuffle(std::begin(m_context.playlist), std::end(m_context.playlist), engine);
|
||||
}
|
||||
|
||||
while (thread_ctrl::state() != thread_state::aborting)
|
||||
{
|
||||
ensure(m_context.current_track < m_context.playlist.size());
|
||||
media_log.notice("audio_decoder: about to decode: %s (index=%d)", m_context.playlist.at(m_context.current_track), m_context.current_track);
|
||||
|
||||
decode_track(m_context.playlist.at(m_context.current_track));
|
||||
track_fully_decoded = true;
|
||||
|
||||
if (has_error)
|
||||
{
|
||||
media_log.notice("audio_decoder: stopping with error...");
|
||||
break;
|
||||
}
|
||||
|
||||
// Let's only decode one track at a time. Wait for the consumer to finish reading the track.
|
||||
media_log.notice("audio_decoder: waiting until track is consumed...");
|
||||
thread_ctrl::wait_on(track_fully_consumed, false);
|
||||
track_fully_consumed = false;
|
||||
}
|
||||
|
||||
media_log.notice("audio_decoder: finished playlist");
|
||||
});
|
||||
}
|
||||
|
||||
u32 audio_decoder::set_next_index(bool next)
|
||||
{
|
||||
return m_context.step_track(next);
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include <thread>
|
||||
#include "Utilities/StrUtil.h"
|
||||
#include "Utilities/Thread.h"
|
||||
#include "Emu/Cell/Modules/cellMusic.h"
|
||||
|
||||
namespace utils
|
||||
{
|
||||
@ -49,22 +50,26 @@ namespace utils
|
||||
audio_decoder();
|
||||
~audio_decoder();
|
||||
|
||||
void set_path(const std::string& path);
|
||||
void set_context(music_selection_context context);
|
||||
void set_swap_endianness(bool swapped);
|
||||
void clear();
|
||||
void stop();
|
||||
void decode();
|
||||
u32 set_next_index(bool next);
|
||||
|
||||
std::mutex m_mtx;
|
||||
shared_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> track_fully_decoded{false};
|
||||
atomic_t<bool> track_fully_consumed{false};
|
||||
atomic_t<bool> has_error{false};
|
||||
std::deque<std::pair<u64, u64>> timestamps_ms;
|
||||
|
||||
private:
|
||||
bool m_swap_endianness = false;
|
||||
std::string m_path;
|
||||
music_selection_context m_context{};
|
||||
std::unique_ptr<named_thread<std::function<void()>>> m_thread;
|
||||
};
|
||||
}
|
||||
|
@ -80,4 +80,5 @@ template u32 get_yaml_node_value<u32>(YAML::Node, std::string&);
|
||||
template u64 get_yaml_node_value<u64>(YAML::Node, std::string&);
|
||||
template s64 get_yaml_node_value<s64>(YAML::Node, std::string&);
|
||||
template f64 get_yaml_node_value<f64>(YAML::Node, std::string&);
|
||||
template std::string get_yaml_node_value<std::string>(YAML::Node, std::string&);
|
||||
template cheat_info get_yaml_node_value<cheat_info>(YAML::Node, std::string&);
|
||||
|
Loading…
x
Reference in New Issue
Block a user