2018-12-20 23:35:49 +01:00
# pragma once
2013-12-27 11:55:11 +01:00
2016-03-21 20:43:03 +01:00
# include "Utilities/Thread.h"
2018-12-20 23:35:49 +01:00
# include "Emu/Memory/vm.h"
2018-12-16 18:40:50 +01:00
# include "Emu/Audio/AudioBackend.h"
2018-12-20 23:35:49 +01:00
# include "Emu/Audio/AudioDumper.h"
2016-03-21 20:43:03 +01:00
2014-03-08 00:15:39 +01:00
// Error codes
2017-09-20 12:01:57 +02:00
enum CellAudioError : u32
2014-03-08 00:15:39 +01:00
{
2014-04-04 15:25:38 +02:00
CELL_AUDIO_ERROR_ALREADY_INIT = 0x80310701 ,
CELL_AUDIO_ERROR_AUDIOSYSTEM = 0x80310702 ,
CELL_AUDIO_ERROR_NOT_INIT = 0x80310703 ,
CELL_AUDIO_ERROR_PARAM = 0x80310704 ,
CELL_AUDIO_ERROR_PORT_FULL = 0x80310705 ,
CELL_AUDIO_ERROR_PORT_ALREADY_RUN = 0x80310706 ,
CELL_AUDIO_ERROR_PORT_NOT_OPEN = 0x80310707 ,
CELL_AUDIO_ERROR_PORT_NOT_RUN = 0x80310708 ,
CELL_AUDIO_ERROR_TRANS_EVENT = 0x80310709 ,
CELL_AUDIO_ERROR_PORT_OPEN = 0x8031070a ,
CELL_AUDIO_ERROR_SHAREDMEMORY = 0x8031070b ,
CELL_AUDIO_ERROR_MUTEX = 0x8031070c ,
CELL_AUDIO_ERROR_EVENT_QUEUE = 0x8031070d ,
CELL_AUDIO_ERROR_AUDIOSYSTEM_NOT_FOUND = 0x8031070e ,
CELL_AUDIO_ERROR_TAG_NOT_FOUND = 0x8031070f ,
2014-03-08 00:15:39 +01:00
} ;
// constants
2013-12-27 11:55:11 +01:00
enum
{
2014-04-04 15:25:38 +02:00
CELL_AUDIO_BLOCK_16 = 16 ,
CELL_AUDIO_BLOCK_8 = 8 ,
CELL_AUDIO_BLOCK_SAMPLES = 256 ,
CELL_AUDIO_CREATEEVENTFLAG_SPU = 0x00000001 ,
CELL_AUDIO_EVENT_HEADPHONE = 1 ,
2018-02-09 15:49:37 +01:00
CELL_AUDIO_EVENT_MIX = 0 ,
2014-04-04 15:25:38 +02:00
CELL_AUDIO_EVENTFLAG_BEFOREMIX = 0x80000000 ,
CELL_AUDIO_EVENTFLAG_DECIMATE_2 = 0x08000000 ,
CELL_AUDIO_EVENTFLAG_DECIMATE_4 = 0x10000000 ,
CELL_AUDIO_EVENTFLAG_HEADPHONE = 0x20000000 ,
CELL_AUDIO_EVENTFLAG_NOMIX = 0x40000000 ,
CELL_AUDIO_MAX_PORT = 4 ,
CELL_AUDIO_MAX_PORT_2 = 8 ,
2018-02-09 15:49:37 +01:00
CELL_AUDIO_MISC_ACCVOL_ALLDEVICE = 0x0000ffffUL ,
2014-04-04 15:25:38 +02:00
CELL_AUDIO_PERSONAL_DEVICE_PRIMARY = 0x8000 ,
CELL_AUDIO_PORT_2CH = 2 ,
CELL_AUDIO_PORT_8CH = 8 ,
CELL_AUDIO_PORTATTR_BGM = 0x0000000000000010ULL ,
CELL_AUDIO_PORTATTR_INITLEVEL = 0x0000000000001000ULL ,
CELL_AUDIO_PORTATTR_OUT_NO_ROUTE = 0x0000000000100000ULL ,
CELL_AUDIO_PORTATTR_OUT_PERSONAL_0 = 0x0000000001000000ULL ,
CELL_AUDIO_PORTATTR_OUT_PERSONAL_1 = 0x0000000002000000ULL ,
CELL_AUDIO_PORTATTR_OUT_PERSONAL_2 = 0x0000000004000000ULL ,
CELL_AUDIO_PORTATTR_OUT_PERSONAL_3 = 0x0000000008000000ULL ,
2018-02-09 15:49:37 +01:00
CELL_AUDIO_PORTATTR_OUT_SECONDARY = 0x0000000000000001ULL ,
2014-04-04 15:25:38 +02:00
CELL_AUDIO_STATUS_CLOSE = 0x1010 ,
CELL_AUDIO_STATUS_READY = 1 ,
CELL_AUDIO_STATUS_RUN = 2 ,
2013-12-27 11:55:11 +01:00
} ;
2014-03-08 00:15:39 +01:00
//libaudio datatypes
struct CellAudioPortParam
2018-02-09 15:49:37 +01:00
{
2014-03-08 00:15:39 +01:00
be_t < u64 > nChannel ;
be_t < u64 > nBlock ;
be_t < u64 > attr ;
be_t < float > level ;
2018-02-09 15:49:37 +01:00
} ;
2014-03-08 00:15:39 +01:00
struct CellAudioPortConfig
2018-02-09 15:49:37 +01:00
{
2016-03-21 20:43:03 +01:00
vm : : bptr < u64 > readIndexAddr ;
2014-03-08 00:15:39 +01:00
be_t < u32 > status ;
be_t < u64 > nChannel ;
be_t < u64 > nBlock ;
be_t < u32 > portSize ;
be_t < u32 > portAddr ;
} ;
2015-01-16 15:36:53 +01:00
enum : u32
{
AUDIO_PORT_COUNT = 8 ,
2018-12-20 23:35:49 +01:00
AUDIO_MAX_BLOCK_COUNT = 32 ,
AUDIO_MAX_CHANNELS_COUNT = 8 ,
AUDIO_PORT_OFFSET = AUDIO_BUFFER_SAMPLES * AUDIO_MAX_BLOCK_COUNT * AUDIO_MAX_CHANNELS_COUNT * sizeof ( f32 ) ,
EXTRA_AUDIO_BUFFERS = 8 ,
MAX_AUDIO_EVENT_QUEUES = 64 ,
AUDIO_BLOCK_SIZE_2CH = 2 * AUDIO_BUFFER_SAMPLES ,
AUDIO_BLOCK_SIZE_8CH = 8 * AUDIO_BUFFER_SAMPLES ,
PORT_BUFFER_TAG_COUNT = 4 ,
PORT_BUFFER_TAG_LAST_2CH = AUDIO_BLOCK_SIZE_2CH - 1 ,
PORT_BUFFER_TAG_DELTA_2CH = PORT_BUFFER_TAG_LAST_2CH / ( PORT_BUFFER_TAG_COUNT - 1 ) ,
PORT_BUFFER_TAG_FIRST_2CH = PORT_BUFFER_TAG_LAST_2CH % ( PORT_BUFFER_TAG_COUNT - 1 ) ,
PORT_BUFFER_TAG_LAST_8CH = AUDIO_BLOCK_SIZE_8CH - 1 ,
PORT_BUFFER_TAG_DELTA_8CH = PORT_BUFFER_TAG_LAST_8CH / ( PORT_BUFFER_TAG_COUNT - 1 ) ,
PORT_BUFFER_TAG_FIRST_8CH = PORT_BUFFER_TAG_LAST_8CH % ( PORT_BUFFER_TAG_COUNT - 1 ) ,
2015-01-16 15:36:53 +01:00
} ;
2016-03-21 20:43:03 +01:00
enum class audio_port_state : u32
2015-01-16 15:36:53 +01:00
{
2016-03-21 20:43:03 +01:00
closed ,
opened ,
started ,
2015-01-16 15:36:53 +01:00
} ;
2016-03-21 20:43:03 +01:00
struct audio_port
2014-03-08 00:15:39 +01:00
{
2018-10-01 19:58:34 +02:00
atomic_t < audio_port_state > state = audio_port_state : : closed ;
2015-01-16 15:36:53 +01:00
2016-03-21 20:43:03 +01:00
u32 number ;
vm : : ptr < char > addr { } ;
vm : : ptr < u64 > index { } ;
2015-11-26 09:06:29 +01:00
2018-12-20 23:35:49 +01:00
u32 num_channels ;
u32 num_blocks ;
2014-03-08 00:15:39 +01:00
u64 attr ;
2018-12-20 23:35:49 +01:00
u64 cur_pos ;
u64 global_counter ; // copy of global counter
u64 active_counter ;
2014-12-27 18:25:51 +01:00
u32 size ;
2018-12-20 23:35:49 +01:00
u64 timestamp ; // copy of global timestamp
2015-04-12 03:36:25 +02:00
2016-03-21 20:43:03 +01:00
struct alignas ( 8 ) level_set_t
2015-04-12 03:36:25 +02:00
{
float value ;
float inc ;
} ;
2018-02-09 15:49:37 +01:00
float level ;
2015-07-08 17:01:59 +02:00
atomic_t < level_set_t > level_set ;
2018-12-20 23:35:49 +01:00
u32 block_size ( ) const
{
return num_channels * AUDIO_BUFFER_SAMPLES ;
}
u32 buf_size ( ) const
{
return block_size ( ) * sizeof ( float ) ;
}
u32 position ( s32 offset = 0 ) const
{
s32 ofs = ( offset % num_blocks ) + num_blocks ;
return ( cur_pos + ofs ) % num_blocks ;
}
u32 buf_addr ( s32 offset = 0 ) const
{
return addr . addr ( ) + position ( offset ) * buf_size ( ) ;
}
to_be_t < float > * get_vm_ptr ( s32 offset = 0 ) const
{
return vm : : _ptr < f32 > ( buf_addr ( offset ) ) ;
}
// Tags
u32 prev_touched_tag_nr ;
f32 tag_backup [ AUDIO_MAX_BLOCK_COUNT ] [ PORT_BUFFER_TAG_COUNT ] = { 0 } ;
constexpr static bool is_tag ( float val ) ;
void tag ( s32 offset = 0 ) ;
void apply_tag_backups ( s32 offset = 0 ) ;
} ;
2018-12-16 18:40:50 +01:00
struct cell_audio_config
{
const s64 period_comparison_margin = 100 ; // When comparing the current period time with the desired period, if it is below this number of usecs we do not wait any longer
const u32 audio_channels = AudioBackend : : get_channels ( ) ;
const u32 audio_sampling_rate = AudioBackend : : get_sampling_rate ( ) ;
const u64 audio_block_period = AUDIO_BUFFER_SAMPLES * 1000000 / audio_sampling_rate ;
const u64 desired_buffer_duration = g_cfg . audio . enable_buffering ? g_cfg . audio . desired_buffer_duration : 0 ;
const u32 audio_buffer_length = AUDIO_BUFFER_SAMPLES * audio_channels ;
const u32 audio_buffer_size = audio_buffer_length * sizeof ( f32 ) ;
const bool buffering_enabled = g_cfg . audio . enable_buffering & & ( desired_buffer_duration > = audio_block_period ) ;
const u64 minimum_block_period = audio_block_period / 2 ; // the block period will not be dynamically lowered below this value (usecs)
const u64 maximum_block_period = audio_block_period + ( audio_block_period - minimum_block_period ) ; // the block period will not be dynamically increased above this value (usecs)
const u32 desired_full_buffers = buffering_enabled ? static_cast < u32 > ( desired_buffer_duration / audio_block_period ) + 1 : 1 ;
const u32 num_allocated_buffers = desired_full_buffers + EXTRA_AUDIO_BUFFERS ; // number of ringbuffer buffers
} ;
2018-12-20 23:35:49 +01:00
class audio_ringbuffer
{
private :
2018-12-16 18:40:50 +01:00
const std : : shared_ptr < AudioBackend > backend ;
const cell_audio_config & cfg ;
2018-12-20 23:35:49 +01:00
const u32 buf_sz ;
std : : unique_ptr < AudioDumper > m_dump ;
std : : unique_ptr < float [ ] > buffer [ MAX_AUDIO_BUFFERS ] ;
const float silence_buffer [ 8 * AUDIO_BUFFER_SAMPLES ] = { 0 } ;
bool backend_open = false ;
bool playing = false ;
bool emu_paused = false ;
2018-12-16 18:40:50 +01:00
u32 backend_capabilities ;
2018-12-20 23:35:49 +01:00
u64 update_timestamp = 0 ;
u64 play_timestamp = 0 ;
u64 last_remainder = 0 ;
u64 enqueued_samples = 0 ;
2018-12-16 18:40:50 +01:00
u32 cur_pos = 0 ;
bool backend_is_playing ( ) const
{
return ( backend_capabilities & AudioBackend : : IS_PLAYING ) ? backend - > IsPlaying ( ) : playing ;
}
2018-12-20 23:35:49 +01:00
public :
2018-12-16 18:40:50 +01:00
audio_ringbuffer ( cell_audio_config & cfg ) ;
2018-12-20 23:35:49 +01:00
~ audio_ringbuffer ( ) ;
void play ( ) ;
void enqueue ( const float * in_buffer = nullptr ) ;
void flush ( ) ;
u64 update ( ) ;
void enqueue_silence ( u32 buf_count = 1 ) ;
float * get_buffer ( u32 num ) const
{
2018-12-16 18:40:50 +01:00
AUDIT ( num < cfg . num_allocated_buffers ) ;
2018-12-20 23:35:49 +01:00
AUDIT ( buffer [ num ] . get ( ) ! = nullptr ) ;
return buffer [ num ] . get ( ) ;
}
u64 get_timestamp ( ) const
{
return get_system_time ( ) - Emu . GetPauseTime ( ) ;
}
float * get_current_buffer ( ) const
{
2018-12-16 18:40:50 +01:00
return get_buffer ( cur_pos ) ;
2018-12-20 23:35:49 +01:00
}
u64 get_enqueued_samples ( ) const
{
2018-12-16 18:40:50 +01:00
AUDIT ( cfg . buffering_enabled ) ;
2018-12-20 23:35:49 +01:00
return enqueued_samples ;
}
bool is_playing ( ) const
{
return playing ;
}
2018-12-16 18:40:50 +01:00
u32 capabilities ( ) const
{
return backend_capabilities ;
}
2014-03-08 00:15:39 +01:00
} ;
2018-12-20 23:35:49 +01:00
class cell_audio_thread
2014-03-08 00:15:39 +01:00
{
2018-10-01 19:58:34 +02:00
vm : : ptr < char > m_buffer ;
vm : : ptr < u64 > m_indexes ;
2015-11-26 09:06:29 +01:00
2018-12-20 23:35:49 +01:00
std : : unique_ptr < audio_ringbuffer > ringbuffer ;
void reset_ports ( s32 offset = 0 ) ;
void advance ( u64 timestamp , bool reset = true ) ;
std : : tuple < u32 , u32 , u32 , u32 > count_port_buffer_tags ( ) ;
template < bool downmix_to_2ch > void mix ( float * out_buffer , s32 offset = 0 ) ;
void finish_port_volume_stepping ( ) ;
constexpr static u64 get_thread_wait_delay ( u64 time_left )
{
return ( time_left > 1000 ) ? time_left - 750 : 100 ;
}
2016-03-21 20:43:03 +01:00
public :
2018-12-16 18:40:50 +01:00
cell_audio_config cfg ;
2015-01-16 15:36:53 +01:00
std : : vector < u64 > keys ;
2018-12-20 23:35:49 +01:00
std : : array < audio_port , AUDIO_PORT_COUNT > ports ;
u64 m_last_period_end = 0 ;
u64 m_counter = 0 ;
u64 m_start_time = 0 ;
u64 m_dynamic_period = 0 ;
2014-03-08 00:15:39 +01:00
2018-10-02 10:40:37 +02:00
void operator ( ) ( ) ;
2017-02-05 14:35:10 +01:00
2018-12-20 23:35:49 +01:00
cell_audio_thread ( vm : : ptr < char > buf , vm : : ptr < u64 > ind )
2018-10-01 19:58:34 +02:00
: m_buffer ( buf )
, m_indexes ( ind )
2017-01-28 00:42:31 +01:00
{
2018-10-01 19:58:34 +02:00
for ( u32 i = 0 ; i < AUDIO_PORT_COUNT ; i + + )
{
ports [ i ] . number = i ;
ports [ i ] . addr = m_buffer + AUDIO_PORT_OFFSET * i ;
ports [ i ] . index = m_indexes + i ;
}
2017-01-28 00:42:31 +01:00
}
2016-03-21 20:43:03 +01:00
audio_port * open_port ( )
2015-01-17 17:14:58 +01:00
{
for ( u32 i = 0 ; i < AUDIO_PORT_COUNT ; i + + )
{
2016-03-21 20:43:03 +01:00
if ( ports [ i ] . state . compare_and_swap_test ( audio_port_state : : closed , audio_port_state : : opened ) )
2015-01-17 17:14:58 +01:00
{
2016-03-21 20:43:03 +01:00
return & ports [ i ] ;
2015-01-17 17:14:58 +01:00
}
}
2016-03-21 20:43:03 +01:00
return nullptr ;
2015-01-17 17:14:58 +01:00
}
2014-03-26 23:45:58 +01:00
} ;
2018-10-01 19:58:34 +02:00
2018-12-20 23:35:49 +01:00
using cell_audio = named_thread < cell_audio_thread > ;