diff --git a/userspace/libraries/LibAudio/Audio.cpp b/userspace/libraries/LibAudio/Audio.cpp index 8d81da73..6beb41bd 100644 --- a/userspace/libraries/LibAudio/Audio.cpp +++ b/userspace/libraries/LibAudio/Audio.cpp @@ -145,7 +145,7 @@ namespace LibAudio LibAudio::Packet packet { .type = LibAudio::Packet::Notify, - .parameter = 0, + .parameter = {}, }; send(m_server_fd, &packet, sizeof(packet), 0); diff --git a/userspace/libraries/LibAudio/include/LibAudio/Protocol.h b/userspace/libraries/LibAudio/include/LibAudio/Protocol.h index bcfd3a17..ff2830c8 100644 --- a/userspace/libraries/LibAudio/include/LibAudio/Protocol.h +++ b/userspace/libraries/LibAudio/include/LibAudio/Protocol.h @@ -40,6 +40,14 @@ namespace LibAudio SetPin, // parameter: pin number // response: nothing // 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; uint64_t parameter; diff --git a/userspace/programs/AudioServer/AudioServer.cpp b/userspace/programs/AudioServer/AudioServer.cpp index 6478d390..eca8ef32 100644 --- a/userspace/programs/AudioServer/AudioServer.cpp +++ b/userspace/programs/AudioServer/AudioServer.cpp @@ -98,12 +98,53 @@ bool AudioServer::on_client_packet(int fd, LibAudio::Packet packet) return false; } 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)); else + { 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(); 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: dwarnln("unknown packet type {}", static_cast(packet.type)); return false; @@ -278,7 +319,7 @@ void AudioServer::send_samples() for (size_t i = 0; i < samples_to_send; i++) { sample_buffer[i] = BAN::Math::clamp( - 0.2 * m_samples[m_samples_sent + i] * BAN::numeric_limits::max(), + m_samples[m_samples_sent + i] * BAN::numeric_limits::max(), BAN::numeric_limits::min(), BAN::numeric_limits::max() ); diff --git a/userspace/programs/AudioServer/AudioServer.h b/userspace/programs/AudioServer/AudioServer.h index 3cfe18ca..c7ff9779 100644 --- a/userspace/programs/AudioServer/AudioServer.h +++ b/userspace/programs/AudioServer/AudioServer.h @@ -8,6 +8,8 @@ #include #include +#include + struct AudioDevice { int fd; @@ -15,6 +17,7 @@ struct AudioDevice uint32_t sample_rate; uint32_t total_pins; uint32_t current_pin; + snd_volume_info volume; }; class AudioServer diff --git a/userspace/programs/AudioServer/main.cpp b/userspace/programs/AudioServer/main.cpp index 46a25b93..aa3672e6 100644 --- a/userspace/programs/AudioServer/main.cpp +++ b/userspace/programs/AudioServer/main.cpp @@ -68,6 +68,8 @@ static BAN::Optional initialize_audio_device(int fd) return {}; if (ioctl(fd, SND_GET_PIN, &result.current_pin) != 0) return {}; + if (ioctl(fd, SND_GET_VOLUME_INFO, &result.volume) != 0) + return {}; return result; } diff --git a/userspace/programs/audioctl/main.cpp b/userspace/programs/audioctl/main.cpp index 5bf38aac..7e4b2b45 100644 --- a/userspace/programs/audioctl/main.cpp +++ b/userspace/programs/audioctl/main.cpp @@ -74,17 +74,20 @@ static uint32_t send_request(int fd, LibAudio::Packet packet, bool wait_response 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_pin = send_request(fd, { .type = LibAudio::Packet::GetPin, .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 = {} }, 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++) { const uint32_t total_pins = send_request(fd, { .type = LibAudio::Packet::QueryPins, .parameter = dev }, true); printf("Device %" PRIu32 "", dev); 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"); for (uint32_t pin = 0; pin < total_pins; pin++) @@ -103,16 +106,20 @@ int main(int argc, char** argv) BAN::Optional device; BAN::Optional pin; + BAN::Optional volume; + BAN::Optional volume_rel; // no value: absolute, false positive, true negative + for (;;) { static option long_options[] { { "list", no_argument, nullptr, 'l' }, { "device", required_argument, nullptr, 'd' }, { "pin", required_argument, nullptr, 'p' }, + { "volume", required_argument, nullptr, 'v' }, { "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) break; @@ -125,6 +132,7 @@ int main(int argc, char** argv) 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, " -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"); return 0; case 'l': @@ -136,6 +144,11 @@ int main(int argc, char** argv) case 'p': pin = parse_u32_or_exit(optarg); break; + case 'v': + if (optarg[0] == '-' || optarg[0] == '+') + volume_rel = (optarg[0] == '-'); + volume = parse_u32_or_exit(optarg + volume_rel.has_value()); + break; case '?': fprintf(stderr, "invalid option %c\n", optopt); 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; const int fd = get_server_fd(); @@ -156,6 +169,30 @@ int main(int argc, char** argv) if (pin.has_value()) 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) list_devices(fd);