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