Serenity Operating System
1/*
2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
3 * Copyright (c) 2021, kleines Filmröllchen <filmroellchen@serenityos.org>
4 * Copyright (c) 2021, David Isaksson <davidisaksson93@gmail.com>
5 *
6 * SPDX-License-Identifier: BSD-2-Clause
7 */
8
9#include <AK/Variant.h>
10#include <AK/Vector.h>
11#include <LibAudio/ConnectionToServer.h>
12#include <LibCore/ArgsParser.h>
13#include <LibCore/EventLoop.h>
14#include <LibCore/System.h>
15#include <LibMain/Main.h>
16#include <math.h>
17#include <stdio.h>
18#include <sys/ioctl.h>
19
20enum AudioVariable : u32 {
21 Volume,
22 Mute,
23 SampleRate
24};
25
26// asctl: audio server control utility
27ErrorOr<int> serenity_main(Main::Arguments arguments)
28{
29 Core::EventLoop loop;
30 auto audio_client = TRY(Audio::ConnectionToServer::try_create());
31 audio_client->async_pause_playback();
32
33 DeprecatedString command = DeprecatedString::empty();
34 Vector<StringView> command_arguments;
35 bool human_mode = false;
36
37 Core::ArgsParser args_parser;
38 args_parser.set_general_help("Send control signals to the audio server and hardware.");
39 args_parser.add_option(human_mode, "Print human-readable output", "human-readable", 'h');
40 args_parser.add_positional_argument(command, "Command, either (g)et or (s)et\n\n\tThe get command accepts a list of variables to print.\n\tThey are printed in the given order.\n\tIf no value is specified, all are printed.\n\n\tThe set command accepts a any number of variables\n\tfollowed by the value they should be set to.\n\n\tPossible variables are (v)olume, (m)ute, sample(r)ate.\n", "command");
41 args_parser.add_positional_argument(command_arguments, "Arguments for the command", "args", Core::ArgsParser::Required::No);
42 args_parser.parse(arguments);
43
44 TRY(Core::System::unveil(nullptr, nullptr));
45 TRY(Core::System::pledge("stdio rpath wpath recvfd thread"));
46
47 if (command.equals_ignoring_ascii_case("get"sv) || command == "g") {
48 // Get variables
49 Vector<AudioVariable> values_to_print;
50 if (command_arguments.is_empty()) {
51 values_to_print.append(AudioVariable::Volume);
52 values_to_print.append(AudioVariable::Mute);
53 values_to_print.append(AudioVariable::SampleRate);
54 } else {
55 for (auto& variable : command_arguments) {
56 if (variable.is_one_of("v"sv, "volume"sv))
57 values_to_print.append(AudioVariable::Volume);
58 else if (variable.is_one_of("m"sv, "mute"sv))
59 values_to_print.append(AudioVariable::Mute);
60 else if (variable.is_one_of("r"sv, "samplerate"sv))
61 values_to_print.append(AudioVariable::SampleRate);
62 else {
63 warnln("Error: Unrecognized variable {}", variable);
64 return 1;
65 }
66 }
67 }
68
69 for (auto to_print : values_to_print) {
70 switch (to_print) {
71 case AudioVariable::Volume: {
72 auto volume = static_cast<int>(round(audio_client->get_main_mix_volume() * 100));
73 if (human_mode)
74 outln("Volume: {}%", volume);
75 else
76 out("{} ", volume);
77 break;
78 }
79 case AudioVariable::Mute: {
80 bool muted = audio_client->is_main_mix_muted();
81 if (human_mode)
82 outln("Muted: {}", muted ? "Yes" : "No");
83 else
84 out("{} ", muted ? 1 : 0);
85 break;
86 }
87 case AudioVariable::SampleRate: {
88 u32 sample_rate = audio_client->get_sample_rate();
89 if (human_mode)
90 outln("Sample rate: {:5d} Hz", sample_rate);
91 else
92 out("{} ", sample_rate);
93 break;
94 }
95 }
96 }
97 if (!human_mode)
98 outln();
99 } else if (command.equals_ignoring_ascii_case("set"sv) || command == "s") {
100 // Set variables
101 HashMap<AudioVariable, Variant<int, bool>> values_to_set;
102 for (size_t i = 0; i < command_arguments.size(); ++i) {
103 if (i == command_arguments.size() - 1) {
104 warnln("Error: value missing for last variable");
105 return 1;
106 }
107 auto& variable = command_arguments[i];
108 if (variable.is_one_of("v"sv, "volume"sv)) {
109 auto volume = command_arguments[++i].to_int();
110 if (!volume.has_value()) {
111 warnln("Error: {} is not an integer volume", command_arguments[i - 1]);
112 return 1;
113 }
114 if (volume.value() < 0 || volume.value() > 100) {
115 warnln("Error: {} is not between 0 and 100", command_arguments[i - 1]);
116 return 1;
117 }
118 values_to_set.set(AudioVariable::Volume, volume.value());
119 } else if (variable.is_one_of("m"sv, "mute"sv)) {
120 auto& mute_text = command_arguments[++i];
121 bool mute;
122 if (mute_text.equals_ignoring_ascii_case("true"sv) || mute_text == "1") {
123 mute = true;
124 } else if (mute_text.equals_ignoring_ascii_case("false"sv) || mute_text == "0") {
125 mute = false;
126 } else {
127 warnln("Error: {} is not one of {{0, 1, true, false}}", mute_text);
128 return 1;
129 }
130 values_to_set.set(AudioVariable::Mute, mute);
131 } else if (variable.is_one_of("r"sv, "samplerate"sv)) {
132 auto sample_rate = command_arguments[++i].to_int();
133 if (!sample_rate.has_value()) {
134 warnln("Error: {} is not an integer sample rate", command_arguments[i - 1]);
135 return 1;
136 }
137 values_to_set.set(AudioVariable::SampleRate, sample_rate.value());
138 } else {
139 warnln("Error: Unrecognized variable {}", command_arguments[i]);
140 return 1;
141 }
142 }
143
144 for (auto to_set : values_to_set) {
145 switch (to_set.key) {
146 case AudioVariable::Volume: {
147 int& volume = to_set.value.get<int>();
148 audio_client->set_main_mix_volume(static_cast<double>(volume) / 100);
149 break;
150 }
151 case AudioVariable::Mute: {
152 bool& mute = to_set.value.get<bool>();
153 audio_client->set_main_mix_muted(mute);
154 break;
155 }
156 case AudioVariable::SampleRate: {
157 int& sample_rate = to_set.value.get<int>();
158 audio_client->set_sample_rate(sample_rate);
159 break;
160 }
161 }
162 }
163 }
164
165 return 0;
166}