2021-11-24 19:41:05 +01:00
# include "Emu/Audio/Cubeb/CubebBackend.h"
# include <algorithm>
# include "util/logs.hpp"
2022-07-08 17:13:38 +02:00
# include "Emu/Audio/audio_device_enumerator.h"
2021-11-24 19:41:05 +01:00
# ifdef _WIN32
# include <Windows.h>
# include <system_error>
# endif
LOG_CHANNEL ( Cubeb ) ;
CubebBackend : : CubebBackend ( )
: AudioBackend ( )
{
# ifdef _WIN32
// Cubeb requires COM to be initialized on the thread calling cubeb_init on Windows
2022-07-08 17:13:38 +02:00
if ( HRESULT hr = CoInitializeEx ( nullptr , COINIT_MULTITHREADED ) ; SUCCEEDED ( hr ) )
2021-11-24 19:41:05 +01:00
{
m_com_init_success = true ;
}
# endif
2022-10-13 14:39:16 +02:00
cubeb * ctx { } ;
if ( int err = cubeb_init ( & ctx , " RPCS3 " , nullptr ) )
2021-11-24 19:41:05 +01:00
{
2022-07-08 17:13:38 +02:00
Cubeb . error ( " cubeb_init() failed: %i " , err ) ;
2021-11-24 19:41:05 +01:00
return ;
}
2022-10-13 14:39:16 +02:00
if ( int err = cubeb_register_device_collection_changed ( ctx , CUBEB_DEVICE_TYPE_OUTPUT , device_collection_changed_cb , this ) )
2022-07-08 17:13:38 +02:00
{
Cubeb . error ( " cubeb_register_device_collection_changed() failed: %i " , err ) ;
}
else
{
m_dev_collection_cb_enabled = true ;
}
2022-10-13 14:39:16 +02:00
Cubeb . notice ( " Using backend %s " , cubeb_get_backend_id ( ctx ) ) ;
std : : lock_guard cb_lock { m_state_cb_mutex } ;
m_ctx = ctx ;
2021-11-24 19:41:05 +01:00
}
CubebBackend : : ~ CubebBackend ( )
{
Close ( ) ;
2022-07-08 17:13:38 +02:00
if ( m_dev_collection_cb_enabled )
{
if ( int err = cubeb_register_device_collection_changed ( m_ctx , CUBEB_DEVICE_TYPE_OUTPUT , nullptr , nullptr ) )
{
Cubeb . error ( " cubeb_register_device_collection_changed() failed: %i " , err ) ;
}
}
2021-11-24 19:41:05 +01:00
if ( m_ctx )
{
cubeb_destroy ( m_ctx ) ;
}
# ifdef _WIN32
if ( m_com_init_success )
{
CoUninitialize ( ) ;
}
# endif
}
2021-12-01 22:36:01 +01:00
bool CubebBackend : : Initialized ( )
{
return m_ctx ! = nullptr ;
}
bool CubebBackend : : Operational ( )
{
2022-07-08 17:13:38 +02:00
return m_stream ! = nullptr & & ! m_reset_req . observe ( ) ;
}
bool CubebBackend : : DefaultDeviceChanged ( )
{
2022-10-13 14:39:16 +02:00
if ( m_default_device . empty ( ) | | m_reset_req . observe ( ) )
{
return false ;
}
device_handle device = GetDevice ( ) ;
if ( ! device . handle )
{
Cubeb . error ( " Selected device not found. Trying alternative approach... " ) ;
device = GetDefaultDeviceAlt ( m_sampling_rate , m_sample_size , m_channels ) ;
}
return ! device . handle | | device . id ! = m_default_device ;
2021-12-01 22:36:01 +01:00
}
2024-03-26 19:39:37 +01:00
bool CubebBackend : : Open ( std : : string_view dev_id , AudioFreq freq , AudioSampleSize sample_size , AudioChannelCnt ch_cnt , audio_channel_layout layout )
2021-11-24 19:41:05 +01:00
{
2022-05-05 15:47:44 +02:00
if ( ! Initialized ( ) )
{
Cubeb . error ( " Open() called uninitialized " ) ;
return false ;
}
2021-11-24 19:41:05 +01:00
2022-10-13 14:39:16 +02:00
Close ( ) ;
std : : lock_guard lock { m_cb_mutex } ;
2021-11-24 19:41:05 +01:00
2022-07-08 17:13:38 +02:00
const bool use_default_device = dev_id . empty ( ) | | dev_id = = audio_device_enumerator : : DEFAULT_DEV_ID ;
2022-08-19 23:28:03 +02:00
if ( use_default_device ) Cubeb . notice ( " Trying to open default device " ) ;
else Cubeb . notice ( " Trying to open device with dev_id='%s' " , dev_id ) ;
device_handle device = GetDevice ( use_default_device ? " " : dev_id ) ;
if ( ! device . handle )
2022-07-08 17:13:38 +02:00
{
if ( use_default_device )
{
2024-03-26 19:39:37 +01:00
device = GetDefaultDeviceAlt ( freq , sample_size , static_cast < u32 > ( ch_cnt ) ) ;
2022-07-08 17:13:38 +02:00
2022-08-19 23:28:03 +02:00
if ( ! device . handle )
2022-07-08 17:13:38 +02:00
{
Cubeb . error ( " Cannot detect default device. Channel count detection unavailable. " ) ;
}
}
else
{
Cubeb . error ( " Device with id=%s not found " , dev_id ) ;
return false ;
}
}
2022-08-19 23:28:03 +02:00
if ( device . ch_cnt = = 0 )
2022-07-08 17:13:38 +02:00
{
Cubeb . error ( " Device reported invalid channel count, using stereo instead " ) ;
2022-08-19 23:28:03 +02:00
device . ch_cnt = 2 ;
2022-07-08 17:13:38 +02:00
}
2024-03-26 14:49:20 +01:00
Cubeb . notice ( " Channel count is %d " , device . ch_cnt ) ;
2022-10-13 14:39:16 +02:00
if ( use_default_device )
{
std : : lock_guard lock { m_state_cb_mutex } ;
m_default_device = device . id ;
}
2021-11-24 19:41:05 +01:00
m_sampling_rate = freq ;
m_sample_size = sample_size ;
2024-03-26 19:39:37 +01:00
setup_channel_layout ( static_cast < u32 > ( ch_cnt ) , device . ch_cnt , layout , Cubeb ) ;
2022-01-11 12:40:06 +01:00
full_sample_size = get_channels ( ) * get_sample_size ( ) ;
2021-11-24 19:41:05 +01:00
cubeb_stream_params stream_param { } ;
stream_param . format = get_convert_to_s16 ( ) ? CUBEB_SAMPLE_S16NE : CUBEB_SAMPLE_FLOAT32NE ;
stream_param . rate = get_sampling_rate ( ) ;
stream_param . channels = get_channels ( ) ;
stream_param . layout = [ & ] ( )
{
2024-03-26 19:39:37 +01:00
switch ( m_layout )
2021-11-24 19:41:05 +01:00
{
2024-03-26 19:39:37 +01:00
case audio_channel_layout : : automatic : break ;
case audio_channel_layout : : mono : return CUBEB_LAYOUT_MONO ;
case audio_channel_layout : : stereo : return CUBEB_LAYOUT_STEREO ;
case audio_channel_layout : : stereo_lfe : return CUBEB_LAYOUT_STEREO_LFE ;
case audio_channel_layout : : quadraphonic : return CUBEB_LAYOUT_QUAD ;
case audio_channel_layout : : quadraphonic_lfe : return CUBEB_LAYOUT_QUAD_LFE ;
case audio_channel_layout : : surround_5_1 : return CUBEB_LAYOUT_3F2_LFE ;
case audio_channel_layout : : surround_7_1 : return CUBEB_LAYOUT_3F4_LFE ;
2021-11-24 19:41:05 +01:00
}
2024-03-26 19:39:37 +01:00
fmt : : throw_exception ( " Invalid audio layout %d " , static_cast < u32 > ( m_layout ) ) ;
2021-11-24 19:41:05 +01:00
} ( ) ;
2022-08-19 23:28:03 +02:00
stream_param . prefs = m_dev_collection_cb_enabled & & device . handle ? CUBEB_STREAM_PREF_DISABLE_DEVICE_SWITCHING : CUBEB_STREAM_PREF_NONE ;
2021-11-24 19:41:05 +01:00
u32 min_latency { } ;
if ( int err = cubeb_get_min_latency ( m_ctx , & stream_param , & min_latency ) )
{
2022-07-08 17:13:38 +02:00
Cubeb . error ( " cubeb_get_min_latency() failed: %i " , err ) ;
2022-05-05 15:47:44 +02:00
min_latency = 0 ;
2021-11-24 19:41:05 +01:00
}
2022-01-11 12:40:06 +01:00
const u32 stream_latency = std : : max ( static_cast < u32 > ( AUDIO_MIN_LATENCY * get_sampling_rate ( ) ) , min_latency ) ;
2022-07-08 17:13:38 +02:00
2022-08-19 23:28:03 +02:00
if ( int err = cubeb_stream_init ( m_ctx , & m_stream , " Main stream " , nullptr , nullptr , device . handle , & stream_param , stream_latency , data_cb , state_cb , this ) )
2021-11-24 19:41:05 +01:00
{
2022-07-08 17:13:38 +02:00
Cubeb . error ( " cubeb_stream_init() failed: %i " , err ) ;
2022-05-05 15:47:44 +02:00
m_stream = nullptr ;
2022-07-08 17:13:38 +02:00
return false ;
2021-12-02 00:58:28 +01:00
}
2022-07-08 17:13:38 +02:00
if ( int err = cubeb_stream_start ( m_stream ) )
2021-12-02 00:58:28 +01:00
{
2022-07-08 17:13:38 +02:00
Cubeb . error ( " cubeb_stream_start() failed: %i " , err ) ;
2022-10-13 14:39:16 +02:00
Close ( ) ;
2022-07-08 17:13:38 +02:00
return false ;
2021-11-24 19:41:05 +01:00
}
2022-07-08 17:13:38 +02:00
if ( int err = cubeb_stream_set_volume ( m_stream , 1.0 ) )
2021-11-24 19:41:05 +01:00
{
2022-07-08 17:13:38 +02:00
Cubeb . error ( " cubeb_stream_set_volume() failed: %i " , err ) ;
2021-11-24 19:41:05 +01:00
}
2022-01-11 12:40:06 +01:00
2022-05-05 15:47:44 +02:00
return true ;
2021-11-24 19:41:05 +01:00
}
2022-10-13 14:39:16 +02:00
void CubebBackend : : Close ( )
2021-11-24 19:41:05 +01:00
{
2022-05-05 15:47:44 +02:00
if ( m_stream ! = nullptr )
2021-11-24 19:41:05 +01:00
{
2022-05-05 15:47:44 +02:00
if ( int err = cubeb_stream_stop ( m_stream ) )
{
2022-07-08 17:13:38 +02:00
Cubeb . error ( " cubeb_stream_stop() failed: %i " , err ) ;
2022-05-05 15:47:44 +02:00
}
2021-11-24 19:41:05 +01:00
2022-05-05 15:47:44 +02:00
cubeb_stream_destroy ( m_stream ) ;
}
2021-11-24 19:41:05 +01:00
2022-10-13 14:39:16 +02:00
{
std : : lock_guard lock { m_cb_mutex } ;
m_stream = nullptr ;
m_playing = false ;
m_last_sample . fill ( 0 ) ;
}
2022-07-08 17:13:38 +02:00
2022-10-13 14:39:16 +02:00
std : : lock_guard lock { m_state_cb_mutex } ;
2022-08-19 23:28:03 +02:00
m_default_device . clear ( ) ;
2021-11-24 19:41:05 +01:00
}
void CubebBackend : : Play ( )
{
2022-05-05 15:47:44 +02:00
if ( m_stream = = nullptr )
{
Cubeb . error ( " Play() called uninitialized " ) ;
return ;
}
2021-11-24 19:41:05 +01:00
if ( m_playing ) return ;
std : : lock_guard lock ( m_cb_mutex ) ;
m_playing = true ;
}
void CubebBackend : : Pause ( )
{
2022-05-05 15:47:44 +02:00
if ( m_stream = = nullptr )
{
Cubeb . error ( " Pause() called uninitialized " ) ;
return ;
}
if ( ! m_playing ) return ;
2021-11-24 19:41:05 +01:00
std : : lock_guard lock ( m_cb_mutex ) ;
m_playing = false ;
2022-01-11 12:40:06 +01:00
m_last_sample . fill ( 0 ) ;
2021-11-24 19:41:05 +01:00
}
f64 CubebBackend : : GetCallbackFrameLen ( )
{
2022-05-05 15:47:44 +02:00
if ( m_stream = = nullptr )
{
Cubeb . error ( " GetCallbackFrameLen() called uninitialized " ) ;
return AUDIO_MIN_LATENCY ;
}
2022-01-11 12:40:06 +01:00
u32 stream_latency { } ;
if ( int err = cubeb_stream_get_latency ( m_stream , & stream_latency ) )
2021-11-24 19:41:05 +01:00
{
2022-07-08 17:13:38 +02:00
Cubeb . error ( " cubeb_stream_get_latency() failed: %i " , err ) ;
2022-05-05 15:47:44 +02:00
stream_latency = 0 ;
2021-11-24 19:41:05 +01:00
}
2022-01-11 12:40:06 +01:00
return std : : max < f64 > ( AUDIO_MIN_LATENCY , static_cast < f64 > ( stream_latency ) / get_sampling_rate ( ) ) ;
2021-11-24 19:41:05 +01:00
}
2022-08-19 23:28:03 +02:00
CubebBackend : : device_handle CubebBackend : : GetDevice ( std : : string_view dev_id )
2022-07-08 17:13:38 +02:00
{
const bool default_dev = dev_id . empty ( ) ;
2022-08-19 23:28:03 +02:00
if ( default_dev ) Cubeb . notice ( " Searching for default device " ) ;
else Cubeb . notice ( " Searching for device with dev_id='%s' " , dev_id ) ;
2022-07-08 17:13:38 +02:00
cubeb_device_collection dev_collection { } ;
if ( int err = cubeb_enumerate_devices ( m_ctx , CUBEB_DEVICE_TYPE_OUTPUT , & dev_collection ) )
{
Cubeb . error ( " cubeb_enumerate_devices() failed: %i " , err ) ;
return { } ;
}
if ( dev_collection . count = = 0 )
{
Cubeb . error ( " No output devices available " ) ;
if ( int err = cubeb_device_collection_destroy ( m_ctx , & dev_collection ) )
{
Cubeb . error ( " cubeb_device_collection_destroy() failed: %i " , err ) ;
}
return { } ;
}
2022-08-19 23:28:03 +02:00
Cubeb . notice ( " Found %d possible output devices " , dev_collection . count ) ;
device_handle result { } ;
2022-07-08 17:13:38 +02:00
for ( u64 dev_idx = 0 ; dev_idx < dev_collection . count ; dev_idx + + )
{
const cubeb_device_info & dev_info = dev_collection . device [ dev_idx ] ;
2022-08-19 23:28:03 +02:00
device_handle device { } ;
device . handle = dev_info . devid ;
device . ch_cnt = dev_info . max_channels ;
if ( dev_info . device_id )
{
device . id = dev_info . device_id ;
}
if ( device . id . empty ( ) )
2022-07-08 17:13:38 +02:00
{
Cubeb . error ( " device_id is missing from device " ) ;
continue ;
}
if ( default_dev )
{
if ( dev_info . preferred & CUBEB_DEVICE_PREF_MULTIMEDIA )
{
2022-08-19 23:28:03 +02:00
result = std : : move ( device ) ;
2022-07-08 17:13:38 +02:00
break ;
}
}
2022-08-19 23:28:03 +02:00
else if ( device . id = = dev_id )
2022-07-08 17:13:38 +02:00
{
2022-08-19 23:28:03 +02:00
result = std : : move ( device ) ;
2022-07-08 17:13:38 +02:00
break ;
}
}
2022-08-19 23:28:03 +02:00
if ( result . handle )
{
Cubeb . notice ( " Found device '%s' with %d channels " , result . id , result . ch_cnt ) ;
}
else
{
Cubeb . notice ( " No device found for dev_id='%s' " , dev_id ) ;
}
2022-07-08 17:13:38 +02:00
if ( int err = cubeb_device_collection_destroy ( m_ctx , & dev_collection ) )
{
Cubeb . error ( " cubeb_device_collection_destroy() failed: %i " , err ) ;
}
return result ;
} ;
2024-03-26 19:39:37 +01:00
CubebBackend : : device_handle CubebBackend : : GetDefaultDeviceAlt ( AudioFreq freq , AudioSampleSize sample_size , u32 ch_cnt )
2022-07-08 17:13:38 +02:00
{
2022-08-19 23:28:03 +02:00
Cubeb . notice ( " Starting alternative search for default device with freq=%d, sample_size=%d and ch_cnt=%d " , static_cast < u32 > ( freq ) , static_cast < u32 > ( sample_size ) , static_cast < u32 > ( ch_cnt ) ) ;
2022-07-08 17:13:38 +02:00
cubeb_stream_params param =
{
. format = sample_size = = AudioSampleSize : : S16 ? CUBEB_SAMPLE_S16NE : CUBEB_SAMPLE_FLOAT32NE ,
. rate = static_cast < u32 > ( freq ) ,
. channels = static_cast < u32 > ( ch_cnt ) ,
. layout = CUBEB_LAYOUT_UNDEFINED ,
. prefs = CUBEB_STREAM_PREF_DISABLE_DEVICE_SWITCHING
} ;
u32 min_latency { } ;
if ( int err = cubeb_get_min_latency ( m_ctx , & param , & min_latency ) )
{
Cubeb . error ( " cubeb_get_min_latency() failed: %i " , err ) ;
min_latency = 100 ;
}
cubeb_stream * tmp_stream { } ;
static auto dummy_data_cb = [ ] ( cubeb_stream * , void * , void const * , void * , long ) - > long { return 0 ; } ;
static auto dummy_state_cb = [ ] ( cubeb_stream * , void * , cubeb_state ) { } ;
if ( int err = cubeb_stream_init ( m_ctx , & tmp_stream , " Default device detector " , nullptr , nullptr , nullptr , & param , min_latency , dummy_data_cb , dummy_state_cb , nullptr ) )
{
Cubeb . error ( " cubeb_stream_init() failed: %i " , err ) ;
return { } ;
}
cubeb_device * crnt_dev { } ;
2022-08-19 23:28:03 +02:00
if ( int err = cubeb_stream_get_current_device ( tmp_stream , & crnt_dev ) ; err ! = CUBEB_OK | | ! crnt_dev )
2022-07-08 17:13:38 +02:00
{
2022-08-19 23:28:03 +02:00
Cubeb . error ( " cubeb_stream_get_current_device() failed: err=%i, crnt_dev=%d " , err , ! ! crnt_dev ) ;
2022-07-08 17:13:38 +02:00
cubeb_stream_destroy ( tmp_stream ) ;
return { } ;
}
2022-08-19 23:28:03 +02:00
std : : string out_dev_name ;
if ( crnt_dev - > output_name )
{
out_dev_name = crnt_dev - > output_name ;
}
2022-07-08 17:13:38 +02:00
if ( int err = cubeb_stream_device_destroy ( tmp_stream , crnt_dev ) )
{
Cubeb . error ( " cubeb_stream_device_destroy() failed: %i " , err ) ;
}
cubeb_stream_destroy ( tmp_stream ) ;
if ( out_dev_name . empty ( ) )
{
2022-08-19 23:28:03 +02:00
Cubeb . notice ( " No default device available " ) ;
2022-07-08 17:13:38 +02:00
return { } ;
}
return GetDevice ( out_dev_name ) ;
}
2022-08-20 00:43:03 +02:00
long CubebBackend : : data_cb ( cubeb_stream * stream , void * user_ptr , void const * /* input_buffer */ , void * output_buffer , long nframes )
2021-11-24 19:41:05 +01:00
{
2022-08-19 23:28:03 +02:00
if ( nframes < = 0 )
{
Cubeb . error ( " data_cb called with nframes=%d " , nframes ) ;
return 0 ;
}
if ( ! output_buffer )
{
Cubeb . error ( " data_cb called with invalid output_buffer " ) ;
return CUBEB_ERROR ;
}
2022-08-20 00:43:03 +02:00
if ( ! stream )
{
Cubeb . error ( " data_cb called with invalid stream " ) ;
return CUBEB_ERROR ;
}
2021-11-24 19:41:05 +01:00
CubebBackend * const cubeb = static_cast < CubebBackend * > ( user_ptr ) ;
2022-08-19 23:28:03 +02:00
ensure ( cubeb ) ;
2021-11-24 19:41:05 +01:00
std : : unique_lock lock ( cubeb - > m_cb_mutex , std : : defer_lock ) ;
2022-10-13 14:39:16 +02:00
if ( ! cubeb - > m_reset_req . observe ( ) & & lock . try_lock_for ( std : : chrono : : microseconds { 50 } ) & & cubeb - > m_write_callback & & cubeb - > m_playing )
2022-08-20 00:43:03 +02:00
{
2022-10-13 14:39:16 +02:00
if ( stream ! = cubeb - > m_stream )
{
// Cubeb.error("data_cb called with unknown stream");
return CUBEB_ERROR ;
}
2022-08-20 00:43:03 +02:00
2022-01-11 12:40:06 +01:00
const u32 sample_size = cubeb - > full_sample_size . observe ( ) ;
const u32 bytes_req = nframes * sample_size ;
2021-11-24 19:41:05 +01:00
u32 written = std : : min ( cubeb - > m_write_callback ( bytes_req , output_buffer ) , bytes_req ) ;
written - = written % sample_size ;
if ( written > = sample_size )
{
2022-01-11 12:40:06 +01:00
memcpy ( cubeb - > m_last_sample . data ( ) , static_cast < u8 * > ( output_buffer ) + written - sample_size , sample_size ) ;
2021-11-24 19:41:05 +01:00
}
for ( u32 i = written ; i < bytes_req ; i + = sample_size )
{
2022-01-11 12:40:06 +01:00
memcpy ( static_cast < u8 * > ( output_buffer ) + i , cubeb - > m_last_sample . data ( ) , sample_size ) ;
2021-11-24 19:41:05 +01:00
}
}
2022-01-11 12:40:06 +01:00
else
{
// Stream parameters are modified only after stream_destroy. stream_destroy will return
// only after this callback returns, so it's safe to access full_sample_size here.
2022-10-13 14:39:16 +02:00
memset ( output_buffer , 0 , nframes * cubeb - > full_sample_size ) ;
2022-01-11 12:40:06 +01:00
}
2021-11-24 19:41:05 +01:00
return nframes ;
}
2022-08-20 00:43:03 +02:00
void CubebBackend : : state_cb ( cubeb_stream * stream , void * user_ptr , cubeb_state state )
2021-11-24 19:41:05 +01:00
{
2022-08-20 00:43:03 +02:00
if ( ! stream )
{
Cubeb . error ( " state_cb called with invalid stream " ) ;
return ;
}
2021-11-24 19:41:05 +01:00
CubebBackend * const cubeb = static_cast < CubebBackend * > ( user_ptr ) ;
2022-08-19 23:28:03 +02:00
ensure ( cubeb ) ;
2021-11-24 19:41:05 +01:00
2022-08-20 00:43:03 +02:00
std : : lock_guard lock ( cubeb - > m_state_cb_mutex ) ;
if ( stream ! = cubeb - > m_stream )
2021-11-24 19:41:05 +01:00
{
2022-08-20 00:43:03 +02:00
Cubeb . error ( " state_cb called with unknown stream " ) ;
return ;
}
2022-05-05 15:47:44 +02:00
2022-08-20 00:43:03 +02:00
switch ( state )
{
case CUBEB_STATE_ERROR :
{
Cubeb . error ( " Stream entered error state " ) ;
2022-07-08 17:13:38 +02:00
if ( ! cubeb - > m_reset_req . test_and_set ( ) & & cubeb - > m_state_callback )
{
cubeb - > m_state_callback ( AudioStateEvent : : UNSPECIFIED_ERROR ) ;
}
2022-08-20 00:43:03 +02:00
break ;
}
case CUBEB_STATE_STARTED :
{
Cubeb . notice ( " Stream started " ) ;
break ;
}
case CUBEB_STATE_STOPPED :
{
Cubeb . notice ( " Stream stopped " ) ;
break ;
}
case CUBEB_STATE_DRAINED :
{
Cubeb . notice ( " Stream drained " ) ;
break ;
}
default :
{
Cubeb . notice ( " Stream entered unknown state %d " , static_cast < u32 > ( state ) ) ;
break ;
}
2022-07-08 17:13:38 +02:00
}
}
2022-08-20 00:43:03 +02:00
void CubebBackend : : device_collection_changed_cb ( cubeb * context , void * user_ptr )
2022-07-08 17:13:38 +02:00
{
2022-08-19 23:28:03 +02:00
Cubeb . notice ( " Device collection changed " ) ;
2022-08-20 00:43:03 +02:00
if ( ! context )
{
Cubeb . error ( " device_collection_changed_cb called with invalid stream " ) ;
return ;
}
2022-07-08 17:13:38 +02:00
CubebBackend * const cubeb = static_cast < CubebBackend * > ( user_ptr ) ;
2022-08-19 23:28:03 +02:00
ensure ( cubeb ) ;
2022-07-08 17:13:38 +02:00
2022-10-13 14:39:16 +02:00
if ( cubeb - > m_reset_req . observe ( ) )
{
return ;
}
std : : lock_guard cb_lock { cubeb - > m_state_cb_mutex } ;
2022-07-08 17:13:38 +02:00
2022-08-20 00:43:03 +02:00
if ( context ! = cubeb - > m_ctx )
{
2023-07-28 12:09:06 +02:00
Cubeb . error ( " device_collection_changed_cb called with unknown context " ) ;
2022-08-20 00:43:03 +02:00
return ;
}
2022-07-08 17:13:38 +02:00
// Non default device is used (or default device cannot be detected)
2022-08-19 23:28:03 +02:00
if ( cubeb - > m_default_device . empty ( ) )
2022-07-08 17:13:38 +02:00
{
2022-10-13 14:39:16 +02:00
Cubeb . notice ( " Skipping default device notification " ) ;
2022-07-08 17:13:38 +02:00
return ;
}
2022-10-13 14:39:16 +02:00
if ( cubeb - > m_state_callback )
2022-07-08 17:13:38 +02:00
{
2022-10-13 14:39:16 +02:00
cubeb - > m_state_callback ( AudioStateEvent : : DEFAULT_DEVICE_MAYBE_CHANGED ) ;
2021-11-24 19:41:05 +01:00
}
}