mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-07-11 17:28:36 +12:00
cellMusic/Decode: implement playlist shuffle and repeat
This commit is contained in:
parent
683fa2a392
commit
c40439ae6b
13 changed files with 790 additions and 247 deletions
|
@ -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
|
||||
{
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue