mirror of
https://github.com/owntone/owntone-server.git
synced 2025-01-24 13:13:17 -05:00
[alsa] Changes to commit 0cb4e0b: multiple devices
This commit is contained in:
parent
0cb4e0b862
commit
4e1e5efedc
@ -219,6 +219,9 @@ In the config file, you can select ALSA for local audio. This is the default.
|
|||||||
When using ALSA, the server will try to syncronize playback with AirPlay. You
|
When using ALSA, the server will try to syncronize playback with AirPlay. You
|
||||||
can adjust the syncronization in the config file.
|
can adjust the syncronization in the config file.
|
||||||
|
|
||||||
|
For most setups the default values in the config file should work. If they
|
||||||
|
don't, there is help here: [README_ALSA.md](https://github.com/ejurgensen/forked-daapd/blob/master/README_ALSA.md)
|
||||||
|
|
||||||
|
|
||||||
## Local audio, Bluetooth and more through Pulseaudio
|
## Local audio, Bluetooth and more through Pulseaudio
|
||||||
|
|
||||||
|
@ -144,7 +144,7 @@ Simple mixer control 'Analogue Playback Boost',0
|
|||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
This card has multiple controls but we want the first mixer listed with a `pvolume` capability - in this case that mixer value required for the server configuration is called `Anaolgue`.
|
This card has multiple controls but we want the first mixer listed with a `pvolume` capability - in this case that mixer value required for the server configuration is called `Analogue`.
|
||||||
|
|
||||||
For the server configuration, we will use:
|
For the server configuration, we will use:
|
||||||
```
|
```
|
||||||
@ -163,7 +163,7 @@ This is the name of the underlying physical device used for the mixer - it is ty
|
|||||||
|
|
||||||
## Handling Devices that cannot concurrently play multiple audio streams
|
## Handling Devices that cannot concurrently play multiple audio streams
|
||||||
|
|
||||||
Some devices such as various RPI DAC boards (IQaudio DAC, Allo Boss DAC...) can not have multiple streams openned at the same time/cannot play multiple sound files at the same time: this results in `Device or resource busy` errors. You can confirm if your sound card has this problem by using the example below once have determined the names/cards information as above.
|
Some devices such as various RPI DAC boards (IQaudio DAC, Allo Boss DAC...) cannot have multiple streams openned at the same time/cannot play multiple sound files at the same time. This results in `Device or resource busy` errors. You can confirm if your sound card has this problem by using the example below once have determined the names/cards information as above.
|
||||||
|
|
||||||
Using our `hw:1` device we try:
|
Using our `hw:1` device we try:
|
||||||
|
|
||||||
@ -288,13 +288,12 @@ audio {
|
|||||||
}
|
}
|
||||||
|
|
||||||
alsa "dac" {
|
alsa "dac" {
|
||||||
card="dac"
|
|
||||||
mixer="Analogue"
|
mixer="Analogue"
|
||||||
mixer_device="hw:1"
|
mixer_device="hw:1"
|
||||||
}
|
}
|
||||||
|
|
||||||
alsa "headphones" {
|
alsa "hw:0,0" {
|
||||||
card = "hw:0,0"
|
nickname = "headphones"
|
||||||
mixer = "PCM"
|
mixer = "PCM"
|
||||||
mixer_device = "hw:0"
|
mixer_device = "hw:0"
|
||||||
}
|
}
|
||||||
|
@ -216,8 +216,6 @@ audio {
|
|||||||
# socket.
|
# socket.
|
||||||
# server = ""
|
# server = ""
|
||||||
|
|
||||||
# ALSA settings can be definied in their own 'alsa { }' sections for
|
|
||||||
# multiple devices
|
|
||||||
# Audio PCM device name for local audio output - ALSA only
|
# Audio PCM device name for local audio output - ALSA only
|
||||||
# card = "default"
|
# card = "default"
|
||||||
|
|
||||||
@ -249,21 +247,21 @@ audio {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# ALSA device settings
|
# ALSA device settings
|
||||||
# Multiple sections can be defined for multiple ALSA devices on a host (i.e. a
|
# If you have multiple ALSA devices you can configure them individually via
|
||||||
# RPi with onboard headphones and DAC hat). ALSA may see these devices as hw:0
|
# sections like the below. Make sure to set the "card name" correctly. See the
|
||||||
# and hw:1 via 'aplay -l' (see README_ALSA)
|
# README about ALSA for details. Note that these settings will override the ALSA
|
||||||
#
|
# settings in the "audio" section above.
|
||||||
# Overrides ALSA settings in 'audio' section
|
#alsa "card name" {
|
||||||
#alsa "alsa default" {
|
# Name - used in the speaker list in Remote
|
||||||
# Audio PCM device name for local audio output
|
# If not set, the card name will be used
|
||||||
# card = "default"
|
# nickname = "Computer"
|
||||||
|
|
||||||
# Mixer channel to use for volume control
|
# Mixer channel to use for volume control
|
||||||
# If not set, PCM will be used if available, otherwise Master.
|
# If not set, PCM will be used if available, otherwise Master
|
||||||
# mixer = ""
|
# mixer = ""
|
||||||
|
|
||||||
# Mixer device to use for volume control
|
# Mixer device to use for volume control
|
||||||
# If not set, the value for "card" will be used.
|
# If not set, the card name will be used
|
||||||
# mixer_device = ""
|
# mixer_device = ""
|
||||||
#}
|
#}
|
||||||
|
|
||||||
|
@ -128,7 +128,7 @@ static cfg_opt_t sec_audio[] =
|
|||||||
/* local ALSA audio section structure */
|
/* local ALSA audio section structure */
|
||||||
static cfg_opt_t sec_alsa[] =
|
static cfg_opt_t sec_alsa[] =
|
||||||
{
|
{
|
||||||
CFG_STR("card", "default", CFGF_NONE),
|
CFG_STR("nickname", NULL, CFGF_NONE),
|
||||||
CFG_STR("mixer", NULL, CFGF_NONE),
|
CFG_STR("mixer", NULL, CFGF_NONE),
|
||||||
CFG_STR("mixer_device", NULL, CFGF_NONE),
|
CFG_STR("mixer_device", NULL, CFGF_NONE),
|
||||||
CFG_INT("offset_ms", 0, CFGF_NONE),
|
CFG_INT("offset_ms", 0, CFGF_NONE),
|
||||||
|
@ -111,6 +111,15 @@ struct alsa_playback_session
|
|||||||
struct alsa_playback_session *next;
|
struct alsa_playback_session *next;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Info about the device, which is not required by the player, only internally
|
||||||
|
struct alsa_extra
|
||||||
|
{
|
||||||
|
const char *card_name;
|
||||||
|
const char *mixer_name;
|
||||||
|
const char *mixer_device_name;
|
||||||
|
int offset_ms;
|
||||||
|
};
|
||||||
|
|
||||||
struct alsa_session
|
struct alsa_session
|
||||||
{
|
{
|
||||||
enum output_device_state state;
|
enum output_device_state state;
|
||||||
@ -119,7 +128,6 @@ struct alsa_session
|
|||||||
int callback_id;
|
int callback_id;
|
||||||
|
|
||||||
const char *devname;
|
const char *devname;
|
||||||
const char *card_name;
|
|
||||||
const char *mixer_name;
|
const char *mixer_name;
|
||||||
const char *mixer_device_name;
|
const char *mixer_device_name;
|
||||||
|
|
||||||
@ -969,30 +977,20 @@ static struct alsa_session *
|
|||||||
alsa_session_make(struct output_device *device, int callback_id)
|
alsa_session_make(struct output_device *device, int callback_id)
|
||||||
{
|
{
|
||||||
struct alsa_session *as;
|
struct alsa_session *as;
|
||||||
cfg_t *cfg_audio;
|
struct alsa_extra *ae;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
CHECK_NULL(L_LAUDIO, as = calloc(1, sizeof(struct alsa_session)));
|
ae = device->extra_device_info;
|
||||||
|
|
||||||
DPRINTF(E_DBG, L_LAUDIO, "alsa dev=%s type=%s callback=%d\n", device->name, device->type_name, callback_id);
|
CHECK_NULL(L_LAUDIO, as = calloc(1, sizeof(struct alsa_session)));
|
||||||
|
|
||||||
as->device_id = device->id;
|
as->device_id = device->id;
|
||||||
as->callback_id = callback_id;
|
as->callback_id = callback_id;
|
||||||
|
|
||||||
cfg_audio = cfg_size(cfg, "alsa") == 0 ? cfg_getsec(cfg, "audio") : cfg_gettsec(cfg, "alsa", device->name);
|
as->devname = ae->card_name;
|
||||||
|
as->mixer_name = ae->mixer_name;
|
||||||
as->devname = cfg_getstr(cfg_audio, "card");
|
as->mixer_device_name = ae->mixer_device_name;
|
||||||
as->mixer_name = cfg_getstr(cfg_audio, "mixer");
|
as->offset_ms = ae->offset_ms;
|
||||||
as->mixer_device_name = cfg_getstr(cfg_audio, "mixer_device");
|
|
||||||
if (!as->mixer_device_name || strlen(as->mixer_device_name) == 0)
|
|
||||||
as->mixer_device_name = cfg_getstr(cfg_audio, "card");
|
|
||||||
|
|
||||||
as->offset_ms = cfg_getint(cfg_audio, "offset_ms");
|
|
||||||
if (abs(as->offset_ms) > 1000)
|
|
||||||
{
|
|
||||||
DPRINTF(E_LOG, L_LAUDIO, "The ALSA offset_ms (%d) set in the configuration is out of bounds\n", as->offset_ms);
|
|
||||||
as->offset_ms = 1000 * (as->offset_ms/abs(as->offset_ms));
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = mixer_open(&as->mixer, as->mixer_device_name, as->mixer_name);
|
ret = mixer_open(&as->mixer, as->mixer_device_name, as->mixer_name);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
@ -1117,6 +1115,14 @@ alsa_device_cb_set(struct output_device *device, int callback_id)
|
|||||||
as->callback_id = callback_id;
|
as->callback_id = callback_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
alsa_device_free_extra(struct output_device *device)
|
||||||
|
{
|
||||||
|
struct alsa_extra *ae = device->extra_device_info;
|
||||||
|
|
||||||
|
free(ae);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
alsa_write(struct output_buffer *obuf)
|
alsa_write(struct output_buffer *obuf)
|
||||||
{
|
{
|
||||||
@ -1178,20 +1184,44 @@ alsa_write(struct output_buffer *obuf)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
alsa_device_add(cfg_t* cfg_audio, int id, const char* devname)
|
alsa_device_add(cfg_t* cfg_audio, int id)
|
||||||
{
|
{
|
||||||
struct output_device *device;
|
struct output_device *device;
|
||||||
|
struct alsa_extra *ae;
|
||||||
|
int ret;
|
||||||
|
|
||||||
CHECK_NULL(L_LAUDIO, device = calloc(1, sizeof(struct output_device)));
|
CHECK_NULL(L_LAUDIO, device = calloc(1, sizeof(struct output_device)));
|
||||||
|
CHECK_NULL(L_LAUDIO, ae = calloc(1, sizeof(struct alsa_extra)));
|
||||||
|
|
||||||
device->id = id;
|
device->id = id;
|
||||||
device->name = strdup(devname);
|
device->name = strdup(cfg_getstr(cfg_audio, "nickname"));
|
||||||
device->type = OUTPUT_TYPE_ALSA;
|
device->type = OUTPUT_TYPE_ALSA;
|
||||||
device->type_name = outputs_name(device->type);
|
device->type_name = outputs_name(device->type);
|
||||||
device->has_video = 0;
|
device->extra_device_info = ae;
|
||||||
|
|
||||||
DPRINTF(E_INFO, L_LAUDIO, "Adding ALSA device '%s' with name '%s'\n", cfg_getstr(cfg_audio, "card"), device->name);
|
// The audio section will have no title, so there we get the value from the
|
||||||
|
// "card" option
|
||||||
|
ae->card_name = cfg_title(cfg_audio);
|
||||||
|
if (!ae->card_name)
|
||||||
|
ae->card_name = cfg_getstr(cfg_audio, "card");
|
||||||
|
|
||||||
player_device_add(device);
|
ae->mixer_name = cfg_getstr(cfg_audio, "mixer");
|
||||||
|
ae->mixer_device_name = cfg_getstr(cfg_audio, "mixer_device_name");
|
||||||
|
if (!ae->mixer_device_name || strlen(ae->mixer_device_name) == 0)
|
||||||
|
ae->mixer_device_name = ae->card_name;
|
||||||
|
|
||||||
|
ae->offset_ms = cfg_getint(cfg_audio, "offset_ms");
|
||||||
|
if (abs(ae->offset_ms) > 1000)
|
||||||
|
{
|
||||||
|
DPRINTF(E_LOG, L_LAUDIO, "The ALSA offset_ms (%d) set in the configuration is out of bounds\n", ae->offset_ms);
|
||||||
|
ae->offset_ms = 1000 * (ae->offset_ms/abs(ae->offset_ms));
|
||||||
|
}
|
||||||
|
|
||||||
|
DPRINTF(E_INFO, L_LAUDIO, "Adding ALSA device '%s' with name '%s'\n", ae->card_name, device->name);
|
||||||
|
|
||||||
|
ret = player_device_add(device);
|
||||||
|
if (ret < 0)
|
||||||
|
outputs_device_free(device);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
@ -1215,14 +1245,14 @@ alsa_init(void)
|
|||||||
alsa_cfg_secn = cfg_size(cfg, "alsa");
|
alsa_cfg_secn = cfg_size(cfg, "alsa");
|
||||||
if (alsa_cfg_secn == 0)
|
if (alsa_cfg_secn == 0)
|
||||||
{
|
{
|
||||||
alsa_device_add(cfg_audio, 0, cfg_getstr(cfg_audio, "nickname"));
|
alsa_device_add(cfg_audio, 0);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (i = 0; i < alsa_cfg_secn; ++i)
|
for (i = 0; i < alsa_cfg_secn; ++i)
|
||||||
{
|
{
|
||||||
cfg_alsasec = cfg_getnsec(cfg, "alsa", i);
|
cfg_alsasec = cfg_getnsec(cfg, "alsa", i);
|
||||||
alsa_device_add(cfg_alsasec, i, cfg_alsasec->title);
|
alsa_device_add(cfg_alsasec, i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1259,5 +1289,6 @@ struct output_definition output_alsa =
|
|||||||
.device_probe = alsa_device_probe,
|
.device_probe = alsa_device_probe,
|
||||||
.device_volume_set = alsa_device_volume_set,
|
.device_volume_set = alsa_device_volume_set,
|
||||||
.device_cb_set = alsa_device_cb_set,
|
.device_cb_set = alsa_device_cb_set,
|
||||||
|
.device_free_extra = alsa_device_free_extra,
|
||||||
.write = alsa_write,
|
.write = alsa_write,
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user