alsa-acp: Add libacp based card device

libacp is a port and wrapper around the pulseaudio card profile code.
It uses a set of templates for construct a card profile and mixer port
settings. It also has support for UCM when available for the hardware.
This commit is contained in:
Wim Taymans 2020-05-15 19:42:15 +02:00
parent 5c6247daef
commit 1612f5e4d2
31 changed files with 15304 additions and 11 deletions

View file

@ -453,3 +453,6 @@
#mesondefine PIPEWIRE_VERSION_MAJOR
#mesondefine PIPEWIRE_VERSION_MINOR
#mesondefine PIPEWIRE_VERSION_MICRO
#mesondefine PA_ALSA_PATHS_DIR
#mesondefine PA_ALSA_PROFILE_SETS_DIR

View file

@ -52,6 +52,7 @@ else
endif
spa_plugindir = join_paths(pipewire_libdir, spa_name)
alsadatadir = join_paths('/usr', 'share', 'pulseaudio', 'alsa-mixer')
pipewire_headers_dir = join_paths(pipewire_name, 'pipewire')
@ -179,6 +180,8 @@ cdata.set('VERSION', '"@0@"'.format(pipewire_version))
cdata.set('PLUGINDIR', '"@0@"'.format(spa_plugindir))
# FIXME: --with-memory-alignment],[8,N,malloc,pagesize (default is 32)]) option
cdata.set('MEMORY_ALIGNMENT_MALLOC', 1)
cdata.set_quoted('PA_ALSA_PATHS_DIR', join_paths(alsadatadir, 'paths'))
cdata.set_quoted('PA_ALSA_PROFILE_SETS_DIR', join_paths(alsadatadir, 'profile-sets'))
check_headers = [['dlfcn.h','HAVE_DLFCN_H'],

View file

@ -38,9 +38,9 @@ extern "C" {
static inline int spa_debug_dict(int indent, const struct spa_dict *dict)
{
const struct spa_dict_item *item;
spa_debug("%*s flags:%08x n_items:%d", indent, "", dict->flags, dict->n_items);
spa_debug("%*sflags:%08x n_items:%d", indent, "", dict->flags, dict->n_items);
spa_dict_for_each(item, dict) {
spa_debug("%*s%s = \"%s\"", indent, "", item->key, item->value);
spa_debug("%*s %s = \"%s\"", indent, "", item->key, item->value);
}
return 0;
}

View file

@ -98,6 +98,7 @@ extern "C" {
* playback of midi */
#define SPA_NAME_API_ALSA_SEQ_BRIDGE "api.alsa.seq.bridge" /**< an alsa Node interface for
* bridging midi ports */
#define SPA_NAME_API_ALSA_ACP_DEVICE "api.alsa.acp.device" /**< an alsa ACP Device interface */
/** keys for bluez5 factory names */
#define SPA_NAME_API_BLUEZ5_ENUM_DBUS "api.bluez5.enum.dbus" /**< a dbus Device interface */

1352
spa/plugins/alsa/acp/acp.c Normal file

File diff suppressed because it is too large Load diff

239
spa/plugins/alsa/acp/acp.h Normal file
View file

@ -0,0 +1,239 @@
/* ALSA Card Profile
*
* Copyright © 2020 Wim Taymans
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifndef ACP_H
#define ACP_H
#ifdef __cplusplus
extern "C" {
#else
#include <stdbool.h>
#endif
#include <stdio.h>
#include <stdarg.h>
#include <stdint.h>
#include <poll.h>
#ifdef __GNUC__
#define ACP_PRINTF_FUNC(fmt, arg1) __attribute__((format(printf, fmt, arg1)))
#else
#define ACP_PRINTF_FUNC(fmt, arg1)
#endif
struct acp_dict_item {
const char *key;
const char *value;
};
#define ACP_DICT_ITEM_INIT(key,value) (struct acp_dict_item) { key, value }
struct acp_dict {
uint32_t flags;
uint32_t n_items;
const struct acp_dict_item *items;
};
struct acp_format {
uint32_t flags;
uint32_t format_mask;
uint32_t rate_mask;
uint32_t channels;
uint32_t map[64];
};
#define ACP_DICT_INIT(items,n_items) (struct acp_dict) { 0, n_items, items }
#define ACP_DICT_INIT_ARRAY(items) (struct acp_dict) { 0, sizeof(items)/sizeof((items)[0]), items }
#define acp_dict_for_each(item, dict) \
for ((item) = (dict)->items; \
(item) < &(dict)->items[(dict)->n_items]; \
(item)++)
enum acp_direction {
ACP_DIRECTION_PLAYBACK = 1,
ACP_DIRECTION_CAPTURE = 2
};
enum acp_available {
ACP_AVAILABLE_UNKNOWN = 0,
ACP_AVAILABLE_NO = 1,
ACP_AVAILABLE_YES = 2
};
enum acp_port_type {
ACP_PORT_TYPE_UNKNOWN = 0,
ACP_PORT_TYPE_AUX = 1,
ACP_PORT_TYPE_SPEAKER = 2,
ACP_PORT_TYPE_HEADPHONES = 3,
ACP_PORT_TYPE_LINE = 4,
ACP_PORT_TYPE_MIC = 5,
ACP_PORT_TYPE_HEADSET = 6,
ACP_PORT_TYPE_HANDSET = 7,
ACP_PORT_TYPE_EARPIECE = 8,
ACP_PORT_TYPE_SPDIF = 9,
ACP_PORT_TYPE_HDMI = 10,
ACP_PORT_TYPE_TV = 11,
ACP_PORT_TYPE_RADIO = 12,
ACP_PORT_TYPE_VIDEO = 13,
ACP_PORT_TYPE_USB = 14,
ACP_PORT_TYPE_BLUETOOTH = 15,
ACP_PORT_TYPE_PORTABLE = 16,
ACP_PORT_TYPE_HANDSFREE = 17,
ACP_PORT_TYPE_CAR = 18,
ACP_PORT_TYPE_HIFI = 19,
ACP_PORT_TYPE_PHONE = 20,
ACP_PORT_TYPE_NETWORK = 21,
ACP_PORT_TYPE_ANALOG = 22,
};
struct acp_device;
struct acp_card_events {
#define ACP_VERSION_CARD_EVENTS 0
uint32_t version;
void (*destroy) (void *data);
void (*props_changed) (void *data);
void (*profile_changed) (void *data, uint32_t old_index, uint32_t new_index);
void (*profile_available) (void *data, uint32_t index,
enum acp_available old, enum acp_available available);
void (*port_available) (void *data, uint32_t index,
enum acp_available old, enum acp_available available);
void (*volume_changed) (void *data, struct acp_device *dev);
void (*mute_changed) (void *data, struct acp_device *dev);
};
struct acp_port {
uint32_t index;
#define ACP_PORT_ACTIVE (1<<0)
uint32_t flags;
char *name;
char *description;
uint32_t priority;
enum acp_direction direction;
enum acp_available available;
char *available_group; /* a string indentifier which determine the group of devices
* handling the available state simulteneously */
enum acp_port_type type;
struct acp_dict props;
uint32_t n_profiles;
struct acp_card_profile **profiles;
};
struct acp_device {
uint32_t index;
#define ACP_DEVICE_ACTIVE (1<<0)
#define ACP_DEVICE_HW_VOLUME (1<<1)
#define ACP_DEVICE_HW_MUTE (1<<2)
uint32_t flags;
char *name;
char *description;
uint32_t priority;
enum acp_direction direction;
struct acp_dict props;
char **device_strings;
struct acp_format format;
struct acp_port **ports;
uint32_t n_ports;
};
struct acp_card_profile {
uint32_t index;
#define ACP_PROFILE_ACTIVE (1<<0)
uint32_t flags;
char *name;
char *description;
uint32_t priority;
enum acp_available available;
struct acp_dict props;
uint32_t n_devices;
struct acp_device **devices;
};
struct acp_card {
uint32_t index;
uint32_t flags;
struct acp_dict props;
uint32_t n_profiles;
struct acp_card_profile **profiles;
uint32_t active_profile_index;
uint32_t n_devices;
struct acp_device **devices;
uint32_t n_ports;
struct acp_port **ports;
uint32_t preferred_input_port_index;
uint32_t preferred_output_port_index;
};
struct acp_card *acp_card_new(uint32_t index, const struct acp_dict *props);
void acp_card_add_listener(struct acp_card *card,
const struct acp_card_events *events, void *user_data);
void acp_card_destroy(struct acp_card *card);
int acp_card_poll_descriptors_count(struct acp_card *card);
int acp_card_poll_descriptors(struct acp_card *card, struct pollfd *pfds, unsigned int space);
int acp_card_poll_descriptors_revents(struct acp_card *card, struct pollfd *pfds,
unsigned int nfds, unsigned short *revents);
int acp_card_handle_events(struct acp_card *card);
int acp_card_set_profile(struct acp_card *card, uint32_t profile_index);
int acp_device_set_port(struct acp_device *dev, uint32_t port_index);
int acp_device_set_volume(struct acp_device *dev, const float *volume, uint32_t n_volume);
int acp_device_get_volume(struct acp_device *dev, float *volume, uint32_t n_volume);
int acp_device_set_mute(struct acp_device *dev, bool mute);
int acp_device_get_mute(struct acp_device *dev, bool *mute);
typedef void (*acp_log_func) (void *data,
int level, const char *file, int line, const char *func,
const char *fmt, va_list arg) ACP_PRINTF_FUNC(6,0);
void acp_set_log_func(acp_log_func, void *data);
void acp_set_log_level(int level);
#ifdef __cplusplus
}
#endif
#endif /* ACP_H */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,432 @@
#ifndef fooalsamixerhfoo
#define fooalsamixerhfoo
/***
This file is part of PulseAudio.
Copyright 2004-2006 Lennart Poettering
Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2.1 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
***/
#include <alsa/asoundlib.h>
typedef struct pa_alsa_mixer pa_alsa_mixer;
typedef struct pa_alsa_setting pa_alsa_setting;
typedef struct pa_alsa_mixer_id pa_alsa_mixer_id;
typedef struct pa_alsa_option pa_alsa_option;
typedef struct pa_alsa_element pa_alsa_element;
typedef struct pa_alsa_jack pa_alsa_jack;
typedef struct pa_alsa_path pa_alsa_path;
typedef struct pa_alsa_path_set pa_alsa_path_set;
typedef struct pa_alsa_mapping pa_alsa_mapping;
typedef struct pa_alsa_profile pa_alsa_profile;
typedef struct pa_alsa_decibel_fix pa_alsa_decibel_fix;
typedef struct pa_alsa_profile_set pa_alsa_profile_set;
typedef struct pa_alsa_port_data pa_alsa_port_data;
typedef struct pa_alsa_profile pa_alsa_profile;
typedef struct pa_alsa_device pa_alsa_device;
typedef enum pa_alsa_switch_use {
PA_ALSA_SWITCH_IGNORE,
PA_ALSA_SWITCH_MUTE, /* make this switch follow mute status */
PA_ALSA_SWITCH_OFF, /* set this switch to 'off' unconditionally */
PA_ALSA_SWITCH_ON, /* set this switch to 'on' unconditionally */
PA_ALSA_SWITCH_SELECT /* allow the user to select switch status through a setting */
} pa_alsa_switch_use_t;
typedef enum pa_alsa_volume_use {
PA_ALSA_VOLUME_IGNORE,
PA_ALSA_VOLUME_MERGE, /* merge this volume slider into the global volume slider */
PA_ALSA_VOLUME_OFF, /* set this volume to minimal unconditionally */
PA_ALSA_VOLUME_ZERO, /* set this volume to 0dB unconditionally */
PA_ALSA_VOLUME_CONSTANT /* set this volume to a constant value unconditionally */
} pa_alsa_volume_use_t;
typedef enum pa_alsa_enumeration_use {
PA_ALSA_ENUMERATION_IGNORE,
PA_ALSA_ENUMERATION_SELECT
} pa_alsa_enumeration_use_t;
typedef enum pa_alsa_required {
PA_ALSA_REQUIRED_IGNORE,
PA_ALSA_REQUIRED_SWITCH,
PA_ALSA_REQUIRED_VOLUME,
PA_ALSA_REQUIRED_ENUMERATION,
PA_ALSA_REQUIRED_ANY
} pa_alsa_required_t;
typedef enum pa_alsa_direction {
PA_ALSA_DIRECTION_ANY,
PA_ALSA_DIRECTION_OUTPUT,
PA_ALSA_DIRECTION_INPUT
} pa_alsa_direction_t;
#include "acp.h"
#include "device-port.h"
#include "alsa-util.h"
#include "alsa-ucm.h"
#include "card.h"
/* A setting combines a couple of options into a single entity that
* may be selected. Only one setting can be active at the same
* time. */
struct pa_alsa_setting {
pa_alsa_path *path;
PA_LLIST_FIELDS(pa_alsa_setting);
pa_idxset *options;
char *name;
char *description;
unsigned priority;
};
/* An entry for one ALSA mixer */
struct pa_alsa_mixer {
snd_mixer_t *mixer_handle;
int card_index;
bool used_for_probe_only:1;
bool used_for_poll:1;
};
/* ALSA mixer element identifier */
struct pa_alsa_mixer_id {
char *name;
int index;
};
/* An option belongs to an element and refers to one enumeration item
* of the element is an enumeration item, or a switch status if the
* element is a switch item. */
struct pa_alsa_option {
pa_alsa_element *element;
PA_LLIST_FIELDS(pa_alsa_option);
char *alsa_name;
int alsa_idx;
char *name;
char *description;
unsigned priority;
pa_alsa_required_t required;
pa_alsa_required_t required_any;
pa_alsa_required_t required_absent;
};
/* An element wraps one specific ALSA element. A series of elements
* make up a path (see below). If the element is an enumeration or switch
* element it may include a list of options. */
struct pa_alsa_element {
pa_alsa_path *path;
PA_LLIST_FIELDS(pa_alsa_element);
struct pa_alsa_mixer_id alsa_id;
pa_alsa_direction_t direction;
pa_alsa_switch_use_t switch_use;
pa_alsa_volume_use_t volume_use;
pa_alsa_enumeration_use_t enumeration_use;
pa_alsa_required_t required;
pa_alsa_required_t required_any;
pa_alsa_required_t required_absent;
long constant_volume;
bool override_map:1;
bool direction_try_other:1;
bool has_dB:1;
long min_volume, max_volume;
long volume_limit; /* -1 for no configured limit */
double min_dB, max_dB;
pa_channel_position_mask_t masks[SND_MIXER_SCHN_LAST + 1][2];
unsigned n_channels;
pa_channel_position_mask_t merged_mask;
PA_LLIST_HEAD(pa_alsa_option, options);
pa_alsa_decibel_fix *db_fix;
};
struct pa_alsa_jack {
pa_alsa_path *path;
PA_LLIST_FIELDS(pa_alsa_jack);
snd_mixer_t *mixer_handle;
char *mixer_device_name;
char *name; /* E g "Headphone" */
char *alsa_name; /* E g "Headphone Jack" */
bool has_control; /* is the jack itself present? */
bool plugged_in; /* is this jack currently plugged in? */
snd_mixer_elem_t *melem; /* Jack detection handle */
pa_available_t state_unplugged, state_plugged;
pa_alsa_required_t required;
pa_alsa_required_t required_any;
pa_alsa_required_t required_absent;
pa_dynarray *ucm_devices; /* pa_alsa_ucm_device */
pa_dynarray *ucm_hw_mute_devices; /* pa_alsa_ucm_device */
bool append_pcm_to_name;
};
pa_alsa_jack *pa_alsa_jack_new(pa_alsa_path *path, const char *mixer_device_name, const char *name);
void pa_alsa_jack_free(pa_alsa_jack *jack);
void pa_alsa_jack_set_has_control(pa_alsa_jack *jack, bool has_control);
void pa_alsa_jack_set_plugged_in(pa_alsa_jack *jack, bool plugged_in);
void pa_alsa_jack_add_ucm_device(pa_alsa_jack *jack, pa_alsa_ucm_device *device);
void pa_alsa_jack_add_ucm_hw_mute_device(pa_alsa_jack *jack, pa_alsa_ucm_device *device);
/* A path wraps a series of elements into a single entity which can be
* used to control it as if it had a single volume slider, a single
* mute switch and a single list of selectable options. */
struct pa_alsa_path {
pa_alsa_direction_t direction;
pa_device_port* port;
char *name;
char *description_key;
char *description;
char *available_group;
pa_device_port_type_t device_port_type;
unsigned priority;
bool autodetect_eld_device;
pa_alsa_mixer *eld_mixer_handle;
int eld_device;
pa_proplist *proplist;
bool probed:1;
bool supported:1;
bool has_mute:1;
bool has_volume:1;
bool has_dB:1;
bool mute_during_activation:1;
/* These two are used during probing only */
bool has_req_any:1;
bool req_any_present:1;
long min_volume, max_volume;
double min_dB, max_dB;
/* This is used during parsing only, as a shortcut so that we
* don't have to iterate the list all the time */
pa_alsa_element *last_element;
pa_alsa_option *last_option;
pa_alsa_setting *last_setting;
pa_alsa_jack *last_jack;
PA_LLIST_HEAD(pa_alsa_element, elements);
PA_LLIST_HEAD(pa_alsa_setting, settings);
PA_LLIST_HEAD(pa_alsa_jack, jacks);
};
/* A path set is simply a set of paths that are applicable to a
* device */
struct pa_alsa_path_set {
pa_hashmap *paths;
pa_alsa_direction_t direction;
};
void pa_alsa_setting_dump(pa_alsa_setting *s);
void pa_alsa_option_dump(pa_alsa_option *o);
void pa_alsa_jack_dump(pa_alsa_jack *j);
void pa_alsa_element_dump(pa_alsa_element *e);
pa_alsa_path *pa_alsa_path_new(const char *paths_dir, const char *fname, pa_alsa_direction_t direction);
pa_alsa_path *pa_alsa_path_synthesize(const char *element, pa_alsa_direction_t direction);
pa_alsa_element *pa_alsa_element_get(pa_alsa_path *p, const char *section, bool prefixed);
int pa_alsa_path_probe(pa_alsa_path *p, pa_alsa_mapping *mapping, snd_mixer_t *m, bool ignore_dB);
void pa_alsa_path_dump(pa_alsa_path *p);
int pa_alsa_path_get_volume(pa_alsa_path *p, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v);
int pa_alsa_path_get_mute(pa_alsa_path *path, snd_mixer_t *m, bool *muted);
int pa_alsa_path_set_volume(pa_alsa_path *path, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v, bool deferred_volume, bool write_to_hw);
int pa_alsa_path_set_mute(pa_alsa_path *path, snd_mixer_t *m, bool muted);
int pa_alsa_path_select(pa_alsa_path *p, pa_alsa_setting *s, snd_mixer_t *m, bool device_is_muted);
void pa_alsa_path_set_callback(pa_alsa_path *p, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata);
void pa_alsa_path_free(pa_alsa_path *p);
pa_alsa_path_set *pa_alsa_path_set_new(pa_alsa_mapping *m, pa_alsa_direction_t direction, const char *paths_dir);
void pa_alsa_path_set_dump(pa_alsa_path_set *s);
void pa_alsa_path_set_set_callback(pa_alsa_path_set *ps, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata);
void pa_alsa_path_set_free(pa_alsa_path_set *s);
int pa_alsa_path_set_is_empty(pa_alsa_path_set *s);
struct pa_alsa_device {
struct acp_device device;
pa_card *card;
pa_alsa_direction_t direction;
pa_proplist *proplist;
pa_alsa_mapping *mapping;
pa_alsa_ucm_mapping_context *ucm_context;
pa_hashmap *ports;
pa_dynarray port_array;
pa_device_port *active_port;
snd_mixer_t *mixer_handle;
pa_alsa_path_set *mixer_path_set;
pa_alsa_path *mixer_path;
snd_pcm_t *pcm_handle;
unsigned muted:1;
unsigned decibel_volume:1;
pa_cvolume real_volume;
pa_volume_t base_volume;
unsigned n_volume_steps;
int (*read_volume)(pa_alsa_device *dev);
int (*read_mute)(pa_alsa_device *dev);
void (*set_volume)(pa_alsa_device *dev, const pa_cvolume *v);
void (*set_mute)(pa_alsa_device *dev, bool m);
};
struct pa_alsa_mapping {
pa_alsa_profile_set *profile_set;
char *name;
char *description;
unsigned priority;
pa_alsa_direction_t direction;
/* These are copied over to the resultant sink/source */
pa_proplist *proplist;
pa_sample_spec sample_spec;
pa_channel_map channel_map;
char **device_strings;
char **input_path_names;
char **output_path_names;
char **input_element; /* list of fallbacks */
char **output_element;
pa_alsa_path_set *input_path_set;
pa_alsa_path_set *output_path_set;
unsigned supported;
bool exact_channels:1;
bool fallback:1;
/* The "y" in "hw:x,y". This is set to -1 before the device index has been
* queried, or if the query failed. */
int hw_device_index;
/* Temporarily used during probing */
snd_pcm_t *input_pcm;
snd_pcm_t *output_pcm;
pa_proplist *input_proplist;
pa_proplist *output_proplist;
pa_alsa_device output;
pa_alsa_device input;
/* ucm device context*/
pa_alsa_ucm_mapping_context ucm_context;
};
struct pa_alsa_profile {
struct acp_card_profile profile;
pa_alsa_profile_set *profile_set;
char *input_name;
char *output_name;
bool supported:1;
bool fallback_input:1;
bool fallback_output:1;
char **input_mapping_names;
char **output_mapping_names;
pa_idxset *input_mappings;
pa_idxset *output_mappings;
struct {
pa_dynarray devices;
} out;
};
struct pa_alsa_decibel_fix {
char *key;
pa_alsa_profile_set *profile_set;
char *name; /* Alsa volume element name. */
int index; /* Alsa volume element index. */
long min_step;
long max_step;
/* An array that maps alsa volume element steps to decibels. The steps can
* be used as indices to this array, after subtracting min_step from the
* real value.
*
* The values are actually stored as integers representing millibels,
* because that's the format the alsa API uses. */
long *db_values;
};
struct pa_alsa_profile_set {
pa_hashmap *mappings;
pa_hashmap *profiles;
pa_hashmap *decibel_fixes;
pa_hashmap *input_paths;
pa_hashmap *output_paths;
bool auto_profiles;
bool ignore_dB:1;
bool probed:1;
};
void pa_alsa_mapping_dump(pa_alsa_mapping *m);
void pa_alsa_profile_dump(pa_alsa_profile *p);
void pa_alsa_decibel_fix_dump(pa_alsa_decibel_fix *db_fix);
pa_alsa_mapping *pa_alsa_mapping_get(pa_alsa_profile_set *ps, const char *name);
pa_alsa_profile_set* pa_alsa_profile_set_new(const char *fname, const pa_channel_map *bonus);
void pa_alsa_profile_set_probe(pa_alsa_profile_set *ps, pa_hashmap *mixers, const char *dev_id, const pa_sample_spec *ss, unsigned default_n_fragments, unsigned default_fragment_size_msec);
void pa_alsa_profile_set_free(pa_alsa_profile_set *s);
void pa_alsa_profile_set_dump(pa_alsa_profile_set *s);
void pa_alsa_profile_set_drop_unsupported(pa_alsa_profile_set *s);
void pa_alsa_mixer_use_for_poll(pa_hashmap *mixers, snd_mixer_t *mixer_handle);
/* Data structure for inclusion in pa_device_port for alsa
* sinks/sources. This contains nothing that needs to be freed
* individually */
struct pa_alsa_port_data {
pa_alsa_path *path;
pa_alsa_setting *setting;
bool suspend_when_unavailable;
};
void pa_alsa_add_ports(pa_hashmap *ports, pa_alsa_path_set *ps, pa_card *card);
void pa_alsa_path_set_add_ports(pa_alsa_path_set *ps, pa_alsa_profile *cp, pa_hashmap *ports, pa_hashmap *extra, pa_core *core);
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,300 @@
#ifndef fooalsaucmhfoo
#define fooalsaucmhfoo
/***
This file is part of PulseAudio.
Copyright 2011 Wolfson Microelectronics PLC
Author Margarita Olaya <magi@slimlogic.co.uk>
Copyright 2012 Feng Wei <wei.feng@freescale.com>, Freescale Ltd.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2.1 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
***/
#ifdef HAVE_ALSA_UCM
#include <alsa/use-case.h>
#else
typedef void snd_use_case_mgr_t;
#endif
#include "compat.h"
#include "alsa-mixer.h"
/** For devices: List of verbs, devices or modifiers available */
#define PA_ALSA_PROP_UCM_NAME "alsa.ucm.name"
/** For devices: List of supported devices per verb*/
#define PA_ALSA_PROP_UCM_DESCRIPTION "alsa.ucm.description"
/** For devices: Playback device name e.g PlaybackPCM */
#define PA_ALSA_PROP_UCM_SINK "alsa.ucm.sink"
/** For devices: Capture device name e.g CapturePCM*/
#define PA_ALSA_PROP_UCM_SOURCE "alsa.ucm.source"
/** For devices: Playback roles */
#define PA_ALSA_PROP_UCM_PLAYBACK_ROLES "alsa.ucm.playback.roles"
/** For devices: Playback control device name */
#define PA_ALSA_PROP_UCM_PLAYBACK_CTL_DEVICE "alsa.ucm.playback.ctldev"
/** For devices: Playback control volume ID string. e.g PlaybackVolume */
#define PA_ALSA_PROP_UCM_PLAYBACK_VOLUME "alsa.ucm.playback.volume"
/** For devices: Playback switch e.g PlaybackSwitch */
#define PA_ALSA_PROP_UCM_PLAYBACK_SWITCH "alsa.ucm.playback.switch"
/** For devices: Playback mixer device name */
#define PA_ALSA_PROP_UCM_PLAYBACK_MIXER_DEVICE "alsa.ucm.playback.mixer.device"
/** For devices: Playback mixer identifier */
#define PA_ALSA_PROP_UCM_PLAYBACK_MIXER_ELEM "alsa.ucm.playback.mixer.element"
/** For devices: Playback mixer master identifier */
#define PA_ALSA_PROP_UCM_PLAYBACK_MASTER_ELEM "alsa.ucm.playback.master.element"
/** For devices: Playback mixer master type */
#define PA_ALSA_PROP_UCM_PLAYBACK_MASTER_TYPE "alsa.ucm.playback.master.type"
/** For devices: Playback mixer master identifier */
#define PA_ALSA_PROP_UCM_PLAYBACK_MASTER_ID "alsa.ucm.playback.master.id"
/** For devices: Playback mixer master type */
#define PA_ALSA_PROP_UCM_PLAYBACK_MASTER_TYPE "alsa.ucm.playback.master.type"
/** For devices: Playback priority */
#define PA_ALSA_PROP_UCM_PLAYBACK_PRIORITY "alsa.ucm.playback.priority"
/** For devices: Playback rate */
#define PA_ALSA_PROP_UCM_PLAYBACK_RATE "alsa.ucm.playback.rate"
/** For devices: Playback channels */
#define PA_ALSA_PROP_UCM_PLAYBACK_CHANNELS "alsa.ucm.playback.channels"
/** For devices: Capture roles */
#define PA_ALSA_PROP_UCM_CAPTURE_ROLES "alsa.ucm.capture.roles"
/** For devices: Capture control device name */
#define PA_ALSA_PROP_UCM_CAPTURE_CTL_DEVICE "alsa.ucm.capture.ctldev"
/** For devices: Capture controls volume ID string. e.g CaptureVolume */
#define PA_ALSA_PROP_UCM_CAPTURE_VOLUME "alsa.ucm.capture.volume"
/** For devices: Capture switch e.g CaptureSwitch */
#define PA_ALSA_PROP_UCM_CAPTURE_SWITCH "alsa.ucm.capture.switch"
/** For devices: Capture mixer device name */
#define PA_ALSA_PROP_UCM_CAPTURE_MIXER_DEVICE "alsa.ucm.capture.mixer.device"
/** For devices: Capture mixer identifier */
#define PA_ALSA_PROP_UCM_CAPTURE_MIXER_ELEM "alsa.ucm.capture.mixer.element"
/** For devices: Capture mixer identifier */
#define PA_ALSA_PROP_UCM_CAPTURE_MASTER_ELEM "alsa.ucm.capture.master.element"
/** For devices: Capture mixer identifier */
#define PA_ALSA_PROP_UCM_CAPTURE_MASTER_TYPE "alsa.ucm.capture.master.type"
/** For devices: Capture mixer identifier */
#define PA_ALSA_PROP_UCM_CAPTURE_MASTER_ID "alsa.ucm.capture.master.id"
/** For devices: Capture mixer identifier */
#define PA_ALSA_PROP_UCM_CAPTURE_MASTER_TYPE "alsa.ucm.capture.master.type"
/** For devices: Capture priority */
#define PA_ALSA_PROP_UCM_CAPTURE_PRIORITY "alsa.ucm.capture.priority"
/** For devices: Capture rate */
#define PA_ALSA_PROP_UCM_CAPTURE_RATE "alsa.ucm.capture.rate"
/** For devices: Capture channels */
#define PA_ALSA_PROP_UCM_CAPTURE_CHANNELS "alsa.ucm.capture.channels"
/** For devices: Quality of Service */
#define PA_ALSA_PROP_UCM_QOS "alsa.ucm.qos"
/** For devices: The modifier (if any) that this device corresponds to */
#define PA_ALSA_PROP_UCM_MODIFIER "alsa.ucm.modifier"
/* Corresponds to the "JackCTL" UCM value. */
#define PA_ALSA_PROP_UCM_JACK_DEVICE "alsa.ucm.jack_device"
/* Corresponds to the "JackControl" UCM value. */
#define PA_ALSA_PROP_UCM_JACK_CONTROL "alsa.ucm.jack_control"
/* Corresponds to the "JackHWMute" UCM value. */
#define PA_ALSA_PROP_UCM_JACK_HW_MUTE "alsa.ucm.jack_hw_mute"
typedef struct pa_alsa_ucm_verb pa_alsa_ucm_verb;
typedef struct pa_alsa_ucm_modifier pa_alsa_ucm_modifier;
typedef struct pa_alsa_ucm_device pa_alsa_ucm_device;
typedef struct pa_alsa_ucm_config pa_alsa_ucm_config;
typedef struct pa_alsa_ucm_mapping_context pa_alsa_ucm_mapping_context;
typedef struct pa_alsa_ucm_port_data pa_alsa_ucm_port_data;
typedef struct pa_alsa_ucm_volume pa_alsa_ucm_volume;
int pa_alsa_ucm_query_profiles(pa_alsa_ucm_config *ucm, int card_index);
pa_alsa_profile_set* pa_alsa_ucm_add_profile_set(pa_alsa_ucm_config *ucm, pa_channel_map *default_channel_map);
int pa_alsa_ucm_set_profile(pa_alsa_ucm_config *ucm, pa_card *card, const char *new_profile, const char *old_profile);
int pa_alsa_ucm_get_verb(snd_use_case_mgr_t *uc_mgr, const char *verb_name, const char *verb_desc, pa_alsa_ucm_verb **p_verb);
void pa_alsa_ucm_add_ports(
pa_hashmap **hash,
pa_proplist *proplist,
pa_alsa_ucm_mapping_context *context,
bool is_sink,
pa_card *card,
snd_pcm_t *pcm_handle,
bool ignore_dB);
void pa_alsa_ucm_add_ports_combination(
pa_hashmap *hash,
pa_alsa_ucm_mapping_context *context,
bool is_sink,
pa_hashmap *ports,
pa_alsa_profile *cp,
pa_core *core);
int pa_alsa_ucm_set_port(pa_alsa_ucm_mapping_context *context, pa_device_port *port, bool is_sink);
void pa_alsa_ucm_free(pa_alsa_ucm_config *ucm);
void pa_alsa_ucm_mapping_context_free(pa_alsa_ucm_mapping_context *context);
void pa_alsa_ucm_roled_stream_begin(pa_alsa_ucm_config *ucm, const char *role, pa_direction_t dir);
void pa_alsa_ucm_roled_stream_end(pa_alsa_ucm_config *ucm, const char *role, pa_direction_t dir);
/* UCM - Use Case Manager is available on some audio cards */
struct pa_alsa_ucm_device {
PA_LLIST_FIELDS(pa_alsa_ucm_device);
pa_proplist *proplist;
pa_device_port_type_t type;
unsigned playback_priority;
unsigned capture_priority;
unsigned playback_rate;
unsigned capture_rate;
unsigned playback_channels;
unsigned capture_channels;
/* These may be different per verb, so we store this as a hashmap of verb -> volume_control. We might eventually want to
* make this a hashmap of verb -> per-verb-device-properties-struct. */
pa_hashmap *playback_volumes;
pa_hashmap *capture_volumes;
pa_alsa_mapping *playback_mapping;
pa_alsa_mapping *capture_mapping;
pa_idxset *conflicting_devices;
pa_idxset *supported_devices;
/* One device may be part of multiple ports, since each device has
* a dedicated port, and in addition to that we sometimes generate ports
* that represent combinations of devices. */
pa_dynarray *ucm_ports; /* struct ucm_port */
pa_alsa_jack *jack;
pa_dynarray *hw_mute_jacks; /* pa_alsa_jack */
pa_available_t available;
char *eld_mixer_device_name;
int eld_device;
};
void pa_alsa_ucm_device_update_available(pa_alsa_ucm_device *device);
struct pa_alsa_ucm_modifier {
PA_LLIST_FIELDS(pa_alsa_ucm_modifier);
pa_proplist *proplist;
int n_confdev;
int n_suppdev;
const char **conflicting_devices;
const char **supported_devices;
pa_direction_t action_direction;
char *media_role;
/* Non-NULL if the modifier has its own PlaybackPCM/CapturePCM */
pa_alsa_mapping *playback_mapping;
pa_alsa_mapping *capture_mapping;
/* Count how many role matched streams are running */
int enabled_counter;
};
struct pa_alsa_ucm_verb {
PA_LLIST_FIELDS(pa_alsa_ucm_verb);
pa_proplist *proplist;
unsigned priority;
PA_LLIST_HEAD(pa_alsa_ucm_device, devices);
PA_LLIST_HEAD(pa_alsa_ucm_modifier, modifiers);
};
struct pa_alsa_ucm_config {
pa_sample_spec default_sample_spec;
pa_channel_map default_channel_map;
unsigned default_fragment_size_msec;
unsigned default_n_fragments;
snd_use_case_mgr_t *ucm_mgr;
pa_alsa_ucm_verb *active_verb;
pa_hashmap *mixers;
PA_LLIST_HEAD(pa_alsa_ucm_verb, verbs);
PA_LLIST_HEAD(pa_alsa_jack, jacks);
};
struct pa_alsa_ucm_mapping_context {
pa_alsa_ucm_config *ucm;
pa_direction_t direction;
pa_idxset *ucm_devices;
pa_idxset *ucm_modifiers;
};
struct pa_alsa_ucm_port_data {
pa_alsa_ucm_config *ucm;
pa_device_port *core_port;
/* A single port will be associated with multiple devices if it represents
* a combination of devices. */
pa_dynarray *devices; /* pa_alsa_ucm_device */
/* profile name -> pa_alsa_path for volume control */
pa_hashmap *paths;
/* Current path, set when activating profile */
pa_alsa_path *path;
/* ELD info */
char *eld_mixer_device_name;
int eld_device; /* PCM device number */
};
struct pa_alsa_ucm_volume {
char *mixer_elem; /* mixer element identifier */
char *master_elem; /* master mixer element identifier */
char *master_type;
};
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,156 @@
#ifndef fooalsautilhfoo
#define fooalsautilhfoo
/***
This file is part of PulseAudio.
Copyright 2004-2006 Lennart Poettering
Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2.1 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
***/
#include <stdbool.h>
#include <alsa/asoundlib.h>
#include "compat.h"
#include "alsa-mixer.h"
int pa_alsa_set_hw_params(
snd_pcm_t *pcm_handle,
pa_sample_spec *ss, /* modified at return */
snd_pcm_uframes_t *period_size, /* modified at return */
snd_pcm_uframes_t *buffer_size, /* modified at return */
snd_pcm_uframes_t tsched_size,
bool *use_mmap, /* modified at return */
bool *use_tsched, /* modified at return */
bool require_exact_channel_number);
int pa_alsa_set_sw_params(
snd_pcm_t *pcm,
snd_pcm_uframes_t avail_min,
bool period_event);
#if 0
/* Picks a working mapping from the profile set based on the specified ss/map */
snd_pcm_t *pa_alsa_open_by_device_id_auto(
const char *dev_id,
char **dev, /* modified at return */
pa_sample_spec *ss, /* modified at return */
pa_channel_map* map, /* modified at return */
int mode,
snd_pcm_uframes_t *period_size, /* modified at return */
snd_pcm_uframes_t *buffer_size, /* modified at return */
snd_pcm_uframes_t tsched_size,
bool *use_mmap, /* modified at return */
bool *use_tsched, /* modified at return */
pa_alsa_profile_set *ps,
pa_alsa_mapping **mapping); /* modified at return */
#endif
/* Uses the specified mapping */
snd_pcm_t *pa_alsa_open_by_device_id_mapping(
const char *dev_id,
char **dev, /* modified at return */
pa_sample_spec *ss, /* modified at return */
pa_channel_map* map, /* modified at return */
int mode,
snd_pcm_uframes_t *period_size, /* modified at return */
snd_pcm_uframes_t *buffer_size, /* modified at return */
snd_pcm_uframes_t tsched_size,
bool *use_mmap, /* modified at return */
bool *use_tsched, /* modified at return */
pa_alsa_mapping *mapping);
/* Opens the explicit ALSA device */
snd_pcm_t *pa_alsa_open_by_device_string(
const char *dir,
char **dev, /* modified at return */
pa_sample_spec *ss, /* modified at return */
pa_channel_map* map, /* modified at return */
int mode,
snd_pcm_uframes_t *period_size, /* modified at return */
snd_pcm_uframes_t *buffer_size, /* modified at return */
snd_pcm_uframes_t tsched_size,
bool *use_mmap, /* modified at return */
bool *use_tsched, /* modified at return */
bool require_exact_channel_number);
/* Opens the explicit ALSA device with a fallback list */
snd_pcm_t *pa_alsa_open_by_template(
char **template,
const char *dev_id,
char **dev, /* modified at return */
pa_sample_spec *ss, /* modified at return */
pa_channel_map* map, /* modified at return */
int mode,
snd_pcm_uframes_t *period_size, /* modified at return */
snd_pcm_uframes_t *buffer_size, /* modified at return */
snd_pcm_uframes_t tsched_size,
bool *use_mmap, /* modified at return */
bool *use_tsched, /* modified at return */
bool require_exact_channel_number);
#if 0
void pa_alsa_dump(pa_log_level_t level, snd_pcm_t *pcm);
void pa_alsa_dump_status(snd_pcm_t *pcm);
#endif
void pa_alsa_refcnt_inc(void);
void pa_alsa_refcnt_dec(void);
void pa_alsa_init_proplist_pcm_info(pa_core *c, pa_proplist *p, snd_pcm_info_t *pcm_info);
void pa_alsa_init_proplist_card(pa_core *c, pa_proplist *p, int card);
void pa_alsa_init_proplist_pcm(pa_core *c, pa_proplist *p, snd_pcm_t *pcm);
#if 0
void pa_alsa_init_proplist_ctl(pa_proplist *p, const char *name);
#endif
bool pa_alsa_init_description(pa_proplist *p, pa_card *card);
char *pa_alsa_get_driver_name(int card);
char *pa_alsa_get_driver_name_by_pcm(snd_pcm_t *pcm);
char *pa_alsa_get_reserve_name(const char *device);
unsigned int *pa_alsa_get_supported_rates(snd_pcm_t *pcm, unsigned int fallback_rate);
pa_sample_format_t *pa_alsa_get_supported_formats(snd_pcm_t *pcm, pa_sample_format_t fallback_format);
bool pa_alsa_pcm_is_hw(snd_pcm_t *pcm);
bool pa_alsa_pcm_is_modem(snd_pcm_t *pcm);
const char* pa_alsa_strerror(int errnum);
#if 0
bool pa_alsa_may_tsched(bool want);
#endif
snd_mixer_elem_t *pa_alsa_mixer_find_card(snd_mixer_t *mixer, const char *name, unsigned int device);
snd_mixer_elem_t *pa_alsa_mixer_find_pcm(snd_mixer_t *mixer, const char *name, unsigned int device);
snd_mixer_t *pa_alsa_open_mixer(pa_hashmap *mixers, int alsa_card_index, bool probe);
snd_mixer_t *pa_alsa_open_mixer_by_name(pa_hashmap *mixers, const char *dev, bool probe);
snd_mixer_t *pa_alsa_open_mixer_for_pcm(pa_hashmap *mixers, snd_pcm_t *pcm, bool probe);
void pa_alsa_mixer_free(pa_alsa_mixer *mixer);
typedef struct pa_hdmi_eld pa_hdmi_eld;
struct pa_hdmi_eld {
char monitor_name[17];
};
int pa_alsa_get_hdmi_eld(snd_hctl_elem_t *elem, pa_hdmi_eld *eld);
#endif

View file

@ -0,0 +1,150 @@
/* ALSA Card Profile
*
* Copyright © 2018 Wim Taymans
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifndef PA_ARRAY_H
#define PA_ARRAY_H
#ifdef __cplusplus
extern "C" {
#endif
#include <errno.h>
#include <string.h>
typedef struct pa_array {
void *data; /**< pointer to array data */
size_t size; /**< length of array in bytes */
size_t alloc; /**< number of allocated memory in \a data */
size_t extend; /**< number of bytes to extend with */
} pa_array;
#define PW_ARRAY_INIT(extend) (struct pa_array) { NULL, 0, 0, extend }
#define pa_array_get_len_s(a,s) ((a)->size / (s))
#define pa_array_get_unchecked_s(a,idx,s,t) (t*)((uint8_t*)(a)->data + (int)((idx)*(s)))
#define pa_array_check_index_s(a,idx,s) ((idx) < pa_array_get_len_s(a,s))
#define pa_array_get_len(a,t) pa_array_get_len_s(a,sizeof(t))
#define pa_array_get_unchecked(a,idx,t) pa_array_get_unchecked_s(a,idx,sizeof(t),t)
#define pa_array_check_index(a,idx,t) pa_array_check_index_s(a,idx,sizeof(t))
#define pa_array_first(a) ((a)->data)
#define pa_array_end(a) (void*)((uint8_t*)(a)->data + (int)(a)->size)
#define pa_array_check(a,p) ((void*)((uint8_t*)p + (int)sizeof(*p)) <= pa_array_end(a))
#define pa_array_for_each(pos, array) \
for (pos = (__typeof__(pos)) pa_array_first(array); \
pa_array_check(array, pos); \
(pos)++)
#define pa_array_consume(pos, array) \
while (pos = (__typeof__(pos)) pa_array_first(array) && \
pa_array_check(array, pos)
#define pa_array_remove(a,p) \
({ \
(a)->size -= sizeof(*(p)); \
memmove(p, ((uint8_t*)(p) + (int)sizeof(*(p))), \
(uint8_t*)pa_array_end(a) - (uint8_t*)(p)); \
})
static inline void pa_array_init(pa_array *arr, size_t extend)
{
arr->data = NULL;
arr->size = arr->alloc = 0;
arr->extend = extend;
}
static inline void pa_array_clear(pa_array *arr)
{
free(arr->data);
}
static inline void pa_array_reset(pa_array *arr)
{
arr->size = 0;
}
static inline int pa_array_ensure_size(pa_array *arr, size_t size)
{
size_t alloc, need;
alloc = arr->alloc;
need = arr->size + size;
if (alloc < need) {
void *data;
alloc = alloc > arr->extend ? alloc : arr->extend;
while (alloc < need)
alloc *= 2;
if ((data = realloc(arr->data, alloc)) == NULL)
return -errno;
arr->data = data;
arr->alloc = alloc;
}
return 0;
}
static inline void *pa_array_add(pa_array *arr, size_t size)
{
void *p;
if (pa_array_ensure_size(arr, size) < 0)
return NULL;
p = (void*)((uint8_t*)arr->data + (int)arr->size);
arr->size += size;
return p;
}
static inline void *pa_array_add_fixed(pa_array *arr, size_t size)
{
void *p;
if (arr->alloc < arr->size + size) {
errno = ENOSPC;
return NULL;
}
p = ((uint8_t*)arr->data + (int)arr->size);
arr->size += size;
return p;
}
#define pa_array_add_ptr(a,p) \
*((void**) pa_array_add(a, sizeof(void*))) = (p)
static inline int pa_array_add_data(pa_array *arr, const void *data, size_t size)
{
void *d;
if ((d = pa_array_add(arr, size)) == NULL)
return -1;
memcpy(d, data, size);
return size;
}
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* PA_ARRAY_H */

View file

@ -0,0 +1,70 @@
/***
This file is part of PulseAudio.
Copyright 2004-2006 Lennart Poettering
Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2.1 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
***/
#ifndef PULSE_CARD_H
#define PULSE_CARD_H
#ifdef __cplusplus
extern "C" {
#else
#include <stdbool.h>
#endif
#include "compat.h"
typedef struct pa_card pa_card;
struct pa_card {
struct acp_card card;
pa_core *core;
char *name;
char *driver;
pa_proplist *proplist;
bool use_ucm;
pa_alsa_ucm_config ucm;
pa_alsa_profile_set *profile_set;
pa_hashmap *ports;
pa_hashmap *profiles;
pa_hashmap *jacks;
struct {
pa_dynarray ports;
pa_dynarray profiles;
pa_dynarray devices;
} out;
const struct acp_card_events *events;
void *user_data;
};
bool pa_alsa_device_init_description(pa_proplist *p, pa_card *card);
#ifdef __cplusplus
}
#endif
#endif /* PULSE_CARD_H */

View file

@ -0,0 +1,474 @@
/***
This file is part of PulseAudio.
Copyright 2004-2006 Lennart Poettering
Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2.1 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
***/
#ifndef PULSE_CHANNELMAP_H
#define PULSE_CHANNELMAP_H
#ifdef __cplusplus
extern "C" {
#endif
#define PA_CHANNELS_MAX 64
#define PA_CHANNEL_MAP_SNPRINT_MAX 336
typedef enum pa_channel_map_def {
PA_CHANNEL_MAP_AIFF,
PA_CHANNEL_MAP_ALSA,
PA_CHANNEL_MAP_AUX,
PA_CHANNEL_MAP_WAVEEX,
PA_CHANNEL_MAP_OSS,
PA_CHANNEL_MAP_DEF_MAX,
PA_CHANNEL_MAP_DEFAULT = PA_CHANNEL_MAP_AIFF
} pa_channel_map_def_t;
typedef enum pa_channel_position {
PA_CHANNEL_POSITION_INVALID = -1,
PA_CHANNEL_POSITION_MONO = 0,
PA_CHANNEL_POSITION_FRONT_LEFT, /**< Apple, Dolby call this 'Left' */
PA_CHANNEL_POSITION_FRONT_RIGHT, /**< Apple, Dolby call this 'Right' */
PA_CHANNEL_POSITION_FRONT_CENTER, /**< Apple, Dolby call this 'Center' */
/** \cond fulldocs */
PA_CHANNEL_POSITION_LEFT = PA_CHANNEL_POSITION_FRONT_LEFT,
PA_CHANNEL_POSITION_RIGHT = PA_CHANNEL_POSITION_FRONT_RIGHT,
PA_CHANNEL_POSITION_CENTER = PA_CHANNEL_POSITION_FRONT_CENTER,
/** \endcond */
PA_CHANNEL_POSITION_REAR_CENTER, /**< Microsoft calls this 'Back Center', Apple calls this 'Center Surround', Dolby calls this 'Surround Rear Center' */
PA_CHANNEL_POSITION_REAR_LEFT, /**< Microsoft calls this 'Back Left', Apple calls this 'Left Surround' (!), Dolby calls this 'Surround Rear Left' */
PA_CHANNEL_POSITION_REAR_RIGHT, /**< Microsoft calls this 'Back Right', Apple calls this 'Right Surround' (!), Dolby calls this 'Surround Rear Right' */
PA_CHANNEL_POSITION_LFE, /**< Microsoft calls this 'Low Frequency', Apple calls this 'LFEScreen' */
/** \cond fulldocs */
PA_CHANNEL_POSITION_SUBWOOFER = PA_CHANNEL_POSITION_LFE,
/** \endcond */
PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER, /**< Apple, Dolby call this 'Left Center' */
PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER, /**< Apple, Dolby call this 'Right Center */
PA_CHANNEL_POSITION_SIDE_LEFT, /**< Apple calls this 'Left Surround Direct', Dolby calls this 'Surround Left' (!) */
PA_CHANNEL_POSITION_SIDE_RIGHT, /**< Apple calls this 'Right Surround Direct', Dolby calls this 'Surround Right' (!) */
PA_CHANNEL_POSITION_AUX0,
PA_CHANNEL_POSITION_AUX1,
PA_CHANNEL_POSITION_AUX2,
PA_CHANNEL_POSITION_AUX3,
PA_CHANNEL_POSITION_AUX4,
PA_CHANNEL_POSITION_AUX5,
PA_CHANNEL_POSITION_AUX6,
PA_CHANNEL_POSITION_AUX7,
PA_CHANNEL_POSITION_AUX8,
PA_CHANNEL_POSITION_AUX9,
PA_CHANNEL_POSITION_AUX10,
PA_CHANNEL_POSITION_AUX11,
PA_CHANNEL_POSITION_AUX12,
PA_CHANNEL_POSITION_AUX13,
PA_CHANNEL_POSITION_AUX14,
PA_CHANNEL_POSITION_AUX15,
PA_CHANNEL_POSITION_AUX16,
PA_CHANNEL_POSITION_AUX17,
PA_CHANNEL_POSITION_AUX18,
PA_CHANNEL_POSITION_AUX19,
PA_CHANNEL_POSITION_AUX20,
PA_CHANNEL_POSITION_AUX21,
PA_CHANNEL_POSITION_AUX22,
PA_CHANNEL_POSITION_AUX23,
PA_CHANNEL_POSITION_AUX24,
PA_CHANNEL_POSITION_AUX25,
PA_CHANNEL_POSITION_AUX26,
PA_CHANNEL_POSITION_AUX27,
PA_CHANNEL_POSITION_AUX28,
PA_CHANNEL_POSITION_AUX29,
PA_CHANNEL_POSITION_AUX30,
PA_CHANNEL_POSITION_AUX31,
PA_CHANNEL_POSITION_TOP_CENTER, /**< Apple calls this 'Top Center Surround' */
PA_CHANNEL_POSITION_TOP_FRONT_LEFT, /**< Apple calls this 'Vertical Height Left' */
PA_CHANNEL_POSITION_TOP_FRONT_RIGHT, /**< Apple calls this 'Vertical Height Right' */
PA_CHANNEL_POSITION_TOP_FRONT_CENTER, /**< Apple calls this 'Vertical Height Center' */
PA_CHANNEL_POSITION_TOP_REAR_LEFT, /**< Microsoft and Apple call this 'Top Back Left' */
PA_CHANNEL_POSITION_TOP_REAR_RIGHT, /**< Microsoft and Apple call this 'Top Back Right' */
PA_CHANNEL_POSITION_TOP_REAR_CENTER, /**< Microsoft and Apple call this 'Top Back Center' */
PA_CHANNEL_POSITION_MAX
} pa_channel_position_t;
typedef struct pa_channel_map {
uint8_t channels;
pa_channel_position_t map[PA_CHANNELS_MAX];
} pa_channel_map;
static inline int pa_channels_valid(uint8_t channels)
{
return channels > 0 && channels <= PA_CHANNELS_MAX;
}
static inline int pa_channel_map_valid(const pa_channel_map *map)
{
unsigned c;
if (!pa_channels_valid(map->channels))
return 0;
for (c = 0; c < map->channels; c++)
if (map->map[c] < 0 || map->map[c] >= PA_CHANNEL_POSITION_MAX)
return 0;
return 1;
}
static inline pa_channel_map* pa_channel_map_init(pa_channel_map *m)
{
unsigned c;
m->channels = 0;
for (c = 0; c < PA_CHANNELS_MAX; c++)
m->map[c] = PA_CHANNEL_POSITION_INVALID;
return m;
}
static inline pa_channel_map* pa_channel_map_init_auto(pa_channel_map *m, unsigned channels, pa_channel_map_def_t def)
{
pa_assert(m);
pa_assert(pa_channels_valid(channels));
pa_assert(def < PA_CHANNEL_MAP_DEF_MAX);
pa_channel_map_init(m);
m->channels = (uint8_t) channels;
switch (def) {
case PA_CHANNEL_MAP_ALSA:
switch (channels) {
case 1:
m->map[0] = PA_CHANNEL_POSITION_MONO;
return m;
case 8:
m->map[6] = PA_CHANNEL_POSITION_SIDE_LEFT;
m->map[7] = PA_CHANNEL_POSITION_SIDE_RIGHT;
/* Fall through */
case 6:
m->map[5] = PA_CHANNEL_POSITION_LFE;
/* Fall through */
case 5:
m->map[4] = PA_CHANNEL_POSITION_FRONT_CENTER;
/* Fall through */
case 4:
m->map[2] = PA_CHANNEL_POSITION_REAR_LEFT;
m->map[3] = PA_CHANNEL_POSITION_REAR_RIGHT;
/* Fall through */
case 2:
m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
m->map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
return m;
default:
return NULL;
}
default:
break;
}
return NULL;
}
static inline pa_channel_map* pa_channel_map_init_extend(pa_channel_map *m,
unsigned channels, pa_channel_map_def_t def)
{
unsigned i, c;
pa_channel_map_init(m);
for (c = channels; c > 0; c--) {
if (pa_channel_map_init_auto(m, c, def) == NULL)
continue;
for (i = 0; c < channels; c++, i++)
m->map[c] = PA_CHANNEL_POSITION_AUX0 + i;
m->channels = (uint8_t) channels;
return m;
}
return NULL;
}
typedef uint64_t pa_channel_position_mask_t;
#define PA_CHANNEL_POSITION_MASK(f) ((pa_channel_position_mask_t) (1ULL << (f)))
#define PA_CHANNEL_POSITION_MASK_LEFT \
(PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_LEFT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_REAR_LEFT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_SIDE_LEFT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_LEFT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_LEFT)) \
#define PA_CHANNEL_POSITION_MASK_RIGHT \
(PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_RIGHT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_REAR_RIGHT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_SIDE_RIGHT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_RIGHT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_RIGHT))
#define PA_CHANNEL_POSITION_MASK_CENTER \
(PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_CENTER) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_REAR_CENTER) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_CENTER) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_CENTER) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_CENTER))
#define PA_CHANNEL_POSITION_MASK_FRONT \
(PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_LEFT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_RIGHT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_CENTER) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_LEFT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_RIGHT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_CENTER))
#define PA_CHANNEL_POSITION_MASK_REAR \
(PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_REAR_LEFT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_REAR_RIGHT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_REAR_CENTER) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_LEFT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_RIGHT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_CENTER))
#define PA_CHANNEL_POSITION_MASK_LFE \
PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_LFE)
#define PA_CHANNEL_POSITION_MASK_HFE \
(PA_CHANNEL_POSITION_MASK_REAR | PA_CHANNEL_POSITION_MASK_FRONT \
| PA_CHANNEL_POSITION_MASK_LEFT | PA_CHANNEL_POSITION_MASK_RIGHT \
| PA_CHANNEL_POSITION_MASK_CENTER)
#define PA_CHANNEL_POSITION_MASK_SIDE_OR_TOP_CENTER \
(PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_SIDE_LEFT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_SIDE_RIGHT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_CENTER))
#define PA_CHANNEL_POSITION_MASK_TOP \
(PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_CENTER) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_LEFT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_RIGHT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_CENTER) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_LEFT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_RIGHT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_CENTER))
#define PA_CHANNEL_POSITION_MASK_ALL \
((pa_channel_position_mask_t) (PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_MAX)-1))
static const char *const pa_position_table[PA_CHANNEL_POSITION_MAX] = {
[PA_CHANNEL_POSITION_MONO] = "mono",
[PA_CHANNEL_POSITION_FRONT_CENTER] = "front-center",
[PA_CHANNEL_POSITION_FRONT_LEFT] = "front-left",
[PA_CHANNEL_POSITION_FRONT_RIGHT] = "front-right",
[PA_CHANNEL_POSITION_REAR_CENTER] = "rear-center",
[PA_CHANNEL_POSITION_REAR_LEFT] = "rear-left",
[PA_CHANNEL_POSITION_REAR_RIGHT] = "rear-right",
[PA_CHANNEL_POSITION_LFE] = "lfe",
[PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER] = "front-left-of-center",
[PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER] = "front-right-of-center",
[PA_CHANNEL_POSITION_SIDE_LEFT] = "side-left",
[PA_CHANNEL_POSITION_SIDE_RIGHT] = "side-right",
[PA_CHANNEL_POSITION_AUX0] = "aux0",
[PA_CHANNEL_POSITION_AUX1] = "aux1",
[PA_CHANNEL_POSITION_AUX2] = "aux2",
[PA_CHANNEL_POSITION_AUX3] = "aux3",
[PA_CHANNEL_POSITION_AUX4] = "aux4",
[PA_CHANNEL_POSITION_AUX5] = "aux5",
[PA_CHANNEL_POSITION_AUX6] = "aux6",
[PA_CHANNEL_POSITION_AUX7] = "aux7",
[PA_CHANNEL_POSITION_AUX8] = "aux8",
[PA_CHANNEL_POSITION_AUX9] = "aux9",
[PA_CHANNEL_POSITION_AUX10] = "aux10",
[PA_CHANNEL_POSITION_AUX11] = "aux11",
[PA_CHANNEL_POSITION_AUX12] = "aux12",
[PA_CHANNEL_POSITION_AUX13] = "aux13",
[PA_CHANNEL_POSITION_AUX14] = "aux14",
[PA_CHANNEL_POSITION_AUX15] = "aux15",
[PA_CHANNEL_POSITION_AUX16] = "aux16",
[PA_CHANNEL_POSITION_AUX17] = "aux17",
[PA_CHANNEL_POSITION_AUX18] = "aux18",
[PA_CHANNEL_POSITION_AUX19] = "aux19",
[PA_CHANNEL_POSITION_AUX20] = "aux20",
[PA_CHANNEL_POSITION_AUX21] = "aux21",
[PA_CHANNEL_POSITION_AUX22] = "aux22",
[PA_CHANNEL_POSITION_AUX23] = "aux23",
[PA_CHANNEL_POSITION_AUX24] = "aux24",
[PA_CHANNEL_POSITION_AUX25] = "aux25",
[PA_CHANNEL_POSITION_AUX26] = "aux26",
[PA_CHANNEL_POSITION_AUX27] = "aux27",
[PA_CHANNEL_POSITION_AUX28] = "aux28",
[PA_CHANNEL_POSITION_AUX29] = "aux29",
[PA_CHANNEL_POSITION_AUX30] = "aux30",
[PA_CHANNEL_POSITION_AUX31] = "aux31",
[PA_CHANNEL_POSITION_TOP_CENTER] = "top-center",
[PA_CHANNEL_POSITION_TOP_FRONT_CENTER] = "top-front-center",
[PA_CHANNEL_POSITION_TOP_FRONT_LEFT] = "top-front-left",
[PA_CHANNEL_POSITION_TOP_FRONT_RIGHT] = "top-front-right",
[PA_CHANNEL_POSITION_TOP_REAR_CENTER] = "top-rear-center",
[PA_CHANNEL_POSITION_TOP_REAR_LEFT] = "top-rear-left",
[PA_CHANNEL_POSITION_TOP_REAR_RIGHT] = "top-rear-right"
};
static inline pa_channel_position_t pa_channel_position_from_string(const char *p)
{
pa_channel_position_t i;
/* Some special aliases */
if (pa_streq(p, "left"))
return PA_CHANNEL_POSITION_LEFT;
else if (pa_streq(p, "right"))
return PA_CHANNEL_POSITION_RIGHT;
else if (pa_streq(p, "center"))
return PA_CHANNEL_POSITION_CENTER;
else if (pa_streq(p, "subwoofer"))
return PA_CHANNEL_POSITION_SUBWOOFER;
for (i = 0; i < PA_CHANNEL_POSITION_MAX; i++)
if (pa_streq(p, pa_position_table[i]))
return i;
return PA_CHANNEL_POSITION_INVALID;
}
static inline pa_channel_map *pa_channel_map_parse(pa_channel_map *rmap, const char *s)
{
const char *state;
pa_channel_map map;
char *p;
pa_channel_map_init(&map);
if (pa_streq(s, "stereo")) {
map.channels = 2;
map.map[0] = PA_CHANNEL_POSITION_LEFT;
map.map[1] = PA_CHANNEL_POSITION_RIGHT;
goto finish;
} else if (pa_streq(s, "surround-21")) {
map.channels = 3;
map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
map.map[2] = PA_CHANNEL_POSITION_LFE;
goto finish;
} else if (pa_streq(s, "surround-40")) {
map.channels = 4;
map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
map.map[2] = PA_CHANNEL_POSITION_REAR_LEFT;
map.map[3] = PA_CHANNEL_POSITION_REAR_RIGHT;
goto finish;
} else if (pa_streq(s, "surround-41")) {
map.channels = 5;
map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
map.map[2] = PA_CHANNEL_POSITION_REAR_LEFT;
map.map[3] = PA_CHANNEL_POSITION_REAR_RIGHT;
map.map[4] = PA_CHANNEL_POSITION_LFE;
goto finish;
} else if (pa_streq(s, "surround-50")) {
map.channels = 5;
map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
map.map[2] = PA_CHANNEL_POSITION_REAR_LEFT;
map.map[3] = PA_CHANNEL_POSITION_REAR_RIGHT;
map.map[4] = PA_CHANNEL_POSITION_FRONT_CENTER;
goto finish;
} else if (pa_streq(s, "surround-51")) {
map.channels = 6;
map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
map.map[2] = PA_CHANNEL_POSITION_REAR_LEFT;
map.map[3] = PA_CHANNEL_POSITION_REAR_RIGHT;
map.map[4] = PA_CHANNEL_POSITION_FRONT_CENTER;
map.map[5] = PA_CHANNEL_POSITION_LFE;
goto finish;
} else if (pa_streq(s, "surround-71")) {
map.channels = 8;
map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
map.map[2] = PA_CHANNEL_POSITION_REAR_LEFT;
map.map[3] = PA_CHANNEL_POSITION_REAR_RIGHT;
map.map[4] = PA_CHANNEL_POSITION_FRONT_CENTER;
map.map[5] = PA_CHANNEL_POSITION_LFE;
map.map[6] = PA_CHANNEL_POSITION_SIDE_LEFT;
map.map[7] = PA_CHANNEL_POSITION_SIDE_RIGHT;
goto finish;
}
state = NULL;
map.channels = 0;
while ((p = pa_split(s, ",", &state))) {
pa_channel_position_t f;
if (map.channels >= PA_CHANNELS_MAX) {
pa_xfree(p);
return NULL;
}
if ((f = pa_channel_position_from_string(p)) == PA_CHANNEL_POSITION_INVALID) {
pa_xfree(p);
return NULL;
}
map.map[map.channels++] = f;
pa_xfree(p);
}
finish:
if (!pa_channel_map_valid(&map))
return NULL;
*rmap = map;
return rmap;
}
static inline const char* pa_channel_position_to_string(pa_channel_position_t pos) {
if (pos < 0 || pos >= PA_CHANNEL_POSITION_MAX)
return NULL;
return pa_position_table[pos];
}
static inline int pa_channel_map_equal(const pa_channel_map *a, const pa_channel_map *b)
{
unsigned c;
if (PA_UNLIKELY(a == b))
return 1;
if (a->channels != b->channels)
return 0;
for (c = 0; c < a->channels; c++)
if (a->map[c] != b->map[c])
return 0;
return 1;
}
static inline char* pa_channel_map_snprint(char *s, size_t l, const pa_channel_map *map) {
unsigned channel;
bool first = true;
char *e;
if (!pa_channel_map_valid(map)) {
pa_snprintf(s, l, _("(invalid)"));
return s;
}
*(e = s) = 0;
for (channel = 0; channel < map->channels && l > 1; channel++) {
l -= pa_snprintf(e, l, "%s%s",
first ? "" : ",",
pa_channel_position_to_string(map->map[channel]));
e = strchr(e, 0);
first = false;
}
return s;
}
#ifdef __cplusplus
}
#endif
#endif /* PULSE_CHANNELMAP_H */

View file

@ -0,0 +1,159 @@
/***
This file is part of PulseAudio.
Copyright 2004-2009 Lennart Poettering
Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2.1 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
***/
#include "compat.h"
#include "device-port.h"
#include "alsa-mixer.h"
pa_device_port_new_data *pa_device_port_new_data_init(pa_device_port_new_data *data)
{
pa_assert(data);
pa_zero(*data);
data->type = PA_DEVICE_PORT_TYPE_UNKNOWN;
data->available = PA_AVAILABLE_UNKNOWN;
return data;
}
void pa_device_port_new_data_set_name(pa_device_port_new_data *data, const char *name)
{
pa_assert(data);
pa_xfree(data->name);
data->name = pa_xstrdup(name);
}
void pa_device_port_new_data_set_description(pa_device_port_new_data *data, const char *description)
{
pa_assert(data);
pa_xfree(data->description);
data->description = pa_xstrdup(description);
}
void pa_device_port_new_data_set_available(pa_device_port_new_data *data, pa_available_t available)
{
pa_assert(data);
data->available = available;
}
void pa_device_port_new_data_set_available_group(pa_device_port_new_data *data, const char *group)
{
pa_assert(data);
pa_xfree(data->available_group);
data->available_group = pa_xstrdup(group);
}
void pa_device_port_new_data_set_direction(pa_device_port_new_data *data, pa_direction_t direction)
{
pa_assert(data);
data->direction = direction;
}
void pa_device_port_new_data_set_type(pa_device_port_new_data *data, pa_device_port_type_t type)
{
pa_assert(data);
data->type = type;
}
void pa_device_port_new_data_done(pa_device_port_new_data *data)
{
pa_assert(data);
pa_xfree(data->name);
pa_xfree(data->description);
pa_xfree(data->available_group);
}
pa_device_port *pa_device_port_new(pa_core *c, pa_device_port_new_data *data, size_t extra)
{
pa_device_port *p;
pa_assert(data);
pa_assert(data->name);
pa_assert(data->description);
pa_assert(data->direction == PA_DIRECTION_OUTPUT || data->direction == PA_DIRECTION_INPUT);
p = calloc(1, sizeof(pa_device_port) + extra);
p->port.name = data->name;
data->name = NULL;
p->port.description = data->description;
data->description = NULL;
p->port.priority = 0;
p->port.available = (enum acp_available) data->available;
p->port.available_group = data->available_group;
data->available_group = NULL;
p->profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
p->port.direction = data->direction == PA_DIRECTION_OUTPUT ?
ACP_DIRECTION_PLAYBACK : ACP_DIRECTION_CAPTURE;
p->port.type = (enum acp_port_type) data->type;
p->proplist = pa_proplist_new();
p->user_data = (void*)((uint8_t*)p + sizeof(pa_device_port));
return p;
}
void pa_device_port_set_available(pa_device_port *p, pa_available_t status)
{
enum acp_available old = p->port.available;
if (old == (enum acp_available) status)
return;
p->port.available = (enum acp_available) status;
if (p->card && p->card->events && p->card->events->port_available)
p->card->events->port_available(p->card->user_data, p->port.index,
old, (enum acp_available) status);
}
bool pa_alsa_device_init_description(pa_proplist *p, pa_card *card) {
const char *s, *d = NULL, *k;
pa_assert(p);
if (pa_proplist_contains(p, PA_PROP_DEVICE_DESCRIPTION))
return true;
if (card)
if ((s = pa_proplist_gets(card->proplist, PA_PROP_DEVICE_DESCRIPTION)))
d = s;
if (!d)
if ((s = pa_proplist_gets(p, PA_PROP_DEVICE_FORM_FACTOR)))
if (pa_streq(s, "internal"))
d = _("Built-in Audio");
if (!d)
if ((s = pa_proplist_gets(p, PA_PROP_DEVICE_CLASS)))
if (pa_streq(s, "modem"))
d = _("Modem");
if (!d)
d = pa_proplist_gets(p, PA_PROP_DEVICE_PRODUCT_NAME);
if (!d)
return false;
k = pa_proplist_gets(p, PA_PROP_DEVICE_PROFILE_DESCRIPTION);
if (d && k)
pa_proplist_setf(p, PA_PROP_DEVICE_DESCRIPTION, "%s %s", d, k);
else if (d)
pa_proplist_sets(p, PA_PROP_DEVICE_DESCRIPTION, d);
return true;
}

View file

@ -0,0 +1,598 @@
/***
This file is part of PulseAudio.
Copyright 2004-2006 Lennart Poettering
Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2.1 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
***/
#ifndef PULSE_COMPAT_H
#define PULSE_COMPAT_H
#ifdef __cplusplus
extern "C" {
#else
#include <stdbool.h>
#endif
#include <stdio.h>
#include <stdarg.h>
#include <stdint.h>
#include <inttypes.h>
#include <stdlib.h>
#include <stdarg.h>
#include <unistd.h>
typedef struct pa_core pa_core;
typedef void *(*pa_copy_func_t)(const void *p);
typedef void (*pa_free_cb_t)(void *p);
#ifdef __GNUC__
#define PA_LIKELY(x) (__builtin_expect(!!(x),1))
#define PA_UNLIKELY(x) (__builtin_expect(!!(x),0))
#define PA_PRINTF_FUNC(fmt, arg1) __attribute__((format(printf, fmt, arg1)))
#else
#define PA_LIKELY(x) (x)
#define PA_UNLIKELY(x) (x)
#define PA_PRINTF_FUNC(fmt, arg1)
#endif
#define PA_MIN(a,b) \
({ \
__typeof__(a) _a = (a); \
__typeof__(b) _b = (b); \
PA_LIKELY(_a < _b) ? _a : _b; \
})
#define PA_MAX(a,b) \
({ \
__typeof__(a) _a = (a); \
__typeof__(b) _b = (b); \
PA_LIKELY(_a > _b) ? _a : _b; \
})
#define PA_CLAMP_UNLIKELY(v,low,high) \
({ \
__typeof__(v) _v = (v); \
__typeof__(low) _low = (low); \
__typeof__(high) _high = (high); \
PA_MIN(PA_MAX(_v, _low), _high); \
})
#define PA_PTR_TO_UINT(p) ((unsigned int) ((uintptr_t) (p)))
#include "array.h"
#include "llist.h"
#include "hashmap.h"
#include "dynarray.h"
#include "idxset.h"
#include "proplist.h"
typedef enum pa_direction {
PA_DIRECTION_OUTPUT = 0x0001U, /**< Output direction */
PA_DIRECTION_INPUT = 0x0002U /**< Input direction */
} pa_direction_t;
/* This enum replaces pa_port_available_t (defined in pulse/def.h) for
* internal use, so make sure both enum types stay in sync. */
typedef enum pa_available {
PA_AVAILABLE_UNKNOWN = 0,
PA_AVAILABLE_NO = 1,
PA_AVAILABLE_YES = 2,
} pa_available_t;
#define PA_RATE_MAX (48000U*8U)
typedef enum pa_sample_format {
PA_SAMPLE_U8, /**< Unsigned 8 Bit PCM */
PA_SAMPLE_ALAW, /**< 8 Bit a-Law */
PA_SAMPLE_ULAW, /**< 8 Bit mu-Law */
PA_SAMPLE_S16LE, /**< Signed 16 Bit PCM, little endian (PC) */
PA_SAMPLE_S16BE, /**< Signed 16 Bit PCM, big endian */
PA_SAMPLE_FLOAT32LE, /**< 32 Bit IEEE floating point, little endian (PC), range -1.0 to 1.0 */
PA_SAMPLE_FLOAT32BE, /**< 32 Bit IEEE floating point, big endian, range -1.0 to 1.0 */
PA_SAMPLE_S32LE, /**< Signed 32 Bit PCM, little endian (PC) */
PA_SAMPLE_S32BE, /**< Signed 32 Bit PCM, big endian */
PA_SAMPLE_S24LE, /**< Signed 24 Bit PCM packed, little endian (PC). \since 0.9.15 */
PA_SAMPLE_S24BE, /**< Signed 24 Bit PCM packed, big endian. \since 0.9.15 */
PA_SAMPLE_S24_32LE, /**< Signed 24 Bit PCM in LSB of 32 Bit words, little endian (PC). \since 0.9.15 */
PA_SAMPLE_S24_32BE, /**< Signed 24 Bit PCM in LSB of 32 Bit words, big endian. \since 0.9.15 */
PA_SAMPLE_MAX, /**< Upper limit of valid sample types */
PA_SAMPLE_INVALID = -1 /**< An invalid value */
} pa_sample_format_t;
static inline int pa_sample_format_valid(unsigned format)
{
return format < PA_SAMPLE_MAX;
}
#ifdef WORDS_BIGENDIAN
#define PA_SAMPLE_S16NE PA_SAMPLE_S16BE
#define PA_SAMPLE_FLOAT32NE PA_SAMPLE_FLOAT32BE
#define PA_SAMPLE_S32NE PA_SAMPLE_S32BE
#define PA_SAMPLE_S24NE PA_SAMPLE_S24BE
#define PA_SAMPLE_S24_32NE PA_SAMPLE_S24_32BE
#define PA_SAMPLE_S16RE PA_SAMPLE_S16LE
#define PA_SAMPLE_FLOAT32RE PA_SAMPLE_FLOAT32LE
#define PA_SAMPLE_S32RE PA_SAMPLE_S32LE
#define PA_SAMPLE_S24RE PA_SAMPLE_S24LE
#define PA_SAMPLE_S24_32RE PA_SAMPLE_S24_32LE
#else
#define PA_SAMPLE_S16NE PA_SAMPLE_S16LE
#define PA_SAMPLE_FLOAT32NE PA_SAMPLE_FLOAT32LE
#define PA_SAMPLE_S32NE PA_SAMPLE_S32LE
#define PA_SAMPLE_S24NE PA_SAMPLE_S24LE
#define PA_SAMPLE_S24_32NE PA_SAMPLE_S24_32LE
#define PA_SAMPLE_S16RE PA_SAMPLE_S16BE
#define PA_SAMPLE_FLOAT32RE PA_SAMPLE_FLOAT32BE
#define PA_SAMPLE_S32RE PA_SAMPLE_S32BE
#define PA_SAMPLE_S24RE PA_SAMPLE_S24BE
#define PA_SAMPLE_S24_32RE PA_SAMPLE_S24_32BE
#endif
static const size_t pa_sample_size_table[] = {
[PA_SAMPLE_U8] = 1,
[PA_SAMPLE_ULAW] = 1,
[PA_SAMPLE_ALAW] = 1,
[PA_SAMPLE_S16LE] = 2,
[PA_SAMPLE_S16BE] = 2,
[PA_SAMPLE_FLOAT32LE] = 4,
[PA_SAMPLE_FLOAT32BE] = 4,
[PA_SAMPLE_S32LE] = 4,
[PA_SAMPLE_S32BE] = 4,
[PA_SAMPLE_S24LE] = 3,
[PA_SAMPLE_S24BE] = 3,
[PA_SAMPLE_S24_32LE] = 4,
[PA_SAMPLE_S24_32BE] = 4
};
static inline const char *pa_sample_format_to_string(pa_sample_format_t f)
{
static const char* const table[]= {
[PA_SAMPLE_U8] = "u8",
[PA_SAMPLE_ALAW] = "aLaw",
[PA_SAMPLE_ULAW] = "uLaw",
[PA_SAMPLE_S16LE] = "s16le",
[PA_SAMPLE_S16BE] = "s16be",
[PA_SAMPLE_FLOAT32LE] = "float32le",
[PA_SAMPLE_FLOAT32BE] = "float32be",
[PA_SAMPLE_S32LE] = "s32le",
[PA_SAMPLE_S32BE] = "s32be",
[PA_SAMPLE_S24LE] = "s24le",
[PA_SAMPLE_S24BE] = "s24be",
[PA_SAMPLE_S24_32LE] = "s24-32le",
[PA_SAMPLE_S24_32BE] = "s24-32be",
};
if (!pa_sample_format_valid(f))
return NULL;
return table[f];
}
typedef struct pa_sample_spec {
pa_sample_format_t format;
uint32_t rate;
uint8_t channels;
} pa_sample_spec;
typedef uint64_t pa_usec_t;
#define PA_MSEC_PER_SEC ((pa_usec_t) 1000ULL)
#define PA_USEC_PER_SEC ((pa_usec_t) 1000000ULL)
#define PA_USEC_PER_MSEC ((pa_usec_t) 1000ULL)
static inline size_t pa_usec_to_bytes(pa_usec_t t, const pa_sample_spec *spec) {
return (size_t) (((t * spec->rate) / PA_USEC_PER_SEC)) *
(pa_sample_size_table[spec->format] * spec->channels);
}
static inline int pa_sample_rate_valid(uint32_t rate) {
return rate > 0 && rate <= PA_RATE_MAX * 101 / 100;
}
static inline size_t pa_frame_size(const pa_sample_spec *spec) {
return pa_sample_size_table[spec->format] * spec->channels;
}
typedef enum pa_log_level {
PA_LOG_ERROR = 0, /* Error messages */
PA_LOG_WARN = 1, /* Warning messages */
PA_LOG_NOTICE = 2, /* Notice messages */
PA_LOG_INFO = 3, /* Info messages */
PA_LOG_DEBUG = 4, /* Debug messages */
PA_LOG_LEVEL_MAX
} pa_log_level_t;
extern int _acp_log_level;
extern acp_log_func _acp_log_func;
extern void * _acp_log_data;
#define pa_log_level_enabled(lev) (_acp_log_level >= (int)(lev))
#define pa_log_levelv_meta(lev,f,l,func,fmt,ap) \
({ \
if (pa_log_level_enabled (lev) && _acp_log_func) \
_acp_log_func(_acp_log_data,lev,f,l,func,fmt,ap); \
})
static inline PA_PRINTF_FUNC(5, 6) void pa_log_level_meta(enum pa_log_level level,
const char *file, int line, const char *func,
const char *fmt, ...)
{
va_list args;
va_start(args,fmt);
pa_log_levelv_meta(level,file,line,func,fmt,args);
va_end(args);
}
#define pa_logl(lev,fmt,...) pa_log_level_meta(lev,__FILE__, __LINE__, __func__, fmt, ##__VA_ARGS__)
#define pa_log_error(fmt,...) pa_logl(PA_LOG_ERROR, fmt, ##__VA_ARGS__)
#define pa_log_warn(fmt,...) pa_logl(PA_LOG_WARN, fmt, ##__VA_ARGS__)
#define pa_log_notice(fmt,...) pa_logl(PA_LOG_NOTICE, fmt, ##__VA_ARGS__)
#define pa_log_info(fmt,...) pa_logl(PA_LOG_INFO, fmt, ##__VA_ARGS__)
#define pa_log_debug(fmt,...) pa_logl(PA_LOG_DEBUG, fmt, ##__VA_ARGS__)
#define pa_log pa_log_error
#define pa_assert_se(expr) \
do { \
if (PA_UNLIKELY(!(expr))) { \
fprintf(stderr, "'%s' failed at %s:%u %s()\n", \
#expr , __FILE__, __LINE__, __func__); \
abort(); \
} \
} while (false)
#define pa_assert(expr) \
do { \
if (PA_UNLIKELY(!(expr))) { \
fprintf(stderr, "'%s' failed at %s:%u %s()\n", \
#expr , __FILE__, __LINE__, __func__); \
abort(); \
} \
} while (false)
#define pa_assert_not_reached() \
do { \
fprintf(stderr, "Code should not be reached at %s:%u %s()\n", \
__FILE__, __LINE__, __func__); \
abort(); \
} while (false)
#define pa_memzero(x,l) (memset((x), 0, (l)))
#define pa_zero(x) (pa_memzero(&(x), sizeof(x)))
#define PA_ELEMENTSOF(x) (sizeof(x)/sizeof((x)[0]))
#define pa_streq(a,b) (!strcmp((a),(b)))
#define pa_strneq(a,b,n) (!strncmp((a),(b),(n)))
#define pa_strnull(s) ((s) ? (s) : "null")
#define pa_startswith(s,pfx) (strstr(s, pfx) == s)
#define pa_snprintf snprintf
#define pa_xstrdup(s) ((s) != NULL ? strdup(s) : NULL)
#define pa_xstrndup(s,n) ((s) != NULL ? strndup(s,n) : NULL)
#define pa_xfree free
#define pa_xmalloc malloc
#define pa_xnew0(t,n) calloc(n, sizeof(t))
#define pa_xnew(t,n) pa_xnew0(t,n)
#define pa_xrealloc realloc
#define pa_xrenew(t,p,n) ((t*) realloc(p, (n)*sizeof(t)))
static inline void* pa_xmemdup(const void *p, size_t l) {
return memcpy(malloc(l), p, l);
}
#define pa_xnewdup(t,p,n) ((t*) pa_xmemdup((p), (n)*sizeof(t)))
static inline void pa_xfreev(void**a)
{
int i;
for (i = 0; a && a[i]; i++)
free(a[i]);
free(a);
}
static inline void pa_xstrfreev(char **a) {
pa_xfreev((void**)a);
}
#define pa_cstrerror strerror
#define PA_PATH_SEP "/"
#define PA_PATH_SEP_CHAR '/'
#define PA_WHITESPACE "\n\r \t"
static PA_PRINTF_FUNC(1,2) inline char *pa_sprintf_malloc(const char *fmt, ...)
{
char *res;
va_list args;
va_start(args, fmt);
if (vasprintf(&res, fmt, args) < 0)
res = NULL;
va_end(args);
return res;
}
#define pa_fopen_cloexec(f,m) fopen(f,m"e")
static inline char *pa_path_get_filename(const char *p)
{
char *fn;
if (!p)
return NULL;
if ((fn = strrchr(p, PA_PATH_SEP_CHAR)))
return fn+1;
return (char*) p;
}
static inline bool pa_is_path_absolute(const char *fn)
{
return *fn == PA_PATH_SEP_CHAR;
}
static inline char* pa_maybe_prefix_path(const char *path, const char *prefix)
{
if (pa_is_path_absolute(path))
return pa_xstrdup(path);
return pa_sprintf_malloc("%s" PA_PATH_SEP "%s", prefix, path);
}
static inline bool pa_endswith(const char *s, const char *sfx)
{
size_t l1, l2;
l1 = strlen(s);
l2 = strlen(sfx);
return l1 >= l2 && pa_streq(s + l1 - l2, sfx);
}
static inline char *pa_replace(const char*s, const char*a, const char *b)
{
struct pa_array res;
size_t an, bn;
an = strlen(a);
bn = strlen(b);
pa_array_init(&res, an);
for (;;) {
const char *p;
if (!(p = strstr(s, a)))
break;
pa_array_add_data(&res, s, p-s);
pa_array_add_data(&res, b, bn);
s = p + an;
}
pa_array_add_data(&res, s, strlen(s) + 1);
return res.data;
}
static inline char *pa_split(const char *c, const char *delimiter, const char**state)
{
const char *current = *state ? *state : c;
size_t l;
if (!*current)
return NULL;
l = strcspn(current, delimiter);
*state = current+l;
if (**state)
(*state)++;
return pa_xstrndup(current, l);
}
static inline char *pa_split_spaces(const char *c, const char **state)
{
const char *current = *state ? *state : c;
size_t l;
if (!*current || *c == 0)
return NULL;
current += strspn(current, PA_WHITESPACE);
l = strcspn(current, PA_WHITESPACE);
*state = current+l;
return pa_xstrndup(current, l);
}
static inline char **pa_split_spaces_strv(const char *s)
{
char **t, *e;
unsigned i = 0, n = 8;
const char *state = NULL;
t = pa_xnew(char*, n);
while ((e = pa_split_spaces(s, &state))) {
t[i++] = e;
if (i >= n) {
n *= 2;
t = pa_xrenew(char*, t, n);
}
}
if (i <= 0) {
pa_xfree(t);
return NULL;
}
t[i] = NULL;
return t;
}
static inline char* pa_str_strip_suffix(const char *str, const char *suffix)
{
size_t str_l, suf_l, prefix;
char *ret;
str_l = strlen(str);
suf_l = strlen(suffix);
if (str_l < suf_l)
return NULL;
prefix = str_l - suf_l;
if (!pa_streq(&str[prefix], suffix))
return NULL;
ret = pa_xmalloc(prefix + 1);
memcpy(ret, str, prefix);
ret[prefix] = '\0';
return ret;
}
static inline const char *pa_split_in_place(const char *c, const char *delimiter, size_t *n, const char**state)
{
const char *current = *state ? *state : c;
size_t l;
if (!*current)
return NULL;
l = strcspn(current, delimiter);
*state = current+l;
if (**state)
(*state)++;
*n = l;
return current;
}
static inline const char *pa_split_spaces_in_place(const char *c, size_t *n, const char **state)
{
const char *current = *state ? *state : c;
size_t l;
if (!*current || *c == 0)
return NULL;
current += strspn(current, PA_WHITESPACE);
l = strcspn(current, PA_WHITESPACE);
*state = current+l;
*n = l;
return current;
}
static inline bool pa_str_in_list_spaces(const char *haystack, const char *needle)
{
const char *s;
size_t n;
const char *state = NULL;
if (!haystack || !needle)
return false;
while ((s = pa_split_spaces_in_place(haystack, &n, &state))) {
if (pa_strneq(needle, s, n))
return true;
}
return false;
}
static inline char *pa_strip(char *s)
{
char *e, *l = NULL;
s += strspn(s, PA_WHITESPACE);
for (e = s; *e; e++)
if (!strchr(PA_WHITESPACE, *e))
l = e;
if (l)
*(l+1) = 0;
else
*s = 0;
return s;
}
static inline int pa_atod(const char *s, double *ret_d)
{
char *x;
*ret_d = strtod(s, &x);
return 0;
}
static inline int pa_atoi(const char *s, int32_t *ret_i)
{
*ret_i = (int32_t) atoi(s);
return 0;
}
static inline int pa_atou(const char *s, uint32_t *ret_u)
{
*ret_u = (uint32_t) atoi(s);
return 0;
}
static inline int pa_atol(const char *s, long *ret_l)
{
char *x;
*ret_l = strtol(s, &x, 0);
return 0;
}
static inline int pa_parse_boolean(const char *v)
{
if (pa_streq(v, "1") || !strcasecmp(v, "y") || !strcasecmp(v, "t")
|| !strcasecmp(v, "yes") || !strcasecmp(v, "true") || !strcasecmp(v, "on"))
return 1;
else if (pa_streq(v, "0") || !strcasecmp(v, "n") || !strcasecmp(v, "f")
|| !strcasecmp(v, "no") || !strcasecmp(v, "false") || !strcasecmp(v, "off"))
return 0;
errno = EINVAL;
return -1;
}
static inline const char *pa_yes_no(bool b) {
return b ? "yes" : "no";
}
static inline const char *pa_strna(const char *x) {
return x ? x : "n/a";
}
static inline pa_sample_spec* pa_sample_spec_init(pa_sample_spec *spec)
{
spec->format = PA_SAMPLE_INVALID;
spec->rate = 0;
spec->channels = 0;
return spec;
}
static inline char *pa_readlink(const char *p) {
#ifdef HAVE_READLINK
size_t l = 100;
for (;;) {
char *c;
ssize_t n;
c = pa_xmalloc(l);
if ((n = readlink(p, c, l-1)) < 0) {
pa_xfree(c);
return NULL;
}
if ((size_t) n < l-1) {
c[n] = 0;
return c;
}
pa_xfree(c);
l *= 2;
}
#else
return NULL;
#endif
}
#define _(...) (__VA_ARGS__)
#define N_(...) (__VA_ARGS__)
#include "channelmap.h"
#include "volume.h"
#ifdef __cplusplus
}
#endif
#endif /* PULSE_COMPAT_H */

View file

@ -0,0 +1,389 @@
/***
This file is part of PulseAudio.
Copyright 2004-2006 Lennart Poettering
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2.1 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
***/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <dirent.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include "compat.h"
#include "conf-parser.h"
#define WHITESPACE " \t\n"
#define COMMENTS "#;\n"
/* Run the user supplied parser for an assignment */
static int normal_assignment(pa_config_parser_state *state) {
const pa_config_item *item;
pa_assert(state);
for (item = state->item_table; item->parse; item++) {
if (item->lvalue && !pa_streq(state->lvalue, item->lvalue))
continue;
if (item->section && !state->section)
continue;
if (item->section && !pa_streq(state->section, item->section))
continue;
state->data = item->data;
return item->parse(state);
}
pa_log("[%s:%u] Unknown lvalue '%s' in section '%s'.", state->filename, state->lineno, state->lvalue, pa_strna(state->section));
return -1;
}
/* Parse a proplist entry. */
static int proplist_assignment(pa_config_parser_state *state) {
pa_assert(state);
pa_assert(state->proplist);
if (pa_proplist_sets(state->proplist, state->lvalue, state->rvalue) < 0) {
pa_log("[%s:%u] Failed to parse a proplist entry: %s = %s", state->filename, state->lineno, state->lvalue, state->rvalue);
return -1;
}
return 0;
}
/* Parse a variable assignment line */
static int parse_line(pa_config_parser_state *state) {
char *c;
state->lvalue = state->buf + strspn(state->buf, WHITESPACE);
if ((c = strpbrk(state->lvalue, COMMENTS)))
*c = 0;
if (!*state->lvalue)
return 0;
if (pa_startswith(state->lvalue, ".include ")) {
char *path = NULL, *fn;
int r;
fn = pa_strip(state->lvalue + 9);
if (!pa_is_path_absolute(fn)) {
const char *k;
if ((k = strrchr(state->filename, '/'))) {
char *dir = pa_xstrndup(state->filename, k - state->filename);
fn = path = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", dir, fn);
pa_xfree(dir);
}
}
r = pa_config_parse(fn, NULL, state->item_table, state->proplist, false, state->userdata);
pa_xfree(path);
return r;
}
if (*state->lvalue == '[') {
size_t k;
k = strlen(state->lvalue);
pa_assert(k > 0);
if (state->lvalue[k-1] != ']') {
pa_log("[%s:%u] Invalid section header.", state->filename, state->lineno);
return -1;
}
pa_xfree(state->section);
state->section = pa_xstrndup(state->lvalue + 1, k-2);
if (pa_streq(state->section, "Properties")) {
if (!state->proplist) {
pa_log("[%s:%u] \"Properties\" section is not allowed in this file.", state->filename, state->lineno);
return -1;
}
state->in_proplist = true;
} else
state->in_proplist = false;
return 0;
}
if (!(state->rvalue = strchr(state->lvalue, '='))) {
pa_log("[%s:%u] Missing '='.", state->filename, state->lineno);
return -1;
}
*state->rvalue = 0;
state->rvalue++;
state->lvalue = pa_strip(state->lvalue);
state->rvalue = pa_strip(state->rvalue);
if (state->in_proplist)
return proplist_assignment(state);
else
return normal_assignment(state);
}
#ifndef OS_IS_WIN32
static int conf_filter(const struct dirent *entry) {
return pa_endswith(entry->d_name, ".conf");
}
#endif
/* Go through the file and parse each line */
int pa_config_parse(const char *filename, FILE *f, const pa_config_item *t, pa_proplist *proplist, bool use_dot_d,
void *userdata) {
int r = -1;
bool do_close = !f;
pa_config_parser_state state;
pa_assert(filename);
pa_assert(t);
pa_zero(state);
if (!f && !(f = pa_fopen_cloexec(filename, "r"))) {
if (errno == ENOENT) {
pa_log_debug("Failed to open configuration file '%s': %s", filename, pa_cstrerror(errno));
r = 0;
goto finish;
}
pa_log_warn("Failed to open configuration file '%s': %s", filename, pa_cstrerror(errno));
goto finish;
}
pa_log_debug("Parsing configuration file '%s'", filename);
state.filename = filename;
state.item_table = t;
state.userdata = userdata;
if (proplist)
state.proplist = pa_proplist_new();
while (!feof(f)) {
if (!fgets(state.buf, sizeof(state.buf), f)) {
if (feof(f))
break;
pa_log_warn("Failed to read configuration file '%s': %s", filename, pa_cstrerror(errno));
goto finish;
}
state.lineno++;
if (parse_line(&state) < 0)
goto finish;
}
if (proplist)
pa_proplist_update(proplist, PA_UPDATE_REPLACE, state.proplist);
r = 0;
finish:
if (state.proplist)
pa_proplist_free(state.proplist);
pa_xfree(state.section);
if (do_close && f)
fclose(f);
if (use_dot_d) {
#ifdef OS_IS_WIN32
char *dir_name = pa_sprintf_malloc("%s.d", filename);
char *pattern = pa_sprintf_malloc("%s\\*.conf", dir_name);
HANDLE fh;
WIN32_FIND_DATA wfd;
fh = FindFirstFile(pattern, &wfd);
if (fh != INVALID_HANDLE_VALUE) {
do {
if (!(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
char *filename2 = pa_sprintf_malloc("%s\\%s", dir_name, wfd.cFileName);
pa_config_parse(filename2, NULL, t, proplist, false, userdata);
pa_xfree(filename2);
}
} while (FindNextFile(fh, &wfd));
FindClose(fh);
} else {
DWORD err = GetLastError();
if (err == ERROR_PATH_NOT_FOUND) {
pa_log_debug("Pattern %s did not match any files, ignoring.", pattern);
} else {
LPVOID msgbuf;
DWORD fret = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&msgbuf, 0, NULL);
if (fret != 0) {
pa_log_warn("FindFirstFile(%s) failed with error %ld (%s), ignoring.", pattern, err, (char*)msgbuf);
LocalFree(msgbuf);
} else {
pa_log_warn("FindFirstFile(%s) failed with error %ld, ignoring.", pattern, err);
pa_log_warn("FormatMessage failed with error %ld", GetLastError());
}
}
}
pa_xfree(pattern);
pa_xfree(dir_name);
#else
char *dir_name;
int n;
struct dirent **entries = NULL;
dir_name = pa_sprintf_malloc("%s.d", filename);
n = scandir(dir_name, &entries, conf_filter, alphasort);
if (n >= 0) {
int i;
for (i = 0; i < n; i++) {
char *filename2;
filename2 = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", dir_name, entries[i]->d_name);
pa_config_parse(filename2, NULL, t, proplist, false, userdata);
pa_xfree(filename2);
free(entries[i]);
}
free(entries);
} else {
if (errno == ENOENT)
pa_log_debug("%s does not exist, ignoring.", dir_name);
else
pa_log_warn("scandir(\"%s\") failed: %s", dir_name, pa_cstrerror(errno));
}
pa_xfree(dir_name);
#endif
}
return r;
}
int pa_config_parse_int(pa_config_parser_state *state) {
int *i;
int32_t k;
pa_assert(state);
i = state->data;
if (pa_atoi(state->rvalue, &k) < 0) {
pa_log("[%s:%u] Failed to parse numeric value: %s", state->filename, state->lineno, state->rvalue);
return -1;
}
*i = (int) k;
return 0;
}
int pa_config_parse_unsigned(pa_config_parser_state *state) {
unsigned *u;
uint32_t k;
pa_assert(state);
u = state->data;
if (pa_atou(state->rvalue, &k) < 0) {
pa_log("[%s:%u] Failed to parse numeric value: %s", state->filename, state->lineno, state->rvalue);
return -1;
}
*u = (unsigned) k;
return 0;
}
int pa_config_parse_size(pa_config_parser_state *state) {
size_t *i;
uint32_t k;
pa_assert(state);
i = state->data;
if (pa_atou(state->rvalue, &k) < 0) {
pa_log("[%s:%u] Failed to parse numeric value: %s", state->filename, state->lineno, state->rvalue);
return -1;
}
*i = (size_t) k;
return 0;
}
int pa_config_parse_bool(pa_config_parser_state *state) {
int k;
bool *b;
pa_assert(state);
b = state->data;
if ((k = pa_parse_boolean(state->rvalue)) < 0) {
pa_log("[%s:%u] Failed to parse boolean value: %s", state->filename, state->lineno, state->rvalue);
return -1;
}
*b = !!k;
return 0;
}
int pa_config_parse_not_bool(pa_config_parser_state *state) {
int k;
bool *b;
pa_assert(state);
b = state->data;
if ((k = pa_parse_boolean(state->rvalue)) < 0) {
pa_log("[%s:%u] Failed to parse boolean value: %s", state->filename, state->lineno, state->rvalue);
return -1;
}
*b = !k;
return 0;
}
int pa_config_parse_string(pa_config_parser_state *state) {
char **s;
pa_assert(state);
s = state->data;
pa_xfree(*s);
*s = *state->rvalue ? pa_xstrdup(state->rvalue) : NULL;
return 0;
}

View file

@ -0,0 +1,88 @@
#ifndef fooconfparserhfoo
#define fooconfparserhfoo
/***
This file is part of PulseAudio.
Copyright 2004-2006 Lennart Poettering
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2.1 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
***/
#include <stdio.h>
#include <stdbool.h>
#include "compat.h"
/* An abstract parser for simple, line based, shallow configuration
* files consisting of variable assignments only. */
typedef struct pa_config_parser_state pa_config_parser_state;
typedef int (*pa_config_parser_cb_t)(pa_config_parser_state *state);
/* Wraps info for parsing a specific configuration variable */
typedef struct pa_config_item {
const char *lvalue; /* name of the variable */
pa_config_parser_cb_t parse; /* Function that is called to parse the variable's value */
void *data; /* Where to store the variable's data */
const char *section;
} pa_config_item;
struct pa_config_parser_state {
const char *filename;
unsigned lineno;
char *section;
char *lvalue;
char *rvalue;
void *data; /* The data pointer of the current pa_config_item. */
void *userdata; /* The pointer that was given to pa_config_parse(). */
/* Private data to be used only by conf-parser.c. */
const pa_config_item *item_table;
char buf[4096];
pa_proplist *proplist;
bool in_proplist;
};
/* The configuration file parsing routine. Expects a table of
* pa_config_items in *t that is terminated by an item where lvalue is
* NULL.
*
* If use_dot_d is true, then after parsing the file named by the filename
* argument, the function will parse all files ending with ".conf" in
* alphabetical order from a directory whose name is filename + ".d", if such
* directory exists.
*
* Some configuration files may contain a Properties section, which
* is a bit special. Normally all accepted lvalues must be predefined
* in the pa_config_item table, but in the Properties section the
* pa_config_item table is ignored, and all lvalues are accepted (as
* long as they are valid proplist keys). If the proplist pointer is
* non-NULL, the parser will parse any section named "Properties" as
* properties, and those properties will be merged into the given
* proplist. If proplist is NULL, then sections named "Properties"
* are not allowed at all in the configuration file. */
int pa_config_parse(const char *filename, FILE *f, const pa_config_item *t, pa_proplist *proplist, bool use_dot_d,
void *userdata);
/* Generic parsers for integers, size_t, booleans and strings */
int pa_config_parse_int(pa_config_parser_state *state);
int pa_config_parse_unsigned(pa_config_parser_state *state);
int pa_config_parse_size(pa_config_parser_state *state);
int pa_config_parse_bool(pa_config_parser_state *state);
int pa_config_parse_not_bool(pa_config_parser_state *state);
int pa_config_parse_string(pa_config_parser_state *state);
#endif

View file

@ -0,0 +1,104 @@
/***
This file is part of PulseAudio.
Copyright 2004-2006 Lennart Poettering
Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2.1 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
***/
#ifndef PULSE_DEVICE_PORT_H
#define PULSE_DEVICE_PORT_H
#ifdef __cplusplus
extern "C" {
#else
#include <stdbool.h>
#endif
#include "compat.h"
typedef struct pa_card pa_card;
typedef struct pa_device_port pa_device_port;
/** Port type. \since 14.0 */
typedef enum pa_device_port_type {
PA_DEVICE_PORT_TYPE_UNKNOWN = 0,
PA_DEVICE_PORT_TYPE_AUX = 1,
PA_DEVICE_PORT_TYPE_SPEAKER = 2,
PA_DEVICE_PORT_TYPE_HEADPHONES = 3,
PA_DEVICE_PORT_TYPE_LINE = 4,
PA_DEVICE_PORT_TYPE_MIC = 5,
PA_DEVICE_PORT_TYPE_HEADSET = 6,
PA_DEVICE_PORT_TYPE_HANDSET = 7,
PA_DEVICE_PORT_TYPE_EARPIECE = 8,
PA_DEVICE_PORT_TYPE_SPDIF = 9,
PA_DEVICE_PORT_TYPE_HDMI = 10,
PA_DEVICE_PORT_TYPE_TV = 11,
PA_DEVICE_PORT_TYPE_RADIO = 12,
PA_DEVICE_PORT_TYPE_VIDEO = 13,
PA_DEVICE_PORT_TYPE_USB = 14,
PA_DEVICE_PORT_TYPE_BLUETOOTH = 15,
PA_DEVICE_PORT_TYPE_PORTABLE = 16,
PA_DEVICE_PORT_TYPE_HANDSFREE = 17,
PA_DEVICE_PORT_TYPE_CAR = 18,
PA_DEVICE_PORT_TYPE_HIFI = 19,
PA_DEVICE_PORT_TYPE_PHONE = 20,
PA_DEVICE_PORT_TYPE_NETWORK = 21,
PA_DEVICE_PORT_TYPE_ANALOG = 22,
} pa_device_port_type_t;
struct pa_device_port {
struct acp_port port;
pa_card *card;
pa_proplist *proplist;
pa_hashmap *profiles;
pa_dynarray prof;
void (*impl_free)(struct pa_device_port *port);
void *user_data;
};
#define PA_DEVICE_PORT_DATA(p) (p->user_data);
typedef struct pa_device_port_new_data {
char *name;
char *description;
pa_available_t available;
char *available_group;
pa_direction_t direction;
pa_device_port_type_t type;
} pa_device_port_new_data;
pa_device_port_new_data *pa_device_port_new_data_init(pa_device_port_new_data *data);
void pa_device_port_new_data_set_name(pa_device_port_new_data *data, const char *name);
void pa_device_port_new_data_set_description(pa_device_port_new_data *data, const char *description);
void pa_device_port_new_data_set_available(pa_device_port_new_data *data, pa_available_t available);
void pa_device_port_new_data_set_available_group(pa_device_port_new_data *data, const char *group);
void pa_device_port_new_data_set_direction(pa_device_port_new_data *data, pa_direction_t direction);
void pa_device_port_new_data_set_type(pa_device_port_new_data *data, pa_device_port_type_t type);
void pa_device_port_new_data_done(pa_device_port_new_data *data);
pa_device_port *pa_device_port_new(pa_core *c, pa_device_port_new_data *data, size_t extra);
void pa_device_port_set_available(pa_device_port *p, pa_available_t status);
#ifdef __cplusplus
}
#endif
#endif /* PULSE_DEVICE_PORT_H */

View file

@ -0,0 +1,159 @@
/* ALSA Card Profile
*
* Copyright © 2020 Wim Taymans
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifndef PA_DYNARRAY_H
#define PA_DYNARRAY_H
#ifdef __cplusplus
extern "C" {
#endif
#include "array.h"
typedef struct pa_dynarray_item {
void *ptr;
} pa_dynarray_item;
typedef struct pa_dynarray {
pa_array array;
pa_free_cb_t free_cb;
} pa_dynarray;
static inline void pa_dynarray_init(pa_dynarray *array, pa_free_cb_t free_cb)
{
pa_array_init(&array->array, 16);
array->free_cb = free_cb;
}
static inline void pa_dynarray_item_free(pa_dynarray *array, pa_dynarray_item *item)
{
if (array->free_cb)
array->free_cb(item->ptr);
}
static inline void pa_dynarray_clear(pa_dynarray *array)
{
pa_dynarray_item *item;
pa_array_for_each(item, &array->array)
pa_dynarray_item_free(array, item);
pa_array_clear(&array->array);
}
static inline pa_dynarray* pa_dynarray_new(pa_free_cb_t free_cb)
{
pa_dynarray *d = calloc(1, sizeof(*d));
pa_dynarray_init(d, free_cb);
return d;
}
static inline void pa_dynarray_free(pa_dynarray *array)
{
pa_dynarray_clear(array);
free(array);
}
static inline void pa_dynarray_append(pa_dynarray *array, void *p)
{
pa_dynarray_item *item = pa_array_add(&array->array, sizeof(*item));
item->ptr = p;
}
static inline pa_dynarray_item *pa_dynarray_find_item(pa_dynarray *array, void *p)
{
pa_dynarray_item *item;
pa_array_for_each(item, &array->array) {
if (item->ptr == p)
return item;
}
return NULL;
}
static inline pa_dynarray_item *pa_dynarray_get_item(pa_dynarray *array, unsigned i)
{
if (!pa_array_check_index(&array->array, i, pa_dynarray_item))
return NULL;
return pa_array_get_unchecked(&array->array, i, pa_dynarray_item);
}
static inline void *pa_dynarray_get(pa_dynarray *array, unsigned i)
{
pa_dynarray_item *item = pa_dynarray_get_item(array, i);
if (item == NULL)
return NULL;
return item->ptr;
}
static inline int pa_dynarray_insert_by_index(pa_dynarray *array, void *p, unsigned i)
{
unsigned j, len;
pa_dynarray_item *item;
len = pa_array_get_len(&array->array, pa_dynarray_item);
if (i > len)
return -EINVAL;
item = pa_array_add(&array->array, sizeof(*item));
for (j = len; j > i; j--) {
item--;
item[1].ptr = item[0].ptr;
}
item->ptr = p;
return 0;
}
static inline int pa_dynarray_remove_by_index(pa_dynarray *array, unsigned i)
{
pa_dynarray_item *item = pa_dynarray_get_item(array, i);
if (item == NULL)
return -ENOENT;
pa_dynarray_item_free(array, item);
pa_array_remove(&array->array, item);
return 0;
}
static inline int pa_dynarray_remove_by_data(pa_dynarray *array, void *p)
{
pa_dynarray_item *item = pa_dynarray_find_item(array, p);
if (item == NULL)
return -ENOENT;
pa_dynarray_item_free(array, item);
pa_array_remove(&array->array, item);
return 0;
}
static inline unsigned pa_dynarray_size(pa_dynarray *array)
{
return pa_array_get_len(&array->array, pa_dynarray_item);
}
#define PA_DYNARRAY_FOREACH(elem, array, idx) \
for ((idx) = 0; ((elem) = pa_dynarray_get(array, idx)); (idx)++)
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* PA_DYNARRAY_H */

View file

@ -0,0 +1,219 @@
/* ALSA Card Profile
*
* Copyright © 2020 Wim Taymans
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifndef PA_HASHMAP_H
#define PA_HASHMAP_H
#ifdef __cplusplus
extern "C" {
#endif
#include "array.h"
typedef unsigned (*pa_hash_func_t)(const void *p);
typedef int (*pa_compare_func_t)(const void *a, const void *b);
typedef struct pa_hashmap_item {
void *key;
void *value;
} pa_hashmap_item;
typedef struct pa_hashmap {
pa_array array;
pa_hash_func_t hash_func;
pa_compare_func_t compare_func;
pa_free_cb_t key_free_func;
pa_free_cb_t value_free_func;
} pa_hashmap;
static inline pa_hashmap *pa_hashmap_new(pa_hash_func_t hash_func, pa_compare_func_t compare_func)
{
pa_hashmap *m = calloc(1, sizeof(pa_hashmap));
pa_array_init(&m->array, 16);
m->hash_func = hash_func;
m->compare_func = compare_func;
return m;
}
static inline pa_hashmap *pa_hashmap_new_full(pa_hash_func_t hash_func, pa_compare_func_t compare_func,
pa_free_cb_t key_free_func, pa_free_cb_t value_free_func)
{
pa_hashmap *m = pa_hashmap_new(hash_func, compare_func);
m->key_free_func = key_free_func;
m->value_free_func = value_free_func;
return m;
}
static inline void pa_hashmap_item_free(pa_hashmap *h, pa_hashmap_item *item)
{
if (h->key_free_func)
h->key_free_func(item->key);
if (h->value_free_func)
h->value_free_func(item->value);
}
static inline void pa_hashmap_remove_all(pa_hashmap *h)
{
pa_hashmap_item *item;
pa_array_for_each(item, &h->array)
pa_hashmap_item_free(h, item);
pa_array_reset(&h->array);
}
static inline void pa_hashmap_free(pa_hashmap *h)
{
pa_hashmap_remove_all(h);
pa_array_clear(&h->array);
free(h);
}
static inline pa_hashmap_item* pa_hashmap_find_free(pa_hashmap *h)
{
pa_hashmap_item *item;
pa_array_for_each(item, &h->array) {
if (item->key == NULL)
return item;
}
return pa_array_add(&h->array, sizeof(*item));
}
static inline pa_hashmap_item* pa_hashmap_find(const pa_hashmap *h, const void *key)
{
pa_hashmap_item *item = NULL;
pa_array_for_each(item, &h->array) {
if (item->key != NULL && h->compare_func(item->key, key) == 0)
return item;
}
return NULL;
}
static inline void* pa_hashmap_get(const pa_hashmap *h, const void *key)
{
const pa_hashmap_item *item = pa_hashmap_find(h, key);
if (item == NULL)
return NULL;
return item->value;
}
static inline int pa_hashmap_put(pa_hashmap *h, void *key, void *value)
{
pa_hashmap_item *item = pa_hashmap_find(h, key);
if (item != NULL)
return -1;
item = pa_hashmap_find_free(h);
item->key = key;
item->value = value;
return 0;
}
static inline void* pa_hashmap_remove(pa_hashmap *h, const void *key)
{
pa_hashmap_item *item = pa_hashmap_find(h, key);
void *value;
if (item == NULL)
return NULL;
value = item->value;
if (h->key_free_func)
h->key_free_func(item->key);
item->key = NULL;
item->value = NULL;
return value;
}
static inline int pa_hashmap_remove_and_free(pa_hashmap *h, const void *key)
{
void *val = pa_hashmap_remove(h, key);
if (val && h->value_free_func)
h->value_free_func(val);
return val ? 0 : -1;
}
static inline void *pa_hashmap_first(const pa_hashmap *h)
{
pa_hashmap_item *item;
pa_array_for_each(item, &h->array) {
if (item->key != NULL)
return item->value;
}
return NULL;
}
static inline void *pa_hashmap_iterate(const pa_hashmap *h, void **state, const void **key)
{
pa_hashmap_item *it = *state;
if (it == NULL)
*state = pa_array_first(&h->array);
do {
it = *state;
if (!pa_array_check(&h->array, it))
return NULL;
*state = it + 1;
} while (it->key == NULL);
if (key)
*key = it->key;
return it->value;
}
static inline bool pa_hashmap_isempty(const pa_hashmap *h)
{
pa_hashmap_item *item;
pa_array_for_each(item, &h->array)
if (item->key != NULL)
return false;
return true;
}
static inline unsigned pa_hashmap_size(const pa_hashmap *h)
{
unsigned count = 0;
pa_hashmap_item *item;
pa_array_for_each(item, &h->array)
if (item->key != NULL)
count++;
return count;
}
static inline void pa_hashmap_sort(pa_hashmap *h,
int (*compar)(const void *, const void *))
{
qsort((void*)h->array.data,
pa_array_get_len(&h->array, pa_hashmap_item),
sizeof(pa_hashmap_item), compar);
}
#define PA_HASHMAP_FOREACH(e, h, state) \
for ((state) = NULL, (e) = pa_hashmap_iterate((h), &(state), NULL); \
(e); (e) = pa_hashmap_iterate((h), &(state), NULL))
/* A macro to ease itration through all key, value pairs */
#define PA_HASHMAP_FOREACH_KV(k, e, h, state) \
for ((state) = NULL, (e) = pa_hashmap_iterate((h), &(state), (const void **) &(k)); \
(e); (e) = pa_hashmap_iterate((h), &(state), (const void **) &(k)))
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* PA_HASHMAP_H */

View file

@ -0,0 +1,192 @@
/* ALSA Card Profile
*
* Copyright © 2020 Wim Taymans
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifndef PA_IDXSET_H
#define PA_IDXSET_H
#ifdef __cplusplus
extern "C" {
#endif
#include "array.h"
#define PA_IDXSET_INVALID ((uint32_t) -1)
typedef unsigned (*pa_hash_func_t)(const void *p);
typedef int (*pa_compare_func_t)(const void *a, const void *b);
typedef struct pa_idxset_item {
void *ptr;
} pa_idxset_item;
typedef struct pa_idxset {
pa_array array;
pa_hash_func_t hash_func;
pa_compare_func_t compare_func;
} pa_idxset;
static inline unsigned pa_idxset_trivial_hash_func(const void *p)
{
return PA_PTR_TO_UINT(p);
}
static inline int pa_idxset_trivial_compare_func(const void *a, const void *b)
{
return a < b ? -1 : (a > b ? 1 : 0);
}
static inline unsigned pa_idxset_string_hash_func(const void *p)
{
unsigned hash = 0;
const char *c;
for (c = p; *c; c++)
hash = 31 * hash + (unsigned) *c;
return hash;
}
static inline int pa_idxset_string_compare_func(const void *a, const void *b)
{
return strcmp(a, b);
}
static inline pa_idxset *pa_idxset_new(pa_hash_func_t hash_func, pa_compare_func_t compare_func)
{
pa_idxset *s = calloc(1, sizeof(pa_idxset));
pa_array_init(&s->array, 16);
s->hash_func = hash_func;
s->compare_func = compare_func;
return s;
}
static inline void pa_idxset_free(pa_idxset *s, pa_free_cb_t free_cb)
{
if (free_cb) {
pa_idxset_item *item;
pa_array_for_each(item, &s->array)
free_cb(item->ptr);
}
pa_array_clear(&s->array);
free(s);
}
static inline pa_idxset_item* pa_idxset_find(const pa_idxset *s, const void *ptr)
{
pa_idxset_item *item;
pa_array_for_each(item, &s->array) {
if (item->ptr == ptr)
return item;
}
return NULL;
}
static inline int pa_idxset_put(pa_idxset*s, void *p, uint32_t *idx)
{
pa_idxset_item *item = pa_idxset_find(s, p);
int res = item ? -1 : 0;
if (item == NULL) {
item = pa_idxset_find(s, NULL);
if (item == NULL)
item = pa_array_add(&s->array, sizeof(*item));
item->ptr = p;
}
if (idx)
*idx = item - (pa_idxset_item*)s->array.data;
return res;
}
static inline pa_idxset *pa_idxset_copy(pa_idxset *s, pa_copy_func_t copy_func)
{
pa_idxset_item *item;
pa_idxset *copy = pa_idxset_new(s->hash_func, s->compare_func);
pa_array_for_each(item, &s->array) {
if (item->ptr)
pa_idxset_put(copy, copy_func ? copy_func(item->ptr) : item->ptr, NULL);
}
return copy;
}
static inline bool pa_idxset_isempty(const pa_idxset *s)
{
pa_idxset_item *item;
pa_array_for_each(item, &s->array)
if (item->ptr != NULL)
return false;
return true;
}
static inline unsigned pa_idxset_size(pa_idxset*s)
{
unsigned count = 0;
pa_idxset_item *item;
pa_array_for_each(item, &s->array)
if (item->ptr != NULL)
count++;
return count;
}
static inline void *pa_idxset_search(pa_idxset *s, uint32_t *idx)
{
pa_idxset_item *item;
for (item = pa_array_get_unchecked(&s->array, *idx, pa_idxset_item);
pa_array_check(&s->array, item); item++, (*idx)++) {
if (item->ptr != NULL)
return item->ptr;
}
*idx = PA_IDXSET_INVALID;
return NULL;
}
static inline void *pa_idxset_next(pa_idxset *s, uint32_t *idx)
{
(*idx)++;;
return pa_idxset_search(s, idx);
}
static inline void* pa_idxset_first(pa_idxset *s, uint32_t *idx)
{
uint32_t i = 0;
void *ptr = pa_idxset_search(s, &i);
if (idx)
*idx = i;
return ptr;
}
static inline void* pa_idxset_get_by_data(pa_idxset*s, const void *p, uint32_t *idx)
{
pa_idxset_item *item = pa_idxset_find(s, p);
if (item == NULL)
return NULL;
if (idx)
*idx = item - (pa_idxset_item*)s->array.data;
return item->ptr;
}
#define PA_IDXSET_FOREACH(e, s, idx) \
for ((e) = pa_idxset_first((s), &(idx)); (e); (e) = pa_idxset_next((s), &(idx)))
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* PA_IDXSET_H */

View file

@ -0,0 +1,109 @@
#ifndef foollistfoo
#define foollistfoo
/***
This file is part of PulseAudio.
Copyright 2004-2006 Lennart Poettering
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
***/
/* Some macros for maintaining doubly linked lists */
/* The head of the linked list. Use this in the structure that shall
* contain the head of the linked list */
#define PA_LLIST_HEAD(t,name) \
t *name
/* The pointers in the linked list's items. Use this in the item structure */
#define PA_LLIST_FIELDS(t) \
t *next, *prev
/* Initialize the list's head */
#define PA_LLIST_HEAD_INIT(t,item) \
do { \
(item) = (t*) NULL; } \
while(0)
/* Initialize a list item */
#define PA_LLIST_INIT(t,item) \
do { \
t *_item = (item); \
pa_assert(_item); \
_item->prev = _item->next = NULL; \
} while(0)
/* Prepend an item to the list */
#define PA_LLIST_PREPEND(t,head,item) \
do { \
t **_head = &(head), *_item = (item); \
pa_assert(_item); \
if ((_item->next = *_head)) \
_item->next->prev = _item; \
_item->prev = NULL; \
*_head = _item; \
} while (0)
/* Remove an item from the list */
#define PA_LLIST_REMOVE(t,head,item) \
do { \
t **_head = &(head), *_item = (item); \
pa_assert(_item); \
if (_item->next) \
_item->next->prev = _item->prev; \
if (_item->prev) \
_item->prev->next = _item->next; \
else { \
pa_assert(*_head == _item); \
*_head = _item->next; \
} \
_item->next = _item->prev = NULL; \
} while(0)
/* Find the head of the list */
#define PA_LLIST_FIND_HEAD(t,item,head) \
do { \
t **_head = (head), *_item = (item); \
*_head = _item; \
pa_assert(_head); \
while ((*_head)->prev) \
*_head = (*_head)->prev; \
} while (0)
/* Insert an item after another one (a = where, b = what) */
#define PA_LLIST_INSERT_AFTER(t,head,a,b) \
do { \
t **_head = &(head), *_a = (a), *_b = (b); \
pa_assert(_b); \
if (!_a) { \
if ((_b->next = *_head)) \
_b->next->prev = _b; \
_b->prev = NULL; \
*_head = _b; \
} else { \
if ((_b->next = _a->next)) \
_b->next->prev = _b; \
_b->prev = _a; \
_a->next = _b; \
} \
} while (0)
#define PA_LLIST_FOREACH(i,head) \
for (i = (head); i; i = i->next)
#define PA_LLIST_FOREACH_SAFE(i,n,head) \
for (i = (head); i && ((n = i->next), 1); i = n)
#endif

View file

@ -0,0 +1,23 @@
acp_sources = [
'acp.c',
'compat.c',
'alsa-mixer.c',
'alsa-ucm.c',
'alsa-util.c',
'conf-parser.c',
]
acp_c_args = [
'-D_GNU_SOURCE',
'-DHAVE_ALSA_UCM',
'-DHAVE_CONFIG_H',
'-DHAVE_READLINK',
]
acp_lib = static_library(
'acp',
acp_sources,
c_args : acp_c_args,
include_directories : [configinc],
dependencies : [ alsa_dep, mathlib, ],
install : true)

View file

@ -0,0 +1,208 @@
/* ALSA Card Profile
*
* Copyright © 2020 Wim Taymans
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifndef PA_PROPLIST_H
#define PA_PROPLIST_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdio.h>
#include "array.h"
#include "acp.h"
#define PA_PROP_DEVICE_DESCRIPTION "device.description"
#define PA_PROP_DEVICE_CLASS "device.class"
#define PA_PROP_DEVICE_FORM_FACTOR "device.form_factor"
#define PA_PROP_DEVICE_INTENDED_ROLES "device.intended_roles"
#define PA_PROP_DEVICE_PROFILE_NAME "device.profile.name"
#define PA_PROP_DEVICE_STRING "device.string"
#define PA_PROP_DEVICE_API "device.api"
#define PA_PROP_DEVICE_PRODUCT_NAME "device.product.name"
#define PA_PROP_DEVICE_PROFILE_DESCRIPTION "device.profile.description"
typedef struct pa_proplist_item {
char *key;
char *value;
} pa_proplist_item;
typedef struct pa_proplist {
struct pa_array array;
} pa_proplist;
static inline pa_proplist* pa_proplist_new(void)
{
pa_proplist *p = calloc(1, sizeof(*p));
pa_array_init(&p->array, 16);
return p;
}
static inline pa_proplist_item* pa_proplist_item_find(const pa_proplist *p, const void *key)
{
pa_proplist_item *item;
pa_array_for_each(item, &p->array) {
if (strcmp(key, item->key) == 0)
return item;
}
return NULL;
}
static inline void pa_proplist_item_free(pa_proplist_item* it)
{
free(it->key);
free(it->value);
}
static inline void pa_proplist_clear(pa_proplist* p)
{
pa_proplist_item *item;
pa_array_for_each(item, &p->array)
pa_proplist_item_free(item);
pa_array_reset(&p->array);
}
static inline void pa_proplist_free(pa_proplist* p)
{
pa_proplist_clear(p);
pa_array_clear(&p->array);
free(p);
}
static inline unsigned pa_proplist_size(const pa_proplist *p)
{
return pa_array_get_len(&p->array, pa_proplist_item);
}
static inline int pa_proplist_contains(const pa_proplist *p, const char *key)
{
return pa_proplist_item_find(p, key) ? 1 : 0;
}
static inline int pa_proplist_sets(pa_proplist *p, const char *key, const char *value)
{
pa_proplist_item *item = pa_proplist_item_find(p, key);
if (item != NULL)
pa_proplist_item_free(item);
else
item = pa_array_add(&p->array, sizeof(*item));
item->key = strdup(key);
item->value = strdup(value);
return 0;
}
static inline int pa_proplist_unset(pa_proplist *p, const char *key)
{
pa_proplist_item *item = pa_proplist_item_find(p, key);
if (item == NULL)
return -ENOENT;
pa_proplist_item_free(item);
pa_array_remove(&p->array, item);
return 0;
}
static PA_PRINTF_FUNC(3,4) inline int pa_proplist_setf(pa_proplist *p, const char *key, const char *format, ...)
{
pa_proplist_item *item = pa_proplist_item_find(p, key);
va_list args;
va_start(args, format);
if (item != NULL)
pa_proplist_item_free(item);
else
item = pa_array_add(&p->array, sizeof(*item));
item->key = strdup(key);
vasprintf(&item->value, format, args);
va_end(args);
return 0;
}
static inline const char *pa_proplist_gets(const pa_proplist *p, const char *key)
{
pa_proplist_item *item = pa_proplist_item_find(p, key);
return item ? item->value : NULL;
}
typedef enum pa_update_mode {
PA_UPDATE_SET
/**< Replace the entire property list with the new one. Don't keep
* any of the old data around. */,
PA_UPDATE_MERGE
/**< Merge new property list into the existing one, not replacing
* any old entries if they share a common key with the new
* property list. */,
PA_UPDATE_REPLACE
/**< Merge new property list into the existing one, replacing all
* old entries that share a common key with the new property
* list. */
} pa_update_mode_t;
static inline void pa_proplist_update(pa_proplist *p, pa_update_mode_t mode, const pa_proplist *other)
{
pa_proplist_item *item;
if (mode == PA_UPDATE_SET)
pa_proplist_clear(p);
pa_array_for_each(item, &other->array) {
if (mode == PA_UPDATE_MERGE && pa_proplist_contains(p, item->key))
continue;
pa_proplist_sets(p, item->key, item->value);
}
}
static inline pa_proplist* pa_proplist_new_dict(const struct acp_dict *dict)
{
pa_proplist *p = pa_proplist_new();
if (dict) {
const struct acp_dict_item *item;
struct acp_dict_item *it;
acp_dict_for_each(item, dict) {
it = pa_array_add(&p->array, sizeof(*it));
it->key = strdup(item->key);
it->value = strdup(item->value);
}
}
return p;
}
static inline void pa_proplist_as_dict(const pa_proplist *p, struct acp_dict *dict)
{
dict->n_items = pa_proplist_size(p);
dict->items = p->array.data;
}
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* PA_PROPLIST_H */

View file

@ -0,0 +1,196 @@
/***
This file is part of PulseAudio.
Copyright 2004-2006 Lennart Poettering
Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2.1 of the License,
or (at your option) any later version.
PulseAudio is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
***/
#ifndef PA_VOLUME_H
#define PA_VOLUME_H
#ifdef __cplusplus
extern "C" {
#endif
#include <math.h>
typedef uint32_t pa_volume_t;
#define PA_VOLUME_MUTED ((pa_volume_t) 0U)
#define PA_VOLUME_NORM ((pa_volume_t) 0x10000U)
#define PA_VOLUME_MAX ((pa_volume_t) UINT32_MAX/2)
#ifdef INFINITY
#define PA_DECIBEL_MININFTY ((double) -INFINITY)
#else
#define PA_DECIBEL_MININFTY ((double) -200.0)
#endif
#define PA_CLAMP_VOLUME(v) (PA_CLAMP_UNLIKELY((v), PA_VOLUME_MUTED, PA_VOLUME_MAX))
typedef struct pa_cvolume {
uint32_t channels; /**< Number of channels */
pa_volume_t values[PA_CHANNELS_MAX]; /**< Per-channel volume */
} pa_cvolume;
static inline double pa_volume_linear_to_dB(double v)
{
return 20.0 * log10(v);
}
static inline double pa_sw_volume_to_linear(pa_volume_t v)
{
double f;
if (v <= PA_VOLUME_MUTED)
return 0.0;
if (v == PA_VOLUME_NORM)
return 1.0;
f = ((double) v / PA_VOLUME_NORM);
return f*f*f;
}
static inline double pa_sw_volume_to_dB(pa_volume_t v)
{
if (v <= PA_VOLUME_MUTED)
return PA_DECIBEL_MININFTY;
return pa_volume_linear_to_dB(pa_sw_volume_to_linear(v));
}
static inline double pa_volume_dB_to_linear(double v)
{
return pow(10.0, v / 20.0);
}
static inline pa_volume_t pa_sw_volume_from_linear(double v)
{
if (v <= 0.0)
return PA_VOLUME_MUTED;
return (pa_volume_t) PA_CLAMP_VOLUME((uint64_t) lround(cbrt(v) * PA_VOLUME_NORM));
}
static inline pa_volume_t pa_sw_volume_from_dB(double dB)
{
if (isinf(dB) < 0 || dB <= PA_DECIBEL_MININFTY)
return PA_VOLUME_MUTED;
return pa_sw_volume_from_linear(pa_volume_dB_to_linear(dB));
}
static inline pa_cvolume* pa_cvolume_set(pa_cvolume *a, unsigned channels, pa_volume_t v)
{
uint32_t i;
a->channels = (uint8_t) channels;
for (i = 0; i < a->channels; i++)
a->values[i] = PA_CLAMP_VOLUME(v);
return a;
}
static inline int pa_cvolume_equal(const pa_cvolume *a, const pa_cvolume *b)
{
uint32_t i;
if (PA_UNLIKELY(a == b))
return 1;
if (a->channels != b->channels)
return 0;
for (i = 0; i < a->channels; i++)
if (a->values[i] != b->values[i])
return 0;
return 1;
}
static inline pa_volume_t pa_sw_volume_multiply(pa_volume_t a, pa_volume_t b)
{
uint64_t result;
result = ((uint64_t) a * (uint64_t) b + (uint64_t) PA_VOLUME_NORM / 2ULL) /
(uint64_t) PA_VOLUME_NORM;
if (result > (uint64_t)PA_VOLUME_MAX)
pa_log_warn("pa_sw_volume_multiply: Volume exceeds maximum allowed value and will be clipped. Please check your volume settings.");
return (pa_volume_t) PA_CLAMP_VOLUME(result);
}
static inline pa_cvolume *pa_sw_cvolume_multiply(pa_cvolume *dest,
const pa_cvolume *a, const pa_cvolume *b)
{
unsigned i;
dest->channels = PA_MIN(a->channels, b->channels);
for (i = 0; i < dest->channels; i++)
dest->values[i] = pa_sw_volume_multiply(a->values[i], b->values[i]);
return dest;
}
static inline pa_cvolume *pa_sw_cvolume_multiply_scalar(pa_cvolume *dest,
const pa_cvolume *a, pa_volume_t b)
{
unsigned i;
for (i = 0; i < a->channels; i++)
dest->values[i] = pa_sw_volume_multiply(a->values[i], b);
dest->channels = (uint8_t) i;
return dest;
}
static inline pa_volume_t pa_sw_volume_divide(pa_volume_t a, pa_volume_t b)
{
uint64_t result;
if (b <= PA_VOLUME_MUTED)
return 0;
result = ((uint64_t) a * (uint64_t) PA_VOLUME_NORM + (uint64_t) b / 2ULL) / (uint64_t) b;
if (result > (uint64_t)PA_VOLUME_MAX)
pa_log_warn("pa_sw_volume_divide: Volume exceeds maximum allowed value and will be clipped. Please check your volume settings.");
return (pa_volume_t) PA_CLAMP_VOLUME(result);
}
static inline pa_cvolume *pa_sw_cvolume_divide_scalar(pa_cvolume *dest,
const pa_cvolume *a, pa_volume_t b) {
unsigned i;
for (i = 0; i < a->channels; i++)
dest->values[i] = pa_sw_volume_divide(a->values[i], b);
dest->channels = (uint8_t) i;
return dest;
}
static inline pa_cvolume *pa_sw_cvolume_divide(pa_cvolume *dest,
const pa_cvolume *a, const pa_cvolume *b)
{
unsigned i;
dest->channels = PA_MIN(a->channels, b->channels);
for (i = 0; i < dest->channels; i++)
dest->values[i] = pa_sw_volume_divide(a->values[i], b->values[i]);
return dest;
}
#define pa_cvolume_reset(a, n) pa_cvolume_set((a), (n), PA_VOLUME_NORM)
#define pa_cvolume_mute(a, n) pa_cvolume_set((a), (n), PA_VOLUME_MUTED)
static inline int pa_cvolume_compatible_with_channel_map(const pa_cvolume *v,
const pa_channel_map *cm)
{
return v->channels == cm->channels;
}
static inline pa_volume_t pa_cvolume_max(const pa_cvolume *a)
{
pa_volume_t m = PA_VOLUME_MUTED;
unsigned c;
for (c = 0; c < a->channels; c++)
if (a->values[c] > m)
m = a->values[c];
return m;
}
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* PA_VOLUME_H */

View file

@ -0,0 +1,799 @@
/* Spa ALSA Device
*
* Copyright © 2018 Wim Taymans
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include <stddef.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <poll.h>
#include <alsa/asoundlib.h>
#include <spa/support/log.h>
#include <spa/utils/type.h>
#include <spa/node/node.h>
#include <spa/utils/keys.h>
#include <spa/utils/names.h>
#include <spa/support/loop.h>
#include <spa/support/plugin.h>
#include <spa/monitor/device.h>
#include <spa/monitor/utils.h>
#include <spa/param/param.h>
#include <spa/pod/filter.h>
#include <spa/pod/parser.h>
#include <spa/debug/pod.h>
#include "acp/acp.h"
#define NAME "alsa-device"
#define MAX_POLL 16
static const char default_device[] = "hw:0";
struct props {
char device[64];
};
static void reset_props(struct props *props)
{
strncpy(props->device, default_device, 64);
}
struct impl {
struct spa_handle handle;
struct spa_device device;
struct spa_log *log;
struct spa_loop *loop;
uint32_t info_all;
struct spa_device_info info;
struct spa_param_info params[4];
struct spa_hook_list hooks;
struct props props;
uint32_t n_nodes;
uint32_t profile;
struct acp_card *card;
struct pollfd pfds[MAX_POLL];
int n_pfds;
struct spa_source sources[MAX_POLL];
};
static void handle_acp_poll(struct spa_source *source)
{
struct impl *this = source->data;
int i;
for (i = 0; i < this->n_pfds; i++)
this->pfds[i].revents = this->sources[i].rmask;
acp_card_handle_events(this->card);
for (i = 0; i < this->n_pfds; i++)
this->sources[i].rmask = 0;
}
static int setup_sources(struct impl *this)
{
int i;
for (i = 0; i < this->n_pfds; i++) {
spa_loop_remove_source(this->loop, &this->sources[i]);
}
this->n_pfds = acp_card_poll_descriptors(this->card, this->pfds, MAX_POLL);
for (i = 0; i < this->n_pfds; i++) {
this->sources[i].func = handle_acp_poll;
this->sources[i].data = this;
this->sources[i].fd = this->pfds[i].fd;
this->sources[i].mask = this->pfds[i].events;
this->sources[i].rmask = 0;
spa_loop_add_source(this->loop, &this->sources[i]);
}
return 0;
}
static int emit_node(struct impl *this, struct acp_device *dev)
{
struct spa_dict_item *items;
const struct acp_dict_item *it;
uint32_t n_items;
char device_name[128], path[180], channels[16];
char card_id[16], *p;
struct spa_device_object_info info;
struct acp_card *card = this->card;
const char *stream, *devstr;;
info = SPA_DEVICE_OBJECT_INFO_INIT();
info.type = SPA_TYPE_INTERFACE_Node;
if (dev->direction == ACP_DIRECTION_PLAYBACK) {
info.factory_name = SPA_NAME_API_ALSA_PCM_SINK;
stream = "playback";
} else {
info.factory_name = SPA_NAME_API_ALSA_PCM_SOURCE;
stream = "capture";
}
info.change_mask = SPA_DEVICE_OBJECT_CHANGE_MASK_PROPS;
n_items = dev->props.n_items + 5;
items = alloca(n_items * sizeof(*items));
snprintf(card_id, sizeof(card), "%d", card->index);
devstr = dev->device_strings[0];
p = strstr(devstr, "%f");
if (p) {
snprintf(device_name, sizeof(device_name), "%.*s%d%s",
(int)SPA_PTRDIFF(p, devstr), devstr,
card->index, p+2);
} else {
snprintf(device_name, sizeof(device_name), "%s", devstr);
}
snprintf(path, sizeof(path), "alsa:pcm:%s:%s:%s", card_id, device_name, stream);
items[0] = SPA_DICT_ITEM_INIT(SPA_KEY_OBJECT_PATH, path);
items[1] = SPA_DICT_ITEM_INIT(SPA_KEY_API_ALSA_PATH, device_name);
items[2] = SPA_DICT_ITEM_INIT(SPA_KEY_API_ALSA_PCM_CARD, card_id);
items[3] = SPA_DICT_ITEM_INIT(SPA_KEY_API_ALSA_PCM_STREAM, stream);
snprintf(channels, sizeof(channels), "%d", dev->format.channels);
items[4] = SPA_DICT_ITEM_INIT(SPA_KEY_AUDIO_CHANNELS, channels);
n_items = 5;
acp_dict_for_each(it, &dev->props)
items[n_items++] = SPA_DICT_ITEM_INIT(it->key, it->value);
info.props = &SPA_DICT_INIT(items, n_items);
spa_device_emit_object_info(&this->hooks, dev->index, &info);
return 0;
}
static int emit_info(struct impl *this, bool full)
{
int err = 0;
struct spa_dict_item *items;
uint32_t n_items;
const struct acp_dict_item *it;
struct acp_card *card = this->card;
char path[128];
if (full)
this->info.change_mask = this->info_all;
if (this->info.change_mask) {
n_items = card->props.n_items + 4;
items = alloca(n_items * sizeof(*items));
n_items = 0;
#define ADD_ITEM(key, value) items[n_items++] = SPA_DICT_ITEM_INIT(key, value)
snprintf(path, sizeof(path), "alsa:pcm:%d", card->index);
ADD_ITEM(SPA_KEY_OBJECT_PATH, path);
ADD_ITEM(SPA_KEY_DEVICE_API, "alsa:pcm");
ADD_ITEM(SPA_KEY_MEDIA_CLASS, "Audio/Device");
ADD_ITEM(SPA_KEY_API_ALSA_PATH, (char *)this->props.device);
acp_dict_for_each(it, &card->props)
ADD_ITEM(it->key, it->value);
this->info.props = &SPA_DICT_INIT(items, n_items);
#undef ADD_ITEM
spa_device_emit_info(&this->hooks, &this->info);
this->info.change_mask = 0;
}
return err;
}
static int impl_add_listener(void *object,
struct spa_hook *listener,
const struct spa_device_events *events,
void *data)
{
struct impl *this = object;
struct spa_hook_list save;
struct acp_card *card;
struct acp_card_profile *profile;
uint32_t i;
spa_return_val_if_fail(this != NULL, -EINVAL);
spa_return_val_if_fail(events != NULL, -EINVAL);
card = this->card;
profile = card->profiles[card->active_profile_index];
spa_hook_list_isolate(&this->hooks, &save, listener, events, data);
if (events->info || events->object_info)
emit_info(this, true);
for (i = 0; i < profile->n_devices; i++)
emit_node(this, profile->devices[i]);
spa_hook_list_join(&this->hooks, &save);
return 0;
}
static int impl_sync(void *object, int seq)
{
struct impl *this = object;
spa_return_val_if_fail(this != NULL, -EINVAL);
spa_device_emit_result(&this->hooks, seq, 0, 0, NULL);
return 0;
}
static struct spa_pod *build_profile(struct spa_pod_builder *b, uint32_t id,
struct acp_card_profile *pr)
{
struct spa_pod_frame f[2];
uint32_t i, n_classes, n_capture = 0, n_playback = 0;
for (i = 0; i < pr->n_devices; i++) {
switch (pr->devices[i]->direction) {
case ACP_DIRECTION_PLAYBACK:
n_playback++;
break;
case ACP_DIRECTION_CAPTURE:
n_capture++;
break;
}
}
n_classes = n_capture > 0 ? 1 : 0;
n_classes += n_playback > 0 ? 1 : 0;
spa_pod_builder_int(b, n_classes);
spa_pod_builder_push_object(b, &f[0], SPA_TYPE_OBJECT_ParamProfile, id);
spa_pod_builder_add(b,
SPA_PARAM_PROFILE_index, SPA_POD_Int(pr->index),
SPA_PARAM_PROFILE_name, SPA_POD_String(pr->name),
SPA_PARAM_PROFILE_description, SPA_POD_String(pr->description),
SPA_PARAM_PROFILE_priority, SPA_POD_Int(pr->priority),
SPA_PARAM_PROFILE_available, SPA_POD_Id(pr->available),
0);
spa_pod_builder_prop(b, SPA_PARAM_PROFILE_classes, 0);
spa_pod_builder_push_struct(b, &f[1]);
spa_pod_builder_int(b, n_classes);
if (n_capture > 0) {
spa_pod_builder_add_struct(b,
SPA_POD_String("Audio/Source"),
SPA_POD_Int(n_capture));
}
if (n_playback > 0) {
spa_pod_builder_add_struct(b,
SPA_POD_String("Audio/Sink"),
SPA_POD_Int(n_playback));
}
spa_pod_builder_pop(b, &f[1]);
return spa_pod_builder_pop(b, &f[0]);
}
static struct spa_pod *build_route(struct spa_pod_builder *b, uint32_t id,
struct acp_port *p, struct acp_device *dev)
{
struct spa_pod_frame f[2];
const struct acp_dict_item *item;
uint32_t i;
enum spa_direction direction;
switch (p->direction) {
case ACP_DIRECTION_PLAYBACK:
direction = SPA_DIRECTION_OUTPUT;
break;
case ACP_DIRECTION_CAPTURE:
direction = SPA_DIRECTION_INPUT;
break;
default:
errno = EINVAL;
return NULL;
}
spa_pod_builder_push_object(b, &f[0], SPA_TYPE_OBJECT_ParamRoute, id);
spa_pod_builder_add(b,
SPA_PARAM_ROUTE_index, SPA_POD_Int(p->index),
SPA_PARAM_ROUTE_direction, SPA_POD_Id(direction),
SPA_PARAM_ROUTE_name, SPA_POD_String(p->name),
SPA_PARAM_ROUTE_description, SPA_POD_String(p->description),
SPA_PARAM_ROUTE_priority, SPA_POD_Int(p->priority),
SPA_PARAM_ROUTE_available, SPA_POD_Id(p->available),
0);
spa_pod_builder_prop(b, SPA_PARAM_ROUTE_info, 0);
spa_pod_builder_push_struct(b, &f[1]);
spa_pod_builder_int(b, p->props.n_items);
acp_dict_for_each(item, &p->props) {
spa_pod_builder_add(b,
SPA_POD_String(item->key),
SPA_POD_String(item->value),
NULL);
}
spa_pod_builder_pop(b, &f[1]);
spa_pod_builder_prop(b, SPA_PARAM_ROUTE_profiles, 0);
spa_pod_builder_push_array(b, &f[1]);
for (i = 0; i < p->n_profiles; i++)
spa_pod_builder_int(b, p->profiles[i]->index);
spa_pod_builder_pop(b, &f[1]);
if (dev != NULL) {
uint32_t channels = dev->format.channels;
float volumes[channels];
bool mute;
spa_pod_builder_prop(b, SPA_PARAM_ROUTE_device, 0);
spa_pod_builder_int(b, dev->index);
spa_pod_builder_prop(b, SPA_PARAM_ROUTE_props, 0);
spa_pod_builder_push_object(b, &f[1], SPA_TYPE_OBJECT_Props, id);
if (SPA_FLAG_IS_SET(dev->flags, ACP_DEVICE_HW_MUTE)) {
acp_device_get_mute(dev, &mute);
spa_pod_builder_add(b,
SPA_PROP_mute, SPA_POD_Bool(mute),
0);
}
if (SPA_FLAG_IS_SET(dev->flags, ACP_DEVICE_HW_VOLUME)) {
spa_zero(volumes);
acp_device_get_volume(dev, volumes, channels);
spa_pod_builder_add(b,
SPA_PROP_channelVolumes, SPA_POD_Array(sizeof(float),
SPA_TYPE_Float, channels, volumes),
0);
}
spa_pod_builder_pop(b, &f[1]);
}
return spa_pod_builder_pop(b, &f[0]);
}
static struct acp_port *find_port_for_device(struct acp_card *card, struct acp_device *dev)
{
uint32_t i;
for (i = 0; i < dev->n_ports; i++) {
struct acp_port *p = dev->ports[i];
if (SPA_FLAG_IS_SET(p->flags, ACP_PORT_ACTIVE))
return p;
}
return NULL;
}
static int impl_enum_params(void *object, int seq,
uint32_t id, uint32_t start, uint32_t num,
const struct spa_pod *filter)
{
struct impl *this = object;
struct spa_pod *param;
struct spa_pod_builder b = { 0 };
uint8_t buffer[4096];
struct spa_result_device_params result;
uint32_t count = 0;
struct acp_card *card;
struct acp_card_profile *pr;
struct acp_port *p;
struct acp_device *dev;
spa_return_val_if_fail(this != NULL, -EINVAL);
spa_return_val_if_fail(num != 0, -EINVAL);
card = this->card;
result.id = id;
result.next = start;
next:
result.index = result.next++;
spa_pod_builder_init(&b, buffer, sizeof(buffer));
switch (id) {
case SPA_PARAM_EnumProfile:
if (result.index >= card->n_profiles)
return 0;
pr = card->profiles[result.index];
param = build_profile(&b, id, pr);
break;
case SPA_PARAM_Profile:
if (result.index > 0)
return 0;
pr = card->profiles[card->active_profile_index];
param = build_profile(&b, id, pr);
break;
case SPA_PARAM_EnumRoute:
if (result.index >= card->n_ports)
return 0;
p = card->ports[result.index];
param = build_route(&b, id, p, NULL);
break;
case SPA_PARAM_Route:
while (true) {
if (result.index >= card->n_devices)
return 0;
dev = card->devices[result.index];
if (SPA_FLAG_IS_SET(dev->flags, ACP_DEVICE_ACTIVE))
break;
result.index++;
}
p = find_port_for_device(card, dev);
if (p == NULL)
return 0;
result.next = result.index + 1;
param = build_route(&b, id, p, dev);
if (param == NULL)
return -errno;
break;
default:
return -ENOENT;
}
if (spa_pod_filter(&b, &result.param, param, filter) < 0)
goto next;
spa_device_emit_result(&this->hooks, seq, 0,
SPA_RESULT_TYPE_DEVICE_PARAMS, &result);
if (++count != num)
goto next;
return 0;
}
static int apply_device_props(struct impl *this, struct acp_device *dev, struct spa_pod *props)
{
float volume = 0;
bool mute = 0;
struct spa_pod_prop *prop;
struct spa_pod_object *obj = (struct spa_pod_object *) props;
int changed = 0;
if (!spa_pod_is_object_type(props, SPA_TYPE_OBJECT_Props))
return -EINVAL;
SPA_POD_OBJECT_FOREACH(obj, prop) {
switch (prop->key) {
case SPA_PROP_volume:
if (spa_pod_get_float(&prop->value, &volume) == 0) {
acp_device_set_volume(dev, &volume, 1);
changed++;
}
break;
case SPA_PROP_mute:
if (spa_pod_get_bool(&prop->value, &mute) == 0) {
acp_device_set_mute(dev, mute);
changed++;
}
break;
case SPA_PROP_channelVolumes:
{
float volumes[64];
uint32_t n_volumes;
if ((n_volumes = spa_pod_copy_array(&prop->value, SPA_TYPE_Float,
volumes, 64)) > 0) {
acp_device_set_volume(dev, volumes, n_volumes);
changed++;
}
break;
}
}
}
return 0;
}
static int impl_set_param(void *object,
uint32_t id, uint32_t flags,
const struct spa_pod *param)
{
struct impl *this = object;
int res;
spa_return_val_if_fail(this != NULL, -EINVAL);
switch (id) {
case SPA_PARAM_Profile:
{
uint32_t id;
if ((res = spa_pod_parse_object(param,
SPA_TYPE_OBJECT_ParamProfile, NULL,
SPA_PARAM_PROFILE_index, SPA_POD_Int(&id))) < 0) {
spa_log_warn(this->log, "can't parse profile");
spa_debug_pod(0, NULL, param);
return res;
}
res = acp_card_set_profile(this->card, id);
break;
}
case SPA_PARAM_Route:
{
uint32_t id, device;
struct spa_pod *props = NULL;
struct acp_device *dev;
if ((res = spa_pod_parse_object(param,
SPA_TYPE_OBJECT_ParamRoute, NULL,
SPA_PARAM_ROUTE_index, SPA_POD_Int(&id),
SPA_PARAM_ROUTE_device, SPA_POD_Int(&device),
SPA_PARAM_ROUTE_props, SPA_POD_OPT_Pod(&props))) < 0) {
spa_log_warn(this->log, "can't parse route");
spa_debug_pod(0, NULL, param);
return res;
}
if (device >= this->card->n_devices)
return -EINVAL;
dev = this->card->devices[device];
res = acp_device_set_port(dev, id);
if (props)
apply_device_props(this, dev, props);
break;
}
default:
return -ENOENT;
}
return 0;
}
static const struct spa_device_methods impl_device = {
SPA_VERSION_DEVICE_METHODS,
.add_listener = impl_add_listener,
.sync = impl_sync,
.enum_params = impl_enum_params,
.set_param = impl_set_param,
};
static void card_props_changed(void *data)
{
struct impl *this = data;
spa_log_info(this->log, "card properties changed");
}
static bool has_device(struct acp_card_profile *pr, uint32_t index)
{
uint32_t i;
for (i = 0; i < pr->n_devices; i++)
if (pr->devices[i]->index == index)
return true;
return false;
}
static void card_profile_changed(void *data, uint32_t old_index, uint32_t new_index)
{
struct impl *this = data;
struct acp_card *card = this->card;
struct acp_card_profile *op = card->profiles[old_index];
struct acp_card_profile *np = card->profiles[new_index];
uint32_t i;
spa_log_info(this->log, "card profile changed from %s to %s",
op->name, np->name);
for (i = 0; i < op->n_devices; i++) {
uint32_t index = op->devices[i]->index;
if (has_device(np, index))
continue;
spa_device_emit_object_info(&this->hooks, index, NULL);
}
for (i = 0; i < np->n_devices; i++) {
emit_node(this, np->devices[i]);
}
setup_sources(this);
this->info.change_mask |= SPA_DEVICE_CHANGE_MASK_PARAMS;
this->params[1].flags ^= SPA_PARAM_INFO_SERIAL;
emit_info(this, false);
}
static void card_profile_available(void *data, uint32_t index,
enum acp_available old, enum acp_available available)
{
struct impl *this = data;
struct acp_card *card = this->card;
struct acp_card_profile *p = card->profiles[index];
spa_log_info(this->log, "card profile %s available %d", p->name, available);
this->info.change_mask |= SPA_DEVICE_CHANGE_MASK_PARAMS;
this->params[0].flags ^= SPA_PARAM_INFO_SERIAL;
emit_info(this, false);
}
static void card_port_available(void *data, uint32_t index,
enum acp_available old, enum acp_available available)
{
struct impl *this = data;
struct acp_card *card = this->card;
struct acp_port *p = card->ports[index];
spa_log_info(this->log, "card port %s available %d", p->name, available);
this->info.change_mask |= SPA_DEVICE_CHANGE_MASK_PARAMS;
this->params[2].flags ^= SPA_PARAM_INFO_SERIAL;
emit_info(this, false);
}
static void on_volume_changed(void *data, struct acp_device *dev)
{
struct impl *this = data;
spa_log_info(this->log, "device %s volume changed", dev->name);
this->info.change_mask |= SPA_DEVICE_CHANGE_MASK_PARAMS;
this->params[3].flags ^= SPA_PARAM_INFO_SERIAL;
emit_info(this, false);
}
static void on_mute_changed(void *data, struct acp_device *dev)
{
struct impl *this = data;
spa_log_debug(this->log, "device %s mute changed", dev->name);
this->info.change_mask |= SPA_DEVICE_CHANGE_MASK_PARAMS;
this->params[3].flags ^= SPA_PARAM_INFO_SERIAL;
emit_info(this, false);
}
struct acp_card_events card_events = {
ACP_VERSION_CARD_EVENTS,
.props_changed = card_props_changed,
.profile_changed = card_profile_changed,
.profile_available = card_profile_available,
.port_available = card_port_available,
.volume_changed = on_volume_changed,
.mute_changed = on_mute_changed,
};
static int impl_get_interface(struct spa_handle *handle, const char *type, void **interface)
{
struct impl *this;
spa_return_val_if_fail(handle != NULL, -EINVAL);
spa_return_val_if_fail(interface != NULL, -EINVAL);
this = (struct impl *) handle;
if (strcmp(type, SPA_TYPE_INTERFACE_Device) == 0)
*interface = &this->device;
else
return -ENOENT;
return 0;
}
static SPA_PRINTF_FUNC(6,0) void impl_acp_log_func(void *data,
int level, const char *file, int line, const char *func,
const char *fmt, va_list arg)
{
struct impl *this = data;
spa_log_logv(this->log, (enum spa_log_level)level, file, line, func, fmt, arg);
}
static int impl_clear(struct spa_handle *handle)
{
return 0;
}
static size_t
impl_get_size(const struct spa_handle_factory *factory,
const struct spa_dict *params)
{
return sizeof(struct impl);
}
static int
impl_init(const struct spa_handle_factory *factory,
struct spa_handle *handle,
const struct spa_dict *info,
const struct spa_support *support,
uint32_t n_support)
{
struct impl *this;
const char *str;
spa_return_val_if_fail(factory != NULL, -EINVAL);
spa_return_val_if_fail(handle != NULL, -EINVAL);
handle->get_interface = impl_get_interface;
handle->clear = impl_clear;
this = (struct impl *) handle;
this->log = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log);
this->loop = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Loop);
if (this->loop == NULL) {
spa_log_error(this->log, "a Loop interface is needed");
return -EINVAL;
}
acp_set_log_func(impl_acp_log_func, this);
acp_set_log_level(6);
this->device.iface = SPA_INTERFACE_INIT(
SPA_TYPE_INTERFACE_Device,
SPA_VERSION_DEVICE,
&impl_device, this);
spa_hook_list_init(&this->hooks);
reset_props(&this->props);
if (info && (str = spa_dict_lookup(info, SPA_KEY_API_ALSA_PATH)))
snprintf(this->props.device, 64, "%s", str);
spa_log_debug(this->log, "probe card %s", this->props.device);
this->card = acp_card_new(atoi(this->props.device+3), NULL);
if (this->card == NULL)
return -errno;
setup_sources(this);
acp_card_add_listener(this->card, &card_events, this);
this->info = SPA_DEVICE_INFO_INIT();
this->info_all = SPA_DEVICE_CHANGE_MASK_PROPS |
SPA_DEVICE_CHANGE_MASK_PARAMS;
this->params[0] = SPA_PARAM_INFO(SPA_PARAM_EnumProfile, SPA_PARAM_INFO_READ);
this->params[1] = SPA_PARAM_INFO(SPA_PARAM_Profile, SPA_PARAM_INFO_READWRITE);
this->params[2] = SPA_PARAM_INFO(SPA_PARAM_EnumRoute, SPA_PARAM_INFO_READ);
this->params[3] = SPA_PARAM_INFO(SPA_PARAM_Route, SPA_PARAM_INFO_READWRITE);
this->info.params = this->params;
this->info.n_params = 4;
return 0;
}
static const struct spa_interface_info impl_interfaces[] = {
{SPA_TYPE_INTERFACE_Device,},
};
static int
impl_enum_interface_info(const struct spa_handle_factory *factory,
const struct spa_interface_info **info,
uint32_t *index)
{
spa_return_val_if_fail(factory != NULL, -EINVAL);
spa_return_val_if_fail(info != NULL, -EINVAL);
spa_return_val_if_fail(index != NULL, -EINVAL);
if (*index >= SPA_N_ELEMENTS(impl_interfaces))
return 0;
*info = &impl_interfaces[(*index)++];
return 1;
}
const struct spa_handle_factory spa_alsa_acp_device_factory = {
SPA_VERSION_HANDLE_FACTORY,
SPA_NAME_API_ALSA_ACP_DEVICE,
NULL,
impl_get_size,
impl_init,
impl_enum_interface_info,
};

View file

@ -31,6 +31,7 @@ extern const struct spa_handle_factory spa_alsa_sink_factory;
extern const struct spa_handle_factory spa_alsa_udev_factory;
extern const struct spa_handle_factory spa_alsa_device_factory;
extern const struct spa_handle_factory spa_alsa_seq_bridge_factory;
extern const struct spa_handle_factory spa_alsa_acp_device_factory;
SPA_EXPORT
int spa_handle_factory_enum(const struct spa_handle_factory **factory, uint32_t *index)
@ -54,6 +55,9 @@ int spa_handle_factory_enum(const struct spa_handle_factory **factory, uint32_t
case 4:
*factory = &spa_alsa_seq_bridge_factory;
break;
case 5:
*factory = &spa_alsa_acp_device_factory;
break;
default:
return 0;
}

View file

@ -1,5 +1,8 @@
subdir('acp')
spa_alsa_sources = ['alsa.c',
'alsa-udev.c',
'alsa-acp-device.c',
'alsa-pcm-device.c',
'alsa-pcm-sink.c',
'alsa-pcm-source.c',
@ -7,9 +10,13 @@ spa_alsa_sources = ['alsa.c',
'alsa-seq-source.c',
'alsa-seq.c']
spa_alsa = shared_library('spa-alsa',
spa_alsa_sources,
include_directories : [spa_inc],
dependencies : [ alsa_dep, libudev_dep, mathlib, ],
install : true,
install_dir : join_paths(spa_plugindir, 'alsa'))
spa_alsa = shared_library(
'spa-alsa',
[ spa_alsa_sources ],
c_args : acp_c_args,
include_directories : [spa_inc, configinc],
dependencies : [ alsa_dep, libudev_dep, mathlib ],
link_with : [ acp_lib ],
install : true,
install_dir : join_paths(spa_plugindir, 'alsa')
)

View file

@ -188,7 +188,7 @@ static struct node *alsa_create_node(struct device *device, uint32_t id,
struct node *node;
struct impl *impl = device->impl;
int res;
const char *dev, *subdev, *stream;
const char *dev, *subdev, *stream, *profile;
int priority;
pw_log_debug("new node %u", id);
@ -210,11 +210,15 @@ static struct node *alsa_create_node(struct device *device, uint32_t id,
pw_properties_set(node->props, PW_KEY_FACTORY_NAME, info->factory_name);
if ((dev = pw_properties_get(node->props, SPA_KEY_API_ALSA_PCM_DEVICE)) == NULL)
dev = "0";
if ((dev = pw_properties_get(node->props, "alsa.device")) == NULL)
dev = "0";
if ((subdev = pw_properties_get(node->props, SPA_KEY_API_ALSA_PCM_SUBDEVICE)) == NULL)
subdev = "0";
if ((subdev = pw_properties_get(node->props, "alsa.subdevice")) == NULL)
subdev = "0";
if ((stream = pw_properties_get(node->props, SPA_KEY_API_ALSA_PCM_STREAM)) == NULL)
stream = "unknown";
if ((profile = pw_properties_get(node->props, "device.profile.name")) == NULL)
profile = "unknown";
if (!strcmp(stream, "capture"))
node->direction = PW_DIRECTION_OUTPUT;
@ -233,6 +237,11 @@ static struct node *alsa_create_node(struct device *device, uint32_t id,
priority -= atol(dev) * 16;
priority -= atol(subdev);
if (strstr(profile, "analog-") == profile)
priority += 9;
else if (strstr(profile, "iec958-") == profile)
priority += 8;
if (pw_properties_get(node->props, PW_KEY_PRIORITY_MASTER) == NULL) {
pw_properties_setf(node->props, PW_KEY_PRIORITY_MASTER, "%d", priority);
pw_properties_setf(node->props, PW_KEY_PRIORITY_SESSION, "%d", priority);