You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

387 lines
13KB

  1. /************************************************************************/
  2. /*! \brief Interactively Test RtAudio parameters.
  3. RtAudio is a command-line utility that allows users to enumerate
  4. installed devices, and to test input, output and duplex operation
  5. of RtAudio devices with various buffer and buffer-size
  6. configurations.
  7. Copyright (c) 2005 Robin Davies.
  8. Permission is hereby granted, free of charge, to any person
  9. obtaining a copy of this software and associated documentation files
  10. (the "Software"), to deal in the Software without restriction,
  11. including without limitation the rights to use, copy, modify, merge,
  12. publish, distribute, sublicense, and/or sell copies of the Software,
  13. and to permit persons to whom the Software is furnished to do so,
  14. subject to the following conditions:
  15. The above copyright notice and this permission notice shall be
  16. included in all copies or substantial portions of the Software.
  17. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  18. EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  19. MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
  20. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
  21. ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
  22. CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  23. WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  24. */
  25. /************************************************************************/
  26. #include "RtAudio.h"
  27. #include "FileWvOut.h"
  28. #include <cmath>
  29. #include <iostream>
  30. #include <stdopt.h>
  31. using namespace std;
  32. using namespace stdopt;
  33. #ifdef _WIN32
  34. // Correct windows.h standards violation.
  35. #undef min
  36. #undef max
  37. #endif
  38. #define DSOUND 1
  39. RtAudio::RtAudioApi rtApi = RtAudio::WINDOWS_DS;
  40. void DisplayHelp(std::ostream &os)
  41. {
  42. os
  43. << "rtaudiotest - Test rtaudio devices." << endl
  44. << endl
  45. << "Syntax:" << endl
  46. << " rtaudiotest [options]* enum" << endl
  47. << " - Display installed devices." << endl
  48. << " rtaudiotest [options]* inputtest <devicenum> [<filename>]" << endl
  49. << " - Capture audio to a .wav file." << endl
  50. << " rtaudiotest [options]* outputtest <devicenum>" << endl
  51. << " - Generate a test signal on the device.." << endl
  52. << " rtaudiotest [options]* duplextest <inputDevicenum> <outputdevicenum>" << endl
  53. << " - Echo input to output." << endl
  54. << "Options:" << endl
  55. << " -h -? Display this message." << endl
  56. << " -dsound Use DirectX drivers." << endl
  57. << " -asio Use ASIO drivers." << endl
  58. << " -buffers N Use N buffers." << endl
  59. << " -size N Use buffers of size N." << endl
  60. << " -srate N Use a sample-rate of N (defaults to 44100)." << endl
  61. << " -channels N Use N channels (defaults to 2)." << endl
  62. << " -seconds N Run the test for N seconds (default 5)." << endl
  63. << "Description: " << endl
  64. << " RtAudio is a command-line utility that allows users to enumerate " << endl
  65. << " installed devices, and to test input, output and duplex operation " << endl
  66. << " of RtAudio devices with various buffer and buffer-size " << endl
  67. << " configurations." << endl
  68. << "Examples:" << endl
  69. << " rtaudio -asio enum" << endl
  70. << " rtaudio -dsound -buffers 4 -size 128 -seconds 3 inputtest 0 test.wav" << endl
  71. ;
  72. }
  73. void EnumerateDevices(RtAudio::RtAudioApi api)
  74. {
  75. RtAudio rt(api);
  76. for (int i = 1; i <= rt.getDeviceCount(); ++i)
  77. {
  78. RtAudioDeviceInfo info = rt.getDeviceInfo(i);
  79. cout << "Device " << i << ": " << info.name << endl;
  80. }
  81. }
  82. struct TestConfiguration
  83. {
  84. long srate;
  85. int channels;
  86. int bufferSize;
  87. int buffers;
  88. int seconds;
  89. };
  90. bool DisplayStats(RtAudio::RtAudioApi api)
  91. {
  92. #ifdef __WINDOWS_DS__
  93. // Display latency results for Windows DSound drivers.
  94. if (api == RtAudio::WINDOWS_DS)
  95. {
  96. RtApiDs::RtDsStatistics s = RtApiDs::getDsStatistics();
  97. cout << " Latency: " << s.latency*1000.0 << "ms" << endl;
  98. if (s.inputFrameSize)
  99. {
  100. cout << " Read overruns: " << s.numberOfReadOverruns << endl;
  101. }
  102. if (s.outputFrameSize)
  103. {
  104. cout << " Write underruns: " << s.numberOfWriteUnderruns << endl;
  105. }
  106. if (s.inputFrameSize)
  107. {
  108. cout << " Read lead time in sample frames (device): " << s.readDeviceSafeLeadBytes/ s.inputFrameSize << endl;
  109. }
  110. if (s.outputFrameSize)
  111. {
  112. cout << " Write lead time in sample frames (device): " << s.writeDeviceSafeLeadBytes / s.outputFrameSize << endl;
  113. cout << " Write lead time in sample frames (buffer): " << s.writeDeviceBufferLeadBytes / s.outputFrameSize << endl;
  114. }
  115. }
  116. #endif
  117. return true;
  118. }
  119. void InputTest( RtAudio::RtAudioApi api,
  120. int inputDevice,
  121. const std::string &fileName,
  122. TestConfiguration &configuration )
  123. {
  124. RtAudio rt(api);
  125. int bufferSize = configuration.bufferSize;
  126. RtAudioDeviceInfo info = rt.getDeviceInfo(inputDevice);
  127. cout << "Reading from device " << inputDevice << " (" << info.name << ")\n";
  128. rt.openStream(0,0,inputDevice,configuration.channels, RTAUDIO_SINT16, configuration.srate,&bufferSize,configuration.buffers);
  129. if (bufferSize != configuration.bufferSize)
  130. {
  131. cout << "The buffer size was changed to " << bufferSize << " by the device." << endl;
  132. configuration.bufferSize = bufferSize;
  133. }
  134. int nTicks = (int)ceil((configuration.srate* configuration.seconds)*1.0/configuration.bufferSize);
  135. if (fileName.length() == 0)
  136. {
  137. // just run the stream.
  138. rt.startStream();
  139. for (int i = 0; i < nTicks; ++i)
  140. {
  141. rt.tickStream();
  142. }
  143. rt.stopStream();
  144. } else
  145. {
  146. if (configuration.seconds > 10) {
  147. throw CommandLineException("Capture of more than 10 seconds of data is not supported.");
  148. }
  149. std::vector<short> data;
  150. // we could be smarter, but the point here is to capture data without interfering with the stream.
  151. // File writes while ticking the stream is not cool.
  152. data.resize(nTicks*configuration.bufferSize*configuration.channels); // potentially very big. That's why we restrict capture to 10 seconds.
  153. short *pData = &data[0];
  154. rt.startStream();
  155. int i;
  156. for (i = 0; i < nTicks; ++i)
  157. {
  158. rt.tickStream();
  159. short *streamBuffer = (short*)rt.getStreamBuffer();
  160. for (int samples = 0; samples < configuration.bufferSize; ++samples)
  161. {
  162. for (int channel = 0; channel < configuration.channels; ++channel)
  163. {
  164. *pData ++ = *streamBuffer++;
  165. }
  166. }
  167. }
  168. rt.stopStream();
  169. remove(fileName.c_str());
  170. FileWvOut wvOut;
  171. wvOut.openFile( fileName.c_str(), configuration.channels, FileWrite::FILE_WAV );
  172. StkFrames frame(1,configuration.channels,false);
  173. pData = &data[0];
  174. for (i = 0; i < nTicks; ++i) {
  175. for (int samples = 0; samples < configuration.bufferSize; ++samples) {
  176. for (int channel = 0; channel < configuration.channels; ++channel) {
  177. frame[channel] = (float)( *pData++*( 1.0 / 32768.0 ) );
  178. }
  179. wvOut.tickFrame(frame);
  180. }
  181. }
  182. wvOut.closeFile();
  183. }
  184. rt.closeStream();
  185. if (DisplayStats(api)) {
  186. cout << "Test succeeded." << endl;
  187. }
  188. }
  189. void OutputTest( RtAudio::RtAudioApi api,
  190. int outputDevice,
  191. TestConfiguration &configuration )
  192. {
  193. RtAudio rt(api);
  194. int bufferSize = configuration.bufferSize;
  195. RtAudioDeviceInfo info = rt.getDeviceInfo(outputDevice);
  196. cout << "Writing to " << info.name << "...\n";
  197. rt.openStream(outputDevice,configuration.channels, 0,0, RTAUDIO_SINT16, configuration.srate,&bufferSize,configuration.buffers);
  198. if (bufferSize != configuration.bufferSize) {
  199. cout << "The buffer size was changed to " << bufferSize << " by the device." << endl;
  200. configuration.bufferSize = bufferSize;
  201. }
  202. rt.startStream();
  203. short *pBuffer = (short*)rt.getStreamBuffer();
  204. int nTicks = (int)ceil((configuration.srate* configuration.seconds)*1.0/configuration.bufferSize);
  205. double phase = 0;
  206. double deltaPhase = 880.0/configuration.srate;
  207. for (int i = 0; i < nTicks; ++i) {
  208. short *p = pBuffer;
  209. for (int samp = 0; samp < configuration.bufferSize; ++samp) {
  210. short val = (short)(sin(phase)*(32768/4)); // sin()*0.25 magnitude. Audible, but not damaging to ears or speakers.
  211. phase += deltaPhase;
  212. for (int chan = 0; chan < configuration.channels; ++chan) {
  213. *p++ = val;
  214. }
  215. }
  216. rt.tickStream();
  217. }
  218. rt.stopStream();
  219. rt.closeStream();
  220. if ( DisplayStats(api) ) {
  221. cout << "Test succeeded." << endl;
  222. }
  223. }
  224. void DuplexTest( RtAudio::RtAudioApi api,
  225. int inputDevice,
  226. int outputDevice,
  227. TestConfiguration &configuration )
  228. {
  229. RtAudio rt(api);
  230. int bufferSize = configuration.bufferSize;
  231. RtAudioDeviceInfo info = rt.getDeviceInfo(inputDevice);
  232. cout << "Reading from " << info.name << ", " << endl;
  233. info = rt.getDeviceInfo(outputDevice);
  234. cout << "Writing to " << info.name << "..." << endl;
  235. rt.openStream(outputDevice,configuration.channels, inputDevice,configuration.channels, RTAUDIO_SINT16, configuration.srate,&bufferSize,configuration.buffers);
  236. if (bufferSize != configuration.bufferSize)
  237. {
  238. cout << "The buffer size was changed to " << bufferSize << " by the device." << endl;
  239. configuration.bufferSize = bufferSize;
  240. }
  241. rt.startStream();
  242. short *pBuffer = (short*)rt.getStreamBuffer();
  243. int nTicks = (int)ceil((configuration.srate* configuration.seconds)*1.0/configuration.bufferSize);
  244. for (int i = 0; i < nTicks; ++i) {
  245. rt.tickStream();
  246. }
  247. rt.stopStream();
  248. rt.closeStream();
  249. if ( DisplayStats(api) ) {
  250. cout << "Test succeeded." << endl;
  251. }
  252. }
  253. int main(int argc, char **argv)
  254. {
  255. try
  256. {
  257. CommandLine commandLine;
  258. TestConfiguration configuration;
  259. bool displayHelp;
  260. bool useDsound;
  261. bool useAsio;
  262. commandLine.AddOption("h",&displayHelp);
  263. commandLine.AddOption("?",&displayHelp);
  264. commandLine.AddOption("dsound",&useDsound);
  265. commandLine.AddOption("asio",&useAsio);
  266. commandLine.AddOption("srate",&configuration.srate,44100L);
  267. commandLine.AddOption("channels",&configuration.channels,2);
  268. commandLine.AddOption("seconds",&configuration.seconds,5);
  269. commandLine.AddOption("buffers",&configuration.buffers,2);
  270. commandLine.AddOption("size",&configuration.bufferSize,128);
  271. commandLine.ProcessCommandLine(argc,argv);
  272. if (displayHelp || commandLine.GetArguments().size() == 0)
  273. {
  274. DisplayHelp(cout);
  275. return 0;
  276. }
  277. if (useDsound)
  278. {
  279. rtApi = RtAudio::WINDOWS_DS;
  280. } else if (useAsio)
  281. {
  282. rtApi = RtAudio::WINDOWS_ASIO;
  283. } else {
  284. throw CommandLineException("Please specify an API to use: '-dsound', or '-asio'");
  285. }
  286. std::string testName;
  287. commandLine.GetArgument(0,&testName);
  288. if (testName == "enum")
  289. {
  290. EnumerateDevices(rtApi);
  291. } else if (testName == "inputtest")
  292. {
  293. int inputDevice;
  294. std::string fileName;
  295. commandLine.GetArgument(1,&inputDevice);
  296. if (commandLine.GetArguments().size() >= 2)
  297. {
  298. commandLine.GetArgument(2,&fileName);
  299. }
  300. InputTest(rtApi,inputDevice,fileName,configuration);
  301. } else if (testName == "outputtest")
  302. {
  303. int inputDevice;
  304. commandLine.GetArgument(1,&inputDevice);
  305. OutputTest(rtApi,inputDevice,configuration);
  306. } else if (testName == "duplextest")
  307. {
  308. int inputDevice;
  309. int outputDevice;
  310. commandLine.GetArgument(1,&inputDevice);
  311. commandLine.GetArgument(2,&outputDevice);
  312. DuplexTest(rtApi,inputDevice,outputDevice,configuration);
  313. } else {
  314. throw CommandLineException("Not a valid test name.");
  315. }
  316. } catch (CommandLineException &e)
  317. {
  318. cerr << e.what() << endl << endl;
  319. cerr << "Run 'rtaudiotest -h' to see the commandline syntax." << endl;
  320. return 3;
  321. } catch (RtError &e)
  322. {
  323. cerr << e.getMessage() << endl;
  324. return 3;
  325. } catch (std::exception &e)
  326. {
  327. cerr << "Error: " << e.what() << endl;
  328. return 3;
  329. }
  330. return 0;
  331. }