replace ::cast_dynamic() with relevant ActionManager::get_*_action() calls
[ardour.git] / tools / sftest.cc
1 /* g++ -o sftest sftest.cc `pkg-config --cflags --libs sndfile` `pkg-config --cflags --libs glibmm-2.4` */
2
3 #include <vector>
4 #include <iostream>
5 #include <iomanip>
6 #include <sstream>
7 #include <cstdlib>
8 #include <cerrno>
9
10 #include <unistd.h>
11 #include <sndfile.h>
12 #include <stdint.h>
13 #include <string.h>
14 #include <getopt.h>
15 #include <fcntl.h>
16 #include <signal.h>
17 #include <float.h>
18
19 #include <glibmm/miscutils.h>
20
21 using namespace std;
22
23 SF_INFO format_info;
24 float* data = 0;
25 bool with_sync = false;
26 bool keep_writing = true;
27
28 void
29 signal_handler (int)
30 {
31         keep_writing = false;
32 }
33
34 int
35 write_one (SNDFILE* sf, uint32_t nframes)
36 {
37         if (sf_write_float (sf, (float*) data, nframes) != nframes) {
38                 return -1;
39         }
40
41         if (with_sync) {
42                 sf_write_sync (sf);
43         }
44
45         return 0;
46 }
47
48 void
49 usage ()
50 {
51         cout << "sftest [ -f HEADER-FORMAT ] [ -F DATA-FORMAT ] [ -r SAMPLERATE ] [ -n NFILES ] [ -b BLOCKSIZE ] [ -s ]";
52
53 #ifdef __APPLE__
54         cout << " [ -D ]";
55 #endif
56         cout << endl;
57
58         cout << "\tHEADER-FORMAT is one of:" << endl
59              << "\t\tWAV" << endl
60              << "\t\tCAF" << endl
61              << "\t\tW64" << endl;
62         cout << "\tDATA-FORMAT is one of:" << endl
63              << "\t\tFLOAT" << endl
64              << "\t\t32" << endl
65              << "\t\t24" << endl
66              << "\t\t16" << endl;
67 }
68
69 int
70 main (int argc, char* argv[])
71 {
72         vector<SNDFILE*> sndfiles;
73         uint32_t sample_size;
74         char optstring[] = "f:r:F:n:c:b:sd:qS:"
75 #ifdef __APPLE__
76                 "D"
77 #endif
78                 ;
79         int channels = 1;
80         int samplerate = 48000;
81         char const *suffix = ".wav";
82         char const *header_format = "wav";
83         char const *data_format = "float";
84         size_t filesize = 10 * 1048576;
85         uint32_t block_size = 64 * 1024;
86         uint32_t nfiles = 100;
87         string dirname = "/tmp";
88         bool quiet = false;
89 #ifdef __APPLE__
90         bool direct = false;
91 #endif
92         const struct option longopts[] = {
93                 { "header-format", 1, 0, 'f' },
94                 { "data-format", 1, 0, 'F' },
95                 { "rate", 1, 0, 'r' },
96                 { "nfiles", 1, 0, 'n' },
97                 { "blocksize", 1, 0, 'b' },
98                 { "channels", 1, 0, 'c' },
99                 { "sync", 0, 0, 's' },
100                 { "dirname", 1, 0, 'd' },
101                 { "quiet", 0, 0, 'q' },
102                 { "filesize", 1, 0, 'S' },
103 #ifdef __APPLE__
104                 { "direct", 0, 0, 'D' },
105 #endif
106                 { 0, 0, 0, 0 }
107         };
108
109         int option_index = 0;
110         int c = 0;
111
112         while (1) {
113                 if ((c = getopt_long (argc, argv, optstring, longopts, &option_index)) == -1) {
114                         break;
115                 }
116
117                 switch (c) {
118                 case 'f':
119                         header_format = optarg;
120                         break;
121
122                 case 'F':
123                         data_format = optarg;
124                         break;
125
126                 case 'r':
127                         samplerate = atoi (optarg);
128                         break;
129
130                 case 'n':
131                         nfiles = atoi (optarg);
132                         break;
133
134                 case 'c':
135                         channels = atoi (optarg);
136
137                 case 'b':
138                         block_size = atoi (optarg);
139                         break;
140                 case 's':
141                         with_sync = true;
142                         break;
143                 case 'S':
144                         filesize = atoi (optarg);
145                         break;
146 #ifdef __APPLE__
147                 case 'D':
148                         direct = true;
149                         break;
150 #endif
151                 case 'd':
152                         dirname = optarg;
153                         break;
154                 case 'q':
155                         quiet = true;
156                         break;
157                 default:
158                         usage ();
159                         return 0;
160                 }
161         }
162
163         /* setup file format */
164         memset (&format_info, 0, sizeof (format_info));
165
166         if (samplerate == 0 || nfiles == 0 || block_size == 0 || channels == 0) {
167                 usage ();
168                 return 1;
169         }
170
171         format_info.samplerate = samplerate;
172         format_info.channels = channels;
173
174         if (strcasecmp (header_format, "wav") == 0) {
175                 format_info.format |= SF_FORMAT_WAV;
176                 suffix = ".wav";
177         } else if (strcasecmp (header_format, "caf") == 0) {
178                 format_info.format |= SF_FORMAT_CAF;
179                 suffix = ".caf";
180         } else if (strcasecmp (header_format, "w64") == 0) {
181                 format_info.format |= SF_FORMAT_W64;
182                 suffix = ".w64";
183         } else {
184                 usage ();
185                 return 0;
186         }
187
188         if (strcasecmp (data_format, "float") == 0) {
189                 format_info.format |= SF_FORMAT_FLOAT;
190                 sample_size = sizeof (float);
191         } else if (strcasecmp (data_format, "32") == 0) {
192                 format_info.format |= SF_FORMAT_PCM_32;
193                 sample_size = 4;
194         } else if (strcasecmp (data_format, "24") == 0) {
195                 format_info.format |= SF_FORMAT_PCM_24;
196                 sample_size = 3;
197         } else if (strcasecmp (data_format, "16") == 0) {
198                 format_info.format |= SF_FORMAT_PCM_16;
199                 sample_size = 2;
200         } else {
201                 usage ();
202                 return 0;
203         }
204
205         string tmpdirname = Glib::build_filename (dirname, "sftest");
206         if (g_mkdir_with_parents (tmpdirname.c_str(), 0755)) {
207                 cerr << "Cannot create output directory\n";
208                 return 1;
209         }
210
211         for (uint32_t n = 0; n < nfiles; ++n) {
212                 SNDFILE* sf;
213                 string path;
214                 stringstream ss;
215
216                 ss << "sf-";
217                 ss << n;
218                 ss << suffix;
219
220                 path = Glib::build_filename (tmpdirname, ss.str());
221
222                 int flags = O_RDWR|O_CREAT|O_TRUNC;
223                 int fd = open (path.c_str(), flags, 0644);
224
225                 if (fd < 0) {
226                         cerr << "Could not open file #" << n << " @ " << path << " (" << strerror (errno) << ")" << endl;
227                         return 1;
228                 }
229
230 #ifdef __APPLE__
231                 if (direct) {
232                         /* Apple man pages say only that it returns "a value other than -1 on success",
233                            which probably means zero, but you just can't be too careful with
234                            those guys.
235                         */
236                         if (fcntl (fd, F_NOCACHE, 1) == -1) {
237                                 cerr << "Cannot set F_NOCACHE on file # " << n << endl;
238                         }
239                 }
240 #endif
241                 if ((sf = sf_open_fd (fd, SFM_RDWR, &format_info, true)) == 0) {
242                         cerr << "Could not open SNDFILE #" << n << " @ " << path << " (" << sf_strerror (0) << ")" << endl;
243                         return 1;
244                 }
245
246                 sndfiles.push_back (sf);
247         }
248
249         if (!quiet) {
250                 cout << nfiles << " files are in " << tmpdirname;
251 #ifdef __APPLE__
252                 cout << " all used " << (direct ? "without" : "with") << " OS buffer cache";
253 #endif
254                 cout << endl;
255                 cout << "Format is " << suffix << ' ' << channels << " channel" << (channels > 1 ? "s" : "") << " written in chunks of " << block_size << " samples, synced ? " << (with_sync ? "yes" : "no") << endl;
256         }
257
258         data = new float[block_size*channels];
259         uint64_t written = 0;
260
261         signal (SIGINT, signal_handler);
262         signal (SIGSTOP, signal_handler);
263
264
265         double max_bandwidth = 0;
266         double min_bandwidth = DBL_MAX;
267
268         while (keep_writing && written < filesize) {
269                 gint64 before;
270                 before = g_get_monotonic_time();
271                 for (vector<SNDFILE*>::iterator s = sndfiles.begin(); s != sndfiles.end(); ++s) {
272                         if (write_one (*s, block_size)) {
273                                 cerr << "Write failed for file #" << distance (sndfiles.begin(), s) << endl;
274                                 return 1;
275                         }
276                 }
277                 written += block_size;
278                 gint64 elapsed = g_get_monotonic_time() - before;
279                 double bandwidth = (sndfiles.size() * block_size * channels * sample_size) / (elapsed/1000000.0);
280                 double data_minutes = written / (double) (60.0 * 48000.0);
281                 const double data_rate = sndfiles.size() * channels * sample_size * samplerate;
282                 stringstream ds;
283                 ds << setprecision (1) << data_minutes;
284
285                 max_bandwidth = max (max_bandwidth, bandwidth);
286                 min_bandwidth = min (min_bandwidth, bandwidth);
287
288                 if (!quiet) {
289                         cout << "BW @ " << written << " samples (" << ds.str() << " minutes) = " << (bandwidth/1048576.0) <<  " MB/sec " << bandwidth / data_rate << " x faster than necessary " << endl;
290                 }
291         }
292
293         cout << "Max bandwidth = " << max_bandwidth / 1048576.0 << " MB/sec" << endl;
294         cout << "Min bandwidth = " << min_bandwidth / 1048576.0 << " MB/sec" << endl;
295
296         if (!quiet) {
297                 cout << "Closing files ...\n";
298                 for (vector<SNDFILE*>::iterator s = sndfiles.begin(); s != sndfiles.end(); ++s) {
299                 sf_close (*s);
300                 }
301                 cout << "Done.\n";
302         }
303
304         return 0;
305 }
306
307