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