AudioServer: Add support for volume control
This commit is contained in:
parent
5647cf24d2
commit
bf4831f468
|
|
@ -145,7 +145,7 @@ namespace LibAudio
|
||||||
|
|
||||||
LibAudio::Packet packet {
|
LibAudio::Packet packet {
|
||||||
.type = LibAudio::Packet::Notify,
|
.type = LibAudio::Packet::Notify,
|
||||||
.parameter = 0,
|
.parameter = {},
|
||||||
};
|
};
|
||||||
|
|
||||||
send(m_server_fd, &packet, sizeof(packet), 0);
|
send(m_server_fd, &packet, sizeof(packet), 0);
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,14 @@ namespace LibAudio
|
||||||
SetPin, // parameter: pin number
|
SetPin, // parameter: pin number
|
||||||
// response: nothing
|
// response: nothing
|
||||||
// set the active pin of the current device
|
// set the active pin of the current device
|
||||||
|
|
||||||
|
GetVolume, // parameter: ignored
|
||||||
|
// response: 10 * volume percentage (uint32_t)
|
||||||
|
// get the volume of the current device
|
||||||
|
|
||||||
|
SetVolume, // parameter: 10 * volume percentage (uint32_t)
|
||||||
|
// response: nothing
|
||||||
|
// set the volume of the current device
|
||||||
} type;
|
} type;
|
||||||
|
|
||||||
uint64_t parameter;
|
uint64_t parameter;
|
||||||
|
|
|
||||||
|
|
@ -98,12 +98,53 @@ bool AudioServer::on_client_packet(int fd, LibAudio::Packet packet)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
reset_kernel_buffer();
|
reset_kernel_buffer();
|
||||||
if (uint32_t pin = packet.parameter; ioctl(device().fd, SND_SET_PIN, &pin) != 0)
|
if (ioctl(device().fd, SND_SET_PIN, &packet.parameter) != 0)
|
||||||
dwarnln("Failed to set pin {}: {}", packet.parameter, strerror(errno));
|
dwarnln("Failed to set pin {}: {}", packet.parameter, strerror(errno));
|
||||||
else
|
else
|
||||||
|
{
|
||||||
device().current_pin = packet.parameter;
|
device().current_pin = packet.parameter;
|
||||||
|
|
||||||
|
// NOTE: different pins have different volume controls
|
||||||
|
if (ioctl(device().fd, SND_GET_VOLUME_INFO, &device().volume) != 0)
|
||||||
|
dwarnln("Failed to query new volume: {}", strerror(errno));
|
||||||
|
}
|
||||||
update();
|
update();
|
||||||
break;
|
break;
|
||||||
|
case LibAudio::Packet::GetVolume:
|
||||||
|
{
|
||||||
|
// NOTE: maybe we should map [0->100%] -> [min, 0] instead?
|
||||||
|
|
||||||
|
const auto& volume = device().volume;
|
||||||
|
if (volume.min_mdB == volume.max_mdB)
|
||||||
|
response = 1000;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const double min_dB = volume.min_mdB / 1000.0;
|
||||||
|
const double max_dB = volume.max_mdB / 1000.0;
|
||||||
|
const double dB = volume.mdB / 1000.0;
|
||||||
|
const double percent = (BAN::Math::pow(10.0, (dB - min_dB) / (max_dB - min_dB)) - 1.0) / 9.0;
|
||||||
|
response = BAN::Math::round(percent * 1000.0);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LibAudio::Packet::SetVolume:
|
||||||
|
{
|
||||||
|
// NOTE: maybe we should map [0->100%] -> [min, 0] instead?
|
||||||
|
|
||||||
|
const auto& volume = device().volume;
|
||||||
|
const double min_dB = volume.min_mdB / 1000.0;
|
||||||
|
const double max_dB = volume.max_mdB / 1000.0;
|
||||||
|
const double percent = packet.parameter / 1000.0;
|
||||||
|
const double dB = min_dB + (max_dB - min_dB) * BAN::Math::log10(1.0 + 9.0 * percent);
|
||||||
|
|
||||||
|
const int32_t mdB = BAN::Math::round(dB * 1000.0);
|
||||||
|
if (ioctl(device().fd, SND_SET_VOLUME_MDB, &mdB) != 0)
|
||||||
|
dwarnln("Failed to set volume: {}", strerror(errno));
|
||||||
|
else if (ioctl(device().fd, SND_GET_VOLUME_INFO, &device().volume) != 0)
|
||||||
|
dwarnln("Failed to query new volume: {}", strerror(errno));
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
dwarnln("unknown packet type {}", static_cast<uint8_t>(packet.type));
|
dwarnln("unknown packet type {}", static_cast<uint8_t>(packet.type));
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -278,7 +319,7 @@ void AudioServer::send_samples()
|
||||||
for (size_t i = 0; i < samples_to_send; i++)
|
for (size_t i = 0; i < samples_to_send; i++)
|
||||||
{
|
{
|
||||||
sample_buffer[i] = BAN::Math::clamp<sample_t>(
|
sample_buffer[i] = BAN::Math::clamp<sample_t>(
|
||||||
0.2 * m_samples[m_samples_sent + i] * BAN::numeric_limits<kernel_sample_t>::max(),
|
m_samples[m_samples_sent + i] * BAN::numeric_limits<kernel_sample_t>::max(),
|
||||||
BAN::numeric_limits<kernel_sample_t>::min(),
|
BAN::numeric_limits<kernel_sample_t>::min(),
|
||||||
BAN::numeric_limits<kernel_sample_t>::max()
|
BAN::numeric_limits<kernel_sample_t>::max()
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,8 @@
|
||||||
#include <LibAudio/Audio.h>
|
#include <LibAudio/Audio.h>
|
||||||
#include <LibAudio/Protocol.h>
|
#include <LibAudio/Protocol.h>
|
||||||
|
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
|
||||||
struct AudioDevice
|
struct AudioDevice
|
||||||
{
|
{
|
||||||
int fd;
|
int fd;
|
||||||
|
|
@ -15,6 +17,7 @@ struct AudioDevice
|
||||||
uint32_t sample_rate;
|
uint32_t sample_rate;
|
||||||
uint32_t total_pins;
|
uint32_t total_pins;
|
||||||
uint32_t current_pin;
|
uint32_t current_pin;
|
||||||
|
snd_volume_info volume;
|
||||||
};
|
};
|
||||||
|
|
||||||
class AudioServer
|
class AudioServer
|
||||||
|
|
|
||||||
|
|
@ -68,6 +68,8 @@ static BAN::Optional<AudioDevice> initialize_audio_device(int fd)
|
||||||
return {};
|
return {};
|
||||||
if (ioctl(fd, SND_GET_PIN, &result.current_pin) != 0)
|
if (ioctl(fd, SND_GET_PIN, &result.current_pin) != 0)
|
||||||
return {};
|
return {};
|
||||||
|
if (ioctl(fd, SND_GET_VOLUME_INFO, &result.volume) != 0)
|
||||||
|
return {};
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -74,17 +74,20 @@ static uint32_t send_request(int fd, LibAudio::Packet packet, bool wait_response
|
||||||
|
|
||||||
static void list_devices(int fd)
|
static void list_devices(int fd)
|
||||||
{
|
{
|
||||||
const uint32_t current_device = send_request(fd, { .type = LibAudio::Packet::GetDevice, .parameter = 0 }, true);
|
const uint32_t current_device = send_request(fd, { .type = LibAudio::Packet::GetDevice, .parameter = {} }, true);
|
||||||
const uint32_t current_pin = send_request(fd, { .type = LibAudio::Packet::GetPin, .parameter = 0 }, true);
|
const uint32_t current_pin = send_request(fd, { .type = LibAudio::Packet::GetPin, .parameter = {} }, true);
|
||||||
|
|
||||||
const uint32_t total_devices = send_request(fd, { .type = LibAudio::Packet::QueryDevices, .parameter = 0 }, true);
|
const uint32_t total_devices = send_request(fd, { .type = LibAudio::Packet::QueryDevices, .parameter = {} }, true);
|
||||||
for (uint32_t dev = 0; dev < total_devices; dev++)
|
for (uint32_t dev = 0; dev < total_devices; dev++)
|
||||||
{
|
{
|
||||||
const uint32_t total_pins = send_request(fd, { .type = LibAudio::Packet::QueryPins, .parameter = dev }, true);
|
const uint32_t total_pins = send_request(fd, { .type = LibAudio::Packet::QueryPins, .parameter = dev }, true);
|
||||||
|
|
||||||
printf("Device %" PRIu32 "", dev);
|
printf("Device %" PRIu32 "", dev);
|
||||||
if (dev == current_device)
|
if (dev == current_device)
|
||||||
printf(" (current)");
|
{
|
||||||
|
const uint32_t volume = send_request(fd, { .type = LibAudio::Packet::GetVolume, .parameter = {} }, true);
|
||||||
|
printf(" (current) %" PRIu32 "%%", volume / 10);
|
||||||
|
}
|
||||||
printf("\n");
|
printf("\n");
|
||||||
|
|
||||||
for (uint32_t pin = 0; pin < total_pins; pin++)
|
for (uint32_t pin = 0; pin < total_pins; pin++)
|
||||||
|
|
@ -103,16 +106,20 @@ int main(int argc, char** argv)
|
||||||
BAN::Optional<uint32_t> device;
|
BAN::Optional<uint32_t> device;
|
||||||
BAN::Optional<uint32_t> pin;
|
BAN::Optional<uint32_t> pin;
|
||||||
|
|
||||||
|
BAN::Optional<uint32_t> volume;
|
||||||
|
BAN::Optional<bool> volume_rel; // no value: absolute, false positive, true negative
|
||||||
|
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
static option long_options[] {
|
static option long_options[] {
|
||||||
{ "list", no_argument, nullptr, 'l' },
|
{ "list", no_argument, nullptr, 'l' },
|
||||||
{ "device", required_argument, nullptr, 'd' },
|
{ "device", required_argument, nullptr, 'd' },
|
||||||
{ "pin", required_argument, nullptr, 'p' },
|
{ "pin", required_argument, nullptr, 'p' },
|
||||||
|
{ "volume", required_argument, nullptr, 'v' },
|
||||||
{ "help", no_argument, nullptr, 'h' },
|
{ "help", no_argument, nullptr, 'h' },
|
||||||
};
|
};
|
||||||
|
|
||||||
int ch = getopt_long(argc, argv, "ld:p:h", long_options, nullptr);
|
int ch = getopt_long(argc, argv, "ld:p:v:h", long_options, nullptr);
|
||||||
if (ch == -1)
|
if (ch == -1)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
@ -125,6 +132,7 @@ int main(int argc, char** argv)
|
||||||
fprintf(stderr, " -l, --list list devices and their pins\n");
|
fprintf(stderr, " -l, --list list devices and their pins\n");
|
||||||
fprintf(stderr, " -d, --device N set device index N as the current one\n");
|
fprintf(stderr, " -d, --device N set device index N as the current one\n");
|
||||||
fprintf(stderr, " -p, --pin N set pin N as the current one\n");
|
fprintf(stderr, " -p, --pin N set pin N as the current one\n");
|
||||||
|
fprintf(stderr, " -v, --volume N set volume to N%%. if + or - is given, volume is relative to the current volume\n");
|
||||||
fprintf(stderr, " -h, --help show this message and exit\n");
|
fprintf(stderr, " -h, --help show this message and exit\n");
|
||||||
return 0;
|
return 0;
|
||||||
case 'l':
|
case 'l':
|
||||||
|
|
@ -136,6 +144,11 @@ int main(int argc, char** argv)
|
||||||
case 'p':
|
case 'p':
|
||||||
pin = parse_u32_or_exit(optarg);
|
pin = parse_u32_or_exit(optarg);
|
||||||
break;
|
break;
|
||||||
|
case 'v':
|
||||||
|
if (optarg[0] == '-' || optarg[0] == '+')
|
||||||
|
volume_rel = (optarg[0] == '-');
|
||||||
|
volume = parse_u32_or_exit(optarg + volume_rel.has_value());
|
||||||
|
break;
|
||||||
case '?':
|
case '?':
|
||||||
fprintf(stderr, "invalid option %c\n", optopt);
|
fprintf(stderr, "invalid option %c\n", optopt);
|
||||||
fprintf(stderr, "see '%s --help' for usage\n", argv[0]);
|
fprintf(stderr, "see '%s --help' for usage\n", argv[0]);
|
||||||
|
|
@ -143,7 +156,7 @@ int main(int argc, char** argv)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!device.has_value() && !pin.has_value())
|
if (!device.has_value() && !pin.has_value() && !volume.has_value())
|
||||||
list = true;
|
list = true;
|
||||||
|
|
||||||
const int fd = get_server_fd();
|
const int fd = get_server_fd();
|
||||||
|
|
@ -156,6 +169,30 @@ int main(int argc, char** argv)
|
||||||
if (pin.has_value())
|
if (pin.has_value())
|
||||||
send_request(fd, { .type = LibAudio::Packet::SetPin, .parameter = pin.value() }, false);
|
send_request(fd, { .type = LibAudio::Packet::SetPin, .parameter = pin.value() }, false);
|
||||||
|
|
||||||
|
if (volume.has_value())
|
||||||
|
{
|
||||||
|
const uint32_t volume10 = volume.value() * 10;
|
||||||
|
|
||||||
|
uint32_t param;
|
||||||
|
if (!volume_rel.has_value())
|
||||||
|
param = volume10;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const uint32_t current = send_request(fd, { .type = LibAudio::Packet::GetVolume, .parameter = {} }, true);
|
||||||
|
if (!volume_rel.value())
|
||||||
|
param = current + volume10;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (current > volume10)
|
||||||
|
param = current - volume10;
|
||||||
|
else
|
||||||
|
param = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
send_request(fd, { .type = LibAudio::Packet::SetVolume, .parameter = param }, false);
|
||||||
|
}
|
||||||
|
|
||||||
if (list)
|
if (list)
|
||||||
list_devices(fd);
|
list_devices(fd);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue