HulaLoop
Simple cross-platform audio loopback and recording.
CLICommon.h
1 #ifndef HULA_CLI_COMMON_H
2 #define HULA_CLI_COMMON_H
3 
4 // Convert numeric defines to strings
5 // https://stackoverflow.com/questions/5459868/concatenate-int-to-string-using-c-preprocessor
6 #define STR_HELPER(x) #x
7 #define STR(x) STR_HELPER(x)
8 
9 #include <iomanip>
10 #include <iostream>
11 #include <string>
12 #include <vector>
13 
14 #include <hlaudio/hlaudio.h>
15 #include <hlcontrol/hlcontrol.h>
16 
17 #include <QCoreApplication>
18 #include <QTextStream>
19 
20 #include <HulaVersion.h>
21 
22 using namespace hula;
23 
24 #define HL_CLI_NAME "hulaloop-cli"
25 #define HL_CLI_ASCII_HEADER "\n" \
26  " _ _ _ _\n" \
27  " | | | | | | | |\n" \
28  " | |__| |_ _| | __ _| | ___ ___ _ __\n" \
29  " | __ | | | | |/ _` | | / _ \\ / _ \\| '_ \\\n" \
30  " | | | | |_| | | (_| | |___| (_) | (_) | |_) |\n" \
31  " |_| |_|\\__,_|_|\\__,_|______\\___/ \\___/| .__/\n" \
32  " | |\n" \
33  " |_|\n" \
34  "----------------------------------------------------\n\n" \
35 
36 
39 #define QCOL(stream, width, text) \
40  stream.setFieldWidth(width); \
41  stream << text; \
42  stream.setFieldWidth(0);
43 
47 namespace hula
48 {
49  class CLI {
50  Q_DECLARE_TR_FUNCTIONS(CLI)
51  };
52 
56  typedef struct HulaImmediateArgs
57  {
61  bool startRecord = false;
62 
66  bool exit = false;
67 
74  std::string delay = "0";
75 
85  std::string duration = STR(HL_INFINITE_RECORD);
86 
93  std::string outputFilePath;
94 
101  std::string inputDevice;
102 
109  std::string outputDevice;
111 
117  inline void printDeviceList(Transport *t)
118  {
119  // Strip one padding char for the extra space
120  int colW = 32 - 1;
121 
122  QTextStream cout(stdout);
123  cout.setAutoDetectUnicode(true);
124  cout.setRealNumberNotation(QTextStream::FixedNotation);
125  cout.setRealNumberPrecision(2);
126  cout.setPadChar('.');
127  cout.setFieldAlignment(QTextStream::AlignLeft);
128 
130 
131  std::vector<Device *> devices;
132  if (settings->getShowRecordDevices())
133  {
134  try
135  {
136  devices = t->getController()->getDevices((DeviceType)(PLAYBACK | LOOPBACK | RECORD));
137  }
138  catch(const AudioException &ae)
139  {
141 
142  fprintf(stderr, "%s%s\n", HL_ERROR_PREFIX, ce.getErrorMessage().c_str());
143  exit(1);
144  }
145  }
146  else
147  {
148  try
149  {
150  devices = t->getController()->getDevices((DeviceType)(PLAYBACK | LOOPBACK));
151  }
152  catch(const AudioException &ae)
153  {
155 
156  fprintf(stderr, "%s%s\n", HL_ERROR_PREFIX, ce.getErrorMessage().c_str());
157  exit(1);
158  }
159  }
160 
161  printf("\n");
162  for (size_t i = 0; i < devices.size(); i++)
163  {
164  QCOL(cout, colW, CLI::tr("Device #%1").arg(i).append(" "));
165  cout << " " << devices[i]->getName().c_str() << endl;
166 
167  QCOL(cout, colW, CLI::tr("Record", "device capability").append(" "));
168  cout << " " << ((devices[i]->getType() & DeviceType::RECORD) ? CLI::tr("Yes") : CLI::tr("No")) << endl;
169 
170  QCOL(cout, colW, CLI::tr("Loopback", "device capability").append(" "));
171  cout << " " << ((devices[i]->getType() & DeviceType::LOOPBACK) ? CLI::tr("Yes") : CLI::tr("No")) << endl;
172 
173  QCOL(cout, colW, CLI::tr("Output", "device capability").append(" "));
174  cout << " " << ((devices[i]->getType() & DeviceType::PLAYBACK) ? CLI::tr("Yes") : CLI::tr("No")) << endl;
175 
176  cout << endl;
177  }
178 
179  Device::deleteDevices(devices);
180  }
181 
195  inline Device *findDevice(Transport *t, const std::string &name, DeviceType type)
196  {
197  Device *device = nullptr;
198  std::vector<Device *> devices;
199 
200  // TODO: Change this when proper device indexing is possible
201  // devices = t->getController()->getDevices(type);
202  try
203  {
204  devices = t->getController()->getDevices((DeviceType)(DeviceType::LOOPBACK | DeviceType::RECORD | DeviceType::PLAYBACK));
205  }
206  catch(const AudioException &ae)
207  {
209 
210  fprintf(stderr, "%s%s\n", HL_ERROR_PREFIX, ce.getErrorMessage().c_str());
211  exit(1);
212  }
213 
214  // Check if we got a numeric id
215  // Since we all do this differently,
216  // we'll just use the relative ordering to pick
217  // ids
218  int id = -1;
219  try
220  {
221  id = std::stoi(name, nullptr);
222  }
223  catch (std::invalid_argument &e)
224  {
225  // Wasn't an id
226  (void)e;
227  }
228 
229  for (size_t i = 0; i < devices.size(); i++)
230  {
231  // Check id and name
232  if (i == id || devices[i]->getName() == name)
233  {
234  device = devices[i];
235  break;
236  }
237  }
238 
239  // Make a copy so that we can delete all
240  if (device != nullptr)
241  {
242  device = new Device(*device);
243  }
244 
245  Device::deleteDevices(devices);
246 
247  if (device == nullptr)
248  {
249  //: The argument in this text is the name or id of the device that the user searched for
250  fprintf(stderr, "\n%s\n", qPrintable(CLI::tr("Could not find device matching: %1").arg(name.c_str())));
251  }
252 
253  return device;
254  }
255 
259  inline void printSettings(const HulaImmediateArgs &args)
260  {
261  int colW = 32;
262 
263  QTextStream cout(stdout);
264  cout.setAutoDetectUnicode(true);
265  cout.setRealNumberNotation(QTextStream::FixedNotation);
266  cout.setRealNumberPrecision(2);
267  cout.setFieldAlignment(QTextStream::AlignLeft);
268 
270 
271  cout << endl;
272 
273  QCOL(cout, colW, CLI::tr("Output file:", "setting"));
274  cout << QString::fromStdString(args.outputFilePath) << endl;
275 
276  // Using SI typeset for the units. Shouldn't change with localization.
277  // https://english.stackexchange.com/questions/2794/punctuation-with-units
278  QCOL(cout, colW, CLI::tr("Delay:"));
279  cout << stod(args.delay) << " " << CLI::tr("s", "abbreviation for seconds") << endl;
280 
281  QCOL(cout, colW, CLI::tr("Duration:"));
282  cout << stod(args.duration) << " " << CLI::tr("s", "abbreviation for seconds") << endl;
283 
284  QCOL(cout, colW, CLI::tr("Sample rate:"));
285  cout << settings->getSampleRate() << " " << CLI::tr("Hz", "unit") << endl;
286 
287  // TODO: Change this once MP3 gets in here
288  QCOL(cout, colW, CLI::tr("Encoding:"));
289  cout << "WAV" << endl;
290 
291  QCOL(cout, colW, CLI::tr("Input device:"));
292  cout << QString::fromStdString(args.inputDevice) << endl;
293 
294  QCOL(cout, colW, CLI::tr("Output device:"));
295  cout << QString::fromStdString(args.outputDevice) << endl;
296  }
297 }
298 
299 #endif // END HULA_CLI_COMMON_H
<hlcontrol/hlcontrol.h>
Device * findDevice(Transport *t, const std::string &name, DeviceType type)
Utility function for searching the list of devices.
Definition: CLICommon.h:195
Extra class for managing the state of the application and all audio related processes.
Definition: Transport.h:33
#define HL_ERROR_PREFIX
Prefix printed before every console message to stderr.
Definition: HulaAudioError.h:39
std::vector< Device * > getDevices(DeviceType type) const
Fetch a list of devices for the given DeviceType.
Definition: Controller.cpp:131
std::string inputDevice
Store the input device name parsed from the CLI flags.
Definition: CLICommon.h:101
<hlaudio/hlaudio.h>
Definition: CLICommon.h:49
static void deleteDevices(std::vector< Device *> devices)
Delete all the device pointers inside the vector.
Definition: Device.cpp:54
std::string outputFilePath
Store the output file path parsed from the CLI flags.
Definition: CLICommon.h:93
static HulaSettings * getInstance()
Retreive the singular instance of HulaSettings or construct a new one if none exists.
Definition: HulaSettings.cpp:31
Controller * getController() const
Get the controller instance.
Definition: Transport.cpp:221
std::string outputDevice
Store the output device name parsed from the CLI flags.
Definition: CLICommon.h:109
void printDeviceList(Transport *t)
Utility CLI function to print the device list to the console.
Definition: CLICommon.h:117
void printSettings(const HulaImmediateArgs &args)
Utility function for printing the current application settings.
Definition: CLICommon.h:259
int getSampleRate()
Get the number of channels for the current global device configuration.
Definition: HulaAudioSettings.cpp:71
Exception class for the control module and higher.
Definition: HulaAudioError.h:131
Wrapper for OS specific device information.
Definition: Device.h:55
Wrapper around translation functions for Qt.
Definition: Controller.h:10
Singleton class containing all settings for the application.
Definition: HulaSettings.h:23
bool getShowRecordDevices()
Getters.
Definition: HulaAudioSettings.cpp:45
DeviceType
Denotes type of Device.
Definition: Device.h:18
int getErrorCode() const
This method returns the error code.
Definition: HulaAudioError.h:162
Exception class for the control module and higher.
Definition: HulaControlError.h:40
Args parsed from CLI flags.
Definition: CLICommon.h:56
struct hula::HulaImmediateArgs HulaImmediateArgs
Args parsed from CLI flags.
std::string delay
Store the delay as a string to be converted by InteractiveCLI.
Definition: CLICommon.h:74
std::string duration
Store the record duration as a string to be converted by InteractiveCLI.
Definition: CLICommon.h:85