2024-04-28 18:20:26 +02:00
# include "stdafx.h"
2025-10-05 18:28:03 +02:00
# include "rx/align.hpp"
2024-04-28 18:20:26 +02:00
# include "Emu/perf_meter.hpp"
# include "Emu/Cell/PPUModule.h"
2025-10-04 15:46:36 +02:00
# include "cellos/sys_sync.h"
# include "cellos/sys_ppu_thread.h"
2024-04-28 18:20:26 +02:00
# include "Emu/savestate_utils.hpp"
# include "sysPrxForUser.h"
2025-10-05 18:28:03 +02:00
# include "rx/asm.hpp"
2024-04-28 18:20:26 +02:00
# include "util/media_utils.h"
# include "cellAtracXdec.h"
vm : : gvar < CellAdecCoreOps > g_cell_adec_core_ops_atracx2ch ;
vm : : gvar < CellAdecCoreOps > g_cell_adec_core_ops_atracx6ch ;
vm : : gvar < CellAdecCoreOps > g_cell_adec_core_ops_atracx8ch ;
vm : : gvar < CellAdecCoreOps > g_cell_adec_core_ops_atracx ;
LOG_CHANNEL ( cellAtracXdec ) ;
template < >
void fmt_class_string < CellAtracXdecError > : : format ( std : : string & out , u64 arg )
{
format_enum ( out , arg , [ ] ( CellAtracXdecError value )
{
2025-04-05 21:50:45 +02:00
switch ( value )
{
STR_CASE ( CELL_ADEC_ERROR_ATX_OK ) ; // CELL_ADEC_ERROR_ATX_OFFSET, CELL_ADEC_ERROR_ATX_NONE
STR_CASE ( CELL_ADEC_ERROR_ATX_BUSY ) ;
STR_CASE ( CELL_ADEC_ERROR_ATX_EMPTY ) ;
STR_CASE ( CELL_ADEC_ERROR_ATX_ATSHDR ) ;
STR_CASE ( CELL_ADEC_ERROR_ATX_NON_FATAL ) ;
STR_CASE ( CELL_ADEC_ERROR_ATX_NOT_IMPLE ) ;
STR_CASE ( CELL_ADEC_ERROR_ATX_PACK_CE_OVERFLOW ) ;
STR_CASE ( CELL_ADEC_ERROR_ATX_ILLEGAL_NPROCQUS ) ;
STR_CASE ( CELL_ADEC_ERROR_ATX_FATAL ) ;
STR_CASE ( CELL_ADEC_ERROR_ATX_ENC_OVERFLOW ) ;
STR_CASE ( CELL_ADEC_ERROR_ATX_PACK_CE_UNDERFLOW ) ;
STR_CASE ( CELL_ADEC_ERROR_ATX_SYNTAX_IDCT ) ;
STR_CASE ( CELL_ADEC_ERROR_ATX_SYNTAX_GAINADJ ) ;
STR_CASE ( CELL_ADEC_ERROR_ATX_SYNTAX_IDSF ) ;
STR_CASE ( CELL_ADEC_ERROR_ATX_SYNTAX_SPECTRA ) ;
STR_CASE ( CELL_ADEC_ERROR_ATX_SYNTAX_IDWL ) ;
STR_CASE ( CELL_ADEC_ERROR_ATX_SYNTAX_GHWAVE ) ;
STR_CASE ( CELL_ADEC_ERROR_ATX_SYNTAX_SHEADER ) ;
STR_CASE ( CELL_ADEC_ERROR_ATX_SYNTAX_IDWL_A ) ;
STR_CASE ( CELL_ADEC_ERROR_ATX_SYNTAX_IDWL_B ) ;
STR_CASE ( CELL_ADEC_ERROR_ATX_SYNTAX_IDWL_C ) ;
STR_CASE ( CELL_ADEC_ERROR_ATX_SYNTAX_IDWL_D ) ;
STR_CASE ( CELL_ADEC_ERROR_ATX_SYNTAX_IDWL_E ) ;
STR_CASE ( CELL_ADEC_ERROR_ATX_SYNTAX_IDSF_A ) ;
STR_CASE ( CELL_ADEC_ERROR_ATX_SYNTAX_IDSF_B ) ;
STR_CASE ( CELL_ADEC_ERROR_ATX_SYNTAX_IDSF_C ) ;
STR_CASE ( CELL_ADEC_ERROR_ATX_SYNTAX_IDSF_D ) ;
STR_CASE ( CELL_ADEC_ERROR_ATX_SYNTAX_IDCT_A ) ;
STR_CASE ( CELL_ADEC_ERROR_ATX_SYNTAX_GC_NGC ) ;
STR_CASE ( CELL_ADEC_ERROR_ATX_SYNTAX_GC_IDLEV_A ) ;
STR_CASE ( CELL_ADEC_ERROR_ATX_SYNTAX_GC_IDLOC_A ) ;
STR_CASE ( CELL_ADEC_ERROR_ATX_SYNTAX_GC_IDLEV_B ) ;
STR_CASE ( CELL_ADEC_ERROR_ATX_SYNTAX_GC_IDLOC_B ) ;
STR_CASE ( CELL_ADEC_ERROR_ATX_SYNTAX_SN_NWVS ) ;
STR_CASE ( CELL_ADEC_ERROR_ATX_FATAL_HANDLE ) ;
STR_CASE ( CELL_ADEC_ERROR_ATX_ASSERT_SAMPLING_FREQ ) ;
STR_CASE ( CELL_ADEC_ERROR_ATX_ASSERT_CH_CONFIG_INDEX ) ;
STR_CASE ( CELL_ADEC_ERROR_ATX_ASSERT_NBYTES ) ;
STR_CASE ( CELL_ADEC_ERROR_ATX_ASSERT_BLOCK_NUM ) ;
STR_CASE ( CELL_ADEC_ERROR_ATX_ASSERT_BLOCK_ID ) ;
STR_CASE ( CELL_ADEC_ERROR_ATX_ASSERT_CHANNELS ) ;
STR_CASE ( CELL_ADEC_ERROR_ATX_UNINIT_BLOCK_SPECIFIED ) ;
STR_CASE ( CELL_ADEC_ERROR_ATX_POSCFG_PRESENT ) ;
STR_CASE ( CELL_ADEC_ERROR_ATX_BUFFER_OVERFLOW ) ;
STR_CASE ( CELL_ADEC_ERROR_ATX_ILL_BLK_TYPE_ID ) ;
STR_CASE ( CELL_ADEC_ERROR_ATX_UNPACK_CHANNEL_BLK_FAILED ) ;
STR_CASE ( CELL_ADEC_ERROR_ATX_ILL_BLK_ID_USED_1 ) ;
STR_CASE ( CELL_ADEC_ERROR_ATX_ILL_BLK_ID_USED_2 ) ;
STR_CASE ( CELL_ADEC_ERROR_ATX_ILLEGAL_ENC_SETTING ) ;
STR_CASE ( CELL_ADEC_ERROR_ATX_ILLEGAL_DEC_SETTING ) ;
STR_CASE ( CELL_ADEC_ERROR_ATX_ASSERT_NSAMPLES ) ;
STR_CASE ( CELL_ADEC_ERROR_ATX_ILL_SYNCWORD ) ;
STR_CASE ( CELL_ADEC_ERROR_ATX_ILL_SAMPLING_FREQ ) ;
STR_CASE ( CELL_ADEC_ERROR_ATX_ILL_CH_CONFIG_INDEX ) ;
STR_CASE ( CELL_ADEC_ERROR_ATX_RAW_DATA_FRAME_SIZE_OVER ) ;
STR_CASE ( CELL_ADEC_ERROR_ATX_SYNTAX_ENHANCE_LENGTH_OVER ) ;
STR_CASE ( CELL_ADEC_ERROR_ATX_SPU_INTERNAL_FAIL ) ;
}
2024-04-28 18:20:26 +02:00
2025-04-05 21:50:45 +02:00
return unknown ;
} ) ;
2024-04-28 18:20:26 +02:00
}
constexpr u32 atracXdecGetSpursMemSize ( u32 nch_in )
{
switch ( nch_in )
{
case 1 : return 0x6000 ;
case 2 : return 0x6000 ;
case 3 : return 0x12880 ;
case 4 : return 0x19c80 ;
case 5 : return - 1 ;
case 6 : return 0x23080 ;
case 7 : return 0x2a480 ;
case 8 : return 0x2c480 ;
default : return - 1 ;
}
}
void AtracXdecDecoder : : alloc_avcodec ( )
{
codec = avcodec_find_decoder ( AV_CODEC_ID_ATRAC3P ) ;
if ( ! codec )
{
fmt : : throw_exception ( " avcodec_find_decoder() failed " ) ;
}
2024-06-12 20:25:03 +02:00
ensure ( ! ( codec - > capabilities & AV_CODEC_CAP_SUBFRAMES ) ) ;
2024-04-28 18:20:26 +02:00
ctx = avcodec_alloc_context3 ( codec ) ;
if ( ! ctx )
{
fmt : : throw_exception ( " avcodec_alloc_context3() failed " ) ;
}
// Allows FFmpeg to output directly into guest memory
ctx - > opaque = this ;
2024-09-17 23:35:27 +02:00
ctx - > thread_type = FF_THREAD_SLICE ; // Silences a warning by FFmpeg about requesting frame threading with a custom get_buffer2(). Default is FF_THREAD_FRAME & FF_THREAD_SLICE
2024-04-28 18:20:26 +02:00
ctx - > get_buffer2 = [ ] ( AVCodecContext * s , AVFrame * frame , int /*flags*/ ) - > int
{
for ( s32 i = 0 ; i < frame - > ch_layout . nb_channels ; i + + )
{
frame - > data [ i ] = static_cast < AtracXdecDecoder * > ( s - > opaque ) - > work_mem . get_ptr ( ) + ATXDEC_MAX_FRAME_LENGTH + ATXDEC_SAMPLES_PER_FRAME * sizeof ( f32 ) * i ;
frame - > linesize [ i ] = ATXDEC_SAMPLES_PER_FRAME * sizeof ( f32 ) ;
}
2025-04-05 21:50:45 +02:00
frame - > buf [ 0 ] = av_buffer_create ( frame - > data [ 0 ] , ATXDEC_SAMPLES_PER_FRAME * sizeof ( f32 ) * frame - > ch_layout . nb_channels , [ ] ( void * , uint8_t * ) { } , nullptr , 0 ) ;
2024-04-28 18:20:26 +02:00
return 0 ;
} ;
packet = av_packet_alloc ( ) ;
if ( ! packet )
{
fmt : : throw_exception ( " av_packet_alloc() failed " ) ;
}
frame = av_frame_alloc ( ) ;
if ( ! frame )
{
fmt : : throw_exception ( " av_frame_alloc() failed " ) ;
}
}
void AtracXdecDecoder : : free_avcodec ( )
{
av_packet_free ( & packet ) ;
av_frame_free ( & frame ) ;
avcodec_free_context ( & ctx ) ;
}
void AtracXdecDecoder : : init_avcodec ( )
{
2024-06-11 19:23:48 +02:00
if ( int err = avcodec_close ( ctx ) ; err )
{
fmt : : throw_exception ( " avcodec_close() failed (err=0x%x='%s') " , err , utils : : av_error_to_string ( err ) ) ;
}
2024-04-28 18:20:26 +02:00
ctx - > block_align = nbytes ;
ctx - > ch_layout . nb_channels = nch_in ;
ctx - > sample_rate = sampling_freq ;
if ( int err = avcodec_open2 ( ctx , codec , nullptr ) ; err )
{
fmt : : throw_exception ( " avcodec_open2() failed (err=0x%x='%s') " , err , utils : : av_error_to_string ( err ) ) ;
}
packet - > data = work_mem . get_ptr ( ) ;
packet - > size = nbytes ;
2025-04-05 21:50:45 +02:00
packet - > buf = av_buffer_create ( work_mem . get_ptr ( ) , nbytes , [ ] ( void * , uint8_t * ) { } , nullptr , 0 ) ;
2024-04-28 18:20:26 +02:00
}
error_code AtracXdecDecoder : : set_config_info ( u32 sampling_freq , u32 ch_config_idx , u32 nbytes )
{
cellAtracXdec . notice ( " AtracXdecDecoder::set_config_info(sampling_freq=%d, ch_config_idx=%d, nbytes=0x%x) " , sampling_freq , ch_config_idx , nbytes ) ;
this - > sampling_freq = sampling_freq ;
this - > ch_config_idx = ch_config_idx ;
this - > nbytes = nbytes ;
2025-10-05 18:28:03 +02:00
this - > nbytes_128_aligned = rx : : alignUp ( nbytes , 0x80 ) ;
2024-04-28 18:20:26 +02:00
this - > nch_in = ch_config_idx < = 4 ? ch_config_idx : ch_config_idx + 1 ;
if ( ch_config_idx > 7u )
{
this - > config_is_set = false ;
2025-04-05 21:50:45 +02:00
return { 0x80004005 , " AtracXdecDecoder::set_config_info() failed: Invalid channel configuration: %d " , ch_config_idx } ;
2024-04-28 18:20:26 +02:00
}
this - > nch_blocks = ATXDEC_NCH_BLOCKS_MAP [ ch_config_idx ] ;
// These checks are performed on the LLE SPU thread
if ( ch_config_idx = = 0u )
{
this - > config_is_set = false ;
2025-04-05 21:50:45 +02:00
return { 0x80004005 , " AtracXdecDecoder::set_config_info() failed: Invalid channel configuration: %d " , ch_config_idx } ;
2024-04-28 18:20:26 +02:00
}
if ( sampling_freq ! = 48000u & & sampling_freq ! = 44100u ) // 32kHz is not supported, even though official docs claim it is
{
this - > config_is_set = false ;
2025-04-05 21:50:45 +02:00
return { 0x80004005 , " AtracXdecDecoder::set_config_info() failed: Invalid sample rate: %d " , sampling_freq } ;
2024-04-28 18:20:26 +02:00
}
if ( nbytes = = 0u | | nbytes > ATXDEC_MAX_FRAME_LENGTH )
{
this - > config_is_set = false ;
2025-04-05 21:50:45 +02:00
return { 0x80004005 , " AtracXdecDecoder::set_config_info() failed: Invalid frame length: 0x%x " , nbytes } ;
2024-04-28 18:20:26 +02:00
}
this - > config_is_set = true ;
return CELL_OK ;
}
error_code AtracXdecDecoder : : init_decode ( u32 bw_pcm , u32 nch_out )
{
if ( bw_pcm < CELL_ADEC_ATRACX_WORD_SZ_16BIT | | ( bw_pcm > CELL_ADEC_ATRACX_WORD_SZ_32BIT & & bw_pcm ! = CELL_ADEC_ATRACX_WORD_SZ_FLOAT ) )
{
2025-04-05 21:50:45 +02:00
return { 0x80004005 , " AtracXdecDecoder::init_decode() failed: Invalid PCM output format " } ;
2024-04-28 18:20:26 +02:00
}
this - > bw_pcm = bw_pcm ;
this - > nch_out = nch_out ; // Not checked for invalid values on LLE
this - > pcm_output_size = ( bw_pcm = = CELL_ADEC_ATRACX_WORD_SZ_16BIT ? sizeof ( s16 ) : sizeof ( f32 ) ) * nch_in * ATXDEC_SAMPLES_PER_FRAME ;
init_avcodec ( ) ;
return CELL_OK ;
}
error_code AtracXdecDecoder : : parse_ats_header ( vm : : cptr < u8 > au_start_addr )
{
const auto ats = std : : bit_cast < AtracXdecAtsHeader > ( vm : : read64 ( au_start_addr . addr ( ) ) ) ;
if ( ats . sync_word ! = 0x0fd0 )
{
2025-04-05 21:50:45 +02:00
return { CELL_ADEC_ERROR_ATX_ATSHDR , " AtracXdecDecoder::parse_ats_header() failed: Invalid sync word: 0x%x " , ats . sync_word } ;
2024-04-28 18:20:26 +02:00
}
const u8 sample_rate_idx = ats . params > > 13 ;
const u8 ch_config_idx = ats . params > > 10 & 7 ;
const u16 nbytes = ( ( ats . params & 0x3ff ) + 1 ) * 8 ;
if ( ch_config_idx = = 0u )
{
2025-04-05 21:50:45 +02:00
return { CELL_ADEC_ERROR_ATX_ATSHDR , " AtracXdecDecoder::parse_ats_header() failed: Invalid channel configuration: %d " , ch_config_idx } ;
2024-04-28 18:20:26 +02:00
}
u32 sampling_freq ;
switch ( sample_rate_idx )
{
case 1 : sampling_freq = 44100 ; break ;
case 2 : sampling_freq = 48000 ; break ;
2025-04-05 21:50:45 +02:00
default : return { CELL_ADEC_ERROR_ATX_ATSHDR , " AtracXdecDecoder::parse_ats_header() failed: Invalid sample rate index: %d " , sample_rate_idx } ;
2024-04-28 18:20:26 +02:00
}
return set_config_info ( sampling_freq , ch_config_idx , nbytes ) ; // Cannot return error here, values were already checked
}
void AtracXdecContext : : exec ( ppu_thread & ppu )
{
perf_meter < " ATXDEC " _u64 > perf0 ;
// Savestates
if ( decoder . config_is_set )
{
decoder . init_avcodec ( ) ;
}
2025-01-12 12:25:06 +01:00
switch ( savestate )
{
case atracxdec_state : : initial : break ;
case atracxdec_state : : waiting_for_cmd : goto label1_wait_for_cmd_state ;
case atracxdec_state : : checking_run_thread_1 : goto label2_check_run_thread_1_state ;
case atracxdec_state : : executing_cmd : goto label3_execute_cmd_state ;
case atracxdec_state : : waiting_for_output : goto label4_wait_for_output_state ;
case atracxdec_state : : checking_run_thread_2 : goto label5_check_run_thread_2_state ;
case atracxdec_state : : decoding : goto label6_decode_state ;
}
2025-04-05 21:50:45 +02:00
for ( ; ; cmd_counter + + )
2024-04-28 18:20:26 +02:00
{
cellAtracXdec . trace ( " Command counter: %llu, waiting for next command... " , cmd_counter ) ;
2025-01-12 12:25:06 +01:00
for ( ; ; )
2024-04-28 18:20:26 +02:00
{
2025-01-12 12:25:06 +01:00
savestate = atracxdec_state : : initial ;
2024-04-28 18:20:26 +02:00
2025-01-12 12:25:06 +01:00
ensure ( sys_mutex_lock ( ppu , queue_mutex , 0 ) = = CELL_OK ) ;
if ( ppu . state & cpu_flag : : again )
2024-04-28 18:20:26 +02:00
{
2025-01-12 12:25:06 +01:00
return ;
2024-04-28 18:20:26 +02:00
}
2025-01-12 12:25:06 +01:00
if ( ! cmd_queue . empty ( ) )
2024-04-28 18:20:26 +02:00
{
2025-01-12 12:25:06 +01:00
break ;
2024-04-28 18:20:26 +02:00
}
2025-01-12 12:25:06 +01:00
savestate = atracxdec_state : : waiting_for_cmd ;
2025-04-05 21:50:45 +02:00
label1_wait_for_cmd_state :
2024-05-07 20:16:26 +02:00
2025-01-12 12:25:06 +01:00
ensure ( sys_cond_wait ( ppu , queue_not_empty , 0 ) = = CELL_OK ) ;
if ( ppu . state & cpu_flag : : again )
2024-05-07 20:16:26 +02:00
{
return ;
}
2025-01-12 12:25:06 +01:00
ensure ( sys_mutex_unlock ( ppu , queue_mutex ) = = CELL_OK ) ;
2024-04-28 18:20:26 +02:00
}
2025-01-12 12:25:06 +01:00
cmd_queue . pop ( cmd ) ;
ensure ( sys_mutex_unlock ( ppu , queue_mutex ) = = CELL_OK ) ;
savestate = atracxdec_state : : checking_run_thread_1 ;
2025-04-05 21:50:45 +02:00
label2_check_run_thread_1_state :
2025-01-12 12:25:06 +01:00
ensure ( sys_mutex_lock ( ppu , run_thread_mutex , 0 ) = = CELL_OK ) ;
if ( ppu . state & cpu_flag : : again )
{
return ;
}
if ( ! run_thread )
{
ensure ( sys_mutex_unlock ( ppu , run_thread_mutex ) = = CELL_OK ) ;
return ;
}
ensure ( sys_mutex_unlock ( ppu , run_thread_mutex ) = = CELL_OK ) ;
savestate = atracxdec_state : : executing_cmd ;
2025-04-05 21:50:45 +02:00
label3_execute_cmd_state :
2025-01-12 12:25:06 +01:00
2024-04-28 18:20:26 +02:00
cellAtracXdec . trace ( " Command type: %d " , static_cast < u32 > ( cmd . type . get ( ) ) ) ;
switch ( cmd . type )
{
case AtracXdecCmdType : : start_seq :
2024-06-11 19:23:48 +02:00
{
2024-04-28 18:20:26 +02:00
first_decode = true ;
skip_next_frame = true ;
// Skip if access units contain an ATS header, the parameters are included in the header and we need to wait for the first decode command to parse them
if ( cmd . atracx_param . au_includes_ats_hdr_flg = = CELL_ADEC_ATRACX_ATS_HDR_NOTINC )
{
if ( decoder . set_config_info ( cmd . atracx_param . sampling_freq , cmd . atracx_param . ch_config_idx , cmd . atracx_param . nbytes ) = = static_cast < s32 > ( 0x80004005 ) )
{
break ;
}
if ( decoder . init_decode ( cmd . atracx_param . bw_pcm , cmd . atracx_param . nch_out ) = = static_cast < s32 > ( 0x80004005 ) )
{
break ;
}
}
atracx_param = cmd . atracx_param ;
break ;
2024-06-11 19:23:48 +02:00
}
2024-04-28 18:20:26 +02:00
case AtracXdecCmdType : : end_seq :
{
// Block savestate creation during callbacks
std : : unique_lock savestate_lock { g_fxo - > get < hle_locks_t > ( ) , std : : try_to_lock } ;
if ( ! savestate_lock . owns_lock ( ) )
{
2024-05-07 20:16:26 +02:00
ppu . state + = cpu_flag : : again ;
2024-04-28 18:20:26 +02:00
return ;
}
// Doesn't do anything else
notify_seq_done . cbFunc ( ppu , notify_seq_done . cbArg ) ;
break ;
}
case AtracXdecCmdType : : decode_au :
{
ensure ( ! ! cmd . au_start_addr ) ; // Not checked on LLE
cellAtracXdec . trace ( " Waiting for output to be consumed... " ) ;
2025-01-12 12:25:06 +01:00
ensure ( sys_mutex_lock ( ppu , output_mutex , 0 ) = = CELL_OK ) ;
2024-04-28 18:20:26 +02:00
2025-01-12 12:25:06 +01:00
if ( ppu . state & cpu_flag : : again )
2024-04-28 18:20:26 +02:00
{
2025-01-12 12:25:06 +01:00
return ;
2024-04-28 18:20:26 +02:00
}
2025-01-12 12:25:06 +01:00
while ( output_locked )
{
savestate = atracxdec_state : : waiting_for_output ;
2025-04-05 21:50:45 +02:00
label4_wait_for_output_state :
2025-01-12 12:25:06 +01:00
ensure ( sys_cond_wait ( ppu , output_consumed , 0 ) = = CELL_OK ) ;
if ( ppu . state & cpu_flag : : again )
{
return ;
}
}
cellAtracXdec . trace ( " Output consumed " ) ;
savestate = atracxdec_state : : checking_run_thread_2 ;
2025-04-05 21:50:45 +02:00
label5_check_run_thread_2_state :
2025-01-12 12:25:06 +01:00
ensure ( sys_mutex_lock ( ppu , run_thread_mutex , 0 ) = = CELL_OK ) ;
if ( ppu . state & cpu_flag : : again )
2024-05-07 20:16:26 +02:00
{
return ;
}
if ( ! run_thread )
2024-04-28 18:20:26 +02:00
{
2025-01-12 12:25:06 +01:00
ensure ( sys_mutex_unlock ( ppu , run_thread_mutex ) = = CELL_OK ) ;
ensure ( sys_mutex_unlock ( ppu , output_mutex ) = = CELL_OK ) ;
2024-04-28 18:20:26 +02:00
return ;
}
2025-01-12 12:25:06 +01:00
ensure ( sys_mutex_unlock ( ppu , run_thread_mutex ) = = CELL_OK ) ;
savestate = atracxdec_state : : decoding ;
2025-04-05 21:50:45 +02:00
label6_decode_state :
2024-04-28 18:20:26 +02:00
u32 error = CELL_OK ;
// Only the first valid ATS header after starting a sequence is parsed. It is ignored on all subsequent access units
if ( first_decode & & atracx_param . au_includes_ats_hdr_flg = = CELL_ADEC_ATRACX_ATS_HDR_INC )
{
// Block savestate creation during callbacks
std : : unique_lock savestate_lock { g_fxo - > get < hle_locks_t > ( ) , std : : try_to_lock } ;
if ( ! savestate_lock . owns_lock ( ) )
{
2024-05-07 20:16:26 +02:00
ppu . state + = cpu_flag : : again ;
2024-04-28 18:20:26 +02:00
return ;
}
if ( error = decoder . parse_ats_header ( cmd . au_start_addr ) ; error ! = CELL_OK )
{
notify_error . cbFunc ( ppu , error , notify_error . cbArg ) ;
}
else if ( decoder . init_decode ( atracx_param . bw_pcm , atracx_param . nch_out ) ! = CELL_OK )
{
notify_error . cbFunc ( ppu , CELL_ADEC_ERROR_ATX_FATAL , notify_error . cbArg ) ;
}
}
// LLE does not initialize the output address if parsing the ATS header fails
vm : : ptr < void > output = vm : : null ;
u32 decoded_samples_num = 0 ;
if ( error ! = CELL_ADEC_ERROR_ATX_ATSHDR )
{
// The LLE SPU thread would crash if you attempt to decode without a valid configuration
ensure ( decoder . config_is_set , " Attempted to decode with invalid configuration " ) ;
output . set ( work_mem . addr ( ) + atracXdecGetSpursMemSize ( decoder . nch_in ) ) ;
const auto au_start_addr = atracx_param . au_includes_ats_hdr_flg = = CELL_ADEC_ATRACX_ATS_HDR_INC ? cmd . au_start_addr . get_ptr ( ) + sizeof ( AtracXdecAtsHeader ) : cmd . au_start_addr . get_ptr ( ) ;
std : : memcpy ( work_mem . get_ptr ( ) , au_start_addr , decoder . nbytes ) ;
if ( int err = avcodec_send_packet ( decoder . ctx , decoder . packet ) ; err )
{
2024-06-12 20:25:03 +02:00
// These errors should never occur
2024-08-05 17:14:48 +02:00
if ( err = = AVERROR ( EAGAIN ) | | err = = averror_eof | | err = = AVERROR ( EINVAL ) | | err = = AVERROR ( ENOMEM ) )
2024-06-12 20:25:03 +02:00
{
fmt : : throw_exception ( " avcodec_send_packet() failed (err=0x%x='%s') " , err , utils : : av_error_to_string ( err ) ) ;
}
// Game sent invalid data
2024-04-28 18:20:26 +02:00
cellAtracXdec . error ( " avcodec_send_packet() failed (err=0x%x='%s') " , err , utils : : av_error_to_string ( err ) ) ;
error = CELL_ADEC_ERROR_ATX_NON_FATAL ; // Not accurate, FFmpeg doesn't provide detailed errors like LLE
2024-06-12 20:25:03 +02:00
av_frame_unref ( decoder . frame ) ;
}
2024-08-05 17:14:48 +02:00
else if ( ( err = avcodec_receive_frame ( decoder . ctx , decoder . frame ) ) )
2024-04-28 18:20:26 +02:00
{
2024-06-11 19:23:48 +02:00
fmt : : throw_exception ( " avcodec_receive_frame() failed (err=0x%x='%s') " , err , utils : : av_error_to_string ( err ) ) ;
2024-04-28 18:20:26 +02:00
}
decoded_samples_num = decoder . frame - > nb_samples ;
ensure ( decoded_samples_num = = 0u | | decoded_samples_num = = ATXDEC_SAMPLES_PER_FRAME ) ;
2024-08-05 17:14:48 +02:00
// The first frame after starting a new sequence or after an error is replaced with silence
2024-04-28 18:20:26 +02:00
if ( skip_next_frame & & error = = CELL_OK )
{
skip_next_frame = false ;
decoded_samples_num = 0 ;
std : : memset ( output . get_ptr ( ) , 0 , ATXDEC_SAMPLES_PER_FRAME * ( decoder . bw_pcm & 0x7full ) * decoder . nch_out ) ;
}
// Convert FFmpeg output to LLE output
2024-06-11 19:23:48 +02:00
const auto output_f32 = vm : : static_ptr_cast < f32 > ( output ) . get_ptr ( ) ;
const auto output_s16 = vm : : static_ptr_cast < s16 > ( output ) . get_ptr ( ) ;
const auto output_s32 = vm : : static_ptr_cast < s32 > ( output ) . get_ptr ( ) ;
const u8 * const ch_map = ATXDEC_AVCODEC_CH_MAP [ decoder . ch_config_idx - 1 ] ;
const u32 nch_in = decoder . nch_in ;
2024-04-28 18:20:26 +02:00
switch ( decoder . bw_pcm )
{
case CELL_ADEC_ATRACX_WORD_SZ_FLOAT :
2024-06-11 19:23:48 +02:00
for ( u32 channel_idx = 0 ; channel_idx < nch_in ; channel_idx + + )
2024-04-28 18:20:26 +02:00
{
2024-06-11 19:23:48 +02:00
const f32 * samples = reinterpret_cast < f32 * > ( decoder . frame - > data [ channel_idx ] ) ;
for ( u32 in_sample_idx = 0 , out_sample_idx = ch_map [ channel_idx ] ; in_sample_idx < decoded_samples_num ; in_sample_idx + + , out_sample_idx + = nch_in )
2024-04-28 18:20:26 +02:00
{
2024-06-11 19:23:48 +02:00
const f32 sample = samples [ in_sample_idx ] ;
2024-04-28 18:20:26 +02:00
if ( sample > = std : : bit_cast < f32 > ( std : : bit_cast < u32 > ( 1.f ) - 1 ) )
{
2024-06-11 19:23:48 +02:00
output_f32 [ out_sample_idx ] = std : : bit_cast < be_t < f32 > > ( " \x3f \x7f \xff \xff " _u32 ) ; // Prevents an unnecessary endian swap
2024-04-28 18:20:26 +02:00
}
else if ( sample < = - 1.f )
{
2024-06-11 19:23:48 +02:00
output_f32 [ out_sample_idx ] = - 1.f ;
2024-04-28 18:20:26 +02:00
}
else
{
2024-06-11 19:23:48 +02:00
output_f32 [ out_sample_idx ] = sample ;
2024-04-28 18:20:26 +02:00
}
}
}
break ;
case CELL_ADEC_ATRACX_WORD_SZ_16BIT :
2024-06-11 19:23:48 +02:00
for ( u32 channel_idx = 0 ; channel_idx < nch_in ; channel_idx + + )
2024-04-28 18:20:26 +02:00
{
2024-06-11 19:23:48 +02:00
const f32 * samples = reinterpret_cast < f32 * > ( decoder . frame - > data [ channel_idx ] ) ;
for ( u32 in_sample_idx = 0 , out_sample_idx = ch_map [ channel_idx ] ; in_sample_idx < decoded_samples_num ; in_sample_idx + + , out_sample_idx + = nch_in )
2024-04-28 18:20:26 +02:00
{
2024-06-11 19:23:48 +02:00
const f32 sample = samples [ in_sample_idx ] ;
2024-04-28 18:20:26 +02:00
if ( sample > = 1.f )
{
2024-06-11 19:23:48 +02:00
output_s16 [ out_sample_idx ] = INT16_MAX ;
2024-04-28 18:20:26 +02:00
}
else if ( sample < = - 1.f )
{
2024-06-11 19:23:48 +02:00
output_s16 [ out_sample_idx ] = INT16_MIN ;
2024-04-28 18:20:26 +02:00
}
else
{
2024-06-11 19:23:48 +02:00
output_s16 [ out_sample_idx ] = static_cast < s16 > ( std : : floor ( sample * 0x8000u ) ) ;
2024-04-28 18:20:26 +02:00
}
}
}
break ;
case CELL_ADEC_ATRACX_WORD_SZ_24BIT :
2024-06-11 19:23:48 +02:00
for ( u32 channel_idx = 0 ; channel_idx < nch_in ; channel_idx + + )
2024-04-28 18:20:26 +02:00
{
2024-06-11 19:23:48 +02:00
const f32 * samples = reinterpret_cast < f32 * > ( decoder . frame - > data [ channel_idx ] ) ;
for ( u32 in_sample_idx = 0 , out_sample_idx = ch_map [ channel_idx ] ; in_sample_idx < decoded_samples_num ; in_sample_idx + + , out_sample_idx + = nch_in )
2024-04-28 18:20:26 +02:00
{
2024-06-11 19:23:48 +02:00
const f32 sample = samples [ in_sample_idx ] ;
2024-04-28 18:20:26 +02:00
if ( sample > = 1.f )
{
2024-06-11 19:23:48 +02:00
output_s32 [ out_sample_idx ] = 0x007fffff ;
2024-04-28 18:20:26 +02:00
}
else if ( sample < = - 1.f )
{
2024-06-11 19:23:48 +02:00
output_s32 [ out_sample_idx ] = 0x00800000 ;
2024-04-28 18:20:26 +02:00
}
else
{
2024-06-11 19:23:48 +02:00
output_s32 [ out_sample_idx ] = static_cast < s32 > ( std : : floor ( sample * 0x00800000u ) ) & 0x00ffffff ;
2024-04-28 18:20:26 +02:00
}
}
}
break ;
case CELL_ADEC_ATRACX_WORD_SZ_32BIT :
2024-06-11 19:23:48 +02:00
for ( u32 channel_idx = 0 ; channel_idx < nch_in ; channel_idx + + )
2024-04-28 18:20:26 +02:00
{
2024-06-11 19:23:48 +02:00
const f32 * samples = reinterpret_cast < f32 * > ( decoder . frame - > data [ channel_idx ] ) ;
for ( u32 in_sample_idx = 0 , out_sample_idx = ch_map [ channel_idx ] ; in_sample_idx < decoded_samples_num ; in_sample_idx + + , out_sample_idx + = nch_in )
2024-04-28 18:20:26 +02:00
{
2024-06-11 19:23:48 +02:00
const f32 sample = samples [ in_sample_idx ] ;
2024-04-28 18:20:26 +02:00
if ( sample > = 1.f )
{
2024-06-11 19:23:48 +02:00
output_s32 [ out_sample_idx ] = INT32_MAX ;
2024-04-28 18:20:26 +02:00
}
else if ( sample < = - 1.f )
{
2024-06-11 19:23:48 +02:00
output_s32 [ out_sample_idx ] = INT32_MIN ;
2024-04-28 18:20:26 +02:00
}
else
{
2024-06-11 19:23:48 +02:00
output_s32 [ out_sample_idx ] = static_cast < s32 > ( std : : floor ( sample * 0x80000000u ) ) ;
2024-04-28 18:20:26 +02:00
}
}
}
}
first_decode = false ;
if ( error ! = CELL_OK )
{
// Block savestate creation during callbacks
std : : unique_lock savestate_lock { g_fxo - > get < hle_locks_t > ( ) , std : : try_to_lock } ;
if ( ! savestate_lock . owns_lock ( ) )
{
2024-05-07 20:16:26 +02:00
ppu . state + = cpu_flag : : again ;
2024-04-28 18:20:26 +02:00
return ;
}
skip_next_frame = true ;
notify_error . cbFunc ( ppu , error , notify_error . cbArg ) ;
}
}
// Block savestate creation during callbacks
std : : unique_lock savestate_lock { g_fxo - > get < hle_locks_t > ( ) , std : : try_to_lock } ;
if ( ! savestate_lock . owns_lock ( ) )
{
2024-05-07 20:16:26 +02:00
ppu . state + = cpu_flag : : again ;
2024-04-28 18:20:26 +02:00
return ;
}
// au_done and pcm_out callbacks are always called after a decode command, even if an error occurred
// The output always has to be consumed as well
notify_au_done . cbFunc ( ppu , cmd . pcm_handle , notify_au_done . cbArg ) ;
output_locked = true ;
2025-01-12 12:25:06 +01:00
ensure ( sys_mutex_unlock ( ppu , output_mutex ) = = CELL_OK ) ;
2024-04-28 18:20:26 +02:00
const u32 output_size = decoded_samples_num * ( decoder . bw_pcm & 0x7fu ) * decoder . nch_out ;
2025-04-05 21:50:45 +02:00
const vm : : var < CellAdecAtracXInfo > bsi_info { { decoder . sampling_freq , decoder . ch_config_idx , decoder . nbytes } } ;
2024-04-28 18:20:26 +02:00
2024-06-11 19:23:48 +02:00
const AdecCorrectPtsValueType correct_pts_type = [ & ]
2024-04-28 18:20:26 +02:00
{
2024-06-11 19:23:48 +02:00
switch ( decoder . sampling_freq )
{
case 32000u : return ADEC_CORRECT_PTS_VALUE_TYPE_ATRACX_32000Hz ;
case 44100u : return ADEC_CORRECT_PTS_VALUE_TYPE_ATRACX_44100Hz ;
case 48000u : return ADEC_CORRECT_PTS_VALUE_TYPE_ATRACX_48000Hz ;
2025-04-05 21:50:45 +02:00
default : return ADEC_CORRECT_PTS_VALUE_TYPE_UNSPECIFIED ;
2024-06-11 19:23:48 +02:00
}
} ( ) ;
2024-04-28 18:20:26 +02:00
notify_pcm_out . cbFunc ( ppu , cmd . pcm_handle , output , output_size , notify_pcm_out . cbArg , vm : : make_var < vm : : bcptr < void > > ( bsi_info ) , correct_pts_type , error ) ;
break ;
}
default :
fmt : : throw_exception ( " Invalid command " ) ;
}
}
}
template < AtracXdecCmdType type >
error_code AtracXdecContext : : send_command ( ppu_thread & ppu , auto & & . . . args )
{
2025-01-12 12:25:06 +01:00
auto & savestate = * ppu . optional_savestate_state ;
const bool signal = savestate . try_read < bool > ( ) . second ;
savestate . clear ( ) ;
2024-04-28 18:20:26 +02:00
2025-01-12 12:25:06 +01:00
if ( ! signal )
2024-04-28 18:20:26 +02:00
{
2025-01-12 12:25:06 +01:00
ensure ( sys_mutex_lock ( ppu , queue_mutex , 0 ) = = CELL_OK ) ;
if ( ppu . state & cpu_flag : : again )
{
return { } ;
}
2024-04-28 18:20:26 +02:00
if constexpr ( type = = AtracXdecCmdType : : close )
{
// Close command is only sent if the queue is empty on LLE
if ( ! cmd_queue . empty ( ) )
{
2025-01-12 12:25:06 +01:00
ensure ( sys_mutex_unlock ( ppu , queue_mutex ) = = CELL_OK ) ;
2024-04-28 18:20:26 +02:00
return { } ;
}
}
if ( cmd_queue . full ( ) )
{
2025-01-12 12:25:06 +01:00
ensure ( sys_mutex_unlock ( ppu , queue_mutex ) = = CELL_OK ) ;
2024-04-28 18:20:26 +02:00
return CELL_ADEC_ERROR_ATX_BUSY ;
}
cmd_queue . emplace ( std : : forward < AtracXdecCmdType > ( type ) , std : : forward < decltype ( args ) > ( args ) . . . ) ;
2025-01-12 12:25:06 +01:00
ensure ( sys_mutex_unlock ( ppu , queue_mutex ) = = CELL_OK ) ;
2024-04-28 18:20:26 +02:00
}
2025-01-12 12:25:06 +01:00
ensure ( sys_cond_signal ( ppu , queue_not_empty ) = = CELL_OK ) ;
if ( ppu . state & cpu_flag : : again )
{
savestate ( true ) ;
}
2024-04-28 18:20:26 +02:00
return CELL_OK ;
}
void atracXdecEntry ( ppu_thread & ppu , vm : : ptr < AtracXdecContext > atxdec )
{
atxdec - > decoder . alloc_avcodec ( ) ;
atxdec - > exec ( ppu ) ;
atxdec - > decoder . free_avcodec ( ) ;
2024-05-07 20:16:26 +02:00
if ( ppu . state & cpu_flag : : again )
2024-04-28 18:20:26 +02:00
{
2024-05-07 20:16:26 +02:00
// For savestates, save argument
ppu . syscall_args [ 0 ] = atxdec . addr ( ) ;
2024-04-28 18:20:26 +02:00
return ;
}
ppu_execute < & sys_ppu_thread_exit > ( ppu , CELL_OK ) ;
}
template < u32 nch_in >
error_code _CellAdecCoreOpGetMemSize_atracx ( vm : : ptr < CellAdecAttr > attr )
{
cellAtracXdec . notice ( " _CellAdecCoreOpGetMemSize_atracx<nch_in=%d>(attr=*0x%x) " , nch_in , attr ) ;
ensure ( ! ! attr ) ; // Not checked on LLE
constexpr u32 mem_size =
2025-04-05 21:50:45 +02:00
sizeof ( AtracXdecContext ) + 0x7f + ATXDEC_SPURS_STRUCTS_SIZE + 0x1d8 + atracXdecGetSpursMemSize ( nch_in ) + ATXDEC_SAMPLES_PER_FRAME * sizeof ( f32 ) * nch_in ;
2024-04-28 18:20:26 +02:00
2025-10-05 18:28:03 +02:00
attr - > workMemSize = rx : : alignUp ( mem_size , 0x80 ) ;
2024-04-28 18:20:26 +02:00
return CELL_OK ;
}
error_code _CellAdecCoreOpOpenExt_atracx ( ppu_thread & ppu , vm : : ptr < AtracXdecContext > handle , vm : : ptr < AdecNotifyAuDone > notifyAuDone , vm : : ptr < void > notifyAuDoneArg , vm : : ptr < AdecNotifyPcmOut > notifyPcmOut , vm : : ptr < void > notifyPcmOutArg ,
vm : : ptr < AdecNotifyError > notifyError , vm : : ptr < void > notifyErrorArg , vm : : ptr < AdecNotifySeqDone > notifySeqDone , vm : : ptr < void > notifySeqDoneArg , vm : : cptr < CellAdecResource > res , vm : : cptr < CellAdecResourceSpurs > spursRes )
{
std : : unique_lock savestate_lock { g_fxo - > get < hle_locks_t > ( ) , std : : try_to_lock } ;
if ( ! savestate_lock . owns_lock ( ) )
{
ppu . state + = cpu_flag : : again ;
return { } ;
}
cellAtracXdec . notice ( " _CellAdecCoreOpOpenExt_atracx(handle=*0x%x, notifyAuDone=*0x%x, notifyAuDoneArg=*0x%x, notifyPcmOut=*0x%x, notifyPcmOutArg=*0x%x, notifyError=*0x%x, notifyErrorArg=*0x%x, notifySeqDone=*0x%x, notifySeqDoneArg=*0x%x, res=*0x%x, spursRes=*0x%x) " ,
handle , notifyAuDone , notifyAuDoneArg , notifyPcmOut , notifyPcmOutArg , notifyError , notifyErrorArg , notifySeqDone , notifySeqDoneArg , res , spursRes ) ;
2025-04-05 21:50:45 +02:00
ensure ( ! ! handle & & ! ! res ) ; // Not checked on LLE
ensure ( handle . aligned ( 0x80 ) ) ; // On LLE, this functions doesn't check the alignment or aligns the address itself. The address should already be aligned to 128 bytes by cellAdec
2024-05-07 20:16:26 +02:00
ensure ( ! ! notifyAuDone & & ! ! notifyAuDoneArg & & ! ! notifyPcmOut & & ! ! notifyPcmOutArg & & ! ! notifyError & & ! ! notifyErrorArg & & ! ! notifySeqDone & & ! ! notifySeqDoneArg ) ; // These should always be set by cellAdec
2024-04-28 18:20:26 +02:00
2024-05-07 20:16:26 +02:00
write_to_ptr ( handle . get_ptr ( ) , AtracXdecContext ( notifyAuDone , notifyAuDoneArg , notifyPcmOut , notifyPcmOutArg , notifyError , notifyErrorArg , notifySeqDone , notifySeqDoneArg ,
2025-10-05 18:28:03 +02:00
vm : : bptr < u8 > : : make ( handle . addr ( ) + rx : : alignUp ( static_cast < u32 > ( sizeof ( AtracXdecContext ) ) , 0x80 ) + ATXDEC_SPURS_STRUCTS_SIZE ) ) ) ;
2024-04-28 18:20:26 +02:00
2025-04-05 21:50:45 +02:00
const vm : : var < sys_mutex_attribute_t > mutex_attr { { SYS_SYNC_PRIORITY , SYS_SYNC_NOT_RECURSIVE , SYS_SYNC_NOT_PROCESS_SHARED , SYS_SYNC_NOT_ADAPTIVE , 0 , 0 , 0 , { " _atd001 " _u64 } } } ;
const vm : : var < sys_cond_attribute_t > cond_attr { { SYS_SYNC_NOT_PROCESS_SHARED , 0 , 0 , { " _atd002 " _u64 } } } ;
2025-01-12 12:25:06 +01:00
ensure ( sys_mutex_create ( ppu , handle . ptr ( & AtracXdecContext : : queue_mutex ) , mutex_attr ) = = CELL_OK ) ;
ensure ( sys_cond_create ( ppu , handle . ptr ( & AtracXdecContext : : queue_not_empty ) , handle - > queue_mutex , cond_attr ) = = CELL_OK ) ;
mutex_attr - > name_u64 = " _atd003 " _u64 ;
cond_attr - > name_u64 = " _atd004 " _u64 ;
ensure ( sys_mutex_create ( ppu , handle . ptr ( & AtracXdecContext : : run_thread_mutex ) , mutex_attr ) = = CELL_OK ) ;
ensure ( sys_cond_create ( ppu , handle . ptr ( & AtracXdecContext : : run_thread_cond ) , handle - > run_thread_mutex , cond_attr ) = = CELL_OK ) ;
mutex_attr - > name_u64 = " _atd005 " _u64 ;
cond_attr - > name_u64 = " _atd006 " _u64 ;
ensure ( sys_mutex_create ( ppu , handle . ptr ( & AtracXdecContext : : output_mutex ) , mutex_attr ) = = CELL_OK ) ;
ensure ( sys_cond_create ( ppu , handle . ptr ( & AtracXdecContext : : output_consumed ) , handle - > output_mutex , cond_attr ) = = CELL_OK ) ;
ensure ( sys_mutex_lock ( ppu , handle - > output_mutex , 0 ) = = CELL_OK ) ;
handle - > output_locked = false ;
ensure ( sys_cond_signal ( ppu , handle - > output_consumed ) = = CELL_OK ) ;
ensure ( sys_mutex_unlock ( ppu , handle - > output_mutex ) = = CELL_OK ) ;
2024-04-28 18:20:26 +02:00
const vm : : var < char [ ] > _name = vm : : make_str ( " HLE ATRAC3plus decoder " ) ;
const auto entry = g_fxo - > get < ppu_function_manager > ( ) . func_addr ( FIND_FUNC ( atracXdecEntry ) ) ;
ppu_execute < & sys_ppu_thread_create > ( ppu , handle . ptr ( & AtracXdecContext : : thread_id ) , entry , handle . addr ( ) , + res - > ppuThreadPriority , + res - > ppuThreadStackSize , SYS_PPU_THREAD_CREATE_JOINABLE , + _name ) ;
return CELL_OK ;
}
error_code _CellAdecCoreOpOpen_atracx ( ppu_thread & ppu , vm : : ptr < AtracXdecContext > handle , vm : : ptr < AdecNotifyAuDone > notifyAuDone , vm : : ptr < void > notifyAuDoneArg , vm : : ptr < AdecNotifyPcmOut > notifyPcmOut , vm : : ptr < void > notifyPcmOutArg ,
vm : : ptr < AdecNotifyError > notifyError , vm : : ptr < void > notifyErrorArg , vm : : ptr < AdecNotifySeqDone > notifySeqDone , vm : : ptr < void > notifySeqDoneArg , vm : : cptr < CellAdecResource > res )
{
cellAtracXdec . notice ( " _CellAdecCoreOpOpen_atracx(handle=*0x%x, notifyAuDone=*0x%x, notifyAuDoneArg=*0x%x, notifyPcmOut=*0x%x, notifyPcmOutArg=*0x%x, notifyError=*0x%x, notifyErrorArg=*0x%x, notifySeqDone=*0x%x, notifySeqDoneArg=*0x%x, res=*0x%x) " ,
handle , notifyAuDone , notifyAuDoneArg , notifyPcmOut , notifyPcmOutArg , notifyError , notifyErrorArg , notifySeqDone , notifySeqDoneArg , res ) ;
return _CellAdecCoreOpOpenExt_atracx ( ppu , handle , notifyAuDone , notifyAuDoneArg , notifyPcmOut , notifyPcmOutArg , notifyError , notifyErrorArg , notifySeqDone , notifySeqDoneArg , res , vm : : null ) ;
}
error_code _CellAdecCoreOpClose_atracx ( ppu_thread & ppu , vm : : ptr < AtracXdecContext > handle )
{
std : : unique_lock savestate_lock { g_fxo - > get < hle_locks_t > ( ) , std : : try_to_lock } ;
if ( ! savestate_lock . owns_lock ( ) )
{
ppu . state + = cpu_flag : : again ;
return { } ;
}
cellAtracXdec . notice ( " _CellAdecCoreOpClose_atracx(handle=*0x%x) " , handle ) ;
ensure ( ! ! handle ) ; // Not checked on LLE
2025-01-12 12:25:06 +01:00
ensure ( sys_mutex_lock ( ppu , handle - > run_thread_mutex , 0 ) = = CELL_OK ) ;
2024-04-28 18:20:26 +02:00
handle - > run_thread = false ;
2025-01-12 12:25:06 +01:00
ensure ( sys_mutex_unlock ( ppu , handle - > run_thread_mutex ) = = CELL_OK ) ;
2024-04-28 18:20:26 +02:00
handle - > send_command < AtracXdecCmdType : : close > ( ppu ) ;
2025-01-12 12:25:06 +01:00
ensure ( sys_mutex_lock ( ppu , handle - > output_mutex , 0 ) = = CELL_OK ) ;
handle - > output_locked = false ;
ensure ( sys_mutex_unlock ( ppu , handle - > output_mutex ) = = CELL_OK ) ;
ensure ( sys_cond_signal ( ppu , handle - > output_consumed ) = = CELL_OK ) ;
2024-04-28 18:20:26 +02:00
2025-01-12 12:25:06 +01:00
vm : : var < u64 > thread_ret ;
ensure ( sys_ppu_thread_join ( ppu , static_cast < u32 > ( handle - > thread_id ) , + thread_ret ) = = CELL_OK ) ;
2024-04-28 18:20:26 +02:00
2025-01-12 12:25:06 +01:00
error_code ret = sys_cond_destroy ( ppu , handle - > queue_not_empty ) ;
ret = ret ? ret : sys_cond_destroy ( ppu , handle - > run_thread_cond ) ;
ret = ret ? ret : sys_cond_destroy ( ppu , handle - > output_consumed ) ;
ret = ret ? ret : sys_mutex_destroy ( ppu , handle - > queue_mutex ) ;
ret = ret ? ret : sys_mutex_destroy ( ppu , handle - > run_thread_mutex ) ;
ret = ret ? ret : sys_mutex_destroy ( ppu , handle - > output_mutex ) ;
2024-04-28 18:20:26 +02:00
2025-01-12 12:25:06 +01:00
return ret ! = CELL_OK ? static_cast < error_code > ( CELL_ADEC_ERROR_FATAL ) : CELL_OK ;
2024-04-28 18:20:26 +02:00
}
error_code _CellAdecCoreOpStartSeq_atracx ( ppu_thread & ppu , vm : : ptr < AtracXdecContext > handle , vm : : cptr < CellAdecParamAtracX > atracxParam )
{
cellAtracXdec . notice ( " _CellAdecCoreOpStartSeq_atracx(handle=*0x%x, atracxParam=*0x%x) " , handle , atracxParam ) ;
ensure ( ! ! handle & & ! ! atracxParam ) ; // Not checked on LLE
cellAtracXdec . notice ( " _CellAdecCoreOpStartSeq_atracx(): sampling_freq=%d, ch_config_idx=%d, nch_out=%d, nbytes=0x%x, extra_config_data=0x%08x, bw_pcm=0x%x, downmix_flag=%d, au_includes_ats_hdr_flg=%d " ,
atracxParam - > sampling_freq , atracxParam - > ch_config_idx , atracxParam - > nch_out , atracxParam - > nbytes , std : : bit_cast < u32 > ( atracxParam - > extra_config_data ) , atracxParam - > bw_pcm , atracxParam - > downmix_flag , atracxParam - > au_includes_ats_hdr_flg ) ;
return handle - > send_command < AtracXdecCmdType : : start_seq > ( ppu , * atracxParam ) ;
}
error_code _CellAdecCoreOpEndSeq_atracx ( ppu_thread & ppu , vm : : ptr < AtracXdecContext > handle )
{
cellAtracXdec . notice ( " _CellAdecCoreOpEndSeq_atracx(handle=*0x%x) " , handle ) ;
ensure ( ! ! handle ) ; // Not checked on LLE
return handle - > send_command < AtracXdecCmdType : : end_seq > ( ppu ) ;
}
error_code _CellAdecCoreOpDecodeAu_atracx ( ppu_thread & ppu , vm : : ptr < AtracXdecContext > handle , s32 pcmHandle , vm : : cptr < CellAdecAuInfo > auInfo )
{
cellAtracXdec . trace ( " _CellAdecCoreOpDecodeAu_atracx(handle=*0x%x, pcmHandle=%d, auInfo=*0x%x) " , handle , pcmHandle , auInfo ) ;
ensure ( ! ! handle & & ! ! auInfo ) ; // Not checked on LLE
cellAtracXdec . trace ( " _CellAdecCoreOpDecodeAu_atracx(): startAddr=*0x%x, size=0x%x, pts=%lld, userData=0x%llx " , auInfo - > startAddr , auInfo - > size , std : : bit_cast < be_t < u64 > > ( auInfo - > pts ) , auInfo - > userData ) ;
return handle - > send_command < AtracXdecCmdType : : decode_au > ( ppu , pcmHandle , * auInfo ) ;
}
2024-08-05 17:14:48 +02:00
void _CellAdecCoreOpGetVersion_atracx ( vm : : ptr < be_t < u32 , 1 > > version )
2024-04-28 18:20:26 +02:00
{
cellAtracXdec . notice ( " _CellAdecCoreOpGetVersion_atracx(version=*0x%x) " , version ) ;
ensure ( ! ! version ) ; // Not checked on LLE
2024-08-05 17:14:48 +02:00
* version = 0x01020000 ;
2024-04-28 18:20:26 +02:00
}
error_code _CellAdecCoreOpRealign_atracx ( vm : : ptr < AtracXdecContext > handle , vm : : ptr < void > outBuffer , vm : : cptr < void > pcmStartAddr )
{
cellAtracXdec . trace ( " _CellAdecCoreOpRealign_atracx(handle=*0x%x, outBuffer=*0x%x, pcmStartAddr=*0x%x) " , handle , outBuffer , pcmStartAddr ) ;
if ( outBuffer )
{
ensure ( ! ! handle & & ! ! pcmStartAddr ) ; // Not checked on LLE
2024-05-07 20:16:26 +02:00
ensure ( vm : : check_addr ( outBuffer . addr ( ) , vm : : page_info_t : : page_writable , handle - > decoder . pcm_output_size ) ) ;
2024-04-28 18:20:26 +02:00
std : : memcpy ( outBuffer . get_ptr ( ) , pcmStartAddr . get_ptr ( ) , handle - > decoder . pcm_output_size ) ;
}
return CELL_OK ;
}
error_code _CellAdecCoreOpReleasePcm_atracx ( ppu_thread & ppu , vm : : ptr < AtracXdecContext > handle , s32 pcmHandle , vm : : cptr < void > outBuffer )
{
cellAtracXdec . trace ( " _CellAdecCoreOpReleasePcm_atracx(handle=*0x%x, pcmHandle=%d, outBuffer=*0x%x) " , handle , pcmHandle , outBuffer ) ;
ensure ( ! ! handle ) ; // Not checked on LLE
2025-01-12 12:25:06 +01:00
auto & savestate = * ppu . optional_savestate_state ;
const bool signal = savestate . try_read < bool > ( ) . second ;
savestate . clear ( ) ;
if ( ! signal )
{
ensure ( sys_mutex_lock ( ppu , handle - > output_mutex , 0 ) = = CELL_OK ) ;
if ( ppu . state & cpu_flag : : again )
{
return { } ;
}
handle - > output_locked = false ;
}
ensure ( sys_cond_signal ( ppu , handle - > output_consumed ) = = CELL_OK ) ;
if ( ppu . state & cpu_flag : : again )
{
savestate ( true ) ;
return { } ;
}
ensure ( sys_mutex_unlock ( ppu , handle - > output_mutex ) = = CELL_OK ) ;
2024-04-28 18:20:26 +02:00
return CELL_OK ;
}
s32 _CellAdecCoreOpGetPcmHandleNum_atracx ( )
{
cellAtracXdec . notice ( " _CellAdecCoreOpGetPcmHandleNum_atracx() " ) ;
return 3 ;
}
u32 _CellAdecCoreOpGetBsiInfoSize_atracx ( )
{
cellAtracXdec . notice ( " _CellAdecCoreOpGetBsiInfoSize_atracx() " ) ;
return sizeof ( CellAdecAtracXInfo ) ;
}
static void init_gvar ( vm : : gvar < CellAdecCoreOps > & var )
{
var - > open . set ( g_fxo - > get < ppu_function_manager > ( ) . func_addr ( FIND_FUNC ( _CellAdecCoreOpOpen_atracx ) ) ) ;
var - > close . set ( g_fxo - > get < ppu_function_manager > ( ) . func_addr ( FIND_FUNC ( _CellAdecCoreOpClose_atracx ) ) ) ;
var - > startSeq . set ( g_fxo - > get < ppu_function_manager > ( ) . func_addr ( FIND_FUNC ( _CellAdecCoreOpStartSeq_atracx ) ) ) ;
var - > endSeq . set ( g_fxo - > get < ppu_function_manager > ( ) . func_addr ( FIND_FUNC ( _CellAdecCoreOpEndSeq_atracx ) ) ) ;
var - > decodeAu . set ( g_fxo - > get < ppu_function_manager > ( ) . func_addr ( FIND_FUNC ( _CellAdecCoreOpDecodeAu_atracx ) ) ) ;
var - > getVersion . set ( g_fxo - > get < ppu_function_manager > ( ) . func_addr ( FIND_FUNC ( _CellAdecCoreOpGetVersion_atracx ) ) ) ;
var - > realign . set ( g_fxo - > get < ppu_function_manager > ( ) . func_addr ( FIND_FUNC ( _CellAdecCoreOpRealign_atracx ) ) ) ;
var - > releasePcm . set ( g_fxo - > get < ppu_function_manager > ( ) . func_addr ( FIND_FUNC ( _CellAdecCoreOpReleasePcm_atracx ) ) ) ;
var - > getPcmHandleNum . set ( g_fxo - > get < ppu_function_manager > ( ) . func_addr ( FIND_FUNC ( _CellAdecCoreOpGetPcmHandleNum_atracx ) ) ) ;
var - > getBsiInfoSize . set ( g_fxo - > get < ppu_function_manager > ( ) . func_addr ( FIND_FUNC ( _CellAdecCoreOpGetBsiInfoSize_atracx ) ) ) ;
var - > openExt . set ( g_fxo - > get < ppu_function_manager > ( ) . func_addr ( FIND_FUNC ( _CellAdecCoreOpOpenExt_atracx ) ) ) ;
}
DECLARE ( ppu_module_manager : : cellAtracXdec ) ( " cellAtracXdec " , [ ] ( )
{
2025-04-05 21:50:45 +02:00
REG_VNID ( cellAtracXdec , 0x076b33ab , g_cell_adec_core_ops_atracx2ch ) . init = [ ] ( )
{
g_cell_adec_core_ops_atracx2ch - > getMemSize . set ( g_fxo - > get < ppu_function_manager > ( ) . func_addr ( FIND_FUNC ( _CellAdecCoreOpGetMemSize_atracx < 2 > ) ) ) ;
init_gvar ( g_cell_adec_core_ops_atracx2ch ) ;
} ;
REG_VNID ( cellAtracXdec , 0x1d210eaa , g_cell_adec_core_ops_atracx6ch ) . init = [ ] ( )
{
g_cell_adec_core_ops_atracx6ch - > getMemSize . set ( g_fxo - > get < ppu_function_manager > ( ) . func_addr ( FIND_FUNC ( _CellAdecCoreOpGetMemSize_atracx < 6 > ) ) ) ;
init_gvar ( g_cell_adec_core_ops_atracx6ch ) ;
} ;
REG_VNID ( cellAtracXdec , 0xe9a86e54 , g_cell_adec_core_ops_atracx8ch ) . init = [ ] ( )
{
g_cell_adec_core_ops_atracx8ch - > getMemSize . set ( g_fxo - > get < ppu_function_manager > ( ) . func_addr ( FIND_FUNC ( _CellAdecCoreOpGetMemSize_atracx < 8 > ) ) ) ;
init_gvar ( g_cell_adec_core_ops_atracx8ch ) ;
} ;
REG_VNID ( cellAtracXdec , 0x4944af9a , g_cell_adec_core_ops_atracx ) . init = [ ] ( )
{
g_cell_adec_core_ops_atracx - > getMemSize . set ( g_fxo - > get < ppu_function_manager > ( ) . func_addr ( FIND_FUNC ( _CellAdecCoreOpGetMemSize_atracx < 8 > ) ) ) ;
init_gvar ( g_cell_adec_core_ops_atracx ) ;
} ;
REG_HIDDEN_FUNC ( _CellAdecCoreOpGetMemSize_atracx < 2 > ) ;
REG_HIDDEN_FUNC ( _CellAdecCoreOpGetMemSize_atracx < 6 > ) ;
REG_HIDDEN_FUNC ( _CellAdecCoreOpGetMemSize_atracx < 8 > ) ;
REG_HIDDEN_FUNC ( _CellAdecCoreOpOpen_atracx ) ;
REG_HIDDEN_FUNC ( _CellAdecCoreOpClose_atracx ) ;
REG_HIDDEN_FUNC ( _CellAdecCoreOpStartSeq_atracx ) ;
REG_HIDDEN_FUNC ( _CellAdecCoreOpEndSeq_atracx ) ;
REG_HIDDEN_FUNC ( _CellAdecCoreOpDecodeAu_atracx ) ;
REG_HIDDEN_FUNC ( _CellAdecCoreOpGetVersion_atracx ) ;
REG_HIDDEN_FUNC ( _CellAdecCoreOpRealign_atracx ) ;
REG_HIDDEN_FUNC ( _CellAdecCoreOpReleasePcm_atracx ) ;
REG_HIDDEN_FUNC ( _CellAdecCoreOpGetPcmHandleNum_atracx ) ;
REG_HIDDEN_FUNC ( _CellAdecCoreOpGetBsiInfoSize_atracx ) ;
REG_HIDDEN_FUNC ( _CellAdecCoreOpOpenExt_atracx ) ;
REG_HIDDEN_FUNC ( atracXdecEntry ) ;
} ) ;