summaryrefslogtreecommitdiff
path: root/src/services/audio-service.c
diff options
context:
space:
mode:
authorHannes Schulze <haschu0103@outlook.de>2025-06-19 23:17:58 +0200
committerHannes Schulze <haschu0103@outlook.de>2025-06-19 23:17:58 +0200
commit1af3d14f3f995645a0de08a54b244c19840709e9 (patch)
tree3c4098863d8a8fd004a2acc8912a23b6036ab4dd /src/services/audio-service.c
parent40efcf60a5f39674b5137f11de7bcfe37fb121b5 (diff)
init hs project
Diffstat (limited to 'src/services/audio-service.c')
-rw-r--r--src/services/audio-service.c930
1 files changed, 0 insertions, 930 deletions
diff --git a/src/services/audio-service.c b/src/services/audio-service.c
deleted file mode 100644
index 06345f2..0000000
--- a/src/services/audio-service.c
+++ /dev/null
@@ -1,930 +0,0 @@
-#include "services/audio-service.h"
-#include <gvc-mixer-stream.h>
-#include <gvc-mixer-control.h>
-#include <gvc-mixer-source.h>
-#include <gvc-mixer-sink.h>
-#include <gtk/gtk.h>
-#include <math.h>
-
-#define DEFAULT_VOLUME 25
-
-//
-// FoobarAudioDeviceKind:
-//
-// The type of an audio device (either speaker/output or microphone/input).
-//
-
-G_DEFINE_ENUM_TYPE(
- FoobarAudioDeviceKind,
- foobar_audio_device_kind,
- G_DEFINE_ENUM_VALUE( FOOBAR_AUDIO_DEVICE_INPUT, "input" ),
- G_DEFINE_ENUM_VALUE( FOOBAR_AUDIO_DEVICE_OUTPUT, "output" ) )
-
-//
-// FoobarAudioDevice:
-//
-// Representation of a real input/output device or an abstract device (like the default device which only acts as a
-// proxy for another device).
-//
-
-struct _FoobarAudioDevice
-{
- GObject parent_instance;
- FoobarAudioService* service;
- FoobarAudioDeviceKind kind;
- GvcMixerStream* stream;
- gboolean is_default;
- gulong notify_handler_id;
-};
-
-enum
-{
- DEVICE_PROP_KIND = 1,
- DEVICE_PROP_ID,
- DEVICE_PROP_NAME,
- DEVICE_PROP_DESCRIPTION,
- DEVICE_PROP_VOLUME,
- DEVICE_PROP_IS_MUTED,
- DEVICE_PROP_IS_DEFAULT,
- DEVICE_PROP_IS_AVAILABLE,
- N_DEVICE_PROPS,
-};
-
-static GParamSpec* device_props[N_DEVICE_PROPS] = { 0 };
-
-static void foobar_audio_device_class_init ( FoobarAudioDeviceClass* klass );
-static void foobar_audio_device_init ( FoobarAudioDevice* self );
-static void foobar_audio_device_get_property ( GObject* object,
- guint prop_id,
- GValue* value,
- GParamSpec* pspec );
-static void foobar_audio_device_set_property ( GObject* object,
- guint prop_id,
- GValue const* value,
- GParamSpec* pspec );
-static void foobar_audio_device_finalize ( GObject* object );
-static FoobarAudioDevice* foobar_audio_device_new ( FoobarAudioService* service,
- FoobarAudioDeviceKind kind );
-static void foobar_audio_device_set_stream ( FoobarAudioDevice* self,
- GvcMixerStream* value );
-static void foobar_audio_device_set_default ( FoobarAudioDevice* self,
- gboolean value );
-static void foobar_audio_device_handle_notify( GObject* object,
- GParamSpec* pspec,
- gpointer userdata );
-
-G_DEFINE_FINAL_TYPE( FoobarAudioDevice, foobar_audio_device, G_TYPE_OBJECT )
-
-//
-// FoobarAudioService:
-//
-// Service managing the available audio input/output devices. This is implemented using the Gnome Volume Control
-// library.
-//
-
-struct _FoobarAudioService
-{
- GObject parent_instance;
- GvcMixerControl* control;
- GListStore* devices;
- GtkSortListModel* sorted_devices;
- GtkFilterListModel* inputs;
- GtkFilterListModel* outputs;
- FoobarAudioDevice* default_input;
- FoobarAudioDevice* default_output;
- gulong default_sink_handler_id;
- gulong default_source_handler_id;
- gulong stream_added_handler_id;
- gulong stream_removed_handler_id;
-};
-
-enum
-{
- PROP_INPUTS = 1,
- PROP_OUTPUTS,
- PROP_DEFAULT_INPUT,
- PROP_DEFAULT_OUTPUT,
- N_PROPS,
-};
-
-static GParamSpec* props[N_PROPS] = { 0 };
-
-static void foobar_audio_service_class_init ( FoobarAudioServiceClass* klass );
-static void foobar_audio_service_init ( FoobarAudioService* self );
-static void foobar_audio_service_get_property ( GObject* object,
- guint prop_id,
- GValue* value,
- GParamSpec* pspec );
-static void foobar_audio_service_finalize ( GObject* object );
-static void foobar_audio_service_handle_default_sink_changed ( GvcMixerControl* control,
- guint id,
- gpointer userdata );
-static void foobar_audio_service_handle_default_source_changed( GvcMixerControl* control,
- guint id,
- gpointer userdata );
-static void foobar_audio_service_handle_stream_added ( GvcMixerControl* control,
- guint id,
- gpointer userdata );
-static void foobar_audio_service_handle_stream_removed ( GvcMixerControl* control,
- guint id,
- gpointer userdata );
-static gboolean foobar_audio_service_identify_stream ( GvcMixerStream* stream,
- FoobarAudioDeviceKind* out_kind );
-static void foobar_audio_service_update_default ( GListModel* list,
- guint id );
-static gint foobar_audio_service_sort_func ( gconstpointer item_a,
- gconstpointer item_b,
- gpointer userdata );
-static gboolean foobar_audio_service_input_filter_func ( gpointer item,
- gpointer userdata );
-static gboolean foobar_audio_service_output_filter_func ( gpointer item,
- gpointer userdata );
-
-G_DEFINE_FINAL_TYPE( FoobarAudioService, foobar_audio_service, G_TYPE_OBJECT )
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Device
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Static initialization for audio devices.
-//
-void foobar_audio_device_class_init( FoobarAudioDeviceClass* klass )
-{
- GObjectClass* object_klass = G_OBJECT_CLASS( klass );
- object_klass->get_property = foobar_audio_device_get_property;
- object_klass->set_property = foobar_audio_device_set_property;
- object_klass->finalize = foobar_audio_device_finalize;
-
- device_props[DEVICE_PROP_KIND] = g_param_spec_enum(
- "kind",
- "Kind",
- "The kind of audio device.",
- FOOBAR_TYPE_AUDIO_DEVICE_KIND,
- FOOBAR_AUDIO_DEVICE_INPUT,
- G_PARAM_READABLE );
- device_props[DEVICE_PROP_ID] = g_param_spec_uint(
- "id",
- "ID",
- "Current numeric ID of the device (may change over time).",
- 0,
- UINT_MAX,
- UINT_MAX,
- G_PARAM_READABLE );
- device_props[DEVICE_PROP_NAME] = g_param_spec_string(
- "name",
- "Name",
- "Current name of the device.",
- NULL,
- G_PARAM_READABLE );
- device_props[DEVICE_PROP_DESCRIPTION] = g_param_spec_string(
- "description",
- "Description",
- "Short textual description of the device.",
- NULL,
- G_PARAM_READABLE );
- device_props[DEVICE_PROP_VOLUME] = g_param_spec_int(
- "volume",
- "Volume",
- "Current volume in percent.",
- 0,
- 100,
- 0,
- G_PARAM_READWRITE );
- device_props[DEVICE_PROP_IS_MUTED] = g_param_spec_int(
- "is-muted",
- "Is Muted",
- "Indicates whether the device is currently muted (i.e. the volume is 0).",
- 0,
- 100,
- 0,
- G_PARAM_READWRITE );
- device_props[DEVICE_PROP_IS_DEFAULT] = g_param_spec_boolean(
- "is-default",
- "Is Default",
- "Indicates whether the device is currently also a default device (either microphone or speaker).",
- FALSE,
- G_PARAM_READABLE );
- device_props[DEVICE_PROP_IS_AVAILABLE] = g_param_spec_boolean(
- "is-available",
- "Is Available",
- "Indicates whether the device is currently available to use/plugged in.",
- FALSE,
- G_PARAM_READABLE );
- g_object_class_install_properties( object_klass, N_DEVICE_PROPS, device_props );
-}
-
-//
-// Instance initialization for audio devices.
-//
-void foobar_audio_device_init( FoobarAudioDevice* self )
-{
- (void)self;
-}
-
-//
-// Property getter implementation, mapping a property id to a method.
-//
-void foobar_audio_device_get_property(
- GObject* object,
- guint prop_id,
- GValue* value,
- GParamSpec* pspec )
-{
- FoobarAudioDevice* self = (FoobarAudioDevice*)object;
-
- switch ( prop_id )
- {
- case DEVICE_PROP_KIND:
- g_value_set_enum( value, foobar_audio_device_get_kind( self ) );
- break;
- case DEVICE_PROP_ID:
- g_value_set_uint( value, foobar_audio_device_get_id( self ) );
- break;
- case DEVICE_PROP_NAME:
- g_value_set_string( value, foobar_audio_device_get_name( self ) );
- break;
- case DEVICE_PROP_DESCRIPTION:
- g_value_set_string( value, foobar_audio_device_get_description( self ) );
- break;
- case DEVICE_PROP_VOLUME:
- g_value_set_int( value, foobar_audio_device_get_volume( self ) );
- break;
- case DEVICE_PROP_IS_MUTED:
- g_value_set_boolean( value, foobar_audio_device_is_muted( self ) );
- break;
- case DEVICE_PROP_IS_DEFAULT:
- g_value_set_boolean( value, foobar_audio_device_is_default( self ) );
- break;
- case DEVICE_PROP_IS_AVAILABLE:
- g_value_set_boolean( value, foobar_audio_device_is_available( self ) );
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID( object, prop_id, pspec );
- break;
- }
-}
-
-//
-// Property setter implementation, mapping a property id to a method.
-//
-void foobar_audio_device_set_property(
- GObject* object,
- guint prop_id,
- GValue const* value,
- GParamSpec* pspec )
-{
- FoobarAudioDevice* self = (FoobarAudioDevice*)object;
-
- switch ( prop_id )
- {
- case DEVICE_PROP_VOLUME:
- foobar_audio_device_set_volume( self, g_value_get_int( value ) );
- break;
- case DEVICE_PROP_IS_MUTED:
- foobar_audio_device_set_muted( self, g_value_get_boolean( value ) );
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID( object, prop_id, pspec );
- break;
- }
-}
-
-//
-// Instance cleanup for audio devices.
-//
-void foobar_audio_device_finalize( GObject* object )
-{
- FoobarAudioDevice* self = (FoobarAudioDevice*)object;
-
- g_clear_signal_handler( &self->notify_handler_id, self->stream );
- g_clear_object( &self->stream );
-
- G_OBJECT_CLASS( foobar_audio_device_parent_class )->finalize( object );
-}
-
-//
-// Create a new audio device object with the specified parent service (captured as an unowned reference).
-//
-FoobarAudioDevice* foobar_audio_device_new(
- FoobarAudioService* service,
- FoobarAudioDeviceKind kind )
-{
- FoobarAudioDevice* self = g_object_new( FOOBAR_TYPE_AUDIO_DEVICE, NULL );
- self->service = service;
- self->kind = kind;
- return self;
-}
-
-//
-// Get the type of this device.
-//
-FoobarAudioDeviceKind foobar_audio_device_get_kind( FoobarAudioDevice* self )
-{
- g_return_val_if_fail( FOOBAR_IS_AUDIO_DEVICE( self ), 0 );
- return self->kind;
-}
-
-//
-// Get a numeric identifier for the device provided by the system.
-//
-guint foobar_audio_device_get_id( FoobarAudioDevice* self )
-{
- g_return_val_if_fail( FOOBAR_IS_AUDIO_DEVICE( self ), 0 );
- return self->stream ? gvc_mixer_stream_get_id( self->stream ) : UINT_MAX;
-}
-
-//
-// Get the internal name for the device. This should not be used in the UI.
-//
-gchar const* foobar_audio_device_get_name( FoobarAudioDevice* self )
-{
- g_return_val_if_fail( FOOBAR_IS_AUDIO_DEVICE( self ), NULL );
- return self->stream ? gvc_mixer_stream_get_name( self->stream ) : NULL;
-}
-
-//
-// Get a human-readable description for the device.
-//
-gchar const* foobar_audio_device_get_description( FoobarAudioDevice* self )
-{
- g_return_val_if_fail( FOOBAR_IS_AUDIO_DEVICE( self ), NULL );
- return self->stream ? gvc_mixer_stream_get_description( self->stream ) : NULL;
-}
-
-//
-// Get the current volume as a percentage value. If muted, this is 0.
-//
-gint foobar_audio_device_get_volume( FoobarAudioDevice* self )
-{
- g_return_val_if_fail( FOOBAR_IS_AUDIO_DEVICE( self ), 0 );
-
- if ( !self->service ) { return 0; }
- if ( self->stream && gvc_mixer_stream_get_is_muted( self->stream ) ) { return 0; }
- gdouble max_volume = gvc_mixer_control_get_vol_max_norm( self->service->control );
- gdouble cur_volume = self->stream ? gvc_mixer_stream_get_volume( self->stream ) : 0;
- gint percentage = (gint)round( 100. * cur_volume / max_volume );
- return CLAMP( percentage, 0, 100 );
-}
-
-//
-// Check whether the device is currently muted, i.e. its volume is 0.
-//
-gboolean foobar_audio_device_is_muted( FoobarAudioDevice* self )
-{
- g_return_val_if_fail( FOOBAR_IS_AUDIO_DEVICE( self ), FALSE );
- return foobar_audio_device_get_volume( self ) == 0;
-}
-
-//
-// Check whether this device is the system-default for its kind.
-//
-// This will always return TRUE for the static default device instance.
-//
-gboolean foobar_audio_device_is_default( FoobarAudioDevice* self )
-{
- g_return_val_if_fail( FOOBAR_IS_AUDIO_DEVICE( self ), FALSE );
- return self->is_default;
-}
-
-//
-// Check whether the device is currently available.
-//
-gboolean foobar_audio_device_is_available( FoobarAudioDevice* self )
-{
- g_return_val_if_fail( FOOBAR_IS_AUDIO_DEVICE( self ), FALSE );
- return self->stream != NULL;
-}
-
-//
-// Update the stream backing this device object.
-//
-void foobar_audio_device_set_stream(
- FoobarAudioDevice* self,
- GvcMixerStream* value )
-{
- g_return_if_fail( FOOBAR_IS_AUDIO_DEVICE( self ) );
-
- if ( self->stream != value )
- {
- g_clear_signal_handler( &self->notify_handler_id, self->stream );
- g_clear_object( &self->stream );
-
- if ( value )
- {
- self->stream = g_object_ref( value );
- self->notify_handler_id = g_signal_connect(
- self->stream,
- "notify",
- G_CALLBACK( foobar_audio_device_handle_notify ),
- self );
- }
-
- g_object_notify_by_pspec( G_OBJECT( self ), device_props[DEVICE_PROP_ID] );
- g_object_notify_by_pspec( G_OBJECT( self ), device_props[DEVICE_PROP_NAME] );
- g_object_notify_by_pspec( G_OBJECT( self ), device_props[DEVICE_PROP_DESCRIPTION] );
- g_object_notify_by_pspec( G_OBJECT( self ), device_props[DEVICE_PROP_VOLUME] );
- g_object_notify_by_pspec( G_OBJECT( self ), device_props[DEVICE_PROP_IS_MUTED] );
- g_object_notify_by_pspec( G_OBJECT( self ), device_props[DEVICE_PROP_IS_AVAILABLE] );
- }
-}
-
-//
-// Update the device's "default" flag. This does not actually make it the default device.
-//
-void foobar_audio_device_set_default(
- FoobarAudioDevice* self,
- gboolean value )
-{
- g_return_if_fail( FOOBAR_IS_AUDIO_DEVICE( self ) );
-
- value = !!value;
- if ( self->is_default != value )
- {
- self->is_default = value;
- g_object_notify_by_pspec( G_OBJECT( self ), device_props[DEVICE_PROP_IS_DEFAULT] );
- }
-}
-
-//
-// Update the device's volume as a percentage value.
-//
-void foobar_audio_device_set_volume(
- FoobarAudioDevice* self,
- gint value )
-{
- g_return_if_fail( FOOBAR_IS_AUDIO_DEVICE( self ) );
-
- if ( !self->service || !self->stream ) { return; }
-
- value = CLAMP( value, 0, 100 );
- if ( foobar_audio_device_get_volume( self ) != value )
- {
- gdouble max_volume = gvc_mixer_control_get_vol_max_norm( self->service->control );
- gdouble new_volume = value * max_volume / 100.;
- gvc_mixer_stream_set_volume( self->stream, (guint32)new_volume );
- gvc_mixer_stream_push_volume( self->stream );
- gvc_mixer_stream_set_is_muted( self->stream, value == 0 );
- gvc_mixer_stream_change_is_muted( self->stream, value == 0 );
- }
-}
-
-//
-// Update the device's "muted" state.
-//
-// If the device was previously muted, the volume is set to either the previous volume or to a default value.
-//
-void foobar_audio_device_set_muted(
- FoobarAudioDevice* self,
- gboolean value )
-{
- g_return_if_fail( FOOBAR_IS_AUDIO_DEVICE( self ) );
-
- if ( foobar_audio_device_is_muted( self ) != value )
- {
- if ( self->stream )
- {
- gvc_mixer_stream_set_is_muted( self->stream, value );
- gvc_mixer_stream_change_is_muted( self->stream, value );
- }
-
- if ( !value && foobar_audio_device_get_volume( self ) == 0 )
- {
- foobar_audio_device_set_volume( self, DEFAULT_VOLUME );
- }
- }
-}
-
-//
-// Let this device become default one for its kind.
-//
-void foobar_audio_device_make_default( FoobarAudioDevice* self )
-{
- g_return_if_fail( FOOBAR_IS_AUDIO_DEVICE( self ) );
-
- if ( !self->service || !self->stream ) { return; }
-
- if ( !foobar_audio_device_is_default( self ) )
- {
- switch ( self->kind )
- {
- case FOOBAR_AUDIO_DEVICE_INPUT:
- gvc_mixer_control_set_default_source( self->service->control, self->stream );
- break;
- case FOOBAR_AUDIO_DEVICE_OUTPUT:
- gvc_mixer_control_set_default_sink( self->service->control, self->stream );
- break;
- default:
- break;
- }
- }
-}
-
-//
-// Called by the underlying stream when one of its properties changes.
-//
-void foobar_audio_device_handle_notify(
- GObject* object,
- GParamSpec* pspec,
- gpointer userdata )
-{
- (void)object;
- FoobarAudioDevice* self = (FoobarAudioDevice*)userdata;
-
- gchar const* property = g_param_spec_get_name( pspec );
- if ( !g_strcmp0( property, "id" ) )
- {
- g_object_notify_by_pspec( G_OBJECT( self ), device_props[DEVICE_PROP_ID] );
- }
- else if ( !g_strcmp0( property, "name" ) )
- {
- g_object_notify_by_pspec( G_OBJECT( self ), device_props[DEVICE_PROP_NAME] );
- }
- else if ( !g_strcmp0( property, "description" ) )
- {
- g_object_notify_by_pspec( G_OBJECT( self ), device_props[DEVICE_PROP_DESCRIPTION] );
- }
- else if ( !g_strcmp0( property, "volume" ) || !g_strcmp0( property, "is-muted" ) )
- {
- g_object_notify_by_pspec( G_OBJECT( self ), device_props[DEVICE_PROP_VOLUME] );
- g_object_notify_by_pspec( G_OBJECT( self ), device_props[DEVICE_PROP_IS_MUTED] );
- }
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Service Implementation
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Static initialization for the audio service.
-//
-void foobar_audio_service_class_init( FoobarAudioServiceClass* klass )
-{
- GObjectClass* object_klass = G_OBJECT_CLASS( klass );
- object_klass->get_property = foobar_audio_service_get_property;
- object_klass->finalize = foobar_audio_service_finalize;
-
- props[PROP_INPUTS] = g_param_spec_object(
- "inputs",
- "Inputs",
- "Sorted list of input devices.",
- G_TYPE_LIST_MODEL,
- G_PARAM_READABLE );
- props[PROP_OUTPUTS] = g_param_spec_object(
- "outputs",
- "Outputs",
- "Sorted list of output devices.",
- G_TYPE_LIST_MODEL,
- G_PARAM_READABLE );
- props[PROP_DEFAULT_INPUT] = g_param_spec_object(
- "default-input",
- "Default Input",
- "The default input device (reference remains constant).",
- FOOBAR_TYPE_AUDIO_DEVICE,
- G_PARAM_READABLE );
- props[PROP_DEFAULT_OUTPUT] = g_param_spec_object(
- "default-output",
- "Default Output",
- "The default output device (reference remains constant).",
- FOOBAR_TYPE_AUDIO_DEVICE,
- G_PARAM_READABLE );
- g_object_class_install_properties( object_klass, N_PROPS, props );
-}
-
-//
-// Instance initialization for the audio service.
-//
-void foobar_audio_service_init( FoobarAudioService* self )
-{
- self->control = gvc_mixer_control_new( "Foobar mixer control" );
- self->devices = g_list_store_new( FOOBAR_TYPE_AUDIO_DEVICE );
-
- GtkCustomSorter* sorter = gtk_custom_sorter_new( foobar_audio_service_sort_func, NULL, NULL );
- self->sorted_devices = gtk_sort_list_model_new(
- G_LIST_MODEL( g_object_ref( self->devices ) ),
- GTK_SORTER( sorter ) );
-
- GtkCustomFilter* input_filter = gtk_custom_filter_new( foobar_audio_service_input_filter_func, NULL, NULL );
- self->inputs = gtk_filter_list_model_new(
- G_LIST_MODEL( g_object_ref( self->sorted_devices ) ),
- GTK_FILTER( input_filter ) );
-
- GtkCustomFilter* output_filter = gtk_custom_filter_new( foobar_audio_service_output_filter_func, NULL, NULL );
- self->outputs = gtk_filter_list_model_new(
- G_LIST_MODEL( g_object_ref( self->sorted_devices ) ),
- GTK_FILTER( output_filter ) );
-
- self->default_input = foobar_audio_device_new( self, FOOBAR_AUDIO_DEVICE_INPUT );
- self->default_output = foobar_audio_device_new( self, FOOBAR_AUDIO_DEVICE_OUTPUT );
- foobar_audio_device_set_default( self->default_input, TRUE );
- foobar_audio_device_set_default( self->default_output, TRUE );
-
- self->default_sink_handler_id = g_signal_connect(
- self->control,
- "default-sink-changed",
- G_CALLBACK( foobar_audio_service_handle_default_sink_changed ),
- self );
- self->default_source_handler_id = g_signal_connect(
- self->control,
- "default-source-changed",
- G_CALLBACK( foobar_audio_service_handle_default_source_changed ),
- self );
- self->stream_added_handler_id = g_signal_connect(
- self->control,
- "stream-added",
- G_CALLBACK( foobar_audio_service_handle_stream_added ),
- self );
- self->stream_removed_handler_id = g_signal_connect(
- self->control,
- "stream-removed",
- G_CALLBACK( foobar_audio_service_handle_stream_removed ),
- self );
-
- gvc_mixer_control_open( self->control );
-}
-
-//
-// Property getter implementation, mapping a property id to a method.
-//
-void foobar_audio_service_get_property(
- GObject* object,
- guint prop_id,
- GValue* value,
- GParamSpec* pspec )
-{
- FoobarAudioService* self = (FoobarAudioService*)object;
-
- switch ( prop_id )
- {
- case PROP_INPUTS:
- g_value_set_object( value, foobar_audio_service_get_inputs( self ) );
- break;
- case PROP_OUTPUTS:
- g_value_set_object( value, foobar_audio_service_get_outputs( self ) );
- break;
- case PROP_DEFAULT_INPUT:
- g_value_set_object( value, foobar_audio_service_get_default_input( self ) );
- break;
- case PROP_DEFAULT_OUTPUT:
- g_value_set_object( value, foobar_audio_service_get_default_output( self ) );
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID( object, prop_id, pspec );
- break;
- }
-}
-
-//
-// Instance cleanup for the audio service.
-//
-void foobar_audio_service_finalize( GObject* object )
-{
- FoobarAudioService* self = (FoobarAudioService*)object;
-
- g_clear_signal_handler( &self->default_sink_handler_id, self->control );
- g_clear_signal_handler( &self->default_source_handler_id, self->control );
- g_clear_signal_handler( &self->stream_added_handler_id, self->control );
- g_clear_signal_handler( &self->stream_removed_handler_id, self->control );
-
- gvc_mixer_control_close( self->control );
-
- for ( guint i = 0; i < g_list_model_get_n_items( G_LIST_MODEL( self->devices ) ); ++i )
- {
- FoobarAudioDevice* device = g_list_model_get_item( G_LIST_MODEL( self->devices ), i );
- device->service = NULL;
- }
-
- self->default_input->service = NULL;
- self->default_output->service = NULL;
-
- g_clear_object( &self->control );
- g_clear_object( &self->inputs );
- g_clear_object( &self->outputs );
- g_clear_object( &self->sorted_devices );
- g_clear_object( &self->devices );
- g_clear_object( &self->default_input );
- g_clear_object( &self->default_output );
-
- G_OBJECT_CLASS( foobar_audio_service_parent_class )->finalize( object );
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Public API
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Create a new audio service instance.
-//
-FoobarAudioService* foobar_audio_service_new( void )
-{
- return g_object_new( FOOBAR_TYPE_AUDIO_SERVICE, NULL );
-}
-
-//
-// Get the default input device. This reference remains constant.
-//
-FoobarAudioDevice* foobar_audio_service_get_default_input( FoobarAudioService* self )
-{
- g_return_val_if_fail( FOOBAR_IS_AUDIO_SERVICE( self ), NULL );
- return self->default_input;
-}
-
-//
-// Get the default output device. This reference remains constant.
-//
-FoobarAudioDevice* foobar_audio_service_get_default_output( FoobarAudioService* self )
-{
- g_return_val_if_fail( FOOBAR_IS_AUDIO_SERVICE( self ), NULL );
- return self->default_output;
-}
-
-//
-// Get a sorted list of input devices.
-//
-GListModel* foobar_audio_service_get_inputs( FoobarAudioService* self )
-{
- g_return_val_if_fail( FOOBAR_IS_AUDIO_SERVICE( self ), NULL );
- return G_LIST_MODEL( self->inputs );
-}
-
-//
-// Get a sorted list of output devices.
-//
-GListModel* foobar_audio_service_get_outputs( FoobarAudioService* self )
-{
- g_return_val_if_fail( FOOBAR_IS_AUDIO_SERVICE( self ), NULL );
- return G_LIST_MODEL( self->outputs );
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Signal Handlers
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Called by the mixer control when the default output device has changed.
-//
-// This will update the "default" state of the device and set the backing stream for the static default device.
-//
-void foobar_audio_service_handle_default_sink_changed(
- GvcMixerControl* control,
- guint id,
- gpointer userdata )
-{
- (void)control;
- FoobarAudioService* self = (FoobarAudioService*)userdata;
-
- foobar_audio_service_update_default( G_LIST_MODEL( self->outputs ), id );
-
- GvcMixerStream* stream = gvc_mixer_control_lookup_stream_id( self->control, id );
- foobar_audio_device_set_stream( self->default_output, stream );
-}
-
-//
-// Called by the mixer control when the default input device has changed.
-//
-// This will update the "default" state of the device and set the backing stream for the static default device.
-//
-void foobar_audio_service_handle_default_source_changed(
- GvcMixerControl* control,
- guint id,
- gpointer userdata )
-{
- (void)control;
- FoobarAudioService* self = (FoobarAudioService*)userdata;
-
- foobar_audio_service_update_default( G_LIST_MODEL( self->inputs ), id );
-
- GvcMixerStream* stream = gvc_mixer_control_lookup_stream_id( self->control, id );
- foobar_audio_device_set_stream( self->default_input, stream );
-}
-
-//
-// Called when an audio device was added.
-//
-void foobar_audio_service_handle_stream_added(
- GvcMixerControl* control,
- guint id,
- gpointer userdata )
-{
- (void)control;
- FoobarAudioService* self = (FoobarAudioService*)userdata;
-
- GvcMixerStream* stream = gvc_mixer_control_lookup_stream_id( self->control, id );
- FoobarAudioDeviceKind kind;
- if ( foobar_audio_service_identify_stream( stream, &kind ) )
- {
- g_autoptr( FoobarAudioDevice ) device = foobar_audio_device_new( self, kind );
- foobar_audio_device_set_stream( device, stream );
- g_list_store_append( self->devices, device );
- }
-}
-
-//
-// Called when an audio device was removed.
-//
-void foobar_audio_service_handle_stream_removed(
- GvcMixerControl* control,
- guint id,
- gpointer userdata )
-{
- (void)control;
- FoobarAudioService* self = (FoobarAudioService*)userdata;
-
- for ( guint i = 0; i < g_list_model_get_n_items( G_LIST_MODEL( self->devices ) ); ++i )
- {
- FoobarAudioDevice* device = g_list_model_get_item( G_LIST_MODEL( self->devices ), i );
- if ( foobar_audio_device_get_id( device ) == id )
- {
- g_object_ref( device );
- device->service = NULL;
- g_list_store_remove( self->devices, i );
- foobar_audio_device_set_stream( device, NULL );
- g_object_unref( device );
- break;
- }
- }
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-// Helper Methods
-// ---------------------------------------------------------------------------------------------------------------------
-
-//
-// Identify whether a stream is an input device or an output device, returning FALSE if unknown.
-//
-gboolean foobar_audio_service_identify_stream(
- GvcMixerStream* stream,
- FoobarAudioDeviceKind* out_kind )
-{
- if ( GVC_IS_MIXER_SINK( stream ) )
- {
- *out_kind = FOOBAR_AUDIO_DEVICE_OUTPUT;
- return TRUE;
- }
-
- if ( GVC_IS_MIXER_SOURCE( stream ) )
- {
- *out_kind = FOOBAR_AUDIO_DEVICE_INPUT;
- return TRUE;
- }
-
- return FALSE;
-}
-
-//
-// Set the "default" state for the device with the specified ID to TRUE and all other devices to FALSE.
-//
-void foobar_audio_service_update_default(
- GListModel* list,
- guint id )
-{
- for ( guint i = 0; i < g_list_model_get_n_items( list ); ++i )
- {
- FoobarAudioDevice* device = g_list_model_get_item( list, i );
- gboolean is_default = foobar_audio_device_get_id( device ) == id;
- foobar_audio_device_set_default( device, is_default );
- }
-}
-
-//
-// Sorting callback for audio devices.
-//
-gint foobar_audio_service_sort_func(
- gconstpointer item_a,
- gconstpointer item_b,
- gpointer userdata )
-{
- (void)userdata;
-
- FoobarAudioDevice* device_a = (FoobarAudioDevice*)item_a;
- FoobarAudioDevice* device_b = (FoobarAudioDevice*)item_b;
- return g_strcmp0(
- foobar_audio_device_get_description( device_a ),
- foobar_audio_device_get_description( device_b ) );
-}
-
-//
-// Filtering callback for the list of input devices.
-//
-gboolean foobar_audio_service_input_filter_func(
- gpointer item,
- gpointer userdata )
-{
- (void)userdata;
-
- FoobarAudioDevice* device = item;
- return foobar_audio_device_get_kind( device ) == FOOBAR_AUDIO_DEVICE_INPUT;
-}
-
-//
-// Filtering callback for the list of output devices.
-//
-gboolean foobar_audio_service_output_filter_func(
- gpointer item,
- gpointer userdata )
-{
- (void)userdata;
-
- FoobarAudioDevice* device = item;
- return foobar_audio_device_get_kind( device ) == FOOBAR_AUDIO_DEVICE_OUTPUT;
-}