forked from SerenityOS/serenity
-
Notifications
You must be signed in to change notification settings - Fork 0
/
aplay.cpp
121 lines (104 loc) · 4.66 KB
/
aplay.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* Copyright (c) 2021-2022, kleines Filmröllchen <filmroellchen@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/Types.h>
#include <LibAudio/ConnectionToServer.h>
#include <LibAudio/Loader.h>
#include <LibCore/ArgsParser.h>
#include <LibCore/EventLoop.h>
#include <LibCore/System.h>
#include <LibFileSystem/FileSystem.h>
#include <LibMain/Main.h>
#include <math.h>
#include <stdio.h>
constexpr size_t LOAD_CHUNK_SIZE = 128 * KiB;
ErrorOr<int> serenity_main(Main::Arguments arguments)
{
TRY(Core::System::pledge("stdio rpath sendfd unix thread proc"));
StringView path {};
bool should_loop = false;
bool show_sample_progress = false;
Core::ArgsParser args_parser;
args_parser.add_positional_argument(path, "Path to audio file", "path");
args_parser.add_option(should_loop, "Loop playback", "loop", 'l');
args_parser.add_option(show_sample_progress, "Show playback progress in samples", "sample-progress", 's');
args_parser.parse(arguments);
// Note: We must determine the absolute path *before* beginning to raise the veil.
auto absolute_path = TRY(FileSystem::absolute_path(path));
TRY(Core::System::unveil("/tmp/session/%sid/portal/audio", "rw"));
TRY(Core::System::unveil(absolute_path, "r"sv));
TRY(Core::System::unveil(nullptr, nullptr));
Core::EventLoop loop;
auto audio_client = TRY(Audio::ConnectionToServer::try_create());
auto maybe_loader = Audio::Loader::create(path);
if (maybe_loader.is_error()) {
warnln("Failed to load audio file: {}", maybe_loader.error().description);
return 1;
}
auto loader = maybe_loader.release_value();
TRY(Core::System::pledge("stdio sendfd thread proc"));
outln("\033[34;1m Playing\033[0m: {}", path);
outln("\033[34;1m Format\033[0m: {} {} Hz, {}-bit, {}",
loader->format_name(),
loader->sample_rate(),
loader->bits_per_sample(),
loader->num_channels() == 1 ? "Mono" : "Stereo");
out("\033[34;1mProgress\033[0m: \033[s");
audio_client->set_self_sample_rate(loader->sample_rate());
auto print_playback_update = [&]() {
out("\033[u");
if (show_sample_progress) {
out("{}/{}", audio_client->total_played_samples(), loader->total_samples());
} else {
auto playing_seconds = static_cast<int>(floor(static_cast<double>(audio_client->total_played_samples()) / static_cast<double>(loader->sample_rate())));
auto playing_minutes = playing_seconds / 60;
auto playing_seconds_of_minute = playing_seconds % 60;
auto total_seconds = static_cast<int>(floor(static_cast<double>(loader->total_samples()) / static_cast<double>(loader->sample_rate())));
auto total_minutes = total_seconds / 60;
auto total_seconds_of_minute = total_seconds % 60;
auto remaining_seconds = total_seconds - playing_seconds;
auto remaining_minutes = remaining_seconds / 60;
auto remaining_seconds_of_minute = remaining_seconds % 60;
out("\033[1m{:02d}:{:02d}\033[0m [{}{:02d}:{:02d}] -- {:02d}:{:02d}",
playing_minutes, playing_seconds_of_minute,
remaining_seconds == 0 ? " " : "-",
remaining_minutes, remaining_seconds_of_minute,
total_minutes, total_seconds_of_minute);
}
fflush(stdout);
};
for (;;) {
auto samples = loader->get_more_samples(LOAD_CHUNK_SIZE);
if (!samples.is_error()) {
if (samples.value().size() > 0) {
print_playback_update();
// We can read and enqueue more samples
TRY(audio_client->async_enqueue(samples.release_value()));
} else if (should_loop) {
// We're done: now loop
auto result = loader->reset();
if (result.is_error()) {
outln();
outln("Error while resetting: {} (at {:x})", result.error().description, result.error().index);
}
} else if (samples.value().size() == 0 && audio_client->remaining_samples() == 0) {
// We're done and the server is done
break;
}
while (audio_client->remaining_samples() > LOAD_CHUNK_SIZE) {
// The server has enough data for now
print_playback_update();
usleep(1'000'000 / 10);
}
} else {
outln();
outln("Error: {} (at {:x})", samples.error().description, samples.error().index);
return 1;
}
}
outln();
return 0;
}