mirror of
https://github.com/owntone/owntone-server.git
synced 2024-12-29 08:33:23 -05:00
Implement master volume & per-speaker relative volume
Volume is set independently for each speaker relative to the master volume. Methods allow to set speaker volume independently and in relation to the master volume.
This commit is contained in:
parent
9e9c3e42cc
commit
0df4ab4555
@ -1389,7 +1389,7 @@ dacp_reply_setproperty(struct evhttp_request *req, struct evbuffer *evbuf, char
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
speaker_enum_cb(uint64_t id, const char *name, int selected, int has_password, void *arg)
|
speaker_enum_cb(uint64_t id, const char *name, int relvol, int selected, int has_password, void *arg)
|
||||||
{
|
{
|
||||||
struct evbuffer *evbuf;
|
struct evbuffer *evbuf;
|
||||||
int len;
|
int len;
|
||||||
|
306
src/player.c
306
src/player.c
@ -70,6 +70,11 @@ enum player_sync_source
|
|||||||
PLAYER_SYNC_LAUDIO,
|
PLAYER_SYNC_LAUDIO,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct volume_param {
|
||||||
|
int volume;
|
||||||
|
uint64_t spk_id;
|
||||||
|
};
|
||||||
|
|
||||||
struct player_command;
|
struct player_command;
|
||||||
typedef int (*cmd_func)(struct player_command *cmd);
|
typedef int (*cmd_func)(struct player_command *cmd);
|
||||||
|
|
||||||
@ -90,6 +95,7 @@ struct player_command
|
|||||||
int nonblock;
|
int nonblock;
|
||||||
|
|
||||||
union {
|
union {
|
||||||
|
struct volume_param vol_param;
|
||||||
void *noarg;
|
void *noarg;
|
||||||
struct spk_enum *spk_enum;
|
struct spk_enum *spk_enum;
|
||||||
struct raop_device *rd;
|
struct raop_device *rd;
|
||||||
@ -183,6 +189,7 @@ static struct raop_device *dev_list;
|
|||||||
static enum laudio_state laudio_status;
|
static enum laudio_state laudio_status;
|
||||||
static int laudio_selected;
|
static int laudio_selected;
|
||||||
static int laudio_volume;
|
static int laudio_volume;
|
||||||
|
static int laudio_relvol;
|
||||||
static int raop_sessions;
|
static int raop_sessions;
|
||||||
|
|
||||||
/* Commands */
|
/* Commands */
|
||||||
@ -246,29 +253,121 @@ status_update(enum play_status status)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Volume helpers */
|
||||||
|
static int
|
||||||
|
rel_to_vol(int relvol)
|
||||||
|
{
|
||||||
|
double vol;
|
||||||
|
|
||||||
|
if (relvol == 100)
|
||||||
|
return master_volume;
|
||||||
|
|
||||||
|
vol = ((double)relvol * (double)master_volume) / 100.0;
|
||||||
|
|
||||||
|
return (int)vol;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
vol_to_rel(int volume)
|
||||||
|
{
|
||||||
|
double rel;
|
||||||
|
|
||||||
|
if (volume == master_volume)
|
||||||
|
return 100;
|
||||||
|
|
||||||
|
rel = ((double)volume / (double)master_volume) * 100.0;
|
||||||
|
|
||||||
|
return (int)rel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Master volume helpers */
|
||||||
|
static void
|
||||||
|
volume_master_update(int newvol)
|
||||||
|
{
|
||||||
|
struct raop_device *rd;
|
||||||
|
|
||||||
|
master_volume = newvol;
|
||||||
|
|
||||||
|
if (laudio_selected)
|
||||||
|
laudio_relvol = vol_to_rel(laudio_volume);
|
||||||
|
|
||||||
|
for (rd = dev_list; rd; rd = rd->next)
|
||||||
|
{
|
||||||
|
if (rd->selected)
|
||||||
|
rd->relvol = vol_to_rel(rd->volume);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
volume_master_find(void)
|
||||||
|
{
|
||||||
|
struct raop_device *rd;
|
||||||
|
int newmaster;
|
||||||
|
|
||||||
|
newmaster = -1;
|
||||||
|
|
||||||
|
if (laudio_selected)
|
||||||
|
newmaster = laudio_volume;
|
||||||
|
|
||||||
|
for (rd = dev_list; rd; rd = rd->next)
|
||||||
|
{
|
||||||
|
if (rd->selected && (rd->volume > newmaster))
|
||||||
|
newmaster = rd->volume;
|
||||||
|
}
|
||||||
|
|
||||||
|
volume_master_update(newmaster);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Device select/deselect hooks */
|
/* Device select/deselect hooks */
|
||||||
static void
|
static void
|
||||||
speaker_select_laudio(void)
|
speaker_select_laudio(void)
|
||||||
{
|
{
|
||||||
laudio_selected = 1;
|
laudio_selected = 1;
|
||||||
|
|
||||||
|
if (laudio_volume > master_volume)
|
||||||
|
{
|
||||||
|
if (player_state == PLAY_STOPPED)
|
||||||
|
volume_master_update(laudio_volume);
|
||||||
|
else
|
||||||
|
laudio_volume = master_volume;
|
||||||
|
}
|
||||||
|
|
||||||
|
laudio_relvol = vol_to_rel(laudio_volume);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
speaker_select_raop(struct raop_device *rd)
|
speaker_select_raop(struct raop_device *rd)
|
||||||
{
|
{
|
||||||
rd->selected = 1;
|
rd->selected = 1;
|
||||||
|
|
||||||
|
if (rd->volume > master_volume)
|
||||||
|
{
|
||||||
|
if (player_state == PLAY_STOPPED)
|
||||||
|
volume_master_update(rd->volume);
|
||||||
|
else
|
||||||
|
rd->volume = master_volume;
|
||||||
|
}
|
||||||
|
|
||||||
|
rd->relvol = vol_to_rel(rd->volume);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
speaker_deselect_laudio(void)
|
speaker_deselect_laudio(void)
|
||||||
{
|
{
|
||||||
laudio_selected = 0;
|
laudio_selected = 0;
|
||||||
|
|
||||||
|
if (laudio_volume == master_volume)
|
||||||
|
volume_master_find();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
speaker_deselect_raop(struct raop_device *rd)
|
speaker_deselect_raop(struct raop_device *rd)
|
||||||
{
|
{
|
||||||
rd->selected = 0;
|
rd->selected = 0;
|
||||||
|
|
||||||
|
if (rd->volume == master_volume)
|
||||||
|
volume_master_find();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1241,7 +1340,6 @@ device_add(struct player_command *cmd)
|
|||||||
struct raop_device *dev;
|
struct raop_device *dev;
|
||||||
struct raop_device *rd;
|
struct raop_device *rd;
|
||||||
int selected;
|
int selected;
|
||||||
int dummy;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
dev = cmd->arg.rd;
|
dev = cmd->arg.rd;
|
||||||
@ -1257,12 +1355,12 @@ device_add(struct player_command *cmd)
|
|||||||
{
|
{
|
||||||
rd = dev;
|
rd = dev;
|
||||||
|
|
||||||
ret = db_speaker_get(rd->id, &selected, &dummy);
|
ret = db_speaker_get(rd->id, &selected, &rd->volume);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
|
{
|
||||||
selected = 0;
|
selected = 0;
|
||||||
|
rd->volume = (master_volume >= 0) ? master_volume : 75;
|
||||||
/* Force master volume for now */
|
}
|
||||||
rd->volume = master_volume;
|
|
||||||
|
|
||||||
if (dev_autoselect && selected)
|
if (dev_autoselect && selected)
|
||||||
speaker_select_raop(rd);
|
speaker_select_raop(rd);
|
||||||
@ -1661,6 +1759,10 @@ get_status(struct player_command *cmd)
|
|||||||
status->shuffle = shuffle;
|
status->shuffle = shuffle;
|
||||||
status->repeat = repeat;
|
status->repeat = repeat;
|
||||||
|
|
||||||
|
/* No devices selected, autoselect local audio */
|
||||||
|
if (master_volume < 0)
|
||||||
|
speaker_select_laudio();
|
||||||
|
|
||||||
status->volume = master_volume;
|
status->volume = master_volume;
|
||||||
|
|
||||||
status->plid = cur_plid;
|
status->plid = cur_plid;
|
||||||
@ -2239,12 +2341,23 @@ speaker_enumerate(struct player_command *cmd)
|
|||||||
if (!dev_list && !laudio_selected)
|
if (!dev_list && !laudio_selected)
|
||||||
speaker_select_laudio();
|
speaker_select_laudio();
|
||||||
|
|
||||||
spk_enum->cb(0, laudio_name, laudio_selected, 0, spk_enum->arg);
|
spk_enum->cb(0, laudio_name, laudio_relvol, laudio_selected, 0, spk_enum->arg);
|
||||||
|
|
||||||
|
#ifdef DEBUG_RELVOL
|
||||||
|
DPRINTF(E_DBG, L_PLAYER, "*** master: %d\n", master_volume);
|
||||||
|
DPRINTF(E_DBG, L_PLAYER, "*** laudio: abs %d rel %d\n", laudio_volume, laudio_relvol);
|
||||||
|
#endif
|
||||||
|
|
||||||
for (rd = dev_list; rd; rd = rd->next)
|
for (rd = dev_list; rd; rd = rd->next)
|
||||||
{
|
{
|
||||||
if (rd->advertised || rd->selected)
|
if (rd->advertised || rd->selected)
|
||||||
spk_enum->cb(rd->id, rd->name, rd->selected, rd->has_password, spk_enum->arg);
|
{
|
||||||
|
spk_enum->cb(rd->id, rd->name, rd->relvol, rd->selected, rd->has_password, spk_enum->arg);
|
||||||
|
|
||||||
|
#ifdef DEBUG_RELVOL
|
||||||
|
DPRINTF(E_DBG, L_PLAYER, "*** %s: abs %d rel %d\n", rd->name, rd->volume, rd->relvol);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -2510,17 +2623,37 @@ static int
|
|||||||
volume_set(struct player_command *cmd)
|
volume_set(struct player_command *cmd)
|
||||||
{
|
{
|
||||||
struct raop_device *rd;
|
struct raop_device *rd;
|
||||||
|
int volume;
|
||||||
|
|
||||||
master_volume = cmd->arg.intval;
|
volume = cmd->arg.intval;
|
||||||
|
|
||||||
laudio_volume = master_volume;
|
if (master_volume == volume)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
master_volume = volume;
|
||||||
|
|
||||||
|
if (laudio_selected)
|
||||||
|
{
|
||||||
|
laudio_volume = rel_to_vol(laudio_relvol);
|
||||||
laudio_set_volume(laudio_volume);
|
laudio_set_volume(laudio_volume);
|
||||||
|
|
||||||
|
#ifdef DEBUG_RELVOL
|
||||||
|
DPRINTF(E_DBG, L_PLAYER, "*** laudio: abs %d rel %d\n", laudio_volume, laudio_relvol);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
cmd->raop_pending = 0;
|
cmd->raop_pending = 0;
|
||||||
|
|
||||||
for (rd = dev_list; rd; rd = rd->next)
|
for (rd = dev_list; rd; rd = rd->next)
|
||||||
{
|
{
|
||||||
rd->volume = master_volume;
|
if (!rd->selected)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
rd->volume = rel_to_vol(rd->relvol);
|
||||||
|
|
||||||
|
#ifdef DEBUG_RELVOL
|
||||||
|
DPRINTF(E_DBG, L_PLAYER, "*** %s: abs %d rel %d\n", rd->name, rd->volume, rd->relvol);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (rd->session)
|
if (rd->session)
|
||||||
cmd->raop_pending += raop_set_volume_one(rd->session, rd->volume, device_command_cb);
|
cmd->raop_pending += raop_set_volume_one(rd->session, rd->volume, device_command_cb);
|
||||||
@ -2532,6 +2665,115 @@ volume_set(struct player_command *cmd)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
volume_setrel_speaker(struct player_command *cmd)
|
||||||
|
{
|
||||||
|
struct raop_device *rd;
|
||||||
|
uint64_t id;
|
||||||
|
int relvol;
|
||||||
|
|
||||||
|
id = cmd->arg.vol_param.spk_id;
|
||||||
|
relvol = cmd->arg.vol_param.volume;
|
||||||
|
|
||||||
|
if (id == 0)
|
||||||
|
{
|
||||||
|
laudio_relvol = relvol;
|
||||||
|
laudio_volume = rel_to_vol(relvol);
|
||||||
|
laudio_set_volume(laudio_volume);
|
||||||
|
|
||||||
|
#ifdef DEBUG_RELVOL
|
||||||
|
DPRINTF(E_DBG, L_PLAYER, "*** laudio: abs %d rel %d\n", laudio_volume, laudio_relvol);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (rd = dev_list; rd; rd = rd->next)
|
||||||
|
{
|
||||||
|
if (rd->id != id)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!rd->selected)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
rd->relvol = relvol;
|
||||||
|
rd->volume = rel_to_vol(relvol);
|
||||||
|
|
||||||
|
#ifdef DEBUG_RELVOL
|
||||||
|
DPRINTF(E_DBG, L_PLAYER, "*** %s: abs %d rel %d\n", rd->name, rd->volume, rd->relvol);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (rd->session)
|
||||||
|
cmd->raop_pending = raop_set_volume_one(rd->session, rd->volume, device_command_cb);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cmd->raop_pending > 0)
|
||||||
|
return 1; /* async */
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
volume_setabs_speaker(struct player_command *cmd)
|
||||||
|
{
|
||||||
|
struct raop_device *rd;
|
||||||
|
uint64_t id;
|
||||||
|
int volume;
|
||||||
|
|
||||||
|
id = cmd->arg.vol_param.spk_id;
|
||||||
|
volume = cmd->arg.vol_param.volume;
|
||||||
|
|
||||||
|
master_volume = volume;
|
||||||
|
|
||||||
|
if (id == 0)
|
||||||
|
{
|
||||||
|
laudio_relvol = 100;
|
||||||
|
laudio_volume = volume;
|
||||||
|
laudio_set_volume(laudio_volume);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
laudio_relvol = vol_to_rel(laudio_volume);
|
||||||
|
|
||||||
|
#ifdef DEBUG_RELVOL
|
||||||
|
DPRINTF(E_DBG, L_PLAYER, "*** laudio: abs %d rel %d\n", laudio_volume, laudio_relvol);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (rd = dev_list; rd; rd = rd->next)
|
||||||
|
{
|
||||||
|
if (!rd->selected)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (rd->id != id)
|
||||||
|
{
|
||||||
|
rd->relvol = vol_to_rel(rd->volume);
|
||||||
|
|
||||||
|
#ifdef DEBUG_RELVOL
|
||||||
|
DPRINTF(E_DBG, L_PLAYER, "*** %s: abs %d rel %d\n", rd->name, rd->volume, rd->relvol);
|
||||||
|
#endif
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rd->relvol = 100;
|
||||||
|
rd->volume = master_volume;
|
||||||
|
|
||||||
|
#ifdef DEBUG_RELVOL
|
||||||
|
DPRINTF(E_DBG, L_PLAYER, "*** %s: abs %d rel %d\n", rd->name, rd->volume, rd->relvol);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (rd->session)
|
||||||
|
cmd->raop_pending = raop_set_volume_one(rd->session, rd->volume, device_command_cb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cmd->raop_pending > 0)
|
||||||
|
return 1; /* async */
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
repeat_set(struct player_command *cmd)
|
repeat_set(struct player_command *cmd)
|
||||||
{
|
{
|
||||||
@ -2988,6 +3230,46 @@ player_volume_set(int vol)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
player_volume_setrel_speaker(uint64_t id, int relvol)
|
||||||
|
{
|
||||||
|
struct player_command cmd;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
command_init(&cmd);
|
||||||
|
|
||||||
|
cmd.func = volume_setrel_speaker;
|
||||||
|
cmd.func_bh = NULL;
|
||||||
|
cmd.arg.vol_param.spk_id = id;
|
||||||
|
cmd.arg.vol_param.volume = relvol;
|
||||||
|
|
||||||
|
ret = sync_command(&cmd);
|
||||||
|
|
||||||
|
command_deinit(&cmd);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
player_volume_setabs_speaker(uint64_t id, int vol)
|
||||||
|
{
|
||||||
|
struct player_command cmd;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
command_init(&cmd);
|
||||||
|
|
||||||
|
cmd.func = volume_setabs_speaker;
|
||||||
|
cmd.func_bh = NULL;
|
||||||
|
cmd.arg.vol_param.spk_id = id;
|
||||||
|
cmd.arg.vol_param.volume = vol;
|
||||||
|
|
||||||
|
ret = sync_command(&cmd);
|
||||||
|
|
||||||
|
command_deinit(&cmd);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
player_repeat_set(enum repeat_mode mode)
|
player_repeat_set(enum repeat_mode mode)
|
||||||
{
|
{
|
||||||
@ -3391,6 +3673,8 @@ player_init(void)
|
|||||||
dev_autoselect = 1;
|
dev_autoselect = 1;
|
||||||
dev_list = NULL;
|
dev_list = NULL;
|
||||||
|
|
||||||
|
master_volume = -1;
|
||||||
|
|
||||||
laudio_selected = 0;
|
laudio_selected = 0;
|
||||||
laudio_status = LAUDIO_CLOSED;
|
laudio_status = LAUDIO_CLOSED;
|
||||||
raop_sessions = 0;
|
raop_sessions = 0;
|
||||||
@ -3423,8 +3707,6 @@ player_init(void)
|
|||||||
else if (laudio_selected)
|
else if (laudio_selected)
|
||||||
speaker_select_laudio(); /* Run the select helper */
|
speaker_select_laudio(); /* Run the select helper */
|
||||||
|
|
||||||
master_volume = laudio_volume;
|
|
||||||
|
|
||||||
audio_buf = evbuffer_new();
|
audio_buf = evbuffer_new();
|
||||||
if (!audio_buf)
|
if (!audio_buf)
|
||||||
{
|
{
|
||||||
|
@ -45,7 +45,7 @@ struct player_status {
|
|||||||
int pos_pl;
|
int pos_pl;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef void (*spk_enum_cb)(uint64_t id, const char *name, int selected, int has_password, void *arg);
|
typedef void (*spk_enum_cb)(uint64_t id, const char *name, int relvol, int selected, int has_password, void *arg);
|
||||||
typedef void (*player_status_handler)(void);
|
typedef void (*player_status_handler)(void);
|
||||||
|
|
||||||
struct player_source;
|
struct player_source;
|
||||||
@ -89,6 +89,12 @@ player_playback_prev(void);
|
|||||||
int
|
int
|
||||||
player_volume_set(int vol);
|
player_volume_set(int vol);
|
||||||
|
|
||||||
|
int
|
||||||
|
player_volume_setrel_speaker(uint64_t id, int relvol);
|
||||||
|
|
||||||
|
int
|
||||||
|
player_volume_setabs_speaker(uint64_t id, int vol);
|
||||||
|
|
||||||
int
|
int
|
||||||
player_repeat_set(enum repeat_mode mode);
|
player_repeat_set(enum repeat_mode mode);
|
||||||
|
|
||||||
|
@ -43,6 +43,7 @@ struct raop_device
|
|||||||
const char *password;
|
const char *password;
|
||||||
|
|
||||||
int volume;
|
int volume;
|
||||||
|
int relvol;
|
||||||
struct raop_session *session;
|
struct raop_session *session;
|
||||||
|
|
||||||
struct raop_device *next;
|
struct raop_device *next;
|
||||||
|
Loading…
Reference in New Issue
Block a user