aplay.cpp 4.7 KB

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