@ -31,36 +31,27 @@
# define TYPE_AUDIO_PW "audio-pipewire"
OBJECT_DECLARE_SIMPLE_TYPE ( AudioPw , AUDIO_PW )
static AudioBackendClass * audio_pw_parent_class ;
struct AudioPw {
AudioMixengBackend parent_obj ;
} ;
static struct audio_driver pw_audio_driver ;
struct pw_thread_loop * thread_loop ;
struct pw_context * context ;
static void audio_pw_class_init ( ObjectClass * klass , const void * data )
{
AudioMixengBackendClass * k = AUDIO_MIXENG_BACKEND_CLASS ( klass ) ;
struct pw_core * core ;
struct spa_hook core_listener ;
int last_seq , pending_seq , error ;
} ;
k - > driver = & pw_audio_driver ;
}
static struct audio_driver pw_audio_driver ;
typedef struct pwvolume {
uint32_t channels ;
float values [ SPA_AUDIO_MAX_CHANNELS ] ;
} pwvolume ;
typedef struct pwaudio {
Audiodev * dev ;
struct pw_thread_loop * thread_loop ;
struct pw_context * context ;
struct pw_core * core ;
struct spa_hook core_listener ;
int last_seq , pending_seq , error ;
} pwaudio ;
typedef struct PWVoice {
pwaudio * g ;
struct pw_stream * stream ;
struct spa_hook stream_listener ;
struct spa_audio_info_raw info ;
@ -236,9 +227,9 @@ static const struct pw_stream_events playback_stream_events = {
static size_t
qpw_read ( HWVoiceIn * hw , void * data , size_t len )
{
AudioPw * c = AUDIO_PW ( hw - > s ) ;
PWVoiceIn * pw = ( PWVoiceIn * ) hw ;
PWVoice * v = & pw - > v ;
pwaudio * c = v - > g ;
const char * error = NULL ;
size_t l ;
int32_t avail ;
@ -273,9 +264,9 @@ done_unlock:
static size_t qpw_buffer_get_free ( HWVoiceOut * hw )
{
AudioPw * c = AUDIO_PW ( hw - > s ) ;
PWVoiceOut * pw = ( PWVoiceOut * ) hw ;
PWVoice * v = & pw - > v ;
pwaudio * c = v - > g ;
const char * error = NULL ;
int32_t filled , avail ;
uint32_t index ;
@ -298,9 +289,9 @@ done_unlock:
static size_t
qpw_write ( HWVoiceOut * hw , void * data , size_t len )
{
AudioPw * c = AUDIO_PW ( hw - > s ) ;
PWVoiceOut * pw = ( PWVoiceOut * ) hw ;
PWVoice * v = & pw - > v ;
pwaudio * c = v - > g ;
const char * error = NULL ;
int32_t filled , avail ;
uint32_t index ;
@ -434,7 +425,7 @@ pw_to_audfmt(enum spa_audio_format fmt, int *endianness,
}
static int
qpw_stream_new ( pwaudio * c , PWVoice * v , const char * stream_name ,
qpw_stream_new ( AudioPw * c , PWVoice * v , const char * stream_name ,
const char * name , enum spa_direction dir )
{
int res ;
@ -452,8 +443,8 @@ qpw_stream_new(pwaudio *c, PWVoice *v, const char *stream_name,
}
/* 75% of the timer period for faster updates */
buf_samples = ( uint64_t ) v - > g - > dev - > timer_period * v - > info . rate
* 3 / 4 / 1000000 ;
buf_samples = ( uint64_t ) AUDIO_MIXENG_BACKEND ( c ) - > dev - > timer_period
* v - > info . rate * 3 / 4 / 1000000 ;
pw_properties_setf ( props , PW_KEY_NODE_LATENCY , " % " PRIu64 " /%u " ,
buf_samples , v - > info . rate ) ;
@ -535,11 +526,11 @@ qpw_set_position(uint32_t channels, uint32_t position[SPA_AUDIO_MAX_CHANNELS])
static int
qpw_init_out ( HWVoiceOut * hw , struct audsettings * as , void * drv_opaque )
{
AudioPw * c = AUDIO_PW ( hw - > s ) ;
PWVoiceOut * pw = ( PWVoiceOut * ) hw ;
PWVoice * v = & pw - > v ;
struct audsettings obt_as = * as ;
pwaudio * c = v - > g = drv_opaque ;
AudiodevPipewireOptions * popts = & c - > dev - > u . pipewire ;
AudiodevPipewireOptions * popts = & AUDIO_MIXENG_BACKEND ( c ) - > dev - > u . pipewire ;
AudiodevPipewirePerDirectionOptions * ppdo = popts - > out ;
int r ;
@ -554,11 +545,11 @@ qpw_init_out(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque)
pw_to_audfmt ( v - > info . format , & obt_as . endianness , & v - > frame_size ) ;
v - > frame_size * = as - > nchannels ;
v - > req = ( uint64_t ) c - > dev - > timer_period * v - > info . rate
v - > req = ( uint64_t ) AUDIO_MIXENG_BACKEND ( c ) - > dev - > timer_period * v - > info . rate
* 1 / 2 / 1000000 * v - > frame_size ;
/* call the function that creates a new stream for playback */
r = qpw_stream_new ( c , v , ppdo - > stream_name ? : c - > dev - > id ,
r = qpw_stream_new ( c , v , ppdo - > stream_name ? : AUDIO_MIXENG_BACKEND ( c ) - > dev - > id ,
ppdo - > name , SPA_DIRECTION_OUTPUT ) ;
if ( r < 0 ) {
pw_thread_loop_unlock ( c - > thread_loop ) ;
@ -582,11 +573,11 @@ qpw_init_out(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque)
static int
qpw_init_in ( HWVoiceIn * hw , struct audsettings * as , void * drv_opaque )
{
AudioPw * c = AUDIO_PW ( hw - > s ) ;
PWVoiceIn * pw = ( PWVoiceIn * ) hw ;
PWVoice * v = & pw - > v ;
struct audsettings obt_as = * as ;
pwaudio * c = v - > g = drv_opaque ;
AudiodevPipewireOptions * popts = & c - > dev - > u . pipewire ;
AudiodevPipewireOptions * popts = & AUDIO_MIXENG_BACKEND ( c ) - > dev - > u . pipewire ;
AudiodevPipewirePerDirectionOptions * ppdo = popts - > in ;
int r ;
@ -602,7 +593,7 @@ qpw_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
v - > frame_size * = as - > nchannels ;
/* call the function that creates a new stream for recording */
r = qpw_stream_new ( c , v , ppdo - > stream_name ? : c - > dev - > id ,
r = qpw_stream_new ( c , v , ppdo - > stream_name ? : AUDIO_MIXENG_BACKEND ( c ) - > dev - > id ,
ppdo - > name , SPA_DIRECTION_INPUT ) ;
if ( r < 0 ) {
pw_thread_loop_unlock ( c - > thread_loop ) ;
@ -621,10 +612,8 @@ qpw_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
}
static void
qpw_voice_fini ( PWVoice * v )
qpw_voice_fini ( AudioPw * c , PWVoice * v )
{
pwaudio * c = v - > g ;
if ( ! v - > stream ) {
return ;
}
@ -637,19 +626,18 @@ qpw_voice_fini(PWVoice *v)
static void
qpw_fini_out ( HWVoiceOut * hw )
{
qpw_voice_fini ( & PW_VOICE_OUT ( hw ) - > v ) ;
qpw_voice_fini ( AUDIO_PW ( hw - > s ) , & PW_VOICE_OUT ( hw ) - > v ) ;
}
static void
qpw_fini_in ( HWVoiceIn * hw )
{
qpw_voice_fini ( & PW_VOICE_IN ( hw ) - > v ) ;
qpw_voice_fini ( AUDIO_PW ( hw - > s ) , & PW_VOICE_IN ( hw ) - > v ) ;
}
static void
qpw_voice_set_enabled ( PWVoice * v , bool enable )
qpw_voice_set_enabled ( AudioPw * c , PWVoice * v , bool enable )
{
pwaudio * c = v - > g ;
pw_thread_loop_lock ( c - > thread_loop ) ;
pw_stream_set_active ( v - > stream , enable ) ;
pw_thread_loop_unlock ( c - > thread_loop ) ;
@ -658,19 +646,18 @@ qpw_voice_set_enabled(PWVoice *v, bool enable)
static void
qpw_enable_out ( HWVoiceOut * hw , bool enable )
{
qpw_voice_set_enabled ( & PW_VOICE_OUT ( hw ) - > v , enable ) ;
qpw_voice_set_enabled ( AUDIO_PW ( hw - > s ) , & PW_VOICE_OUT ( hw ) - > v , enable ) ;
}
static void
qpw_enable_in ( HWVoiceIn * hw , bool enable )
{
qpw_voice_set_enabled ( & PW_VOICE_IN ( hw ) - > v , enable ) ;
qpw_voice_set_enabled ( AUDIO_PW ( hw - > s ) , & PW_VOICE_IN ( hw ) - > v , enable ) ;
}
static void
qpw_voice_set_volume ( PWVoice * v , Volume * vol )
qpw_voice_set_volume ( AudioPw * c , PWVoice * v , Volume * vol )
{
pwaudio * c = v - > g ;
int i , ret ;
pw_thread_loop_lock ( c - > thread_loop ) ;
@ -693,16 +680,16 @@ qpw_voice_set_volume(PWVoice *v, Volume *vol)
static void
qpw_volume_out ( HWVoiceOut * hw , Volume * vol )
{
qpw_voice_set_volume ( & PW_VOICE_OUT ( hw ) - > v , vol ) ;
qpw_voice_set_volume ( AUDIO_PW ( hw - > s ) , & PW_VOICE_OUT ( hw ) - > v , vol ) ;
}
static void
qpw_volume_in ( HWVoiceIn * hw , Volume * vol )
{
qpw_voice_set_volume ( & PW_VOICE_IN ( hw ) - > v , vol ) ;
qpw_voice_set_volume ( AUDIO_PW ( hw - > s ) , & PW_VOICE_IN ( hw ) - > v , vol ) ;
}
static int wait_resync ( pwaudio * pw )
static int wait_resync ( AudioPw * pw )
{
int res ;
pw - > pending_seq = pw_core_sync ( pw - > core , PW_ID_CORE , pw - > pending_seq ) ;
@ -725,7 +712,7 @@ static int wait_resync(pwaudio *pw)
static void
on_core_error ( void * data , uint32_t id , int seq , int res , const char * message )
{
pwaudio * pw = data ;
AudioPw * pw = data ;
error_report ( " error id:%u seq:%d res:%d (%s): %s " ,
id , seq , res , spa_strerror ( res ) , message ) ;
@ -737,7 +724,7 @@ on_core_error(void *data, uint32_t id, int seq, int res, const char *message)
static void
on_core_done ( void * data , uint32_t id , int seq )
{
pwaudio * pw = data ;
AudioPw * pw = data ;
assert ( id = = PW_ID_CORE ) ;
pw - > last_seq = seq ;
if ( pw - > pending_seq = = seq ) {
@ -752,17 +739,20 @@ static const struct pw_core_events core_events = {
. error = on_core_error ,
} ;
static void *
qpw_audio_init ( Audiodev * dev , Error * * errp )
static bool
audio_pw_realize ( AudioBackend * abe , Audiodev * dev , Error * * errp )
{
g_autofree pwaudio * pw = g_new0 ( pwaudio , 1 ) ;
AudioPw * pw = AUDIO_PW ( abe ) ;
assert ( dev - > driver = = AUDIODEV_DRIVER_PIPEWIRE ) ;
trace_pw_audio_init ( ) ;
if ( ! audio_pw_parent_class - > realize ( abe , dev , errp ) ) {
return false ;
}
pw_init ( NULL , NULL ) ;
pw - > dev = dev ;
pw - > thread_loop = pw_thread_loop_new ( " PipeWire thread loop " , NULL ) ;
if ( pw - > thread_loop = = NULL ) {
error_setg_errno ( errp , errno , " Could not create PipeWire loop " ) ;
@ -801,8 +791,7 @@ qpw_audio_init(Audiodev *dev, Error **errp)
}
pw_thread_loop_unlock ( pw - > thread_loop ) ;
return g_steal_pointer ( & pw ) ;
return true ;
fail :
if ( pw - > thread_loop ) {
@ -810,13 +799,13 @@ fail:
}
g_clear_pointer ( & pw - > context , pw_context_destroy ) ;
g_clear_pointer ( & pw - > thread_loop , pw_thread_loop_destroy ) ;
return NULL ;
return false ;
}
static void
qpw_audio_fini ( void * opaque )
audio_pw_finalize ( Object * obj )
{
pwaudio * pw = opaque ;
AudioPw * pw = AUDIO_PW ( obj ) ;
if ( pw - > thread_loop ) {
pw_thread_loop_stop ( pw - > thread_loop ) ;
@ -831,9 +820,7 @@ qpw_audio_fini(void *opaque)
if ( pw - > context ) {
pw_context_destroy ( pw - > context ) ;
}
pw_thread_loop_destroy ( pw - > thread_loop ) ;
g_free ( pw ) ;
g_clear_pointer ( & pw - > thread_loop , pw_thread_loop_destroy ) ;
}
static struct audio_pcm_ops qpw_pcm_ops = {
@ -855,8 +842,6 @@ static struct audio_pcm_ops qpw_pcm_ops = {
static struct audio_driver pw_audio_driver = {
. name = " pipewire " ,
. init = qpw_audio_init ,
. fini = qpw_audio_fini ,
. pcm_ops = & qpw_pcm_ops ,
. max_voices_out = INT_MAX ,
. max_voices_in = INT_MAX ,
@ -864,12 +849,24 @@ static struct audio_driver pw_audio_driver = {
. voice_size_in = sizeof ( PWVoiceIn ) ,
} ;
static void audio_pw_class_init ( ObjectClass * klass , const void * data )
{
AudioBackendClass * b = AUDIO_BACKEND_CLASS ( klass ) ;
AudioMixengBackendClass * k = AUDIO_MIXENG_BACKEND_CLASS ( klass ) ;
audio_pw_parent_class = AUDIO_BACKEND_CLASS ( object_class_get_parent ( klass ) ) ;
b - > realize = audio_pw_realize ;
k - > driver = & pw_audio_driver ;
}
static const TypeInfo audio_types [ ] = {
{
. name = TYPE_AUDIO_PW ,
. parent = TYPE_AUDIO_MIXENG_BACKEND ,
. instance_size = sizeof ( AudioPw ) ,
. class_init = audio_pw_class_init ,
. instance_finalize = audio_pw_finalize ,
} ,
} ;