URY playd
C++ minimalist audio player
main.cpp
Go to the documentation of this file.
1 // This file is part of playd.
2 // playd is licensed under the MIT licence: see LICENSE.txt.
3 
10 #include <algorithm>
11 #include <cstdint>
12 #include <iostream>
13 #include <tuple>
14 
15 #include "io.hpp"
16 #include "response.hpp"
17 #include "player.hpp"
18 #include "messages.h"
19 
20 #ifdef WITH_MP3
21 #include "audio/sources/mp3.hpp"
22 #endif // WITH_MP3
23 #ifdef WITH_SNDFILE
25 #endif // WITH_SNDFILE
26 
28 static const std::string DEFAULT_HOST = "0.0.0.0";
29 
31 static const std::string DEFAULT_PORT = "1350";
32 
34 static const std::map<std::string, Player::SourceFn> SOURCES = {
35 #ifdef WITH_MP3
36  {"mp3", &std::make_unique<Mp3AudioSource, const std::string &>},
37 #endif // WITH_MP3
38 
39 #ifdef WITH_SNDFILE
40  {"flac", &std::make_unique<SndfileAudioSource, const std::string &>},
41  {"ogg", &std::make_unique<SndfileAudioSource, const std::string &>},
42  {"wav", &std::make_unique<SndfileAudioSource, const std::string &>},
43 #endif // WITH_SNDFILE
44 };
45 
52 std::vector<std::string> MakeArgVector(int argc, char *argv[])
53 {
54  std::vector<std::string> args;
55  for (int i = 0; i < argc; i++) args.emplace_back(argv[i]);
56  return args;
57 }
58 
64 int GetDeviceID(const std::vector<std::string> &args)
65 {
66  // Did the user provide an ID at all?
67  if (args.size() < 2) return -1;
68 
69  // Only accept valid numbers (stoi will throw for invalid ones).
70  int id;
71  try {
72  id = std::stoi(args.at(1));
73  } catch (...) {
74  // Only std::{invalid_argument,out_of_range} are thrown here.
75  return -1;
76  }
77 
78  // Only allow valid, outputtable devices; reject input-only devices.
79  if (!SdlAudioSink::IsOutputDevice(id)) return -1;
80 
81  return id;
82 }
83 
88 void ExitWithUsage(const std::string &progname)
89 {
90  std::cerr << "usage: " << progname << " ID [HOST] [PORT]\n";
91  std::cerr << "where ID is one of the following numbers:\n";
92 
93  // Show the user the valid device IDs they can use.
94  auto device_list = SdlAudioSink::GetDevicesInfo();
95  for (const auto &device : device_list) {
96  std::cerr << "\t" << device.first << ": " << device.second
97  << "\n";
98  }
99 
100  std::cerr << "default HOST: " << DEFAULT_HOST << "\n";
101  std::cerr << "default PORT: " << DEFAULT_PORT << "\n";
102 
103  exit(EXIT_FAILURE);
104 }
105 
112 std::pair<std::string, std::string> GetHostAndPort(
113  const std::vector<std::string> &args)
114 {
115  auto size = args.size();
116  return std::make_pair(size > 2 ? args.at(2) : DEFAULT_HOST,
117  size > 3 ? args.at(3) : DEFAULT_PORT);
118 }
119 
126 void ExitWithNetError(const std::string &host, const std::string &port,
127  const std::string &msg)
128 {
129  std::cerr << "Network error: " << msg << "\n";
130  std::cerr << "Is " << host << ":" << port << " available?\n";
131  exit(EXIT_FAILURE);
132 }
133 
138 void ExitWithError(const std::string &msg)
139 {
140  std::cerr << "Unhandled exception in main loop: " << msg << std::endl;
141  exit(EXIT_FAILURE);
142 }
143 
150 int main(int argc, char *argv[])
151 {
152 #ifndef _MSC_VER
153  // If we don't ignore SIGPIPE, certain classes of connection droppage
154  // will crash our program with it.
155  // TODO(CaptainHayashi): a more rigorous ifndef here.
156  signal(SIGPIPE, SIG_IGN);
157 #endif
158 
159  // SDL requires some cleanup and teardown.
160  // This call needs to happen before GetDeviceID, otherwise no device
161  // IDs will be recognised. (This is why it's here, and not in
162  // SetupAudioSystem.)
165 
166 #ifdef WITH_MP3
167  // mpg123 insists on us running its init and exit functions, too.
168  mpg123_init();
169  atexit(mpg123_exit);
170 #endif // WITH_MP3
171 
172  auto args = MakeArgVector(argc, argv);
173 
174  auto device_id = GetDeviceID(args);
175  if (device_id < 0) ExitWithUsage(args.at(0));
176 
177  Player player(device_id,
178  &std::make_unique<SdlAudioSink, const AudioSource &, int>,
179  SOURCES);
180 
181  // Set up the IO now (to avoid a circular dependency).
182  // Make sure the player broadcasts its responses back to the IoCore.
183  IoCore io(player);
184  player.SetIo(io);
185 
186  // Now, actually run the IO loop.
187  std::string host;
188  std::string port;
189  std::tie(host, port) = GetHostAndPort(args);
190  try {
191  io.Run(host, port);
192  } catch (NetError &e) {
193  ExitWithNetError(host, port, e.Message());
194  } catch (Error &e) {
195  ExitWithError(e.Message());
196  }
197 
198  return EXIT_SUCCESS;
199 }
static void CleanupLibrary()
Cleans up the AudioSink&#39;s libraries, if not cleaned up already.
Definition: audio_sink.cpp:109
A playd exception.
Definition: errors.hpp:19
Constant human-readable messages used within playd.
Declaration of the I/O classes used in playd.
Declaration of the Mp3AudioSource class.
const std::string & Message() const
The human-readable message for this error.
Definition: errors.cpp:16
void ExitWithNetError(const std::string &host, const std::string &port, const std::string &msg)
Exits with an error message for a network error.
Definition: main.cpp:126
std::pair< std::string, std::string > GetHostAndPort(const std::vector< std::string > &args)
Gets the host and port from the program arguments.
Definition: main.cpp:112
static bool IsOutputDevice(int id)
Can a sound device output sound?
Definition: audio_sink.cpp:255
void Run(const std::string &host, const std::string &port)
Runs the reactor.
Definition: io.cpp:174
static void InitLibrary()
Initialises the AudioSink&#39;s libraries, if not initialised already.
Definition: audio_sink.cpp:101
Declaration of the Player class, and associated types.
int GetDeviceID(const std::vector< std::string > &args)
Tries to get the output device ID from program arguments.
Definition: main.cpp:64
Declaration of classes pertaining to responses to the client.
A network error.
Definition: errors.hpp:105
void ExitWithError(const std::string &msg)
Exits with an error message for an unhandled exception.
Definition: main.cpp:138
std::vector< std::string > MakeArgVector(int argc, char *argv[])
Creates a vector of strings from a C-style argument vector.
Definition: main.cpp:52
void ExitWithUsage(const std::string &progname)
Reports usage information and exits.
Definition: main.cpp:88
Declaration of the SndfileAudioSource class.
int main(int argc, char *argv[])
The main entry point.
Definition: main.cpp:150
static std::vector< std::pair< int, std::string > > GetDevicesInfo()
Gets the number and name of each output device entry in the AudioSystem.
Definition: audio_sink.cpp:241
A Player contains a loaded audio file and a command API for manipulating it.
Definition: player.hpp:31
The IO core, which services input, routes responses, and executes the Player update routine periodica...
Definition: io.hpp:39